前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从0系统学Android--3.5 最常用和最难用的控件---ListView

从0系统学Android--3.5 最常用和最难用的控件---ListView

作者头像
开发者
发布2019-12-26 15:54:20
5580
发布2019-12-26 15:54:20
举报

本系列持续更新中….

3.5 最常用和最难用的控件---ListView

ListView 是我们在开发中最常使用的控件之一。由于手机屏幕空间比较有限,能够一次性在屏幕上显示的内容不多,ListView 允许用户可以通过手指上下滑动,可以呈现更多的数据。

3.5.1 ListView 的简单使用

首先还是在 Layout 中添加 ListView

代码语言:javascript
复制
<?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">
    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
代码语言:javascript
复制
  private String[] data = {
            "Apple","fdsfs","fdsfssfs","wwww","wwwqqq","qqqqq","eeeee","rrrrrr","tttttt","yyyyyy","uuuuuu","aaaaaa","sssss"
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_listviwe);
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,data);
        ListView listView = findViewById(R.id.lv);
        listView.setAdapter(adapter);
    }

ListView 是用于展示大量数据的,因此数据需要提供好,这些往往都是从网络上或者数据库读取的,这里就用假数据简单测试一下。

数据不能直接传递给 ListView 需要借助适配器来完成。Android 中提供了许多适配器的实现类。这里使用最简单的 ArrayAdapterandroid.R.layout.simple_list_itme_1 是 Android 内置的布局文件,里面只有一个简单的 TextView。然后使用 setAdapter 就将构建好的适配器对象传递进去,这样 Listview 和 数据之间的关联就建立了。

3.5.2 定制 LIstView 的界面

只显示一行文字太过单调了,下面对 ListView 的界面进行定制,显示更加复杂的内容。

定义一个实体类,作为 ListView 适配器的适配类型。新建 Fruit

代码语言:javascript
复制
public class Fruit {
    private String name;
    private int imgId;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getImgId() {
        return imgId;
    }

    public void setImgId(int imgId) {
        this.imgId = imgId;
    }
}

其中 name 表示名字 imgId 对应图片的资源id。

下面为 ListView 的子项创建一个自定义的布局fruit_item.xml

代码语言:javascript
复制
<?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:orientation="horizontal">
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/iv"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/tv_name"
        android:layout_marginLeft="10dp"/>
</LinearLayout>

下面自己建立适配器,这个适配器继承自 ArrayAdapter,并且制定泛型的类型是 Fruit 类型的。

代码语言:javascript
复制
public class FruitAdapter  extends ArrayAdapter<Fruit> {

    private int resourceId;
    public FruitAdapter(@NonNull Context context, 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 fruit = getItem(position);
        View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);

        TextView textView = view.findViewById(R.id.tv_name);
        ImageView imageView = view.findViewById(R.id.iv);
          // 这里没有图片,就偷懒使用了系统的
        imageView.setImageResource(R.mipmap.ic_launcher);
        textView.setText(fruit.getName());
        return view;

    }
}

FruitAdapter 继承 ArrayAdapter 比较简单,只需要重写一组构造方法和一个 getView() 方法就可以了,其他的方法 ArrayAdapter 都实现了。

getItem() 方法在每个子项被滚动到屏幕内的时候都会被调用。

这里使用了 LayoutInflater 来为这个子项加载我们传入的布局,LayoutInflater 的 inflate 方法需要传入三个参数,第一个参数就是要加载的布局,第二个参数就是这个布局要加入到这父布局中,第三个参数指定成 false 表示只让我们在父布局中声明的 layout 属性生效,其实就是为了测量一下这个生成的布局,但是不会将这个 View 添加到父布局中,因为一旦 View 有了父布局后,就不能再添加到 ListView 中了。现在不理解也没关系,记住这是标准写法就行了。

最后我们在 Activity 中将 ListView 与我们自己创建的适配器绑定就可以了。

代码语言:javascript
复制
// 进行数据加工                 
initData();
        FruitAdapter fruitAdapter = new FruitAdapter(this,R.layout.fuit_item,list);
        ListView listView = findViewById(R.id.lv);
 listView.setAdapter(fruitAdapter);
3.5.3 提高 ListView 的运行效率

ListView 还有很多的细节点需要优化,不然运行效率很低。因为在 getView() 方法中每次都将布局重新加载了一遍,当 ListView 快速滚动的时候,性能就会出现问题。

其实在 getView() 方法中还有一个 converView 参数,这个参数就是用于将之前加载好的布局进行缓存的,以便之后可以进行重复使用。修改 FruitDapter 的代码

代码语言:javascript
复制
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Fruit fruit = getItem(position);
        View view ;
        if (convertView == null){
            view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
        }else {
            view = convertView;
        }
        TextView textView = view.findViewById(R.id.tv_name);
        ImageView imageView = view.findViewById(R.id.iv);
        imageView.setImageResource(R.mipmap.ic_launcher);
        textView.setText(fruit.getName());
        return view;

    }

这样我们就充分把缓存的 conventView 利用起来了。

不过目前还需要优化,虽然不会再去重复的加载布局了,但是每次 getView() 方法中还是会调用 ViewfindViewById() 方法来获取一次控件的实例。我们可以借助 ViewHolder 来对这部分进行性能的优化。修改代码

代码语言:javascript
复制
    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        // 获取当前项的 Fruit 实例
        Fruit fruit = getItem(position);
        View view ;
        ViewHolder viewHolder;
        if (convertView == null){
            view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
            viewHolder  = new ViewHolder();
            viewHolder.iv =  view.findViewById(R.id.iv);
            viewHolder.tv = view.findViewById(R.id.tv_name);
            view.setTag(viewHolder); // 将 ViewHolder 存储在 View 中
        }else {

            view = convertView;
            viewHolder = (ViewHolder) view.getTag();
        }
        viewHolder.iv.setImageResource(R.mipmap.ic_launcher);
        viewHolder.tv.setText(fruit.getName());

        return view;

    }
    class ViewHolder{
        ImageView iv;
        TextView tv;
    }

新增加一个 ViewHolder内部类,用于对控件的实例进行缓存,当 convertView 为 null 的时候就创建一个 ViewHolder 并将它存放在 View 中。当convertView 不为 null 的时候将 ViewHolder 实例取出来。这样就没有必要每次都要通过 findViewById() 来获取控件实例了。

通过以上两步,ListView 就优化的非常不错了。

3.5.4 ListView 点击事件

给 ListView 设置点击事件

代码语言:javascript
复制
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // 通过 positon 我们就可以确定是点击的哪一个子项
            }
        });
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android开发者家园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 3.5 最常用和最难用的控件---ListView
    • 3.5.1 ListView 的简单使用
      • 3.5.2 定制 LIstView 的界面
        • 3.5.3 提高 ListView 的运行效率
          • 3.5.4 ListView 点击事件
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档