RecyclerView基础

一、RecyclerView是什么
RecyclerView从2014年发布到现在已经很长时间了,使用已经相当普遍。它是support.v7包中的控件,谷歌在高级版本提出一个新的替代ListView、GridView的控件,同样拥有item回收复用的功能。

二、RecyclerView的优点
1.自带了性能优化:ViewHolder
RecylerView封装了viewholder的回收复用,也就是说RecylerView标准化了ViewHolder,编写Adapter面向的是ViewHolder而不再是View了,复用的 逻辑被封装了,写起来更加简单。

2.高度解耦
提供了一种插拔式的体验,高度的解耦,异常的灵活,针对一个Item的显示RecylerView专门抽取出了相应的类,来控制Item的显示,使其的扩展性非常强。例如:你想控制横向或者纵向滑动列表效果可以通过LinearLayoutManager这个类来进行控制(与GridView效果对应的是GridLayoutManager,与瀑布流对应的还有StaggeredGridLayoutManager等),也就是说RecylerView不再拘泥于ListView的线性展示方式,它也可以实现GridView的效果等多种效果。你想控制Item的分隔线,可以通过继承RecylerView的ItemDecoration这个类,然后针对自己的业务需求去抒写代码。

3.动画效果
可以控制Item增删的动画,可以通过ItemAnimator这个类进行控制,当然针对增删的动画,RecylerView有其自己默认的实现。

三、缺点:
1.RecyclerView没有条目点击事件,需要自己写。
2.需要重写addView

四、简单使用

1.布局:

<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="${relativePackage}.${activityClass}" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/text_menu" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyler_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

2.Activity使用

list = JsonUtil.analyMenu(FileUtil.readAssetFile(this));
            adapter = new MyRecyclerAdapter(list);
            //recyclerView的样式全部由LayoutManager来控制,实现低耦合
            //LayoutManager布局摆放管理器(线性摆放、瀑布流),具体可见源码中的构造方法
            recyler_menu.setLayoutManager(new LinearLayoutManager(this));//默认垂直
            recyler_menu.setAdapter(adapter);

其中RecyclerView必须要设置LayoutManager,他是布局管理器,可以设置滑动的方向,每一行摆放的数量(GridView的效果)G,这些是设置详细可参考LayoutManager的源码。

3.Adapter:

public class MyRecyclerAdapter extends
        RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> {

    private List<MenuData> list;

    public MyRecyclerAdapter(List<MenuData> list) {
        // TODO Auto-generated constructor stub
        this.list = list;
    }

    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tv;

        public MyViewHolder(View view) {
            super(view);
            tv = (TextView) view.findViewById(android.R.id.text1);
        }
    }

    @Override
    public int getItemCount() {
        // TODO Auto-generated method stub
        return list.size();
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        // 绑定数据
        holder.tv.setText(list.get(position).getModuleName());
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int arg1) {
        // 创建ViewHolder
        MyViewHolder holder = new MyViewHolder(View.inflate(
                viewGroup.getContext(), android.R.layout.simple_list_item_1,
                null));
        return holder;
    }
}

4.点击事件:
RecyclerView的点击事件(点击、长按)需要通过接口回调实现,在Adapter中定义接口:

public interface OnItemClickListener{
        void onItemClick(View view, int position);
    }

    public void setOnItemClickListener(OnItemClickListener listener){
        this.mOnItemClickListener = listener;
    }

并在view的点击事件中回调给activity:

    @Override
    public void onBindViewHolder(MyViewHolder holder, final int position) {
        // 绑定数据
        holder.tv.setText(list.get(position).getModuleName());
        if(mOnItemClickListener!=null){
            holder.itemView.setOnClickListener(new OnClickListener() {  
                @Override
                public void onClick(View v) {
                    mOnItemClickListener.onItemClick(v, position);
                }
            });
        }
    }
adapter.setOnItemClickListener(new OnItemClickListener() {

                @Override
                public void onItemClick(View view, int position) {
                    Intent intent=new Intent();
                    Class deClass = null;
                    try {
                        String className=list.get(position).getClassName();
                        deClass = Class.forName(className);
                    } catch (ClassNotFoundException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    intent.setClass(MainActivity.this, deClass);
                    startActivity(intent);
                }
            });

五、扩展:
1.封装一个简单的BaseRecyclerAdapter。RecyclerView的通用适配器:
http://blog.csdn.net/lmj623565791/article/details/51118836/
2.解决position可能错位的问题。
3.设置条目长按事件
4.开源的下拉刷新等控件
5.瀑布流

六.RecyclerView的分割线
RecyclerView没有默认的分割线,需要自己绘制。
RecyclerView.ItemDecoration
1)线性的分割线
2)网格的分割线

(1)可以通过修改Theme.Appcompa主题样式里面的android:listSelector或者 android:listDivider属性
达到改变间隔线的大小和颜色哦!(自己尝试下)

