Android的ListView

时间:2023-03-09 07:09:30
Android的ListView

ListView

ListView 是一个控件,一个在垂直滚动的列表中显示条目的一个控件,这些条目的内容来自于一个ListAdapter。

一个简单的例子

布局文件里新增ListView

<ListView
    android:id="@+id/lv_simple"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</ListView>

适配器继承自BaseAdapter

package com.example.listviewdemo;
// ...import;略
public class MyAdapter extends BaseAdapter {

    private Map<Integer, Integer> map = new HashMap<>();

    //getCount方法:告诉listview要显示多少个条目
    @Override
    public int getCount() {
        return 100;
    }
    //根据postion获取listview上条目对应的Bean数据,该方法不影响数据的展示,可以先不实现
    @Override
    public Object getItem(int position) {
        return null;
    }

    //getItemId:用来获取条目postion行的id,该方法不影响数据的展示,可以先不实现
    @Override
    public long getItemId(int position) {
        return 0;
    }

     //getview:告诉listview条目上显示的内容;返回一个View对象作为条目上的内容展示,该方法返回什么样的view,Listview的条目上就显示什么样的view。必须实现
    //屏幕上每显示一个条目getview方法就会被调用一次;convertView:曾经使用过的view对象,可以被重复使用,使用前要判断。
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        TextView view = null;

        if(convertView != null){//判断converView是否为空,不为空重新使用
            view = (TextView) convertView;
        }else{
            view = new TextView(mContext);//不能复用old view,就创建一个textView对象
        }
        view.setText("postion:"+position);//设置textview的内容
        view.setTextSize(20);
        map.put(view.hashCode(), 1);
        // 从打印结果来看,就new了22个TextView而已
        Log.d("ListView", "创建了"+map.size()+"个TextView对象");
        return view;
    }
}

MainActivity

package com.example.listviewdemo;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

    private Context mContext;
    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        // 1. 找到ListView
        listView = (ListView) findViewById(R.id.lv_simple);
        // 2. 创建适配器Adapter
        MyAdapter adapter = new MyAdapter();
        // 3. listView设置adapter
        listView.setAdapter(adapter);

    }
}

这里需要一个适配器来显示条目,本例使用的适配器继承BaseAdapter,需要实现上面的四个方法。其中getCount()设置显示的条目数,必须设置不然默认为0不会显示。getView()也必须实现,用来告诉listview显示的具体内容。

ListView的优化

上例我们使用了convertView。adapter中getview方法会传进来一个convertView,convertView是指曾经使用过的view对象,可以被重复使用,但是在使用前需要判断是否为空,不为空直接复用,并作为getview方法的返回对象。

使用ArrayAdapter\

稍微改变一下代码就行

这里需要新建一个layout文件来传入ArrayAdapter,这个布局文件决定了每一个子项的布局。为实现和上面一样的功能。只是简单的放置一个TextView。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

让MyAdapter继承ArrayAdapter\

 public int getCount() {
    return mObjects.size();
    }
public class MyAdapter extends ArrayAdapter<String> {
    private Map<Integer, Integer> map = new HashMap<>();
    private int resourseId;

    public MyAdapter(@NonNull Context context, @LayoutRes int resource) {
        super(context, resource);
        resourseId = resource;
    }

    @Override
    public int getCount() {
        return 100;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        View view;

        if(convertView != null){//判断converView是否为空,不为空重新使用
            view = convertView;
        }else{
            view = LayoutInflater.from(getContext()).inflate(resourseId, parent, false);
        }
        TextView textView = (TextView) view.findViewById(R.id.tv_item);
        textView.setText("postion:"+position);//设置textview的内容
        textView.setTextSize(20);
        map.put(view.hashCode(), 1);
        // 从打印结果来看,就new了22个TextView而已
        Log.d("ListView", "创建了"+map.size()+"个TextView对象");
        return view;
    }
}

小例子

三个listview均分布局。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.tigergame.MainActivity">

   <ListView
       android:id="@+id/lv_1"
       android:layout_weight="1"
       android:layout_width="0dp"
       android:layout_height="match_parent">
   </ListView>

    <ListView
        android:id="@+id/lv_2"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="match_parent">
    </ListView>

    <ListView
        android:id="@+id/lv_3"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="match_parent">
    </ListView>

