初学Android控件时,应该就属这两个控件的用法最为复杂,所以在此总结一下这两个控件的用法,方便复习和查阅。
使用前都需要准备适配器 Adapter :其实这两个控件的使用在初学者眼里之所以看起来复杂,最大的原因就是需要根据自己的业务来写一个Adapter类。(当然也可以用系统的 Adapter 而不自己重写,但是就会存在一些需求没办法定制)
使用前都需要准备 item 的 layout :除了准备一个适配器 Adapter 之外,还需要为列表中的每一项准备一个共用的 layout 。这里就不展开说了,因为这些 layout 的写法一般是根据需求来的, layout 中可能会包含 ImageView 、TextView 、CheckBox 等等。
使用时都是先初始化一个前面第一点提到的适配器Adapter,向其传入前面第二点提到的 layout 和所需要展示的数据集合等一些参数,然后将这个适配器通过 ListView.setAdapter(adapter) 方法,使适配器作为参数传入即可(RecyclerView在此之前还有一个步骤,后面会介绍到,只是一行代码的事,所以我认为使用时整体的步骤与 ListView 是基本相同的)。
关于使用步骤,Adapter 的不同实现,会出现传入参数上的差异,可能会导致部分人觉得这里使用时的相同点有点错误,其实我在这里只是想表达在使用时需要用到前面所提到的一些东西,具体的细微差别在使用时还是存在的,不用太纠结这部分。
public class MyTypeAdapter extends ArrayAdapter<MyType> {
private int resourceId;
public MyTypeAdapter(Context context, int resourceId, List<MyType> list){
super(context,textViewResourceId,list);
this.resourceId = resourceId;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
MyType myType = getItem(position);//这里的数据类型和你要展示的数据是一样的
View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
ImageView image = view.findViewById(R.id.image);
TextView text = view.findViewById(R.id.text);
image.setImageResource(myType.getImageId());
text.setText(myType.getText());
return view;
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center" />
LinearLayout>
最后,做完了准备工作,在需要使用 ListView 的布局文件中添加它。然后和使用其他控件一样,在 Activity 中获取,实例化Adapter,并将需要展示的数据集合传入。最后一步,将 Adapter 设置给 ListView ,大功告成。
如何开启点击事件
4.1 对整个 item 的点击事件,只需要对 ListView 增加事件监听即可,在 onItemClick 方法中, position 表示点击的 item 为第几项,一般通过这个下标进行对应的数据处理。
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//do something...
}
});
4.2 对 item 中某个控件的点击事件,则需要在 Adapter 中,在 getView 方法中获取绑定到控件之后,向其正常添加点击事件即可。同样的,getView 方法也有一个 position 参数,也是表示点击的 item 下标。
对 ListView 优化应该从哪里入手呢?想想我们对这个控件的使用准备,显然最先想到的应该是咱们编写的 Adapter 了。
通过新建一个内部类 ViewHolder 将子项的各个控件作为成员属性,重写 getView 方法。
对传入该方法的参数 convertView 进行判断,如果为 null 则进行布局和控件的加载,并将 viewHolder 通过 setTag 存储到 view 中,如果不为 null 则直接 view.getTag() 。
这个做法可以使控件和布局不必每次都被重复加载。
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null){
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
viewHolder = new ViewHolder();
viewHolder.image = view.findViewById(R.id.image);
viewHolder.text = view.findViewById(R.id.text);
view.setTag(viewHolder);//将ViewHolder存储到View中
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
return view;
}
static class ViewHolder {
TextView text;
ImageView image;
}
上面讲的就是 ListView 的一些基本内容,还有许多东西需要去探索。接下来讲的关于 RecyclerView 其实也大同小异,只要理解了需要 Adapter 作为桥梁去连接数据和 ListView ,那其实 RecyclerView 的基本使用也和 ListView 没有很大区别。
使用前添加依赖
implementation 'com.android.support:recyclerview-v7:27.1.1'
Androidx的话是:
implementation 'androidx.recyclerview:recyclerview:1.1.0'
和 ListView 没两样,Adapter 先搞一个,让它继承 RecyclerView.Adapter<> 将其中的泛型指定为 adapter.ViewHolder 。
定义内部类继承 RecyclerView.ViewHolder 。传入的参数通常是 RecyclerView 子项的最外层布局。构造函数接受数据源。由于它继承于 RecyclerView.Adapter ,所以必须重写 onCreateViewHolder() , onBindViewHolder() 和 getItemCount() 三个方法。
第一个方法用于创建 ViewHolder 实例并把加载的布局传入构造函数并返回 holder ,第二个方法则是子项的控件进行绑定并对每个子项赋值,第三个方法返回 recylerview 的子项数目。
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<MyType> mList;
static class ViewHolder extends RecyclerView.ViewHolder{
ImageView mImage;
TextView mText;
public ViewHolder (View view) {
super(view);
mImage = (ImageView) view.findViewById(R.id.image);
mText = (TextView) view.findViewById(R.id.text);
}
}
public MyAdapter (List <MyType> list){
mList = list;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout,parent,false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position){
MyType myType = mList.get(position);
holder.mImage.setImageResource(myType.getImageId());
holder.mText.setText(myType.getText());
}
@Override
public int getItemCount(){
return mList.size();
}
其次编写 item_layout ,这个和 ListView 没有任何区别,除非你需要一些其他的列表布局,不过这些都是细微的差别。
正式使用,和 ListView 的使用相似,获取 RecyclerView 后实例化适配器,传入一些必要的参数,然后 setAdapter ,但在 setAdapter 之前需要使用 LayoutManager 指定布局方式,这就是前面提到的一行代码的事(虽然看到是两行,但是硬要写成一行也不是不行)。LineatLayoutManager 指的是线性布局。
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
点击事件:对于整个子项的点击事件,在 viewHolder 中添加保存最外层 view ,在 onCreatViewHolder 中通过 holder.view.setOnClickListener 进行设置子项的点击事件;对于子项中的控件的点击事件,也同样在这里进行设置事件监听即可。
其他布局
RecyclerView 还可以实现横向滚动,只需要将子项的布局 item_layout 进行修改,即不要使宽度占满屏幕,其他根据需要调整,然后再使用时候设置 LayoutManager 时写
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
RecyclerView 还有 GridLayoutManager 和 StaggeredGridLayoutManager 。
GridLayoutManager layoutManager = new GridLayoutManager(this,5);
设置网格布局的代码如上,第二个参数是网格的列数。瀑布流也大同小异,网上查一下资料就行了。
ListView 的布局就是普通的列表展示,而 RecyclerView 可以横向滚动,还有瀑布流、表格之类的展示,可以适用于不同的业务需求
ListView 需要自行优化,自定义 ViewHolder 等一些内容
RecyclerView 还有一些其他的优点,但是我还没有研究过,所以就不多说了。如果只是用于展示一些简单的文字数据的话用 ListView 代码量应该会少一点。
其实这两个控件的使用都不是很复杂,只是相对于之前学习的其他控件而言,这两个控件就显得相对有些麻烦。
其实使用起来只需要和上面写的一样分四步来看,就很容易记住了。
第一步:在需要的地方添加 ListView \ RecyclerView ,这一步和其他控件没有区别
第二步:编写 Adapter ,把这一步做完基本就搞定百分之九十的工作了
第三步:编写 item_layout ,这个根据业务需求写即可
第四步:在 Activity \ Fragment 或其他使用 ListView 的地方绑定控件,实例化 Adapter 并传入数据,将该 Adapter 设置给 ListView 即可
后面有时间的话再研究研究 RecyclerView ,这东西里面还是有很多要学的。写这一篇东西也参考了其他的博客,简单的使用方法说来说去也都是一回事,难免有点雷同。
有机会的会加一篇 《ListView 和 RecyclerView 的简单实践》,贴上一些图,看着会有看头一点
完