安卓-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());
            }
        }
    }
}
View Code
复制代码

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();
        }
    }
}
View Code
复制代码

 

posted @   LCAC  阅读(59)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示