<style name="AppTheme" parent="AppBaseTheme">
        <item name="android:listDivider">@drawable/item_divider</item>
</style>

(2)写一个条目装饰类,继承RecyclerView.ItemDecoration

decor=new DividerItemDecoration(this, LinearLayoutManager.HORIZONTAL);
        recylerview.addItemDecoration(decor);

重写ItemDecoration:

public class DividerItemDecoration extends ItemDecoration {

    private int mOrientation = LinearLayoutManager.VERTICAL;
    private Drawable mDivider;
    private int[] attrs= new int[]{
            android.R.attr.listDivider
    };

    public DividerItemDecoration(Context context, int orientation) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs);
        mDivider = typedArray.getDrawable(0);
        typedArray.recycle();
        setOrientation(orientation);
    }

    public void setOrientation(int orientation){
        if(orientation!=LinearLayoutManager.HORIZONTAL&&orientation!=LinearLayoutManager.VERTICAL){
            throw new IllegalArgumentException("哥们,逗我ma?非水平和线性的枚举类型");
        }
        this.mOrientation = orientation;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, State state) {
        //2。调用这个绘制方法, RecyclerView会毁掉该绘制方法,需要你自己去绘制条目的间隔线
        if(mOrientation == LinearLayoutManager.VERTICAL){//垂直
            drawVertical(c,parent);
        }else{//水平
            drawHorizontal(c,parent);
        }

        super.onDraw(c, parent, state);
    }

    private void drawHorizontal(Canvas c, RecyclerView parent) {
        int top = parent.getPaddingTop();
        int bottom = parent.getHeight() - parent.getPaddingBottom();
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount ; i++) {
            View child = parent.getChildAt(i);

            RecyclerView.LayoutParams params = (LayoutParams) child.getLayoutParams();
            int left = child.getRight() + params.rightMargin + Math.round(ViewCompat.getTranslationX(child));
            int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top , right, bottom);
            mDivider.draw(c);
        }
    }

    private void drawVertical(Canvas c, RecyclerView parent) {
        // 画水平线
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount ; i++) {
            View child = parent.getChildAt(i);

            RecyclerView.LayoutParams params = (LayoutParams) child.getLayoutParams();
            int top = child.getBottom() + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child));
            int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top , right, bottom);
            mDivider.draw(c);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
            State state) {
        //1.调用此方法(首先会先获取条目之间的间隙宽度---Rect矩形区域)
        // 获得条目的偏移量(所有的条目都回调用一次该方法)
        if(mOrientation == LinearLayoutManager.VERTICAL){//垂直
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        }else{//水平
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0 );
        }

    }

}

七.RecycleView添加头部和底部
ListView.addHeadView();
ListView.addFooterView();
RecyclerView没有这样的方法,需要自己解决
所以我们通过看ListView的源码学习如何解决这个问题!!

思路
通过偷天换日,其实就是改造RecyclerView和适配器:
(1)给RecyclerView提供添加头部和底部view的方法

public class WrapRecyclerView extends RecyclerView{
    private ArrayList<View> mHeaderViewInfos = new ArrayList<View>();
    private ArrayList<View> mFooterViewInfos = new ArrayList<View>();
    private Adapter mAdapter;

    public WrapRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void addHeaderView(View v) {
        mHeaderViewInfos.add(v);

        // Wrap the adapter if it wasn't already wrapped.
        if (mAdapter != null) {
            if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) {
                mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
            }
        }
    }

    public void addFooterView(View v) {
        mFooterViewInfos.add(v);

        // Wrap the adapter if it wasn't already wrapped.
        if (mAdapter != null) {
            if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) {
                mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
            }
        }
    }

    @Override
    public void setAdapter(Adapter adapter) {
        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }
        super.setAdapter(mAdapter);
    }
}

(2)适配器中onCreateViewHolder和getItemViewType、onBindViewHolder进行类型判断并处理。

public class HeaderViewRecyclerAdapter extends Adapter {
    private Adapter mAdapter;

    ArrayList<View> mHeaderViewInfos;
    ArrayList<View> mFooterViewInfos;

    public HeaderViewRecyclerAdapter(ArrayList<View> headerViewInfos,
            ArrayList<View> footerViewInfos, Adapter adapter) {
        mAdapter = adapter;

        if (headerViewInfos == null) {
            mHeaderViewInfos = new ArrayList<View>();
        } else {
            mHeaderViewInfos = headerViewInfos;
        }

        if (footerViewInfos == null) {
            mFooterViewInfos = new ArrayList<View>();
        } else {
            mFooterViewInfos = footerViewInfos;
        }
    }

