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

Android RecyclerView使用

2017-02-06 15:59 489 查看
辛苦堆砌,转载请注明出处,谢谢!

Android中的RecyclerView类似于ListView,但是它使用更加灵活,可以实现更复杂的列表结构。使用RecyclerView需要了解几个概念。

首先是布局管理器,RecyclerView使用布局管理器控制子视图之间的布局关系,有两种布局管理器:

(1)LinearLayoutManager:把子视图按照垂直或者水平的方式进行排列;

(2)GridLayoutManager:把子视图按照垂直或者水平的方式放置在网格中,网格不要求必须是矩阵形式的,可以是每行的列数不同表格。

其次是RecyclerView.Adapter,它用来将数据集的变化反映到视图中。我们必须派生Adapter类,实现数据更新逻辑。RecyclerView.Adapter使用ViewHolder模式,我们需要派生RecyclerView. ViewHolder,这样可以防止过多调用findViewById,提高效率。

最后是RecyclerView.ItemDecoration,它用来提供子视图之间填充的内容,如果不提供,默认和ListView类似,子视图之间紧密挨在一起,我们可以派生该类,实现自己的填充。该类使用装饰器模式,我们可以为RecyclerView添加和移除各种装饰,装饰可以相互叠加。

我们示例代码的结果如图所示,分别给出了垂直,网格和水平三种布局方式:


  

  


水平的不太好展示,可以自己运行程序,然后左右滑动看看。下面看看我们的代码,我们首先定义了两个尺寸,在实现decoration时会用到,在dimens.xml文件中

<dimen name="decoration_margin">10dp</dimen>
<dimen name="decoration_line_stroke">2dp</dimen>然后看看添加空隙的decoration
package com.yjp.recyclerviewtest;

import android.content.Context;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class MarginDecoration extends RecyclerView.ItemDecoration {

private int mDimen;

public MarginDecoration(Context context) {
super();
mDimen = context.getResources().getDimensionPixelOffset(R.dimen.decoration_margin);
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(mDimen, mDimen, mDimen, mDimen);
}
}
MarginDecoration派生自RecyclerView.ItemDecoration,在构造函数中加载了dimens.xml中定义的decoration_margin,然后重写了getItemOffsets方法,该方法返回一个矩形,用来说明两个条目之间的间隙。

上面示例图片中在网格布局中,一列两个条目之间用短横线连接,这是由另一个decoration实现的

package com.yjp.recyclerviewtest;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class LineDecoration extends RecyclerView.ItemDecoration {

private Paint mLinePaint;
private int mLineLength;

public LineDecoration(Context context) {
super();

mLineLength = context.getResources()
.getDimensionPixelOffset(R.dimen.decoration_margin);
int lineStroke = context.getResources()
.getDimensionPixelOffset(R.dimen.decoration_line_stroke);

mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mLinePaint.setColor(Color.RED);
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setStrokeWidth(lineStroke);
}

@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
RecyclerView.LayoutManager layoutManager =
parent.getLayoutManager();

for (int i = 0; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
boolean isLeft = parent.getChildAdapterPosition(view) % 3 == 1;

if (isLeft) {
final int childRight = layoutManager.getDecoratedRight(view);
final int childTop = layoutManager.getDecoratedTop(view);
final int childBottom = layoutManager.getDecoratedBottom(view);
int y = childTop + (childBottom - childTop) / 2;

c.drawLine(childRight - mLineLength, y,
childRight + mLineLength, y,
mLinePaint);
}
}
}
}
该decoration重写了onDraw方法,通过position判断是否是两个一行的前一个条目,如果是的话,绘制短直线。

现在我们就准备好了两个Decoration,后面会将其交给RecyclerView使用。现将他们放在这里,我们再来看看Adapter实现

package com.yjp.recyclerviewtest;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ItemHolder> {

private LayoutInflater mLayoutInflater;
private int mItemsCount;
private OnItemClickListener mOnItemClickListener;

public interface OnItemClickListener {
void onItemClicked(ItemHolder holder, int position);
}

public ItemAdapter(Context context) {
mLayoutInflater = LayoutInflater.from(context);
}

@Override
public ItemAdapter.ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = mLayoutInflater.inflate(R.layout.item_layout, parent, false);
return new ItemHolder(itemView, this);
}

@Override
public void onBindViewHolder(ItemAdapter.ItemHolder holder, int position) {
holder.setContent("条目" + position);
}

@Override
public int getItemCount() {
return mItemsCount;
}

public void insertItem(int position) {
mItemsCount++;
notifyItemInserted(position);
}

public void removeItem(int position) {
mItemsCount--;
notifyItemRemoved(position);
}

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

public OnItemClickListener getOnItemClickListener() {
return mOnItemClickListener;
}

public static class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView mContentView;
private ItemAdapter mItemAdapter;

public ItemHolder(View itemView, ItemAdapter adapter) {
super(itemView);
mItemAdapter = adapter;

itemView.setOnClickListener(this);

mContentView = (TextView) itemView.findViewById(R.id.content);
}

