您的位置:首页 > 其它

RecyclerView的使用

2016-01-22 17:29 253 查看

RecyclerView的基本知识

适配器

示例代码:

public class GalleryAdapter extends
RecyclerView.Adapter<GalleryAdapter.ViewHolder>
{

private LayoutInflater mInflater;
private List<Integer> mDatas;

public GalleryAdapter(Context context, List<Integer> datats)
{
mInflater = LayoutInflater.from(context);
mDatas = datats;
}

public static class ViewHolder extends RecyclerView.ViewHolder
{
public ViewHolder(View arg0)
{
super(arg0);
}

ImageView mImg;
TextView mTxt;
}

@Override
public int getItemCount()
{
return mDatas.size();
}

/**
* 创建ViewHolder
*/
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i)
{
View view = mInflater.inflate(R.layout.activity_index_gallery_item,
viewGroup, false);
ViewHolder viewHolder = new ViewHolder(view);

viewHolder.mImg = (ImageView) view
.findViewById(R.id.id_index_gallery_item_image);
return viewHolder;
}

/**
* 设置值
*/
@Override
public void onBindViewHolder(final ViewHolder viewHolder, final int i)
{
viewHolder.mImg.setImageResource(mDatas.get(i));
}

}


可以看到数据适配器与BaseAdapter比较发生了相当大的变化,主要有3个方法:

getItemCount 这个不用说,获取总的条目数

onCreateViewHolder 创建ViewHolder

onBindViewHolder 将数据绑定至ViewHolder

可见,RecyclerView对ViewHolder也进行了一定的封装,但是如果你仔细观察,你会发出一个疑问,ListView里面有个getView返回View为Item的布局,那么这个Item的样子在哪控制?

其实是这样的,我们创建的ViewHolder必须继承RecyclerView.ViewHolder,这个RecyclerView.ViewHolder的构造时必须传入一个View,这个View相当于我们ListView getView中的convertView (即:我们需要inflate的item布局需要传入)。

还有一点,ListView中convertView是复用的,在RecyclerView中,是把ViewHolder作为缓存的单位了,然后convertView作为ViewHolder的成员变量保持在ViewHolder中,也就是说,假设没有屏幕显示10个条目,则会创建10个ViewHolder缓存起来,每次复用的是ViewHolder,所以他把getView这个方法变为了onCreateViewHolder。有兴趣的自己打印下Log,测试下。

使用

在使用前必须倒入recyclerView V7包。

布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".gallery.MainActivity">

<android.support.v7.widget.RecyclerView
android:id="@+id/id_recyclerview_horizontal"
android:layout_width="match_parent"
android:layout_height="120dp"
android:background="#FF0000"
android:scrollbars="none"
></android.support.v7.widget.RecyclerView>
</RelativeLayout>


其item:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:text="1"
android:gravity="center"
android:background="#2200ff00"

/>
</RelativeLayout>


在Activity中:

//建立适配器
adapter = new ListAdapter(getLayoutInflater(),mDates);

LinearLayoutManager manager = new LinearLayoutManager(this);
//设置布局管理器
recyclerView.setLayoutManager(manager);
//添加分割线
recyclerView.addItemDecoration(new DividerItemDecoration(this,
DividerItemDecoration.VERTICAL_LIST));
//设置Item增加、移除动画
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);


设置布局管理器

RecyclerView.LayoutManager吧,这是一个抽象类,好在系统提供了3个实现类:

LinearLayoutManager 现行管理器,支持横向、纵向。

LinearLayoutManager manager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);//选择方向


GridLayoutManager 网格布局管理器

GridLayoutManager manager = new GridLayoutManager(this,4);


StaggeredGridLayoutManager 瀑布就式布局管理器

StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);


根据不同的样式选择不同的LayoutManager。

添加分割线

我们可以通过该方法添加分割线:

mRecyclerView.addItemDecoration()


该方法的参数为RecyclerView.ItemDecoration,该类为抽象类,官方目前并没有提供默认的实现类.

网上大神实现了两个实现类。

DividerItemDecoration类用于LinearLayoutManager 的分割线:

package com.example.myrecyclerview.list;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;

