前言
ListView和RecyclerView已经可以算是Android里面最常用的控件,也是最难用的几个控件了,RecyclerView相比ListView会更加的灵活,所以RecyclerView现在正逐渐代替LIstView,本篇文章将会讲解这两个控件的基本使用。
ListView的基本使用
首先在布局文件下添加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"
android:orientation="vertical"
tools:context=".MainActivity">
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
接着我们还需要一个Adapter,只有通过Adapter才能在LIstView上面展示数据,Adapter有很多种类:
- ArrayAdapter
- BaseAdapter
- CursorAdapter
- HeaderViewAdapter,
- ResourceCursorAdapter
- SimpleAdapter
- SimpleCursorAdapter
- WrapperListAdapter
我们这里主要说两个Adapter,首先是ArrayAdapter,它是适用于绑定格式单一的数据,数据源可以是数组或者集合。
public class MainActivity extends AppCompatActivity {
private ListView listView;
//字符串组合
private String[] data = {"item_One", "item_Two", "item_Three", "item_Four", "item_Five", "item_Six", "item_Seven", "item_Eight", "item_Nine", "item_Ten"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.list);
//ArrayAdapter使用,第二个参数为Android默认自带的布局
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, data);
listView.setAdapter(arrayAdapter);
}
}
ArrayAdapter适配器使用结果
ArrayAdapter因为是泛型的,所以说也就是可以自定义数据,我们只要在构造函数里面把数据源传入进去,然后在通过LIstView的
setAdapter()
方法进行设置Adapter,这样子LIstView就和数据之间关联起来了。但是这样子实在是太单调了,开发中基本不可能有这么简单的需求。这时候我们可以用BaseAdapter,它也是ArrayAdapter的父类,在实现BaseAdapter之前,我们需要一个ListView子项自定义的一个子布局。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/image_item"
android:layout_margin="10dp"
android:layout_width="50dp"
android:layout_height="50dp" />
<TextView
android:id="@+id/textview_item"
android:layout_marginTop="10dp"
android:layout_marginLeft="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20dp"
android:text="Test"/>
</LinearLayout>
自定义布局写好后,我们就来看看BaseAdapter的具体实现。
public class TestAdapter extends BaseAdapter {
//返回列表长度
@Override
public int getCount() {
return 0;
}
//通过position来返回需要的子view
@Override
public Object getItem(int position) {
return null;
}
//通过position获取子View的Id
@Override
public long getItemId(int position) {
return 0;
}
//重点的方法,重写这个来显示数据
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}
在这些方法里面最重要的就是getView()
方法,这个方法在每个子项滚动到屏幕类的时候就被会调用,也就是说,每次调用的时候都需要将布局重新加载了一遍,这样其实试听会消耗性能的,但其实里面有一个convertView参数,这个参数可以加之前加载好的布局来进行缓存,以便之后可以进行复用,说了这么多,我们来看看具体怎么实现。
public class ListAdapter extends BaseAdapter {
private List<Student> studentList;
private Context context;
//通过LayoutInflater来实例化子布局
private LayoutInflater inflater;
//构造参数,传递上下文,和需要加载的数据
public ListAdapter(Context context, List<Student> list) {
this.context = context;
this.studentList = list;
inflater = LayoutInflater.from(context);
}
private ListAdapter() {
}
//返回数据的长度
@Override
public int getCount() {
return studentList.size();
}
//根据position返回哪个子View
@Override
public Object getItem(int position) {
return studentList.get(position);
}
//根据position返回哪个子View的Id
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
//判断是否之前有缓存
if (convertView == null) {
holder = new ViewHolder();
//注册布局
convertView = inflater.inflate(R.layout.list_item, parent, false);
holder.imageView = convertView.findViewById(R.id.image_item);
holder.textView = convertView.findViewById(R.id.textview_item);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
//绑定数据
holder.imageView.setImageResource(studentList.get(position).getImageId());
holder.textView.setText(studentList.get(position).getName());
return convertView;
}
//内部类控件,对这些控件进行缓存
private static class ViewHolder {
private ImageView imageView;
private TextView textView;
}
}
备注已经写得很清楚了,接下来我们做个测试数据传输进去。
public class MainActivity extends AppCompatActivity {
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = findViewById(R.id.list);
//初始化测试数据
List<Student> studentList = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Student student = new Student();
student.setName("学生" + i);
student.setImageId(R.mipmap.icon);
studentList.add(student);
}
//BaseAdapter使用
ListAdapter listAdapter = new ListAdapter(this,studentList);
listView.setAdapter(listAdapter);
}
}
//测试数据的实例类
public class Student {
private String name;
private int imageId;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getImageId() {
return imageId;
}
public void setImageId(int imageId) {
this.imageId = imageId;
}
}
BaseAdapter运行结果
展示是有了,但是我想点击LIstView怎么办呢,其实也很简单,我们在给LIstView的每一个子View添加一个点击事件就好了。
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.list_item, parent, false);
holder.imageView = convertView.findViewById(R.id.image_item);
holder.textView = convertView.findViewById(R.id.textview_item);
//设置点击事件
holder.imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context,""+position,Toast.LENGTH_SHORT).show();
}
});
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
ListView点击事件返回结果1
当然我们也可以直接对LIstView设置点击事件,只要给LIstView注册一个监听器就可以了
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(MainActivity.this,studentList.get(position).getName(),Toast.LENGTH_SHORT).show();
}
});
ListView点击事件返回结果2
RecyclerView的基本使用
使用RecyclerView之前,你需要导入依赖,版本后要和你依赖的com.android.support:appcompat包一样。
implementation 'com.android.support:recyclerview-v7:28.0.0'
然后在xml布局里面定义RecyclerView
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RecyclerActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.constraint.ConstraintLayout>
然后我们需要实现RecyclerView的Adapter,我们这里实现RecyclerView.Adapter类。`
public class TestAdapter extends RecyclerView.Adapter<指定的ViewHolder> {
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return null;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
}
@Override
public int getItemCount() {
return 0;
}
}
RecyclerView.Adapter这里其实是要传入你要的指定泛型的ViewHolder,这个ViewHolder是继承自RecyclerView.ViewHolder,然后ViewHolder的构造函数中要传入一个View参数,这个参数通常就是RecyclerView子项的最外层布局,这样子就可以通过findviewById()
方法来获取布局中的控件了。接下来我们来看必须要重写的那3个方法。
-
onCreateViewHolder:
它是用来创建ViewHolder实例的,我们可以通过LayoutInflater将ListView自定义的子布局加载进来,然后传入到ViewHolder的构造函数中。 -
onBindViewHolder:
它是对于RecyclerView子项的数据来进行绑定和赋值,每个子项滚动到屏幕内的时候就会执行。 -
getItemCount:
这个就是获取列表的长度数量,和ListView的getCount()
方法一样。
接下来我们来具体写法,子布局就还是用之前的定义的那个布局。
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
private Context context;
private List<Student> listStudent;
public RecyclerAdapter(Context context, List<Student> list) {
listStudent = list;
this.context = context;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(context).inflate(R.layout.list_item, viewGroup, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
viewHolder.imageView.setImageResource(listStudent.get(i).getImageId());
viewHolder.textView.setText(listStudent.get(i).getName());
}
@Override
public int getItemCount() {
return listStudent.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView textView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.image_item);
textView = itemView.findViewById(R.id.textview_item);
}
}
}
接下来就是使用了,使用RecyclView的时候我们还是需要一个布局管理器LinearLayoutManager,它是用来指定RecyclerView的布局方式的,我们通过LinearLayoutManager来把它设置为线性方式,当然也可以用GridLayoutManager布局也是可以的。
public class RecyclerActivity extends AppCompatActivity {
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler);
recyclerView = findViewById(R.id.recycler);
//初始化测试数据
List<Student> studentList = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Student student = new Student();
student.setName("学生Recycler:" + i);
student.setImageId(R.mipmap.icon);
studentList.add(student);
}
RecyclerAdapter recyclerAdapter = new RecyclerAdapter(this,studentList);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(recyclerAdapter);
}
}
RecyclerAdapter效果实现
也可以自定义添加分割线。
recyclerView.addItemDecoration(new DividerItemDecoration(this,1));
分割线实现效果
接下来就是灵活的地方了,ListView里面是只能纵向滚动的,而RecyclerAdapter是可以实现横线滚动的效果的,只需要在布局管理器添加一句话就好了。
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
RecyclerView横向滚动效果
接下来来讲讲RecyclerView的点击事件,RecyclerView是没有像ListView的监听器的,而是需要我们自己给子项来注册监听事件,我们可以在
onBindViewHolder()
方法里进行注册点击事件。这里我们写一个回调方法来返回监听事件。
//定义接口
public interface OnItemClickLitener {
void onItemClick(View view, int position);
void onItemLongClick(View view , int position);
}
//接口设知道Adapter
public void setOnItemClickLitener(OnItemClickLitener onItemClickLitener){
this.onItemClickLitener = onItemClickLitener;
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder viewHolder, int i) {
viewHolder.imageView.setImageResource(listStudent.get(i).getImageId());
viewHolder.textView.setText(listStudent.get(i).getName());
//点击事件注册
if (onItemClickLitener != null){
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = viewHolder.getAdapterPosition();
onItemClickLitener.onItemClick(viewHolder.itemView,pos);
}
});
viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int pos = viewHolder.getAdapterPosition();
onItemClickLitener.onItemLongClick(viewHolder.itemView,pos);
return false;
}
});
}
}
//在Activity继承接口返回
@Override
public void onItemClick(View view, int position) {
Toast.makeText(this,"点击返回:"+position,Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int position) {
Toast.makeText(this,"长按返回:"+position,Toast.LENGTH_SHORT).show();
}
RecyclerView点击监听返回
参考
- [郭霖] 第一行代码Android(第二版)
网友评论