</LinearLayout>

适配器,同样使用了ArrayAdapter

package com.example.tigergame;

import android.content.Context;
import android.graphics.Color;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import com.example.tigergame.bean.Fruit;

import java.util.List;

public class MyAdapter extends ArrayAdapter<Fruit> {

    private int resourceId;
    // 将布局文件给resourceId方便下面使用
    public MyAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<Fruit> objects) {
        super(context, resource, objects);
        resourceId = resource;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Fruit fruit = getItem(position); // 获得当前位置的实例
        View view;
        if (convertView != null) {
            view = convertView;
        } else {
            view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        }
        TextView textView = (TextView) view.findViewById(R.id.tv);

        if ("香蕉".equals(fruit.name)) {
            textView.setText(fruit.name);
            textView.setTextSize(20);
            textView.setTextColor(Color.BLUE);
        } else if ("橘子".equals(fruit.name)) {
            textView.setText(fruit.name);
            textView.setTextSize(20);
            textView.setTextColor(Color.RED);
        } else if ("苹果".equals(fruit.name)) {
            textView.setText(fruit.name);
            textView.setTextSize(20);
            textView.setTextColor(Color.RED);
        } else if ("葡萄".equals(fruit.name)) {
            textView.setText(fruit.name);
            textView.setTextSize(20);
            textView.setTextColor(Color.GREEN);
        } else if ("梨".equals(fruit.name)) {
            textView.setText(fruit.name);
            textView.setTextSize(20);
            textView.setTextColor(Color.BLUE);
        } else if ("芒果".equals(fruit.name)) {
            textView.setText(fruit.name);
            textView.setTextSize(20);
            textView.setTextColor(Color.BLACK);
        } else if ("草莓".equals(fruit.name)) {
            textView.setText(fruit.name);
            textView.setTextSize(20);
            textView.setTextColor(Color.RED);
        } else if ("樱桃".equals(fruit.name)) {
            textView.setText(fruit.name);
            textView.setTextSize(20);
            textView.setTextColor(Color.parseColor("#ff00ff"));
        }

        return view;
    }
}

上面使用到了Fruit的bean类

package com.example.tigergame.bean;

public class Fruit {

    public String name;

    public Fruit(String name) {
        this.name = name;
    }
}

ArrayAdapter需要传入一个子项布局文件,只是显示水果名称而已。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

MainActivity

package com.example.tigergame;

import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

import com.example.tigergame.bean.Fruit;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private Context mContext;

    private ListView listView1;
    private ListView listView2;
    private ListView listView3;

    private List<Fruit> fruitList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        // 初始化水果名称
        initFruits();

        listView1 = (ListView) findViewById(R.id.lv_1);
        listView2 = (ListView) findViewById(R.id.lv_2);
        listView3 = (ListView) findViewById(R.id.lv_3);

        // 适配器,传入水果列表,以列表的长度来决定getCount()显示的条目数
        MyAdapter adapter = new MyAdapter(mContext, R.layout.list_item, fruitList);
        listView1.setAdapter(adapter);
        listView2.setAdapter(adapter);
        listView3.setAdapter(adapter);
    }

    private void initFruits() {
        for (int i = 0; i < 10; i++) {
            Fruit apple = new Fruit("苹果");
            fruitList.add(apple);

            Fruit grape = new Fruit("葡萄");
            fruitList.add(grape);

            Fruit pear = new Fruit("梨");
            fruitList.add(pear);

            Fruit mango = new Fruit("芒果");
            fruitList.add(mango);

            Fruit strawberry = new Fruit("草莓");
            fruitList.add(strawberry);

            Fruit cherry = new Fruit("樱桃");
            fruitList.add(cherry);

            Fruit banana = new Fruit("香蕉");
            fruitList.add(banana);

            Fruit orange = new Fruit("橘子");
            fruitList.add(orange);
        }
    }
}

例子完成,看下长啥样。

Android的ListView

