RecyclerView 的研究和使用

</pre>  最近在需求中遇到实现图片的滚动的效果,之前使用Gallry 这个控件就可以实现,但是在Gallry 这个控件在anroid中被舍弃掉了,因为它比较耗内存的原因,后来很多中解决方案的出现去解决图片滚动的效果比如用ScrollView 还有用viewPager 这种控件,但是自身都存在着很多缺陷,后来出现了RecyclerView 这个控件,替代了<span style="font-size:18px">Gallry 。</span><p></p><p><span style="font-size:18px"><span style="font-size:18px"></span></span></p><p><span style="font-size:18px"><span style="font-size:18px">具体使用方法:</span></span></p><p><span style="font-size:18px"><span style="font-size:18px">1 在代码中会经常这么写;</span></span></p><p><span style="font-size:18px"><span style="font-size:18px"></span></span></p><p><span style="font-size:18px"><span style="font-size:18px"></span></span></p><pre name="code" class="java">LinearLayoutManager NearbySitesLayoutManager = new LinearLayoutManager(mFragment.getActivity());
    NearbySitesLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
    mNearBySitesRecyclerView.setLayoutManager(NearbySitesLayoutManager);
    mNearBySitesRecyclerView.setHasFixedSize(true);
    mNearbySitesImageAdapter = new NearbyImageAdapter(mFragment.getActivity(), mDetailCacheBean.scenicList);
    mNearBySitesRecyclerView.setAdapter(mNearbySitesImageAdapter);
</pre><pre name="code" class="java">

(1)首先我们在创建LayoutManager,对于RecyclerView 这个控件 常见用到的LayoutManager 有:

  • LinearLayoutManager

  • GridLayoutManager

  • StaggeredGridLayoutManager   

LayoutManager:用来确定每一个item如何进行排列摆放,何时展示和隐藏。回收或重用一个View的时候,LayoutManager会向适配器请求新的数据来替换旧的数据,这种机制避免了创建过多的View和频繁的调用findViewById方法,

(2) 

NearbySitesLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL) 

是设置滚动方向的

(3)

mNearBySitesRecyclerView.setHasFixedSize(true);

是固定滚动的item 高度,最好设置下

(4)

 mNearbySitesImageAdapter = new NearbyImageAdapter(mFragment.getActivity(), mDetailCacheBean.scenicList);
    mNearBySitesRecyclerView.setAdapter(mNearbySitesImageAdapter);

设置adapter 这个和Gallery 有些类似


其次是adapter 的创建,和之前adapter 创建有些区别,

 public class NearbyImageAdapter extends RecyclerView.Adapter<NearbyImageAdapter.ViewHolder> {
  @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {....
 // 这里是创建view 的地方,和常规的adaper 的getView 类似,这里还可以设置设置view 的尺寸等
}
@Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
// 这个接口是现实显示数据的地方
}
public static class ViewHolder extends RecyclerView.ViewHolder {
 // 这里是创建ViewHolder的地方 和常规的Adaper 类似
}
  @Override
   public int getItemCount() {
       // item 的个数,类似常规的getCount()
   }
其实在adapter 的使用上,和常用的adapter 有些类似,只是表现的形式有点不一样。


再次,其次在 RecyclerView 的控制如何控制每个item 之间的距离时,网上有很多的说法,其中最常见的一种方法,就是继承ItemDecoration 这个类,常见的写法是

public class SpaceItemDecoration extends RecyclerView.ItemDecoration{  
  
        private int space;  
  
        public SpaceItemDecoration(int space) {  
            this.space = space;  
        }  
  
        @Override  
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {  
  
            if(parent.getChildPosition(view) != 0)  
                outRect.top = space;  
        }  
    }
这种方式去控制item 的空隙,最后用 addItemDecoration()这种方法设置间距其实在项目中,我发现一种相对比较简单的方式,比如要是设置在每个item第一张图片的左边没有 间距,同时最后一张图片的右边没有 间距,同时每个item 的之间的 间距为20dp 我想这是常见的滚动需求。我的实现方式如下:

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout...., parent, false);
        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
        params.rightMargin = 20dp;
        view.setLayoutParams(params);
        ViewHolder vh = new ViewHolder(view);
        return vh;
    }
// 在这个方法,首先给个每个item 创建的时候,设置20dp 的空隙,
  @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) holder.itemView.getLayoutParams();
        if (DataList.size() > 0 && position == DataList.size() - 1) {
            params.rightMargin = 0;
        } else {
            params.rightMargin = 20dp
        }
}

去根据 position 设置 右间距 这是一种控制每个item 间距的一中处理方式。

再次,就是针对每个item 设置点击事件,因为RecyclerView 没有itemOnClick 事件,我们一般会在item 的OnClick 事件中去回调自定义点击接口:

 holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mItemClickListener != null) {
                    mItemClickListener.onItemClick(v, .);
                }
            }
        });


