在前一阵的时候我学习了对RecyclerView的使用,结合对视频,对博客的一些学习,一开始我觉得RecyclerView好像也就那样,不就是对ListView和GrideView的结合吗,可是通过我的继续学习感觉这个组件是非常强大的。
为什么叫RecyclerView(RecyclerView的特性 )
不关心Item是否显示在正确位置,如何显示(LayoutManager可控制)
不关心Item之间分割多少(ItemDecoration可控制)
不关心Item之间增加与删除的动画效果(ItemAnimator可控制)
关注如何去回收和复用View
RecyclerView相关的重要类
- Adapter
- ViewHolder
- LayoutManager 显示风格
- ItemDecoration 显示风格的效果
- ItemAnimator 内部Item的动画效果
RecyclerView能干什么
- 纵/横 ListView
- 纵/横GrideView
- 瀑布流
- 定制Item增加与删除的动画
下面来实现相应的代码
在使用RecyclerView之前要导入相应的jar包(根据不同的版本号去导入)
compile 'com.android.support:recyclerview-v7:24.0.+'
RecyclerView的包导入之后就可以使用了~
实现纵向ListView
创建xml布局
<android.support.v7.widget.RecyclerView
android:id="@+id/main_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
在Activity中声明RecyclerView
recyclerView = (RecyclerView) findViewById(R.id.main_recycler_view);
设置需要显示的数据
private void initList() {
dataList=new ArrayList<>();
for(int i='A';i<='z';i++){
dataList.add(i+"");
}
}
这样会显示从A到小z所有的字母
创建适配器
这个可是比较关键的好几步,有点像对BaseAdapter的使用
1.创建每个Item的布局
2.新建类MyAdapter继承RecyclerView.Adapter 实现三个抽象方法
3.创建ViewHolder,继承RecyclerView.ViewHolder,实现构造方法
4.在抽象方法中将ViewHolder进行创建和绑定
1.首先来看我创建的Item布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#FF7F50"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/item_tv"
android:gravity="center"
/>
</LinearLayout>
2.通过这个布局中的控件创建ViewHolder
class MyViewHolder extends RecyclerView.ViewHolder{
TextView textView;
public MyViewHolder(View itemView) {
super(itemView);
textView= (TextView) itemView.findViewById(R.id.item_tv);
}
}
在ViewHolder中将Item中的控件实例化
3.实现Adapter的构造方法
//实现构造方法
private LayoutInflater inflater;
private Context context;
private List<String> list;
public MyAdapter(Context context, List<String> list) {
this.context = context;
this.list = list;
inflater=LayoutInflater.from(context);
}
4.在Adapter的三个构造方法中将ViewHolder创建,且绑定(数据)
//创建 ViewHolder
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//传入的参数为Item的一个布局
View view =inflater.inflate(R.layout.activity_item,parent,false);
MyViewHolder myViewHolder=new MyViewHolder(view);
return myViewHolder;
}
//绑定ViewHolder
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((MyViewHolder)holder).textView.setText(list.get(position));
}
@Override
public int getItemCount() {
return list.size();
}
绑定适配器,创建 layoutManager
//LinearLayoutManager.VERTICAL垂直样式的ListView
LinearLayoutManager layoutManager = new LinearLayoutManager(this,
LinearLayoutManager.VERTICAL, false);
recyclerView.setAdapter(new MyAdapter(MainActivity.this,dataList));
//设置成ListView的样式
recyclerView.setLayoutManager(layoutManager);
最后结果
这样显示是一个ListView的样式,但是没有分界线(我在最上方加了一个toolbar)
下面就添加分界线
toolBar 怎么使用这里就不在做具体的介绍了
添加Item之间的分界线
网上有很多别人实现的分界线,我们直接可以copy过来使用,并且进行了更改,这一点呢有个大神的博客解释的比较好,我直接copy过来吧,在上面呢我进行了改动,将分界线的颜色设置成彩色
首先是分界线源码
package my.xbl.com.recyclerviewdemo;
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* 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.util.Log;
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)
{
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);
RecyclerView v = new 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);
}
}
}
其次是我改动得地方将它的样式设置成彩色
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size android:height="4dp"/>
<gradient
android:centerColor="#ff00ff00"
android:endColor="#ff0000ff"
android:startColor="#ffff0000"
android:type="linear"/>
</shape>
最后在样式(style中)中引用次样式
<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_02</item>
</style>
下面在来看一下GrideView的样式效果实现
将纵向ListView转换成GridView
对于RecyclerView来说由ListView切换到GridView是非常简单的,而且从纵向的ListView切换到横向的ListView也是非常简单的
纵向ListView
LinearLayoutManager layoutManager = new LinearLayoutManager(MainActivity.this,
LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(layoutManager);
横向ListView
LinearLayoutManager layoutManager_hor = new LinearLayoutManager(MainActivity.this,
LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager_hor);
纵向GridView
//当前Activity,多少列
recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.this,3));
横向GridView
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.HORIZONTAL));
设置横向GridView的时候用到了StaggeredGridLayoutManager 下面会讲到这个的具体使用
在这里呢我把下划线的样式去掉,为Item增加了属性
android:layout_margin="3dp"
为TextView设置了宽度属性
<TextView
android:padding="20dp"
android:layout_width="72dp"
android:layout_height="match_parent"
android:id="@+id/item_tv"
android:text="A"
android:layout_gravity="center"
/>
最后显示效果(其中一个)
最后就是瀑布流效果了,先看最终实现效果
效果有些些的丑
这个瀑布流呢它的TextView的高度是不断地变化的,所以要重新写一个Adapter去实现高度变化的效果
package my.xbl.com.recyclerviewdemo;
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.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* Created by April on 2017/5/30.
* 这个Adapter可以强制性的让使用者使用ViewHolder
*/
public class StaggerAdapter extends RecyclerView.Adapter {
//实现构造方法
private LayoutInflater inflater;
private Context context;
private List<String> list;
//用随机数模拟高度的变化
private List<Integer>heightList;
public StaggerAdapter(Context context, List<String> list) {
this.context = context;
this.list = list;
heightList=new ArrayList<>();
inflater=LayoutInflater.from(context);
//将随机产生的itemView的高度放置到一个List中
for (int i=0;i<list.size();i++){
heightList.add((int)(100+Math.random()*300));
}
}
//创建 ViewHolder
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//传入的参数为Item的一个布局
View view =inflater.inflate(R.layout.activity_item,parent,false);
MyViewHolder myViewHolder=new MyViewHolder(view);
return myViewHolder;
}
//绑定ViewHolder
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//一般这个方法用于添加数据,现在我们将为itemView高度的设计也放到这个方法中
ViewGroup.LayoutParams lp= ((MyViewHolder)holder).itemView.getLayoutParams();
lp.height=heightList.get(position);
((MyViewHolder)holder).itemView.setLayoutParams(lp);
((MyViewHolder)holder).textView.setText(list.get(position));
}
@Override
public int getItemCount() {
return list.size();
}
private class MyViewHolder extends RecyclerView.ViewHolder{
TextView textView;
public MyViewHolder(View itemView) {
super(itemView);
textView= (TextView) itemView.findViewById(R.id.item_tv);
}
}
}
然后就是为RecyclerBView 添加LayoutManager
recyclerView.setAdapter(new StaggerAdapter(StaggerActivity.this,dataList));
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,
StaggeredGridLayoutManager.VERTICAL));
带有动画效果的增加/减少元素
为RecyclerView添加一个系统默认动画
recyclerView.setItemAnimator(new DefaultItemAnimator());
对数据的增加逻辑
在自定义的Adapter中实现对list数据的增加,并刷新Item
//增加数据的方法
public void addData(int pos) {
list.add(pos, "Insert");
//刷新布局,不是调用的notifyDataSetChanged();
notifyItemInserted(pos);
}
对数据的减少逻辑
在自定义Adapter中实现对list数据的移除和对 Item的刷新
//删除数据的方法
public void deleteData(int pos) {
list.remove(pos);
//刷新布局,不是调用的notifyDataSetChanged();
notifyItemRemoved(pos);
}
两个notify的方法是不一样的
最后在Activity中直接调用这两个方法
myAdapter.addData(1);
myAdapter.deleteData(1);
总结:这样的话对RecyclerView显示的效果就实现了,下一篇会讲解如何对RecyclerView中的Item实现点击事件