listView与MVC

listview也是符合MVC思想的,和javaweb对比下。

javaweb mvc
m....mode....javabean
v....view....jsp
c....control...servlet

listview mvc
m....mode....Bean
v....view....listview
c....control...adapter

ListView的显示原理

  1. 要考虑listview显示的条目数 getcount
  2. 考虑listview每个条目显示的内容 getview
  3. 考虑每个item的高度,因为屏幕的多样化
  4. 还要考虑listview的滑动,监听一个旧的条目消失,一个新的条目显示。

注意:ListView的宽高能match_parent的就match_parent。

如果使用的wrap_content。比如我们的子项数为3,getView()显示时不止调用3次。可能十几次。因为使用wrap_content并不能确切知道listview占据的布局空间,不能计算出三个条目能不能放到这个布局空间,也不能计算出能放多少个条目。它就会去尝试,尝试了好几次之后,确认能放下,计算出能放下多少个条目之后,才停止尝试。

而使用match _parent,直接就知道屏幕的宽高,除以一个条目的高度,就能知道布局空间能放下多少个条目。一次性计算出来,一次性显示出来,所以只会调用三次getView()。

上面说的我也很晕,记住ListView控件的宽高尽量match_parent就好。

inflate(int resource, ViewGroup root, boolean attachToRoot)

在这之前,需要了解:我们在开发的过程中给控件所指定的layout_width和layout_height到底是什么意思?该属性的表示一个控件在容器中的大小,就是说这个控件必须在容器中,这个属性才有意义,否则无意义。这就意味着如果我直接将Linearlayout加载进来而不给它指定一个父布局,则inflate布局的根节点的layout_width和layout_height属性将会失效(因为这个时候linearlayout将不处于任何容器中,那么它的根节点的宽高自然会失效)。如果我想让来布局的根节点有效,又不想加载到父布局,就可以设置root不为null,而attachToRoot为false。

第一个参数就是要加载的布局id,第二个参数是指给该布局的指定一个父布局,如果不需要就直接传null。那么这第三个参数attachToRoot又是什么意思呢?

1. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义。第三个参数不填,填写前两个就行。因为没有父布局作为容器,布局文件的layout属性失效。

2. 如果root不为null,attachToRoot设为true,则会给加载的布局文件的指定一个父布局,即root。会自动将第一个参数所指定的View添加到第二个参数所指定的View中。不用再addView

3. 如果root不为null,attachToRoot设为false,表示不将第一个参数所指定的View添加到root中。当该view被添加到父view当中时,因为这个控件在一个布局容器之中(root),所以这些layout属性会生效。这个时候我需要手动的将inflate加载进来的view添加到容器中,因为inflate的最后一个参数false表示不将view添加到父布局。

4. 在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true。

为什么Activity布局的根节点的宽高属性会生效?

布局文件activity_main.xml假设使用了LineraLayout。在对应的MainActivity中一开始调用setContentView(R.layout.activity_main)。由于我们的页面中有一个*View叫做DecorView,DecorView中包含一个竖直方向的LinearLayout,LinearLayout由两部分组成,第一部分是标题栏,第二部分是内容栏,内容栏是一个FrameLayout,我们在Activity中调用setContentView就是将View添加到这个FrameLayout中,所以activity_main.xml是有父布局的,因此设置layout_width和layout_height才有效 。

Android的ListView

再来看上面的例子,MyAdapter中如果view = LayoutInflater.from(getContext()).inflate(resourceId, parent, true);则会抛出出异常。指定为true的话,源码中会调用root.addView(temp, params);而此时的root是我们的ListView,ListView为AdapterView的子类。看下AdapterView的源码。调用addView直接抛出异常!

@Override
public void addView(View child) {
    throw new UnsupportedOperationException("addView(View) is not supported in AdapterView");
  }  

新闻案例

写着写着就不是新闻了...看吧

Android的ListView

这个例子展示了一些网站,点击会跳转到其首页。依然使用ArrayAdapter

主布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.newsdemo.MainActivity">

   <ListView
       android:id="@+id/lv_news"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
   </ListView>
</LinearLayout>