通过这种方式去实现点击的回调。


(5) 在使用RecycleView 如果每个item 的宽高的高度不一致,有的时候,会出现高度不一样,解决方案如下:

mMutexRecyclerLayout = (RecyclerView) view.findViewById(R.id.mutex_recycler_layout);
MyLayoutManager recyclerLayoutManager = new MyLayoutManager(mContext);


重写recyclerLayoutManager 代码如下:


class MyLayoutManager extends LinearLayoutManager {

    public MyLayoutManager(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }


    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
        int childCount = getItemCount();
        int maxHigh = 0;
        for (int i = 0; i < childCount; i++) {
            View view = recycler.getViewForPosition(i);
            if (view != null) {
                measureChild(view, widthSpec, heightSpec);
                int measuredHeight = view.getMeasuredHeight();
                maxHigh = maxHigh >= measuredHeight ? maxHigh : measuredHeight;
            }
        }
        if (maxHigh == 0) {
            super.onMeasure(recycler, state, widthSpec, heightSpec);
        } else {
            setMeasuredDimension(View.MeasureSpec.getSize(widthSpec), maxHigh);
        }
    }
}
 

在onMeasure 的时候,根据测量实际的高度 动态的分配测量的高度

(6)

在item 中默认之间是没有设置间隙的,解决这个问题的之前 的方案是:

// 在这个方法,首先给个每个item 创建的时候,设置20dp 的空隙,
  @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) holder.itemView.getLayoutParams();
        if (DataList.size() > 0 && position == DataList.size() - 1) {
            params.rightMargin = 0;
        } else {
            params.rightMargin = 20dp
        }
}

现在的结局方案是:

mRecyclerView = (RecyclerView) mGiftChoiceLayout.findViewById(R.id.gifts_givers_recyclerview);
mGifsGiverChoiceAdapter = new GifsGiverChoiceAdapter(this, mHotelGiftsChoiceModelList);
mRecyclerView.setAdapter(mGifsGiverChoiceAdapter);
LinearLayoutManager recyclerLayoutManager = new LinearLayoutManager(this);
recyclerLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
RecyclerViewItemDecoration itemDecoration = new RecyclerViewItemDecoration(LinearLayoutManager.HORIZONTAL);
itemDecoration.setColor(Color.WHITE);
itemDecoration.setSize(DeviceInfoUtil.getPixelFromDip(10));
mRecyclerView.addItemDecoration(itemDecoration);

自定义itemDecoration 实现 item 之间的空隙: 代码如下



package ctrip.android.hotel.order.librichtexteditor.viewholders;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
 * Created by bkhu on 16/11/22.
 */
public class RecyclerViewItemDecoration extends RecyclerView.ItemDecoration {

    /**
     * 水平方向
     */
    public static final int HORIZONTAL = LinearLayoutManager.HORIZONTAL;

    /**
     * 垂直方向
     */
    public static final int VERTICAL = LinearLayoutManager.VERTICAL;
    private Paint mPaint;
    private int mSize;
    private int mOrientation;

    public RecyclerViewItemDecoration(int orientation) {
        this.mOrientation = orientation;
        mPaint = new Paint();
    }


    public void setSize(int size) {
        this.mSize = size;
    }

    public void setColor(int color) {
        mPaint.setColor(color);
    }


    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        if (mOrientation == HORIZONTAL) {
            drawVertical(c, parent);
        } else if (mOrientation == VERTICAL) {
            drawHorizontal(c, parent);
        }
    }


    private void drawVertical(Canvas canvas, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();
        int childCount = parent.getChildCount();
        int right = 0;
        for (int i = 0; i < childCount; i++) {
            View itemView = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) itemView.getLayoutParams();
            params.rightMargin = mSize;
            itemView.setLayoutParams(params);
            final int left = itemView.getRight();
            right = left + mSize;
            if (i == childCount - 1)
                right = left;
            canvas.drawRect(left, top, right, bottom, mPaint);


        }

    }

    private void drawHorizontal(Canvas canvas, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();
        int childCount = parent.getChildCount();
        int bottom = 0;
        for (int i = 0; i < childCount; i++) {
            View itemView = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) itemView.getLayoutParams();
            params.bottomMargin = mSize;
            itemView.setLayoutParams(params);
            final int top = itemView.getBottom();
            bottom = top + mSize;
            if (i == childCount - 1)
                bottom = top;
            canvas.drawRect(left, top, right, bottom, mPaint);

        }


    }


}



这里重写onDraw 这个方法,针对垂直和水平的布局,都能在每个item 的之间设置间隙。


具体实现原理的页面在这里

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0630/4400.html



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值