    @Override
    public int getItemCount() {
      if (mAdapter != null) {
            return getFootersCount() + getHeadersCount() + mAdapter.getItemCount();
        } else {
            return getFootersCount() + getHeadersCount();
        }
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        //也要划分三个区域
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {//是头部
            return ;
        }
        //adapter body
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                mAdapter.onBindViewHolder(holder, adjPosition);
                return ;
            }
        }
        //footer
    }

    @Override
    public int getItemViewType(int position) {
        //判断当前条目是什么类型的---决定渲染什么视图给什么数据
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {//是头部
            return RecyclerView.INVALID_TYPE;
        }
        //正常条目部分
     // Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getItemViewType(adjPosition);
            }
        }
        //footer部分
        return RecyclerView.INVALID_TYPE-1;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //header
        if(viewType==RecyclerView.INVALID_TYPE){
            return new HeaderViewHolder(mHeaderViewInfos.get(0));
        }else if(viewType==RecyclerView.INVALID_TYPE-1){//footer
            return new HeaderViewHolder(mFooterViewInfos.get(0));
        }
        // Footer (off-limits positions will throw an IndexOutOfBoundsException)
        return mAdapter.onCreateViewHolder(parent, viewType);
    }

    public int getHeadersCount() {
        return mHeaderViewInfos.size();
    }

    public int getFootersCount() {
        return mFooterViewInfos.size();
    }

    private static class HeaderViewHolder extends ViewHolder{

        public HeaderViewHolder(View view) {
            super(view);
        }
    }
}

八.RecycleView交互动画

ItemTouchHelper
回调接口的三个方法:退拽、侧滑、判断事件(通过十六进制来进行与或运算判断状态)
可参考:http://blog.csdn.net/liaoinstan/article/details/51200618

//条目触摸帮助类
        ItemTouchHelper.Callback callback = new MyItemTouchHelperCallback(adapter);
        itemTouchHelper = new ItemTouchHelper(callback);
        itemTouchHelper.attachToRecyclerView(recyclerView);

重写Callback:

public class MyItemTouchHelperCallback extends Callback {
    private ItemTouchMoveListener moveListener;

    public MyItemTouchHelperCallback(ItemTouchMoveListener moveListener) {
        this.moveListener = moveListener;
    }

    //Callback回调监听时先调用的,用来判断当前是什么动作,比如判断方向(意思就是我要监听哪个方向的拖动)
    @Override
    public int getMovementFlags(RecyclerView recyclerView, ViewHolder holder) {
        //方向:up,down,left,right
        //常量:
        int up = ItemTouchHelper.UP;//1  0x0001
        int down = ItemTouchHelper.DOWN;//2 0x0010
//      ItemTouchHelper.LEFT
//      ItemTouchHelper.RIGHT
        //我要监听的拖拽方向是哪两个方向。
        int dragFlags = ItemTouchHelper.UP|ItemTouchHelper.DOWN;
        //我要监听的swipe侧滑方向是哪个方向
//      int swipeFlags = 0;
        int swipeFlags = ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;


        int flags = makeMovementFlags(dragFlags, swipeFlags);
        return flags;
    }

    @Override
    public boolean isLongPressDragEnabled() {
        // 是否允许长按拖拽效果
        return true;
    }

    //当移动的时候回调的方法--拖拽
    @Override
    public boolean onMove(RecyclerView recyclerView, ViewHolder srcHolder, ViewHolder targetHolder) {
        if(srcHolder.getItemViewType()!=targetHolder.getItemViewType()){
            return false;
        }
        // 在拖拽的过程当中不断地调用adapter.notifyItemMoved(from,to);
        boolean result = moveListener.onItemMove(srcHolder.getAdapterPosition(), targetHolder.getAdapterPosition());
        return result;
    }

    //侧滑的时候回调的
    @Override
    public void onSwiped(ViewHolder holder, int arg1) {
        // 监听侧滑,1.删除数据;2.调用adapter.notifyItemRemove(position)
        moveListener.onItemRemove(holder.getAdapterPosition());
    }


    @Override
    public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
        //判断选中状态
        if(actionState!=ItemTouchHelper.ACTION_STATE_IDLE){
            viewHolder.itemView.setBackgroundColor(viewHolder.itemView.getContext().getResources().getColor(R.color.colorPrimary_pink));
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) {
        // 恢复
        viewHolder.itemView.setBackgroundColor(Color.WHITE);
        super.clearView(recyclerView, viewHolder);
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView,
            ViewHolder viewHolder, float dX, float dY, int actionState,
            boolean isCurrentlyActive) {
        //dX:水平方向移动的增量(负:往左;正:往右)范围:0~View.getWidth  0~1
        if(actionState==ItemTouchHelper.ACTION_STATE_SWIPE){
            //透明度动画
            float alpha = 1-Math.abs(dX)/viewHolder.itemView.getWidth();
            viewHolder.itemView.setAlpha(alpha);//1~0
            viewHolder.itemView.setScaleX(alpha);//1~0
            viewHolder.itemView.setScaleY(alpha);//1~0
        }


        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState,
                isCurrentlyActive);
    }


}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值