子项布局 -- list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="16dp">

    <ImageView
        android:id="@+id/iv_news"
        android:layout_width="80dp"
        android:layout_height="80dp" />

    <LinearLayout
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:weightSum="1">

        <TextView
            android:maxLines="1"
            android:id="@+id/tv_news_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:textColor="#000" />

        <TextView
            android:maxLines="2"
            android:layout_marginTop="3dp"
            android:id="@+id/tv_news_des"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="#555"
            />

    </LinearLayout>
</LinearLayout>

Bean封装子项信息

package com.example.newsdemo.bean;

public class NewsBean {
    // 图片资源id
    public int imgId;
    // 标题
    public String title;
    // 标题的描述
    public String des;
    // 标题所指链接
    public String url;
}

处理子项信息的工具类,返回一个子项列表

package com.example.newsdemo.utils;

import com.example.newsdemo.R;
import com.example.newsdemo.bean.NewsBean;

import java.util.ArrayList;
import java.util.List;

public class NewsUtils {
    public static List<NewsBean> getAllNews() {
        List<NewsBean> list = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
            NewsBean beanBaidu = new NewsBean();
            beanBaidu.imgId = R.drawable.baidusousuo;
            beanBaidu.title = "百度搜索";
            beanBaidu.des = "全球最大的中文搜索引擎、致力于让网民更便捷地获取信息,找到所求。百度超过千亿的中文网页数据库,可以瞬间找到相关的搜索结果。";
            beanBaidu.url = "https://www.baidu.com/";
            list.add(beanBaidu);

            NewsBean beanAmap = new NewsBean();
            beanAmap.imgId = R.drawable.amap;
            beanAmap.title = "高德地图";
            beanAmap.des = "高德地图官方网站,提供全国地图浏览,地点搜索,公交驾车查询服务。可同时查看商家团购、优惠信息。高德地图,您的出行、生活好帮手。";
            beanAmap.url = "http://ditu.amap.com/";
            list.add(beanAmap);

            NewsBean beanBaiduyun = new NewsBean();
            beanBaiduyun.imgId = R.drawable.baiduyun;
            beanBaiduyun.title = "百度云盘";
            beanBaiduyun.des = "百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。";
            beanBaiduyun.url = "https://pan.baidu.com/";
            list.add(beanBaiduyun);

            NewsBean beanDouban = new NewsBean();
            beanDouban.imgId = R.drawable.douban;
            beanDouban.title = "豆瓣电影";
            beanDouban.des = "豆瓣电影提供最新的电影介绍及评论包括上映影片的影讯查询及购票服务。你可以记录想看、在看和看过的电影电视剧,顺便打分、写影评。";
            beanDouban.url = "https://movie.douban.com/";
            list.add(beanDouban);

            NewsBean beanZhihu = new NewsBean();
            beanZhihu.imgId = R.drawable.zhihu;
            beanZhihu.title = "知乎";
            beanZhihu.des = "一个真实的网络问答社区,帮助你寻找答案,分享知识";
            beanZhihu.url = "https://www.zhihu.com/";
            list.add(beanZhihu);

            NewsBean beanGoogletranslate = new NewsBean();
            beanGoogletranslate.imgId = R.drawable.google;
            beanGoogletranslate.title = "谷歌翻译";
            beanGoogletranslate.des = "Google 的免费翻译服务可提供简体中文和另外 100 多种语言之间的互译功能,可让您即时翻译字词、短语和网页内容。";
            beanGoogletranslate.url = "https://translate.google.cn/";
            list.add(beanGoogletranslate);

        }
        return list;
    }
}

适配器

package com.example.newsdemo.adapter;

import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.newsdemo.R;
import com.example.newsdemo.bean.NewsBean;

import java.util.List;

public class NewsAdapter extends ArrayAdapter<NewsBean> {
    private int resourceId;