public void setContent(String content) {
mContentView.setText(content);
}

public String getContent() {
return mContentView.getText().toString();
}

@Override
public void onClick(View v) {
OnItemClickListener listener = mItemAdapter.getOnItemClickListener();
if (listener != null) {
listener.onItemClicked(this, getAdapterPosition());
}
}
}
}
重写了三个主要的方法:
public ItemAdapter.ItemHolder
onCreateViewHolder(ViewGroup parent, int viewType)

在创建ViewHolder时调用,主要创建我们自定义的ViewHolder并在其构造函数调用findViewById,拿到对应视图的引用。

public void onBindViewHolder(ItemAdapter.ItemHolder
holder, int position)

在绑定数据时调用,如果使用回收的视图进行显示,不会调用上面的方法,只会调用这个方法,这样就可以减少findViewById的调用,同时可以复用资源。

public int getItemCount()

返回当前条目数。

这个示例中的Adapter主要依靠调用insertItem和removeItem来动态添加和移除条目数据,另外我们还设置了一个点击监听器,来处理点击事件,最后,可以注意看看ViewHolder,它是Adapter访问操作视图的媒介。下面是我们item的布局,很简单,只有一个TextView

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_blue_bright">

<TextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textAppearance="?android:textAppearanceLarge"
android:textStyle="bold"/>

</FrameLayout>现在我们就准备好了decoration和adapter,就可以使用RecyclerView了,首先是布局
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
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="com.yjp.recyclerviewtest.MainActivity">

<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
我们还使用了一个菜单,资源文件如下:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">

<item android:title="垂直"
android:id="@+id/vertical"
app:showAsAction="never" />
<item android:title="水平"
android:id="@+id/horizontal"
app:showAsAction="never" />
<item android:title="网格"
android:id="@+id/grid"
app:showAsAction="never" />
<item android:title="添加"
android:id="@+id/add"
app:showAsAction="never" />
<item android:title="删除"
android:id="@+id/remove"
app:showAsAction="never" />
</menu>最后是我们的类文件:
package com.yjp.recyclerviewtest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements ItemAdapter.OnItemClickListener {

private RecyclerView mRecyclerView;
private ItemAdapter mItemAdapter;
private MarginDecoration mMarginDecoration;
private LineDecoration mLineDecoration;

private LinearLayoutManager mHorizontalLayoutManager;
private LinearLayoutManager mVerticalLayoutManager;
private GridLayoutManager mGridLayoutManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//装饰
mMarginDecoration = new MarginDecoration(this);
mLineDecoration = new LineDecoration(this);

//创建LayoutManager
mHorizontalLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
mVerticalLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mGridLayoutManager = new GridLayoutManager(this, 2, LinearLayoutManager.VERTICAL, false);
mGridLayoutManager.setSpanSizeLookup(new CustomGridSpanSizeLookup());

mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);

//设置默认布局管理器
mRecyclerView.setLayoutManager(mVerticalLayoutManager);

//设置装饰
mRecyclerView.addItemDecoration(mMarginDecoration);

//设置Adapter
mItemAdapter = new ItemAdapter(this);
mRecyclerView.setAdapter(mItemAdapter);

//设置监听器
mItemAdapter.setOnItemClickListener(this);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.options, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.horizontal:
mRecyclerView.setLayoutManager(mHorizontalLayoutManager);
mRecyclerView.removeItemDecoration(mLineDecoration);
return true;
case R.id.vertical:
mRecyclerView.setLayoutManager(mVerticalLayoutManager);
mRecyclerView.removeItemDecoration(mLineDecoration);
return true;
case R.id.grid:
mRecyclerView.setLayoutManager(mGridLayoutManager);
mRecyclerView.addItemDecoration(mLineDecoration);
return true;
case R.id.add:
mItemAdapter.insertItem(mItemAdapter.getItemCount());
return true;
case R.id.remove:
mItemAdapter.removeItem(mItemAdapter.getItemCount() - 1);
return true;
default:
return false;
}
}

@Override
public void onItemClicked(ItemAdapter.ItemHolder holder, int position) {
Toast.makeText(this, holder.getContent() + "被点击", Toast.LENGTH_SHORT).show();
}
}
注释已经写的很清楚了,唯一需要特别说明的一个东西,我们前面没有准备,就是为GridLayoutManager设置的CustomGridSpanSizeLookup,代码如下:
package com.yjp.recyclerviewtest;

import android.support.v7.widget.GridLayoutManager;

public class CustomGridSpanSizeLookup extends GridLayoutManager.SpanSizeLookup {

@Override
public int getSpanSize(int position) {
return (position % 3 == 0 ? 2: 1);
}

}
它派生自GridLayoutManager.SpanSizeLookup,一个Item占用的span宽度,当position能被3整除时,一个item占用一行,也就是2个span,无法整除的,一个占用1个span,就产生了我们上面网格布局的效果。

可以看到,使用RecyclerView并不是很复杂,但是要理解各个类的作用以及相互之间的关系,在合适的类中做合适的工作,最后将它们放在一起就能产生很好的效果。

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