RecyclerView是经典的ListView的进化与升华,它比ListView更加灵活(在android5.0 以后会逐步替代 ListView),但也因此引入了一定的复杂性。
我们知道,ListView通过使用ViewHolder来提升性能(ListView 的二级优化)。ViewHolder通过保存item中使用到的控件的引用来减少findViewById的调用,以此使ListView滑动得更加顺畅。但这种模式即使不使用也无妨。
同ListView一样,RecyclerView也需要使用Adapter。Adapter的作用主要是负责一个item中视图的布局以及信息的展示。
</pre><pre name="code" class="java">使用开发工具为 AndroidStudio ,在genymotion模拟器上运行,版本为android 6.0。 在 build.gradle 中进行如下图所示的配置(红色框部分):
所需的布局文件:content_main.xml :
<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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_Info"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
布局文件 home_item.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="72dp"
android:layout_margin="5dp"
android:background="#9ffff0">
<TextView
android:id="@+id/tv_num"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:background="#9ffff0">
<TextView
android:id="@+id/tv_stagger"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"/>
</LinearLayout>
自定义分割线需要在 styles.xml 中(主题),如果使用分割线,则不能实现动态图中的最后一个“ 瀑布流”。把 MainActivity 中adapter监听及重载方法注释掉(当然这样有些类就没有其作用),可看到如下图所示:
如下就是没有使用分割线,得到的动态图:
MainActivity 类:
package com.crazy.crazyanti;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends Activity {
private RecyclerView mRecyclerView;
private List<String> mDatas;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
mRecyclerView = (RecyclerView) findViewById(R.id.rv_Info);
// 设置布局容器
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(layoutManager);
// 初始化数据
initData();
// 适配器
adapter = new MyAdapter(this, mDatas);
mRecyclerView.setAdapter(adapter);
// 添加分割线(为了实现动态图的效果,此处不能添加分割线,分割线在 github 上下载)
// mRecyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
// 设置监听
adapter.setmOnItemClickListener(new MyAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, "position" + position + "Click", Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int position) {
Toast.makeText(MainActivity.this, "position" + position + "Long Click", Toast.LENGTH_SHORT).show();
}
});
}
private void initData() {
mDatas = new ArrayList<>();
for (int i = 'A'; i <= 'z'; i++){
mDatas.add("" + (char)i);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id){
case R.id.action_GridView:
mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4));
break;
case R.id.action_ListView:
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
break;
case R.id.action_HorizontalGridView:
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,
StaggeredGridLayoutManager.HORIZONTAL));
break;
case R.id.action_StaggeredGridView:
Intent intent = new Intent(this, StaggeredActivity.class);
startActivity(intent);
break;
}
return true;
}
}
</pre><p></p><p></p><p>DividerItemDecoration 类为 github 上下载的开源框架:</p><p></p><pre name="code" class="java">
package com.crazy.crazyanti;
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* limitations under the License.
*/
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.view.View;
/**
* This class is from the v7 samples of the Android SDK. It's not by me!
* <p/>
* See the license above for details.
*/
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);
}
}
}
package com.crazy.crazyanti;
import android.content.Context;
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.TextView;
import java.util.List;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.CrazyViewHolder> {
private List<String> mDatas;
private LayoutInflater inflater;
public MyAdapter(Context context, List<String> mDatas){
inflater = LayoutInflater.from(context);
this.mDatas = mDatas;
}
// 设置监听接口
public interface OnItemClickListener{
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
private OnItemClickListener mOnItemClickListener;
public void setmOnItemClickListener(OnItemClickListener mOnItemClickListener){
this.mOnItemClickListener = mOnItemClickListener;
}
// 创建 ViewHolder
@Override
public CrazyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = inflater.inflate(R.layout.home_item, viewGroup, false);
return new CrazyViewHolder(v);
}
// 把数目绑定到条目上
@Override
public void onBindViewHolder(CrazyViewHolder crazyViewHolder, final int i) {
String num = mDatas.get(i);
crazyViewHolder.tvNum.setText(num);
// 如果用户设置了回调,则设置点击事件
if (mOnItemClickListener != null){
crazyViewHolder.tvNum.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(v, i);
}
});
crazyViewHolder.tvNum.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mOnItemClickListener.onItemLongClick(v, i);
return false;
}
});
}
}
@Override
public int getItemCount() {
return mDatas.size();
}
public class CrazyViewHolder extends RecyclerView.ViewHolder{
TextView tvNum;
public CrazyViewHolder(View itemView) {
super(itemView);
tvNum = (TextView)itemView.findViewById(R.id.tv_num);
}
}
}
StaggeredActivity 类:
package com.crazy.crazyanti;
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.Menu;
import android.view.MenuItem;
import java.util.ArrayList;
import java.util.List;
public class StaggeredActivity extends Activity {
private RecyclerView mRecyclerView;
private List<String> mDatas;
private StaggeredAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
mRecyclerView = (RecyclerView) findViewById(R.id.rv_Info);
// 设置布局容器
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,
StaggeredGridLayoutManager.VERTICAL));
// 初始化数据
initData();
// 适配器
adapter = new StaggeredAdapter(this, mDatas);
mRecyclerView.setAdapter(adapter);
// 添加分割线
// mRecyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
}
private void initData() {
mDatas = new ArrayList<>();
for (int i = 'A'; i <= 'z'; i++){
mDatas.add("" + (char)i);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id){
case R.id.action_GridView:
mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4));
break;
case R.id.action_ListView:
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
break;
case R.id.action_HorizontalGridView:
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,
StaggeredGridLayoutManager.HORIZONTAL));
break;
case R.id.action_StaggeredGridView:
break;
}
return true;
}
}
package com.crazy.crazyanti;
import android.content.Context;
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.TextView;
import java.util.ArrayList;
import java.util.List;
public class StaggeredAdapter extends Adapter<StaggeredAdapter.CrazyViewHolder> {
private List<String> mDatas;
private LayoutInflater inflater;
// 每一个 item 的高度
private List<Integer> mHeights;
public StaggeredAdapter(Context context, List<String> mDatas){
inflater = LayoutInflater.from(context);
this.mDatas = mDatas;
mHeights = new ArrayList<>();
// 随机产生每个 item 的高度
for (int i = 0; i < mDatas.size(); i++){
mHeights.add((int)(100 + Math.random() * 300));
}
}
// 创建 ViewHolder
@Override
public CrazyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = inflater.inflate(R.layout.staggered_item, viewGroup, false);
return new CrazyViewHolder(v);
}
// 把数目绑定到条目上
@Override
public void onBindViewHolder(CrazyViewHolder crazyViewHolder, int i) {
// 动态的去改变每一个 item 对应的 TextView 的高度
ViewGroup.LayoutParams lp = crazyViewHolder.tvNum.getLayoutParams();
lp.height = mHeights.get(i);
crazyViewHolder.tvNum.setLayoutParams(lp);
String num = mDatas.get(i);
crazyViewHolder.tvNum.setText(num);
}
@Override
public int getItemCount() {
return mDatas.size();
}
public class CrazyViewHolder extends RecyclerView.ViewHolder{
TextView tvNum;
public CrazyViewHolder(View itemView) {
super(itemView);
tvNum = (TextView)itemView.findViewById(R.id.tv_stagger);
}
}
}
drawable 文件下的 divider_bg.xml :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#ffff0000"
android:centerColor="#ff00ff00"
android:endColor="#ff0000ff"
android:type="linear"/>
<size android:height="4dp" android:width="4dp"/>
</shape>
在 styles.xml 中定义主题:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="android:Theme.Material.Light">
<item name="android:listDivider">@drawable/divider_bg</item>
</style>
</resources>