    public NewsAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<NewsBean> objects) {
        super(context, resource, objects);
        resourceId = resource;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        // 根据所在位置获得新闻实例
        NewsBean newsBean = getItem(position);
        View view;
        if (convertView == null) {
            view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        } else {
            view = convertView;
        }
        ImageView newsImg = (ImageView) view.findViewById(R.id.iv_news);
        TextView newsTitle = (TextView) view.findViewById(R.id.tv_news_title);
        TextView newsDes = (TextView) view.findViewById(R.id.tv_news_des);

        newsImg.setImageResource(newsBean.imgId);
        newsTitle.setText(newsBean.title);
        newsDes.setText(newsBean.des);

        return view;

    }
}

MainActivity

package com.example.newsdemo;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;

import com.example.newsdemo.adapter.NewsAdapter;
import com.example.newsdemo.bean.NewsBean;
import com.example.newsdemo.utils.NewsUtils;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private Context mContext;
    private List<NewsBean> news;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;

        ListView listView = (ListView) findViewById(R.id.lv_news);
        // 获取所有新闻
        news = NewsUtils.getAllNews();
        NewsAdapter adapter = new NewsAdapter(mContext, R.layout.list_item, news);
        listView.setAdapter(adapter);
        // 设置子项点击事件监听,点击跳转到浏览器
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            /**
             * listview的条目点击时会调用该方法
             * @param parent 代表listviw
             * @param view 击的条目上的那个view对象
             * @param position 条目的位置
             * @param id 条目的id
             */
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
              // 下面注释的写法也可以获取子项
//                NewsBean bean = (NewsBean) parent.getItemAtPosition(position);
                NewsBean bean = news.get(position);
                String url = bean.url;
                // 这个构造器接收action和uri,相当于setAction和setData两个方法
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                startActivity(intent);
            }
        });
    }
}

常用获取inflate的写法

    // 1.
        //context:上下文, resource:要转换成view对象的layout的id, root:为这个布局指定一个父布局
        view = View.inflate(context, R.layout.item_news_layout, null);

    // 2.
        //通过LayoutInflater将布局转换成view对象
        view =  LayoutInflater.from(context).inflate(R.layout.item_news_layout, parent, false);

    // 3.
        //通过context获取系统服务得到一个LayoutInflater,通过LayoutInflater将一个布局转换为view对象
        LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = layoutInflater.inflate(R.layout.item_news_layout, parent, false);

SimpleAdapter(了解)

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ListView listView = (ListView) findViewById(R.id.lv_news);

    //创建一个simpleAdapter,封装simpleAdapter的数据
    ArrayList<Map<String, Object>> arrayList = new ArrayList<>();
    HashMap<String, Object> hashMap = new HashMap<>();
    hashMap.put("class", "C++");
    hashMap.put("user", R.drawable.baidusousuo);
    arrayList.add(hashMap);

    HashMap<String, Object> hashMap1 = new HashMap<>();
    hashMap1.put("class", "android");
    hashMap1.put("user", R.drawable.amap);
    arrayList.add(hashMap1);

    HashMap<String, Object> hashMap2 = new HashMap<>();
    hashMap2.put("class", "javaEE");
    hashMap2.put("user", R.drawable.baiduyun);
    arrayList.add(hashMap2);
    /**
     *
     * @param  context                         上下文
     * @param  List<? extends Map<String, ?>>  要显示的数据,外层的       List装了所有子项。而内层的map,不同的key对应于一个子项的不同控件
     * @param  int resource                    布局文件
     * @param  from                            map中的Key
     * @param  to                              与from里面的key对应        的控件id,即把map里key对应的value给控件显示。如下面的意思就是把key为class的value给tv_news_title显示,key为user的value给控件iv_news显示
     */

    SimpleAdapter simpleAdapter = new SimpleAdapter(this, arrayList, R.layout.list_item, new String[]{"class", "user"}, new int[]{R.id.tv_news_title, R.id.iv_news});

    listView.setAdapter(simpleAdapter);
}

将数据库的数据显示到ListView

这个例子上次写过。当时只是将查询到的数据简单打印到控制台,现将其显示到手机上。

封装数据的bean

package dao;

public class InfoBean {
    public int _id;
    public String name;
    public String telephone;
}

对数据执行增删改查

查询结果以List返回

package dao;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.example.databasedemo.MyDatabaseOpenHelper;

