listView在开发中经常用到,首先看一下listView的实现效果
- 新建activity_main.xml和list_item.xml布局文件
在activity_main中添加ListView控件,list_item就是列表中每一项的布局(只添加了一个Textview,可以通过修改list_item来定制不同的ListView界面)
2. 新建一个JavaBean类,作为适配器的适配类型
public class Bean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在Bean类中只有一个属性name,用于设置textView的文本
3. 新建一个MyAdapter继承自baseAdapter
public class MyAdapter extends baseAdapter {
private List data;
private Context mContext;
public MyAdapter(List data, Context mContext) {
this.data = data;
this.mContext = mContext;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView= LayoutInflater.from(mContext).inflate(R.layout.list_item,parent,false);
TextView textView=convertView.findViewById(R.id.tv);
textView.setText(data.get(position).getName());
return convertView;
}
}
在MyAdapter中重写了父类的构造方法,用于将context(上下文)和ListView子项的数据传递进来;然后又重写了getView()方法,这个方法是在listView中每个子项滚动到屏幕中时就会调用,在这个方法中通过LayoutInflater.from()方法加载listView子项的布局,并且设置textView需要显示的文本内容。
4. 最后在MainActivity中修改代码,通过setAdapter()方法将创建好的适配器对象传递进去,建立ListView和数据之间的关联。
public class MainActivity extends AppCompatActivity {
private List data=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for(int i=1;i<=50;i++){
Bean bean=new Bean();
bean.setName("hello world"+i);
data.add(bean);
}
ListView listView=findViewById(R.id.listView);
listView.setAdapter(new MyAdapter(data,this));
}
}
当然这个只显示一个TextView的ListView比较简单,可以更为简单的实现(直接使用ArrayAdapter)
修改MainActivity中的代码:
public class MainActivity extends AppCompatActivity {
private List data=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for(int i=1;i<=50;i++){
Bean bean=new Bean();
bean.setName("hello world"+i);
data.add(bean);
}
ArrayAdapter adapter=new ArrayAdapter(this,android.R.layout.simple_list_item_1,data);
ListView listView=findViewById(R.id.listView);
listView.setAdapter(adapter);
}
}
这里的android.R.layout.simple_list_item_1是Android内置的一个布局文件,里面只有一个Textview,当然也可以使用我们创建的list_item.xml,最后实现的效果是相同的。
1.2 ListView的优化我们可以分析一下baseAdapter中的getView()方法,每次都要重新绘制子项view和通过findViewById()方法来获取控件。
- 通过对getView()方法的观察,convertView这个参数我们只是粗略的使用,并没有发挥它的效果,convertView这个参数时用于将之前加载好的布局进行缓存,这样我们就可以通过判断convertView是否为null,如果为null,在调用LayoutInflater来进行绘制View,如果不为null,就可以直接复用。
public class MyAdapter extends baseAdapter {
private List data;
private Context mContext;
public MyAdapter(List data, Context mContext) {
this.data = data;
this.mContext = mContext;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
MyViewHolder viewHolder;
if(convertView ==null){
convertView=LayoutInflater.from(mContext).inflate(R.layout.list_item,parent,false);
viewHolder=new MyViewHolder();
viewHolder.textView=convertView.findViewById(R.id.tv);
convertView.setTag(viewHolder);
}else{
viewHolder= (MyViewHolder) convertView.getTag();
}
viewHolder.textView.setText(data.get(position).getName());
return convertView;
}
class MyViewHolder{
TextView textView;
}
}
2.在MyAdapter中我们新建了一个MyViewHolder类(见名思意,就是view的持有者),用于对控件的实例进行缓存,当convertView 为null时,创建一个viewHolder对象,并将控件的实例都存放在该对象中,然后通过view.setTag()方法,将viewHolder对象存储在convertView 中,当convertView 不为null时,通过view的getTag()方法取出viewHolder对象,这样就不用每次都通过findViewById来获取控件的实例了,同时也大大提高的ListView的运行效率。
1.3 ListView的点击事件ListView的点击事件主要通过setOnItemClickListener方法实现
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for(int i=1;i<=50;i++){
Bean bean=new Bean();
bean.setName("hello world"+i);
data.add(bean);
}
ListView listView=findViewById(R.id.listView);
listView.setAdapter(new MyAdapter(data,this));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
Toast.makeText(MainActivity.this,data.get(position).getName(),Toast.LENGTH_LONG)
.show();
}
});
}
当我们进行点击的时候通过position判断出我们点击的是哪一项,这里用Toast进行显示。
2.RecyclerView上面介绍了ListView的简单使用和性能优化,接下来介绍一下一个更为强大的控件RecyclerView,并且Android官方也更加推荐使用RecyclerView。
2.1 RecyclerView的简单使用- 将上方activity_main中的listView替换为RecyclerView。
- 修改MyAdapter继承自RecyclerView.Adapter,并且新建一个内部类ViewHolder继承自RecyclerView.ViewHolder
public class MyAdapter extends RecyclerView.Adapter{ private List data; private Context mContext; public MyAdapter(List data, Context mContext) { this.data = data; this.mContext = mContext; } @NonNull @Override public MyAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view= LayoutInflater.from(mContext).inflate(R.layout.recycler_item,parent,false); ViewHolder holder=new ViewHolder(view); return holder; } @Override public void onBindViewHolder(@NonNull MyAdapter.ViewHolder holder, int position) { holder.textView.setText(data.get(position).getName()); } @Override public int getItemCount() { return data.size(); } public static class ViewHolder extends RecyclerView.ViewHolder { TextView textView; public ViewHolder(@NonNull View itemView) { super(itemView); textView=itemView.findViewById(R.id.recycler_text); } } }
重写的三个方法:
- onCreateViewHolder:这个方法是用于创建ViewHolder实例的,在这个方法中通过LayoutInflater绘制recycler_item(recyclerview的item布局),然后创建viewHolder实例并且将绘制的view加载到viewholder中去。
- onBindViewHolder:这个方法就类似于ListView中的getView()方法,会在每个item滚动到屏幕中时执行,通过position得到当前项的数据,然后在设置到viewHolder保存的控件实例中
- getItemCount:返回recyclerView子项的个数
- 修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
private List data=new ArrayList<>();
private RecyclerView mRecyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for(int i=1;i<=50;i++){
Bean bean=new Bean();
bean.setName("hello world"+i);
data.add(bean);
}
mRecyclerView=(RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager manager=new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(manager);
mRecyclerView.setAdapter(new MyAdapter(data,this));
}
}
LayoutManager用于指定RecyclerView的布局,我们在这里使用的是LinearLayoutManager (线性布局管理器),所以实现效果和ListView相同。
- 网格布局其实非常简单,只要改变我们所使用的布局管理器就行了,这里使用GridLayoutManager
修改MainActivity中的代码:
mRecyclerView=(RecyclerView) findViewById(R.id.recyclerView);
//第二个参数代表网格布局用几列进行显示
GridLayoutManager manager=new GridLayoutManager(this,4);
mRecyclerView.setLayoutManager(manager);
mRecyclerView.setAdapter(new MyAdapter(data,this));
2. 瀑布流布局也是如此
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for(int i=1;i<=50;i++){
Random random=new Random();
int length=random.nextInt(20)+1;
StringBuilder builder=new StringBuilder();
for(int j=0;j
在这里使用了StaggeredGridLayoutManager ,两个参数分别是:列数;排列方向。
为了更直观的可以看出瀑布流和网格布局的差别(需要每个子项的高度不一样才能看出来),在这里使用了random函数,随机的将参数中传入的字符串重复N遍。
2.3 点击事件
RecyclerView和ListView不同,RecyclerView没有提供Item的点击事件,所以需要我们自己进行设置。
- 在onBindViewHolder方法中通过setOnClickListener实现
@Override
public void onBindViewHolder(@NonNull @org.jetbrains.annotations.NotNull MyAdapter.MyViewHolder holder, int position) {
holder.textView.setText(data.get(position).getName());
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context,"你点击了第"+position+"项",Toast.LENGTH_SHORT).show();
}
});
holder.textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context,"你点击了第"+position+"项",Toast.LENGTH_SHORT).show();
}
});
}
通过 holder.itemView设置的点击事件是RecyclerView单个子项整体的点击事件,而通过holder.textView设置的点击事件是Item子项的点击事件;比如list_item中有button和textView时,可以通过这个方法分别设置button和textView的点击事件。
2. 通过接口回调的方式进行实现
(1)在MyAdapter中定义一个接口,专门用于设置点击事件
public interface OnItemListener{
void onItemClick(View view,int position);
}
(2)定义这个接口的set方法并且声明变量,便于调用
private OnItemListener mListener;
public void setOnItemClickListener(OnItemListener listener){
this.mListener=listener;
}
(3)在onBindViewHolder中设置点击事件
@Override
public void onBindViewHolder(@NonNull @org.jetbrains.annotations.NotNull MyAdapter.MyViewHolder holder, int position) {
holder.textView.setText(data.get(position).getName());
holder.imageView.setImageResource(data.get(position).getImageId());
//根布局的点击事件
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mListener!=null){//判断是否设置了监听
//通过holder.getAdapterPosition()获取当前item的位置
mListener.onItemClick(holder.itemView, holder.getAdapterPosition());
}
}
});
//item子view的点击事件
holder.textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mListener!=null){//判断是否设置了监听
//通过holder.getAdapterPosition()获取当前item的位置
mListener.onItemClick(holder.textView, holder.getAdapterPosition());
}
}
});
}
(4)在MainActivity中设置点击事件的具体效果(通过调用adapter中的setOnItemClickListener)
myAdapter.setOnItemClickListener(new MyAdapter.OnItemListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this,"点击了第"+position+"项",Toast.LENGTH_SHORT).show();
}
});
这样就可以实现RecyclerView的点击事件了。



