<Android 基础(四)> RecyclerView
2016-06-04 12:36
453 查看
介绍
RecyclerView是ListView的豪华增强版。它主要包含以下几处新的特性,如ViewHolder,ItemDecorator,LayoutManager,SmothScroller以及增加或删除item时item动画等。官方推荐我们采用RecyclerView来取代ListView。
相对优势
ViewHolderListView需要自己实现ViewHolder来提高性能,或者不使用ViewHolder,但是使用ViewHolder来绑定对象是一个很好的习惯。RecyclerView很好的帮我们解决了这个问题,RecyclerView.ViewHolder在使用RecyclerView过程中必须实现,因为它是一个抽象类无法直接创建,需要自己完成对应子类的建立然后使用
LayoutManager
ListView只能在垂直方向上滚动,不支持其他的滚动方式,当然开发者有很多自定义的方式完成这些功能,这里就不做争辩,从设计的角度上看,ListView设计之初应该就没有想过让它完成这些复杂的功能,只是为了单纯的列表显示。但是RecyclerView相较于ListView,在滚动上面的功能扩展了许多。它可以支持多种类型列表的展示要求,主要如下:
GridLayoutManager ,支持网格展示,可以水平或者竖直滚动,如展示图片的画廊。
LinearLayoutManager ,可以支持水平和竖直方向上滚动的列表。
StaggeredGridLayoutManager ,可以支持交叉网格风格的列表,类似于瀑布流或者Pinterest。
ItemAnimation
ItemAnimation是RecyclerView中子项在增加,删除或者移动的情况下显示的动画效果,Google越来越重视用户体验,从属性动画的推出开始,这就是一个趋势,开发者在这里可以自己实现自己想要添加的动画效果,当然,如果你是个懒汉,请使用new DefaultItemAnimator() 。
ItemDecoration
ItemDecoration,名字起的很文艺,子项的装饰,RecyclerView在默认情况下并不在item之间展示间隔符。如果你想要添加间隔符,你必须使用RecyclerView.ItemDecoration类来实现。懒汉请使用DividerItemDecoration.java。
Recycler示例
实际效果图
上面这个效果图是使用的StaggeredGridLayoutManager
代码层面
布局文件activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/root_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@color/colorMainBackground"> <include layout="@layout/toolbar"></include> <FrameLayout 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" tools:context=".model.View.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_content" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v7.widget.RecyclerView> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_add" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|right" android:layout_marginBottom="30dp" android:layout_marginRight="30dp" android:src="@drawable/addone" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_del" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|left" android:layout_marginBottom="30dp" android:layout_marginLeft="30dp" android:src="@drawable/delone" /> </FrameLayout> </LinearLayout>
recycler_item.xml
<?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="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> <android.support.v7.widget.CardView android:layout_margin="10dp" android:id="@+id/cv_bg" android:layout_width="match_parent" android:layout_height="match_parent" app:cardCornerRadius="20dp" app:cardElevation="5dp"> <TextView android:id="@+id/tv_name" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" /> </android.support.v7.widget.CardView> </FrameLayout>
主Activity
public class MainActivity extends AppCompatActivity { @Bind(R.id.rv_content) RecyclerView rvContent; @Bind(R.id.fab_add) FloatingActionButton fabAdd; @Bind(R.id.fab_del) FloatingActionButton fabDel; @Bind(R.id.root_layout) LinearLayout rootLayout; private LayoutManager mLayoutManager; //LayoutManager private RVAdapter recyclerAdapter; //RecyclerView对应的Adapter private ArrayList<String> mContentList; //内容list这里只是使用了字符串,当然也可以替换成其他的JavaBean类 private Random mRandom = new Random(); //用于产生随机字符串 private int mSum = 50; //初始化子项数目 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); //ButterKnife注入,减少代码负担 mContentList = new ArrayList<String>(); for (int i = 0; i < mSum; i++) { mContentList.add(getRandomString()); } //随机生成数据 recyclerAdapter = new RVAdapter(mContentList); mLayoutManager = new StaggeredGridLayoutManager(3, LinearLayoutManager.VERTICAL); //3列纵向 rvContent.setAdapter(recyclerAdapter); rvContent.setLayoutManager(mLayoutManager); rvContent.setItemAnimator(new DefaultItemAnimator()); //设置动画效果,可以看下上面的效果图,动画效果还是比较明显的 } @OnClick({R.id.fab_add, R.id.fab_del}) public void onClick(View view) { switch (view.getId()) { case R.id.fab_add: recyclerAdapter.addData(1); //加一个 makeSnackBar(rootLayout, "添加一个 :)", null, null);//显示一个Snackbar break; case R.id.fab_del: recyclerAdapter.removeData(1); //去掉一个 makeSnakeBar(rootLayout, "删除一个 :(", null, null); //显示一个Snackbar break; default: break; } } //用于产生随机字符串的方法 public String getRandomString() { String src = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; StringBuilder dst = new StringBuilder(4); for (int i = 0; i < 4; i++) { dst.append(src.charAt(mRandom.nextInt(62))); } return dst.toString(); } //构建一个Snackbar并显示出来 private void makeSnackBar(View view, String message, String buttonText, View.OnClickListener onClickListener) { Snackbar.make(view, message, Snackbar.LENGTH_SHORT) .setAction(buttonText, onClickListener) .show(); } }
适配器
public class RVAdapter extends RecyclerView.Adapter<RVAdapter.MyViewHolder>{ ArrayList<String> mContentList; Random mRandom = new Random(); //ViewHolder继承自RecyclerView.ViewHolder 子View拿到方便后面访问 public class MyViewHolder extends RecyclerView.ViewHolder { TextView textView; CardView cardView; public MyViewHolder(View itemView) { super(itemView); textView = (TextView) itemView.findViewById(R.id.tv_name); cardView = (CardView) itemView.findViewById(R.id.cv_bg); } } public RVAdapter(ArrayList<String> mContentList) { this.mContentList = mContentList; } //创建ViewHolder,由于RecyclerView.ViewHolder是一个抽象类无法实例化,所以必须实现一个子类才能使用,这里自己尝试的过程中走了一些弯路,注意inflate最后一个参数设置成false不然可能会出现crash @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false); return new MyViewHolder(view); } //onBindView 用于设置需要显示的View中的内容和一些属性值 @Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.cardView.setCardBackgroundColor(getRandomColor()); holder.textView.setText(mContentList.get(position)); holder.itemView.getLayoutParams().height = getRandomHeight(200,400);//产生随机高度,看上去像瀑布 } @Override public int getItemCount() { return mContentList.size(); } //产生随机颜色 private int getRandomColor() { return (0xff000000|mRandom.nextInt(0x00ffffff)); } //产生随机高度 private int getRandomHeight(int min , int max) { return (mRandom.nextInt(max - min) + min ); } //添加一个子项 public void addData(int position) { mContentList.add(position, "Insert One"); notifyItemInserted(position);//不调用这个没有动画效果 } //删除一个子项 public void removeData(int position) { mContentList.remove(position); notifyItemRemoved(position);//不调用这个没有动画效果 } }
需要注意的几个地方:
1. 实现自己的ViewHolder继承recyclerView,ViewHolder,因为抽象类不能实例化
2. inflate子项的时候,最后一个参数设置成false
3. 动画效果需要在Adapter中调用notifyItem***方法才行
StaggeredGridLayoutManager的效果图上面已经有显示了
其他效果
LinearLayoutManager效果图对应修改代码
mLayoutManager = new LinearLayoutManager(this); rvContent.addItemDecoration(new DividerItemDecoration(this, StaggeredGridLayoutManager.VERTICAL));
GridLayoutManager效果图
对应修改代码
mLayoutManager = new GridLayoutManager(this, 3);//3列 rvContent.addItemDecoration(new DividerItemDecoration(this, StaggeredGridLayoutManager.VERTICAL));
StaggeredGridLayoutManager.HORIZONTAL横向的效果图
对应修改代码
mLayoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.HORIZONTAL); RVAdapter.java中onBindViewHolder holder.itemView.getLayoutParams().width = getRandomHeight(200,400);
关于ItemDecoration
这里附上Google Sample中的DividerItemDecoration.java代码 希望可以帮助到大家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) { 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 + Math.round(ViewCompat.getTranslationY(child)); 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 + Math.round(ViewCompat.getTranslationX(child)); 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源码中有很多关于这些View的使用方法,大家可以查阅并参考使用。博文中部分代码:
http://download.csdn.net/detail/poorkick/9541326
相关文章推荐
- <Android 基础(四)> RecyclerView
- Android SDK无法更新或下载很慢
- Android中Activity四种启动模式和taskAffinity属性详解
- android蓝牙开发 蓝牙设备的查找和连接
- android蓝牙主动发起配对实例
- android开发之蓝牙配对连接的方法
- Android研究院之应用开发线程池的经典使用(二十九)
- Android内存优化大全(中)
- Android_SeekBarAndProgressBar
- 在MAC下搭建Android开发环境
- 安卓添加事件遇到的一个问题以及他的解决方法
- android6.0 power显示(亮度等)深入分析(一)PowerManagerService & DisplayPowerController
- Adroid笔记(一)
- Android 手势锁的实现
- android系统 连接管理总结 android ethernet
- Android图像处理(五)镜像、倒影、drawBitmapMesh实现旗帜飘扬效果
- Android之进度条2
- Android中解析JSON格式数据常见方法合集
- android捕获ListView中每个item点击事件
- Android libpng error: Not a PNG file错误解决