RecyclerView是谷歌support-V7包下新增的控件,用来替代ListView的使用,在RecyclerView标准化了ViewHolder类似于ListView中convertView用来做视图缓存
体验流程:
- 设置布局管理器(layoutManager)以控制Item的布局方式 <垂直水平ListView,垂直水平GridView,瀑布流>
- 设置Item的间隔样式
- 设置Item删除和添加的动画
- 实现Item的点击和长按事件
app/build.gradle
implementation 'com.android.support:appcompat-v7:25.3.1'
implementation 'com.android.support:recyclerview-v7:25.3.1'
package com.example.testnewas.activity;
import android.support.v7.app.AppCompatActivity;
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.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.example.testnewas.R;
import com.example.testnewas.adapters.Radapter;
import com.example.testnewas.views.DividerGridItemDecoration;
import com.example.testnewas.views.MyDividerItemDecoration;
import java.util.ArrayList;
public class RActivity extends AppCompatActivity implements View.OnClickListener{
private RecyclerView recyclerView;
private Radapter adapter;
private RecyclerView.LayoutManager layoutManager;
private TextView addItem;
private TextView deleteItem;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_r);
initConfig();
initView();
}
private void initView() {
recyclerView = (RecyclerView) findViewById(R.id.my_recycle_view);
recyclerView.setAdapter(adapter);
//设置布局管理器以控制Item的布局方式,横向、竖向以及瀑布流方式。
recyclerView.setLayoutManager(layoutManager);
//间隔线样式
// recyclerView.addItemDecoration(new MyDividerItemDecoration(this, MyDividerItemDecoration.VERTICAL_LIST));
recyclerView.addItemDecoration(new DividerGridItemDecoration(this));
//默认的Item删除和添加的动画
recyclerView.setItemAnimator(new DefaultItemAnimator());
addItem = (TextView) findViewById(R.id.add_item);
deleteItem = (TextView) findViewById(R.id.delete_item);
addItem.setOnClickListener(this);
deleteItem.setOnClickListener(this);
}
private void initConfig() {
adapter = new Radapter(initData());
//Item点击事件
adapter.setOnItemClickListener(new Radapter.OnItemClickListener() {
@Override
public void onClick(int position) {
Toast.makeText(RActivity.this, "click " + position + " item", Toast.LENGTH_SHORT).show();
}
@Override
public void onLongClick(int position) {
Toast.makeText(RActivity.this, "long click " + position + " item", Toast.LENGTH_SHORT).show();
//长按删除当前item
adapter.deleteItem(position);
}
});
//垂直水平线性列表ListView
// layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
//垂直滚动GridView(每行4)
// layoutManager = new GridLayoutManager(this, 4);
//水平滚动GridView(每列4)
// layoutManager = new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.HORIZONTAL);
//瀑布流
layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
}
//模拟数据
private ArrayList<String> initData() {
ArrayList<String> data = new ArrayList<>();
String str = "item-";
for (int i = 0; i< 30; i++) {
data.add(str + i);
}
return data;
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.add_item:
adapter.addItem(1);
break;
case R.id.delete_item:
adapter.deleteItem(1);
break;
}
}
}
activity_recyclerview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.testnewas.activity.RActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/add_item"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:background="#fff000"
android:text="添加item"/>
<TextView
android:id="@+id/delete_item"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:background="#cccccc"
android:text="删除item"/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycle_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
RecyclerViewAdapter.java
package com.example.testnewas.adapters;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.example.testnewas.R;
import java.util.ArrayList;
import java.util.List;
/**
* Created by ylh on 2017/11/7 0007.
*/
public class Radapter extends RecyclerView.Adapter<Radapter.ViewHodler> {
private ArrayList<String> mData;
private OnItemClickListener onItemClickListener;
private ArrayList<Integer> heights;
/**
* 设置回调监听
* @param onItemClickListener
*/
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public Radapter(ArrayList<String> mData) {
this.mData = mData;
getRandomHeight(this.mData);
}
public void updateData(ArrayList<String> mData) {
this.mData = mData;
notifyDataSetChanged();
}
@Override
public ViewHodler onCreateViewHolder(ViewGroup parent, int viewType) {
//实例化展示的view
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_rv_item, parent, false);
//实例化viewhodler
ViewHodler viewHodler = new ViewHodler(view);
return viewHodler;
}
@Override
public void onBindViewHolder(ViewHodler holder, final int position) {
/**
*瀑布流实现
*/
// 得到item的LayoutParams布局参数
ViewGroup.LayoutParams params= holder.itemView.getLayoutParams();
//把随机的高度赋予item布局
params.height = heights.get(position);
// 把params设置给item布局
holder.itemView.setLayoutParams(params);
//绑定数据
holder.mTv.setText(mData.get(position));
if (onItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onItemClickListener.onClick(position);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
onItemClickListener.onLongClick(position);
//表示此事件已经消费,不会触发单击事件
return true;
}
});
}
}
//得到随机item的高度
private void getRandomHeight(List<String> datas) {
heights = new ArrayList<Integer>();
for (int i = 0; i < datas.size(); i++) {
heights.add((int) (200+Math.random()*100));
}
}
@Override
public int getItemCount() {
return mData == null?0:mData.size();
}
public void addItem(int position) {
if (mData == null) {
mData = new ArrayList<>();
}
mData.add(position, "new Item");
//注意,这里更新数据集不是用adapter.notifyDataSetChanged()而是
//notifyItemInserted(position)与notifyItemRemoved(position) 否则没有动画效果。
notifyItemInserted(position);
getRandomHeight(mData);
}
public void deleteItem(int position) {
if (mData == null || mData.isEmpty()) {
return;
}
mData.remove(position);
notifyItemRemoved(position);
getRandomHeight(mData);
}
public interface OnItemClickListener {
void onClick(int position);
void onLongClick(int position);
}
public static class ViewHodler extends RecyclerView.ViewHolder{
TextView mTv;
public ViewHodler(View itemView) {
super(itemView);
mTv = (TextView) itemView.findViewById(R.id.item_tv);
}
}
}
view_rv_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="100dp"
android:orientation="vertical">
<TextView
android:id="@+id/item_tv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:background="#fccfcc"
android:layout_margin="5dp"
android:text="item"/>
</LinearLayout>
MyDividerItemDecoration.java
package com.example.testnewas.views;
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.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* Created by ylh on 2017/11/7 0007.
*
* RecyclerView的间隔样式
*/
public class MyDividerItemDecoration 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 MyDividerItemDecoration(Context context, int orientation) {
// 获取默认主题的属性
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
// 绘制间隔
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
private void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
/**
* 绘制间隔
*/
private 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);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin +
Math.round(ViewCompat.getTranslationY(child));
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
/**
* 绘制间隔
*/
private 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 +
Math.round(ViewCompat.getTranslationX(child));
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
}
DividerGridItemDecoration.java
package com.example.testnewas.views;
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;
/**
* @author zhy
*
* RecyclerView-GridView的间隔样式
*/
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());
}
}
}
自定义间隔样式
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:listDivider">@drawable/divider</item >
</style>
divider.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<!-- 填充的颜色 -->
<solid android:color ="#ff0000"/>
<!-- 线条大小 -->
<size android:height ="1dp" android:width ="1dp"/>
</shape>
自定义的各种动画效果,github上已经有很多类似的项目了,这里我们直接引用下: RecyclerViewItemAnimators,大家自己下载查看。
提供了SlideInOutLeftItemAnimator,SlideInOutRightItemAnimator,
SlideInOutTopItemAnimator,SlideInOutBottomItemAnimator等动画效果。
瀑布流效果.png