我们常用的列表控件一般有2种,ListView和GridView,现在Android开发者又多了一个选择,那就是RecyclerView,从名字上面可以看出来,这是一个循环复用视图。RecyclerView出现其实已经有一段时间了。现在就来学习一下吧!
整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。
RecyclerView项目结构如下:
layout布局:
子视图布局:
运行效果图:
DividerItemDecoration
调用代码
然后就有和listview的divider分隔线了。这个简单就不上图了。继续看Grib格子,DividerGridItemDecoration.java
调用方法
效果图如下:
如果你觉得到这里就完了就错了,还有更精彩的。我们先把每个元素的边距设置20dp,然后设置一个背景颜色#abcdef,adapter里面的数据调到100个。
运行效果图:
到了这里还是没有感觉到什么强大之处,因为GridView也可以完成这个效果,对吧。接下来看动画。注意,这里更新数据集不是用adapter.notifyDataSetChanged()而是 notifyItemInserted(position)与notifyItemRemoved(position)
我们就随便点击一个item就自动添加一个元素吧。说到这个事件就不得不说recyclerview的个性,人家不支持onItemClickListener,要实现子控件点击事件只能自己去实现。那怎么整呢?我们知道任何view可以可以添加click事件,既然如此就简单了,我们把每一个view都注册click事件不就行了嘛。下面是修改完成之后的源码,事件是基于最外层的Linearlayout视图的。
效果图:
我这边没上GIF动画,不然点击6会自动添加一个视图,点击8会自动减少一个视图。好了,就到这里吧!最后把修改过的资源贴出来
整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。
RecyclerView项目结构如下:
- 你想要控制其显示的方式,请通过布局管理器LayoutManager
- 你想要控制Item间的间隔(可绘制),请通过ItemDecoration
- 你想要控制Item增删的动画,请通过ItemAnimator
- 你想要控制点击、长按事件,请自己写(擦,这点尼玛。)
package com.androiddemo; import java.util.ArrayList; import java.util.List; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.Adapter; import android.support.v7.widget.RecyclerView.ViewHolder; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.android.dev.base.BaseActivity; import com.android.dev.util.Tools; public class RecyclerActivity extends BaseActivity { RecyclerView recyclerView; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycler); recyclerView = (RecyclerView) findViewById(R.id.id_recyclerview); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(new MainAdapter()); } class MainAdapter extends Adapter{ private List mDatas; public MainAdapter() { super(); // TODO Auto-generated constructor stub mDatas = new ArrayList (); for (int i = 0; i < 10; i++) { mDatas.add("索引:" + (int) (Math.random() * 100) / 100f); Tools.sleep(1);// 休眠 } } @Override public int getItemCount() { // TODO Auto-generated method stub return mDatas.size(); } @Override public void onBindViewHolder(MainHolder holder, int position) { // TODO Auto-generated method stub holder.textView.setText(mDatas.get(position)); } @Override public MainHolder onCreateViewHolder(ViewGroup group, int viewType) { // TODO Auto-generated method stub View view = getLayoutInflater().inflate(R.layout.recycler_item, group, false); MainHolder holder = new MainHolder(view); return holder; } } class MainHolder extends ViewHolder { TextView textView; public MainHolder(View view) { super(view); // TODO Auto-generated constructor stub textView = (TextView) view.findViewById(R.id.id_text); } } }
layout布局:
子视图布局:
运行效果图:
直线显示
recyclerview可以通过addItemDecoration来添加分隔线,我们先从最简单的直线入手。DividerItemDecoration
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; /** * This class is from the v7 samples of the Android SDK. It's not by me! * * 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); 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); } } }
调用代码
recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));
然后就有和listview的divider分隔线了。这个简单就不上图了。继续看Grib格子,DividerGridItemDecoration.java
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.RecyclerView.LayoutManager; import android.support.v7.widget.RecyclerView.State; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.View; /** * * @author zhy * */ 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, State state) { drawHorizontal(c, parent); drawVertical(c, parent); } private int getSpanCount(RecyclerView parent) { // 列数 int spanCount = -1; 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) { 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) { 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()); } } }
调用方法
recyclerView.setAdapter(new MainAdapter()); recyclerView.setLayoutManager(new GridLayoutManager(this, 4));// 格子排列 recyclerView.addItemDecoration(new DividerGridItemDecoration(this));
效果图如下:
如果你觉得到这里就完了就错了,还有更精彩的。我们先把每个元素的边距设置20dp,然后设置一个背景颜色#abcdef,adapter里面的数据调到100个。
运行效果图:
到了这里还是没有感觉到什么强大之处,因为GridView也可以完成这个效果,对吧。接下来看动画。注意,这里更新数据集不是用adapter.notifyDataSetChanged()而是 notifyItemInserted(position)与notifyItemRemoved(position)
public void addItem(String text, int position) { mDatas.add(text); notifyItemInserted(position); } public void removeItem(int position) { mDatas.remove(position); notifyItemRemoved(position); }
我们就随便点击一个item就自动添加一个元素吧。说到这个事件就不得不说recyclerview的个性,人家不支持onItemClickListener,要实现子控件点击事件只能自己去实现。那怎么整呢?我们知道任何view可以可以添加click事件,既然如此就简单了,我们把每一个view都注册click事件不就行了嘛。下面是修改完成之后的源码,事件是基于最外层的Linearlayout视图的。
import java.util.ArrayList; import java.util.List; import android.os.Bundle; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.Adapter; import android.support.v7.widget.RecyclerView.ViewHolder; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.TextView; import com.android.dev.base.BaseActivity; import com.android.dev.base.MLog; import com.android.dev.util.Tools; import com.androiddemo.other.DividerGridItemDecoration; public class RecyclerActivity extends BaseActivity implements OnItemClickListener { RecyclerView recyclerView; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycler); recyclerView = (RecyclerView) findViewById(R.id.id_recyclerview); // recyclerView.setLayoutManager(new LinearLayoutManager(this));//线性排列 // recyclerView.addItemDecoration(new // DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST)); final MainAdapter adapter = new MainAdapter(); recyclerView.setAdapter(adapter); adapter.setItemClickListener(new OnItemClickListener() { @Override public void onItemClickListener(int position) { // TODO Auto-generated method stub MLog.makeText("点击:" + position); if (position == 6) { adapter.addItem("添加6", position); } if (position == 8) { adapter.removeItem(8); } } }); // recyclerView.setLayoutManager(new GridLayoutManager(this, 4));// 格子排列 // recyclerView.addItemDecoration(new DividerGridItemDecoration(this)); recyclerView.setLayoutManager(new StaggeredGridLayoutManager(6, StaggeredGridLayoutManager.HORIZONTAL));// 瀑布流 recyclerView.addItemDecoration(new DividerGridItemDecoration(this)); recyclerView.setItemAnimator(new DefaultItemAnimator());// 子视图动画 } class MainAdapter extends Adapter{ private List mDatas; private OnItemClickListener itemClickListener; public MainAdapter() { super(); // TODO Auto-generated constructor stub mDatas = new ArrayList (); for (int i = 0; i < 100; i++) { mDatas.add("索引:" + i); Tools.sleep(1);// 休眠 } } public void setItemClickListener(OnItemClickListener itemClickListener) { this.itemClickListener = itemClickListener; } public void addItem(String text, int position) { mDatas.add(text); notifyItemInserted(position); } public void removeItem(int position) { mDatas.remove(position); notifyItemRemoved(position); } @Override public int getItemCount() { // TODO Auto-generated method stub return mDatas.size(); } @Override public void onBindViewHolder(MainHolder holder, int position) { // TODO Auto-generated method stub holder.textView.setText(mDatas.get(position)); holder.parent.setTag(position); } @Override public MainHolder onCreateViewHolder(ViewGroup group, int viewType) { // TODO Auto-generated method stub View view = getLayoutInflater().inflate(R.layout.recycler_item, group, false); view.setOnClickListener(listener); MainHolder holder = new MainHolder(view); return holder; } OnClickListener listener = new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub if (itemClickListener != null) itemClickListener.onItemClickListener(Integer.parseInt(v .getTag().toString())); } }; } public interface OnItemClickListener { public void onItemClickListener(int position); } class MainHolder extends ViewHolder { View parent; TextView textView; public MainHolder(View view) { super(view); // TODO Auto-generated constructor stub this.parent = view; textView = (TextView) view.findViewById(R.id.id_text); } } @Override public void onItemClick(AdapterView> parent, View view, int position, long id) { // TODO Auto-generated method stub } }
效果图:
我这边没上GIF动画,不然点击6会自动添加一个视图,点击8会自动减少一个视图。好了,就到这里吧!最后把修改过的资源贴出来
收藏的用户(0) X
正在加载信息~
推荐阅读
最新回复 (0)
站点信息
- 文章2300
- 用户1336
- 访客10862077
每日一句
True success inspires others to act.
真正的成功是激励他人行动。
真正的成功是激励他人行动。
语法错误: 意外的令牌“标识符”
全面理解Gradle - 定义Task
Motrix全能下载工具 (支持 BT / 磁力链 / 百度网盘)
谷歌Pixel正在开始起飞?
获取ElementUI Table排序后的数据
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is
亲测!虚拟机VirtualBox安装MAC OS 10.12图文教程
华为手机app闪退重启界面清空log日志问题
android ndk开发之asm/page.h: not found
手机屏幕碎了怎么备份操作?
免ROOT实现模拟点击任意位置
新手必看修改DSDT教程
thinkpad t470p装黑苹果系统10.13.2
新会员