/**
* Created by 陈猛 on 2016/1/22.
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {

private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};

public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;

public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;

private Drawable mDivider;

private int mOrientation;

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

public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}

@Override
public void onDraw(Canvas c, RecyclerView parent) {
//        Log.v("recyclerview - itemdecoration", "onDraw()");

if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}


该实现类可以看到通过读取系统主题中的 android.R.attr.listDivider作为Item间的分割线,并且支持横向和纵向。

获取到listDivider以后,该属性的值是个Drawable,在getItemOffsets中,outRect去设置了绘制的范围。onDraw中实现了真正的绘制。

我们在原来的代码中添加一句:

mRecyclerView.addItemDecoration(new DividerItemDecoration(this,
DividerItemDecoration.VERTICAL_LIST));


该分割线是系统默认的,你可以在theme.xml中找到该属性的使用情况。那么,使用系统的listDivider有什么好处呢?就是方便我们去随意的改变,该属性我们可以直接声明在:

<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:listDivider">@drawable/divider_bg</item>
</style>


然后自己写个drawable即可,下面我们换一种分隔符:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >

<gradient
android:centerColor="#ff00ff00"
android:endColor="#ff0000ff"
android:startColor="#ffff0000"
android:type="linear" />
<size android:height="4dp"android:width="1dp"/>
</shape>


DividerGridItemDecoration为后两者用的:

package com.example.myrecyclerview.grid;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;

/**
* Created by 陈猛 on 2016/1/22.
*/
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration
{

private static final int[] ATTRS = new int[] { android.R.attr.listDivider };
private Drawable mDivider;

public DividerGridItemDecoration(Context context)
{
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
}

@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state)
{

drawHorizontal(c, parent);
drawVertical(c, parent);

}

private int getSpanCount(RecyclerView parent)
{
// 列数
int spanCount = -1;
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager)
{

spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager)
{
spanCount = ((StaggeredGridLayoutManager) layoutManager)
.getSpanCount();
}
return spanCount;
}

public void drawHorizontal(Canvas c, RecyclerView parent)
{
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++)
{
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getLeft() - params.leftMargin;
final int right = child.getRight() + params.rightMargin
+ mDivider.getIntrinsicWidth();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}

public void drawVertical(Canvas c, RecyclerView parent)
{
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++)
{
final View child = parent.getChildAt(i);

final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getTop() - params.topMargin;
final int bottom = child.getBottom() + params.bottomMargin;
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicWidth();

mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}

private boolean isLastColum(RecyclerView parent, int pos, int spanCount,
int childCount)
{
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager)
{
if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边
{
return true;
}
} else if (layoutManager instanceof StaggeredGridLayoutManager)
{
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL)
{
if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边
{
return true;
}
} else
{
childCount = childCount - childCount % spanCount;
if (pos >= childCount)// 如果是最后一列,则不需要绘制右边
return true;
}
}
return false;
}

private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,
int childCount)
{
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager)
{
childCount = childCount - childCount % spanCount;
if (pos >= childCount)// 如果是最后一行,则不需要绘制底部
return true;
} else if (layoutManager instanceof StaggeredGridLayoutManager)
{
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
// StaggeredGridLayoutManager 且纵向滚动
if (orientation == StaggeredGridLayoutManager.VERTICAL)
{
childCount = childCount - childCount % spanCount;
// 如果是最后一行,则不需要绘制底部
if (pos >= childCount)
return true;
} else
// StaggeredGridLayoutManager 且横向滚动
{
// 如果是最后一行,则不需要绘制底部
if ((pos + 1) % spanCount == 0)
{
return true;
}
}
}
return false;
}

@Override
public void getItemOffsets(Rect outRect, int itemPosition,
RecyclerView parent)
{
int spanCount = getSpanCount(parent);
int childCount = parent.getAdapter().getItemCount();
if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边
{
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(),
mDivider.getIntrinsicHeight());
}
}
}


设置Item增加、移除动画

temAnimator也是一个抽象类,好在系统为我们提供了一种默认的实现类,期待系统多

添加些默认的实现。

借助默认的实现,当Item添加和移除的时候,添加动画效果很简单:

// 设置item动画

mRecyclerView.setItemAnimator(new DefaultItemAnimator());


注意,这里更新数据集不是用adapter.notifyDataSetChanged()而是

notifyItemInserted(position)与notifyItemRemoved(position)