import java.util.ArrayList;
import java.util.List;

public class InfoDao {

    private MyDatabaseOpenHelper myDatabaseOpenHelper;
    private SQLiteDatabase db;

    public InfoDao(Context context ,String dbName , int version){
        //创建一个帮助类对象
        myDatabaseOpenHelper = new MyDatabaseOpenHelper(context, dbName, null, version);
        db = myDatabaseOpenHelper.getReadableDatabase();
    }

    public boolean add(InfoBean bean){

        //执行sql语句需要sqliteDatabase对象
        ContentValues values = new ContentValues(); // 是用Map封装的对象
        values.put("name", bean.name);
        values.put("telephone", bean.telephone);
        // 第二个参数可以为空,返回值表示新增的行号,-1表示添加失败
        long result =  db.insert("people", null, values);
        return result != -1;
    }

    public int del(String name){
        //执行sql语句需要sqliteDatabase对象
        int count = db.delete("people", "name = ?", new String[]{name});
        return count;
    }

    public int update(InfoBean bean){
        ContentValues values = new ContentValues();
        values.put("telephone", bean.telephone);
        int count = db.update("people", values, "name = ?", new String[]{bean.name});
        return count;
    }

    public List<InfoBean> query(String name){
        List<InfoBean> beanList = new ArrayList<>();
        // 查询people表中的name为参数指定的"_id", "name", "telephone"字段,按照id递减
        Cursor cursor = db.query("people", new String[]{"_id", "name", "telephone"}, "name = ?", new String[]{name}, null, null, "_id asc");

        //解析Cursor中的数据
        if(cursor != null && cursor.getCount() >0){//判断cursor中是否存在数据

            //循环遍历结果集,获取每一行的内容
            while(cursor.moveToNext()){ //条件,游标能否定位到下一行
                InfoBean bean = new InfoBean();
                //获取数据
                bean._id = cursor.getInt(cursor.getColumnIndex("_id"));
                bean.name = cursor.getString(cursor.getColumnIndex("name"));
                bean.telephone = cursor.getString(cursor.getColumnIndex("telephone"));
                beanList.add(bean);
            }
            cursor.close();//关闭结果集
        }
        return beanList;
    }
}

创建数据库和表

package com.example.databasedemo;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class MyDatabaseOpenHelper extends SQLiteOpenHelper {

    public static final String CREATE_BOOK = "create table people ("
            + "_id integer primary key autoincrement, name varchar(20), telephone varchar(11))";
    public static final String NEW_TABLE = "create table book ("
            + "_id integer primary key autoincrement, price real, pages integer)";

    private Context mContext;

    /**
     *
     * @param context 上下文
     * @param name    数据库的名称
     * @param factory 用来创建cursor对象,填入null使用默认的
     * @param version version:数据库的版本号,从1开始,如果发生改变,onUpgrade方法将会调用,4.0之后只能升不能降
     */
    public MyDatabaseOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;
    }
    // 调用getReadableDatabase()或者getWritableDatabase()时会调用该方法
    // 第一次创建数据库时才能执行该方法,特别适合做表结构的初始化
    // 传入的参数db可以用来执行sql语句
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        db.execSQL(NEW_TABLE);
    }

    // version改变时,调用这个方法,version只能升不能降
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        db.execSQL("drop table if exists book");
        db.execSQL("drop table if exists people");
        onCreate(db);
        db.execSQL("alter table book add author varchar(20)");
        db.execSQL("alter table people add age integer");

    }
}

ListView的适配器

package com.example.databasedemo.adapter;

import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import com.example.databasedemo.R;

import java.util.List;

import dao.InfoBean;

public class DatabaseAdapter extends ArrayAdapter<InfoBean> {

    private int resourseId;

    public DatabaseAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<InfoBean> objects) {
        super(context, resource, objects);
        resourseId = resource;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        View view;

        if (convertView == null) {
            view = LayoutInflater.from(getContext()).inflate(resourseId,parent, false);
        } else {
            view = convertView;
        }
        TextView idText = (TextView) view.findViewById(R.id.tv_id);
        TextView nameText = (TextView) view.findViewById(R.id.tv_name);
        TextView phoneText = (TextView) view.findViewById(R.id.tv_phone);

        InfoBean bean = getItem(position);
        // 这里bean._id是int类型,而参数接收CharSequence,故转成String
        idText.setText(String.valueOf(bean._id));
        nameText.setText(bean.name);
        phoneText.setText(bean.telephone);

        return view;

    }
}

子项布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_id"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"/>

    <TextView
        android:id="@+id/tv_name"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        />

    <TextView
        android:id="@+id/tv_phone"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        />
</LinearLayout>

主布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp"
    android:orientation="vertical"
    tools:context="com.example.databasedemo.MainActivity">

    <LinearLayout

        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/bt_add"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="@string/add" />

        <Button
            android:id="@+id/bt_update"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="@string/update" />

        <Button
            android:id="@+id/bt_del"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="@string/del" />

        <Button
            android:id="@+id/bt_query"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="@string/query" />
    </LinearLayout>
    <!--invisible设置为不可见,但是占据空间
        gone设置不可见,不占据空间-->
    <!--这里先让表头不可见,当用户点击查询时候才显示-->
    <LinearLayout
        android:id="@+id/layout_table_title"
        android:visibility="invisible"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="10dp">
        <TextView
            android:layout_weight="1"
            android:id="@+id/tv_title1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="id"
            android:textColor="#000"/>

        <TextView
            android:layout_weight="1"
            android:id="@+id/tv_title2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="用户"
            android:textColor="#000"/>

        <TextView
            android:layout_weight="1"
            android:id="@+id/tv_title3"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="电话"
            android:textColor="#000"/>
    </LinearLayout>

    <ListView
        android:id="@+id/lv_result"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </ListView>
</LinearLayout>

MainActivity

主要看查询

package com.example.databasedemo;

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Toast;

import com.example.databasedemo.adapter.DatabaseAdapter;

import java.util.List;

import dao.InfoBean;
import dao.InfoDao;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Context mContext;
    private InfoDao infoDao;
    private Button btAdd;
    private Button btDel;
    private Button btUpdate;
    private Button btQuery;
    private ListView listView;
    private LinearLayout linearLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        // 创建数据库,有则打开,没有则create
        infoDao = new InfoDao(mContext, "test.db", 4);

        btAdd = (Button) findViewById(R.id.bt_add);
        btDel = (Button) findViewById(R.id.bt_del);
        btQuery = (Button) findViewById(R.id.bt_query);
        btUpdate = (Button) findViewById(R.id.bt_update);
        listView = (ListView) findViewById(R.id.lv_result);
        linearLayout = (LinearLayout) findViewById(R.id.layout_table_title);

        btAdd.setOnClickListener(this);
        btDel.setOnClickListener(this);
        btUpdate.setOnClickListener(this);
        btQuery.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        InfoBean bean;
        int count;
        switch (v.getId()) {
            case R.id.bt_add:

                bean = new InfoBean();
                bean.name = "张三";
                bean.telephone = "119";
                boolean result = infoDao.add(bean);
                if (result) {
                    Toast.makeText(mContext, "添加成功", Toast.LENGTH_SHORT).show();
                }
                break;

            case R.id.bt_del:

                count = infoDao.del("张三");
                Toast.makeText(mContext, "删除"+count+"行", Toast.LENGTH_SHORT).show();
                break;

            case R.id.bt_update:

                bean = new InfoBean();
                bean.name = "张三";
                bean.telephone = "110";
                count = infoDao.update(bean);
                Toast.makeText(mContext, "更新"+count+"行", Toast.LENGTH_SHORT).show();
                break;

            case R.id.bt_query:
                // 用户查询时候表头才显示
                linearLayout.setVisibility(View.VISIBLE);
                List<InfoBean> list = infoDao.query("张三");
                DatabaseAdapter adapter = new DatabaseAdapter(mContext, R.layout.data_item, list);
                listView.setAdapter(adapter);
                break;

            default:
                break;
        }
    }
}

看下结果

Android的ListView


by @sunhaiyu

2017.4.16