安卓-RecyclerView
RecyclerView是ListView的升级版,与ListView相比使用更方便效率更高
这里直接借鉴Android群英传的例子来进行说明
一、使用RecyclerView.Adapter进行管理RecyclerView的viewitem进行循环利用viewitem
1、我们新建一个类RecyclerAdapter来继承RecyclerView.Adapter并实现3个接口
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) public void onBindViewHolder(@NonNull ViewHolder holder, int position) public int getItemCount()
onCreateViewHolder用来创建ViewHolder对象,并将对应的view设置给ViewHolder;
onBindViewHolder用来设置当前传进来的holder管理的view
getItemCount返回当前的RecyclerView一共有多少个viewitem
注意:
1、当我们调试的时候会发现onCreateViewHolder只在初始化的时候会调用,在滑动或者增删操作的时候均不会再调用该函数
2、当我们在滑动或者增删操作的时候均会调用该函数
出现第一点的原因是因为RecyclerView可以克隆多个,只需要在初始化的时候调用一个ViewHolder即可
出现第二点的原因是因为viewitem是循环利用,所以当某个item即将出现或者出现的时候需要从cache中拿出一个viewitem来进行设置对应的view内容
二、重写RecyclerView.ViewHolder
重写ViewHolder并支持对应的点击监听事件
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public TextView textView; public ViewHolder(@NonNull View itemView) { super(itemView); textView = (TextView) itemView; textView.setOnClickListener(this); } @Override public void onClick(View v) { if (itemClickListener != null) { itemClickListener.onItemClick(v, getLayoutPosition()); } } }
如上所示,在点击viewitem的时候则会调用onClick
这里在使用getLayoutPosition的时候发现还有一个接口getAdapterPosition
在正常的点击事件中,这两个函数所返回的positon是一样的;那么他们不同的地方是什么呢?
具体区别就是adapter和layout的位置会有时间差(<16ms), 如果你改变了Adapter的数据然后刷新视图, layout需要过一段时间才会更新视图, 在这段时间里面, 这两个方法返回的position会不一样.
getLayoutPosition顾名思义为界面显示的位置
这里先做个实验,假设有3个item,然后点击第一个item,onClick的代码如下所示:
public void onClick(View v) { if (mItemClickListener != null) { for (int i = 0; i < 3; ++i) { int position = getLayoutPosition(); System.out.println("onclick:" + position + ", adapter:" + getAdapterPosition()); if (position > 0) { mData.remove(position); notifyDataSetChanged(); } } } }
该for是模拟某个viewitem被连续点击3次,该log如下所示,打印完这些log之后崩溃
onclick:1, adapter:1
onclick:1, adapter:-1
onclick:1, adapter:-1
为何getLayoutPosition一直返回1,而getAdapterPosition在删除之后界面虽然还没刷新但是已经返回-1了
要查看究竟我们可以看下这两个函数的实现
getLayoutPosition:
public final int getLayoutPosition() { return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition; }
只是简单的判断当前的位置,没有判断当前viewitem所在的状态
getAdapterPosition:
public final int getAdapterPosition() { if (mOwnerRecyclerView == null) { return NO_POSITION; } return mOwnerRecyclerView.getAdapterPositionFor(this); } int getAdapterPositionFor(ViewHolder viewHolder) { if (viewHolder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN) || !viewHolder.isBound()) { return RecyclerView.NO_POSITION; } return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition); }
如上所示,会判断当前的viewholder是否被标记为删除的状态,如果标记为删除的状态则返回NO_POSITION 即-1
所以使用的时候可以根据自己的需要看是要用getLayoutPosition还是getAdapterPosition
三、如下主要的代码
注意看下源代码里面的备注
RecyclerAdapter1.java的代码

public class RecyclerAdapter1 extends RecyclerView.Adapter<RecyclerAdapter1.ViewAdapter> { private List<String> mData; public RecyclerAdapter1(List<String> data) { mData = data; } private OnItemClickListener mItemClickListener; public void setOnItemClickListener(OnItemClickListener itemClickListener) { mItemClickListener = itemClickListener; } public interface OnItemClickListener { void onItemClick(View view, int position); } @NonNull @Override public ViewAdapter onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // inflate是用来解析xml配置转化为view,参数如果root为空或者attachToRoot则只返回view,只是独立的view无其他设置 // 如果root有值,并且attachToRoot传true那么会将返回的view关联到root // 因为这里会将返回的view传给ViewAdapter在里面做关联操作,所以这里的attachToRoot传false就可以了 // 如果传true就会出现重复关联的情况,产生崩溃;具体可以看该链接 https://blog.csdn.net/weixin_42949480/article/details/107847702 View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.rc_item, parent, false); return new ViewAdapter(view); } @Override public void onBindViewHolder(@NonNull ViewAdapter holder, int position) { holder.mTextView.setText(mData.get(position) + position); } @Override public int getItemCount() { return mData.size(); } public class ViewAdapter extends RecyclerView.ViewHolder implements View.OnClickListener { public TextView mTextView; public ViewAdapter(@NonNull View itemView) { super(itemView); mTextView = (TextView) itemView; mTextView.setOnClickListener(this); } @Override public void onClick(View v) { if (mItemClickListener != null) { mItemClickListener.onItemClick(v, getLayoutPosition()); } } } }
RecyclerTest1.java的代码

public class RecyclerTest1 extends Activity { private RecyclerAdapter1 mAdapter; private List<String> mData = new ArrayList<>(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.recycler); RecyclerView rcView = findViewById(R.id.rc_list); Spinner spinner = findViewById(R.id.spinner); mAdapter = new RecyclerAdapter1(mData); spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (position == 0) { rcView.setLayoutManager(new LinearLayoutManager(RecyclerTest1.this)); } else if (position == 1) { rcView.setLayoutManager(new GridLayoutManager(RecyclerTest1.this, 3)); } } @Override public void onNothingSelected(AdapterView<?> parent) { } }); rcView.setLayoutManager(new LinearLayoutManager(this)); rcView.setItemAnimator(new DefaultItemAnimator()); rcView.setHasFixedSize(true); // 这个是一个优化的设置,当size的大小不会因为rcView的子item的个数和size改变时,这里可以设置为true;https://blog.csdn.net/zzx410527/article/details/93177979 可以看这个的博客说明 rcView.setAdapter(mAdapter); mData.add("kkqqq"); mData.add("kkqqq"); mData.add("kkqqq"); mAdapter.setOnItemClickListener(new RecyclerAdapter1.OnItemClickListener() { @Override public void onItemClick(View view, int position) { view.animate().translationZ(15).setDuration(300).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); // 这里要setListener(null), 否则会一直循环这个监听的调用 view.animate().setListener(null).translationZ(1).setDuration(200).start(); } }).start(); } }); } public void addItem(View view) { mData.add("kkqqqqq"); mAdapter.notifyDataSetChanged(); } public void delItem(View view) { int count = mData.size(); if (count > 0) { mData.remove(count - 1); mAdapter.notifyDataSetChanged(); } } }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性