否则没有动画效果。

上述为adapter中添加了两个方法:

public void addData(int position) {
mDatas.add(position, "Insert One");
notifyItemInserted(position);
}

public void removeData(int position) {
mDatas.remove(position);
notifyItemRemoved(position);
}


点击事件

RecyclerView并没有提供点击事件,我们可以添加一个回调函数在adapter中:

public interface OnItemClickListener{
void OnClick(View view,int position);
}
private OnItemClickListener onItemClickListener;

public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.tv.setText(mDate.get(position));
//为每一个itemView设置点击事件
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(onItemClickListener!=null){
onItemClickListener.OnClick(view,position);
}

}
});
}


到此为止其基本的设置就介绍完了,下面的是mode:

List:

ListActivity:

package com.example.myrecyclerview.list;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import com.example.myrecyclerview.R;

import java.util.ArrayList;
import java.util.List;

/**
* Created by 陈猛 on 2016/1/21.
*/
public class ListActivity extends Activity{
private RecyclerView recyclerView;
private ListAdapter adapter;
private List<String> mDates;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_activity);
recyclerView = (RecyclerView) findViewById(R.id.list_recyclerview);
initData();
//建立适配器 adapter = new ListAdapter(getLayoutInflater(),mDates); LinearLayoutManager manager = new LinearLayoutManager(this); //设置布局管理器 recyclerView.setLayoutManager(manager); //添加分割线 recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST)); //设置Item增加、移除动画 recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(adapter);
}

protected void initData()
{
mDates = new ArrayList<String>();
for (int i = 'A'; i < 'z'; i++)
{
mDates.add("" + (char) i);
}
}
}



ListAdapter:

package com.example.myrecyclerview.list;

import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.Adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.example.myrecyclerview.R;

import java.util.List;

/**
* Created by 陈猛 on 2016/1/21.
*/
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder> {
private LayoutInflater inflater;
private List<String> mDate;

public ListAdapter(LayoutInflater inflater, List<String> mDate) {
this.inflater = inflater;
this.mDate = mDate;
}

public interface OnItemClickListener{
void OnClick(View view,int position);
}
private OnItemClickListener onItemClickListener;

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

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.list_item,parent,false);
ViewHolder viewHolder = new ViewHolder(view);

return viewHolder;
}

@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.tv.setText(mDate.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(onItemClickListener!=null){
onItemClickListener.OnClick(view,position);
}

}
});
}

@Override
public int getItemCount() {
return mDate.size();
}

public class ViewHolder extends RecyclerView.ViewHolder{

TextView tv;
public ViewHolder(View itemView) {
super(itemView);
tv= (TextView) itemView.findViewById(R.id.textview);
}
}

//增加item
public void addData(int position) {
mDate.add(position, "Insert One");
notifyItemInserted(position);
}

//删除item
public void removeData(int position) {
mDate.remove(position);
notifyItemRemoved(position);
}
}


其分割线的代码已经在上面展示过这里省略。

效果如下:



Grid:

GridActivity:

package com.example.myrecyclerview.grid;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.example.myrecyclerview.R;
import com.example.myrecyclerview.list.DividerItemDecoration;
import com.example.myrecyclerview.list.ListAdapter;

import java.util.ArrayList;
import java.util.List;

/**
* Created by 陈猛 on 2016/1/22.
*/
public class GridActivity extends Activity {

private RecyclerView recyclerView;
private ListAdapter adapter;
private List<String> mDates;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_activity);
recyclerView = (RecyclerView) findViewById(R.id.list_recyclerview);
initData();
adapter = new ListAdapter(getLayoutInflater(),mDates);
GridLayoutManager manager = new GridLayoutManager(this,4);
recyclerView.setLayoutManager(manager);
recyclerView.addItemDecoration(new DividerGridItemDecoration(this));
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(new DefaultItemAnimator());
adapter.setOnItemClickListener(new ListAdapter.OnItemClickListener() {

@Override
public void OnClick(View view, int position) {
if(position%2==0){
adapter.addData(position);
}else {
adapter.removeData(position);
}

}
});

}

protected void initData()
{
mDates = new ArrayList<String>();
for (int i = 'A'; i < 'z'; i++)
{
mDates.add("" + (char) i);
}
}
}


效果图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: