您的位置:首页 > 移动开发 > Android开发

RecyclerView 的研究和使用

2016-03-11 19:21 525 查看
</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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 控件的使用