ListView更换为RecyclerView的过程知识总结

最近在入手一个issue,需要将应用中使用的Listview全部更换为RecyclerView,RecyclerView的各种优点这里就不多介绍了,但是它还有一部分功能封装的不是很完善,主要总结一下这个过程中遇到的一些问题,一起学习。

1、RecyclerView在xml中设置layoutManager

因为本来的页面控制的是在手机上竖屏时显示一列数据,横屏显示两列数据,在平板上采用大布局不管竖屏还是横屏都控制每行显示两列,相当于有三个布局xml文件,如下图所示。
ListView更换为RecyclerView的过程知识总结_第1张图片
因为Android应用自己根据运行的平台加载相应的布局,所以如果想在代码中通过设置GridLayoutManager,控制每行显示的个数,需要增加一些判断,比较麻烦。比较简便的方法是直接在每种布局的xml中是指RecyclerView的layouManager属性等如下图所示:
ListView更换为RecyclerView的过程知识总结_第2张图片
而且需要在根布局中添加命名空间,注意一下红框的地方。recyclerView:spanCount :设置每行的列数。设置成功后,不需要再在代码中添加判断,就可以直接使用了。

手机竖屏:
ListView更换为RecyclerView的过程知识总结_第3张图片

手机横屏:
这里写图片描述

平板:
这里写图片描述

2、RecyclerView点击事件和长按事件

RecyclerView并没有项ListView那样提供子Item的点击事件和长按事件,所以需要自己封装,我本来采用的是在Adapter中添加两个接口回调,但这样不利于代码重用,在网上看了一些博客,学到了很多方法,总的来说总共有三种方法:

  • 封装接口实现点击事件监听
  • 通过RecyclerView的addOnItemTouchListener()实现
  • 当ItemView attach RecyclerView时实现

三种方式实现及对比博客网址:http://blog.devwiki.net/index.php/2016/07/24/three-ways-click-recyclerview-item.html (ps: 讲解的非常好)
我主要采用的是最后一种:

import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.luckyxmobile.timers4meplus.R;

public class ItemClickSupport {
    private final RecyclerView mRecyclerView;
    private OnItemClickListener mOnItemClickListener;
    private OnItemLongClickListener mOnItemLongClickListener;

    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mOnItemClickListener != null) {
                RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
                mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
            }
        }
    };

    private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            if (mOnItemLongClickListener != null) {
                RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
                return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
            }
            return false;
        }
    };
    private RecyclerView.OnChildAttachStateChangeListener mAttachListener
            = new RecyclerView.OnChildAttachStateChangeListener() {
        @Override
        public void onChildViewAttachedToWindow(View view) {
            if (mOnItemClickListener != null) {
                view.setOnClickListener(mOnClickListener);
            }
            if (mOnItemLongClickListener != null) {
                view.setOnLongClickListener(mOnLongClickListener);
            }
        }

        @Override
        public void onChildViewDetachedFromWindow(View view) {

        }
    };

    private ItemClickSupport(RecyclerView recyclerView) {
        mRecyclerView = recyclerView;
        mRecyclerView.setTag(R.id.item_click_support, this);
        mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
    }

    public static ItemClickSupport addTo(RecyclerView view) {
        ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
        if (support == null) {
            support = new ItemClickSupport(view);
        }
        return support;
    }

    public static ItemClickSupport removeFrom(RecyclerView view) {
        ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
        if (support != null) {
            support.detach(view);
        }
        return support;
    }

    public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
        mOnItemClickListener = listener;
        return this;
    }

    public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
        mOnItemLongClickListener = listener;
        return this;
    }

    private void detach(RecyclerView view) {
        view.removeOnChildAttachStateChangeListener(mAttachListener);
        view.setTag(R.id.item_click_support, null);
    }

    //单击事件
    public interface OnItemClickListener {

        void onItemClicked(RecyclerView recyclerView, int position, View v);
    }
    //长按事件
    public interface OnItemLongClickListener {

        boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
    }
}

调用的时候:

ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
            @Override
            public void onItemClicked(RecyclerView recyclerView, int position, View v) {
              //do something
        });
 ItemClickSupport.addTo(mRecyclerView).setOnItemLongClickListener(new ItemClickSupport.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
                   //do something
                return false;
            }
        });

3、封装RecyclerViewCursorAdapter,使RecyclerView支持CursorAdapter

RecyclerView并不支持CursorAdapter,所以我们可以仿照ListView的CursorAdapter去实现。主要就是注册两个观察者去监听数据库数据的变化。

已经封装好的代码BaseAbstractRecycleCursorAdapter.java

CursorFilter.java

但是有两个地方需要注意一下,一个就是hasStableIds() 这个方法RecyclerView.Adapter中不能复写父类的方法,需要在初始化的时候调用setHasStableIds(true); 来完成相同功能,第二个就是notifyDataSetInvalidated() 这个方法没有,统一修改成notifyDataSetChanged() 方法即可。

4、RecyclerView实现ContextMenu

RecyclerView默认没有实现ContextMenu,所有需要重新封装使其支持ContextMenu.

import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.ContextMenu;
import android.view.View;

/**
 *
 * recyclerView默认没有实现ContextMenu,所以重新封装使其支持ContextMenu
 */
public class RecyclerViewWithContextMenu extends RecyclerView{


    private RecyclerViewContextMenuInfo mContextMenuInfo;

    public RecyclerViewWithContextMenu(Context context) {
        super(context);
    }

    public RecyclerViewWithContextMenu(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public RecyclerViewWithContextMenu(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
        return mContextMenuInfo;
    }

    @Override
    public boolean showContextMenuForChild(View originalView) {
        final int longPressPosition = getChildAdapterPosition(originalView);
        if (longPressPosition >= 0) {
            final long longPressId = getAdapter().getItemId(longPressPosition);
            mContextMenuInfo = new RecyclerViewContextMenuInfo(longPressPosition, longPressId);
            return super.showContextMenuForChild(originalView);
        }
        return false;
    }

    public static class RecyclerViewContextMenuInfo implements ContextMenu.ContextMenuInfo {

        public RecyclerViewContextMenuInfo(int position, long id) {
            this.position = position;
            this.id = id;
        }

        final public int position;
        final public long id;
    }


}

一定要注意三个构造函数的参数!!!我最开始的时候只写了含有context参数的构造函数,然后通过IDE自动生成剩余的两个构造函数的时候,它默认把变量mContextInfo也当做参数传入,导致页面加载布局的时候一直报错,找了好久,才发现是这个地方的错误。
在Activity或Fragment中注册ContextMenu

  //为RecyclerView注册contextMenu
  registerForContextMenu(mRecyclerView);
  //设置监听器
  mAlarmsList.setOnCreateContextMenuListener(this);

stackoverflow上大神们关于这个问题提出了好几种解决方案,大家可以看一下:
https://stackoverflow.com/questions/26466877/how-to-create-context-menu-for-recyclerview

你可能感兴趣的:(Android)