转载请注明:
http://blog.csdn.net/sinat_30276961/article/details/50210277
上一篇,介绍了RecyclerView的特点,并展示了一个基本的RecyclerView需要涉及到的步骤:获取RecyclerView,创建并设置排布方式,复写adapter和内部的ViewHolder,并设置adapter。本篇将展示一下,水平排布、网格排布和添加删除动画效果。
在上一篇,有说过,RecyclerView的子View的排布方式有:
1. LinearLayoutManager 线性排列
2. GridLayoutManager 网格排列
3. StaggeredGridLayoutManager 瀑布流排列
对于LinearLayoutManager,它有两种方式:横向和纵向。上一篇展示的是纵向。
对于GridLayoutManager,它呈现的是网格式排布,类似于GridView。
添加和删除动画用到的是默认的动画效果。
按照惯例,我先呈现一下效果:
用RecyclerView实现横向,那可是分分钟搞定的事啊。如果是用ListView,要么复写它,要么就用GridView去展示一行的数据,都不怎么理想。
第一个实例代码也很简单,就是布局文件里放置两个RecyclerView,然后每个item的布局文件贴一下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:gravity="center_horizontal"
android:background="@drawable/recycler_list_bg">
<TextView
android:layout_above="@+id/logo"
android:id="@+id/text"
android:textColor="#000000"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="1"
android:layout_marginBottom="16dp"
android:layout_centerHorizontal="true"
android:textAppearance="?attr/textAppearanceListItem"/>
<com.office.steven.androidtest.commonwidget.CircleImageView
android:id="@+id/logo"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
app:civ_border_width="2dp"
app:civ_border_color="#ff999999"/>
RelativeLayout>
这里有设置android:ems,是为了横向时,TextView呈现文字能舒服点。
主体代码,只要注意一点就行:创建的LinearLayoutManager设置为LinearLayoutManager.HORIZONTAL。
再来看看LinearLayoutManager的构造函数。
/**
* @param context Current context, will be used to access resources.
* @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link
* #VERTICAL}.
* @param reverseLayout When set to true, layouts from end to start.
*/
public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
setOrientation(orientation);
setReverseLayout(reverseLayout);
}
前面两个不说了,第三个的效果就是反向排列。
接着,来看看GridLayoutManager,它是继承自LinearLayoutManager。我们来看看它的构造函数:
/**
* Creates a vertical GridLayoutManager
*
* @param context Current context, will be used to access resources.
* @param spanCount The number of columns in the grid
*/
public GridLayoutManager(Context context, int spanCount) {
super(context);
setSpanCount(spanCount);
}
可以看到,相比较LinearLayoutManager,它多了个spanCount。这个参数是用来控制有多少列的。
ok,我直接贴出添加和删除核心代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_delete_test);
mGridCb = (CheckBox) findViewById(R.id.is_grid);
mGridCb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mGridCb.isChecked()) {
setGrid();
// 如果当前的添加删除的索引值超过了当前展示的grid的item数量,那就设置索引值为item的数量
if (mFromIndex > mGridAdapter.getNum())
mFromIndex = mGridAdapter.getNum();
mRandomFrom.setText(String.valueOf(mFromIndex));
} else {
setLinear();
// 同上
if (mFromIndex > mAdapter.getNum())
mFromIndex = mAdapter.getNum();
mRandomFrom.setText(String.valueOf(mFromIndex));
}
}
});
// 用来随机产生一个数,增加/减少item时根据这个数值
mRandomNum = (Button) findViewById(R.id.random_option_num);
mRandomNum.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 随机产生1到10的数值
mNum = new Random().nextInt(10)+1;
mRandomNum.setText(String.valueOf(mNum));
}
});
// 用来随机产生一个数,指定增加/减少操作的起始index
mRandomFrom = (Button) findViewById(R.id.random_from_num);
mRandomFrom.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 先获取当前的RecyclerView还剩多少item
final int leftNum;
if (mGridCb.isChecked()) {
leftNum = mGridAdapter.getNum();
} else {
leftNum = mAdapter.getNum();
}
// 索引值只能在[0,leftNum+1)取
mFromIndex = new Random().nextInt(leftNum+1);
mRandomFrom.setText(String.valueOf(mFromIndex));
}
});
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this){
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
};
mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mLayoutManager);
mGridLayoutManager = new GridLayoutManager(this, 5);
mGridAdapter = new GridRecyclerViewAdapter(this, R.layout.item_recycler_view_grid, 10);
mGridAdapter.setOnItemClickListener(new CommonAdapter.OnRecyclerViewItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(AddDeleteTest.this, "Grid " + position + " click", Toast.LENGTH_SHORT).show();
}
});
mAdapter = new RecyclerViewAdapter(this, R.layout.item_normal_recycler_view, 3);
mAdapter.setOnItemClickListener(new CommonAdapter.OnRecyclerViewItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(AddDeleteTest.this, "Linear " + position + " click", Toast.LENGTH_SHORT).show();
}
});
mRecyclerView.setAdapter(mAdapter);
}
在第6-24行,我用CheckBox来切换RecyclerView的排布方式。
private void setGrid() {
mRecyclerView.setLayoutManager(mGridLayoutManager);
mRecyclerView.setAdapter(mGridAdapter);
}
private void setLinear() {
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
}
mFromIndex是增加和删除Item的起始值。增加:在当前索引值往后增加item;删除:在当前索引值往前删除item
第26行和第36行的mRandomNum和mRandomFrom,这两个是textview,点击时,我就随机产生一个数赋给它。这两个分别代表添加和删除的数量,添加和删除的索引值。
在55行和65行,分别创建了线性布局和网格布局。然后针对两个布局,分别创建了各自的adapter。
接着,贴出添加和删除的代码:
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_add) {
if (mGridCb.isChecked()) {
final int num = mGridAdapter.getNum();
mGridAdapter.setNum(num+mNum);
mGridAdapter.notifyItemRangeInserted(mFromIndex, mNum);
} else {
final int num = mAdapter.getNum();
mAdapter.setNum(num+mNum);
mAdapter.notifyItemRangeInserted(mFromIndex, mNum);
}
return true;
} else if (id == R.id.action_delete) {
if (mGridCb.isChecked()) {
final int num = mGridAdapter.getNum();
int deleteNum = mNum;
if (mNum > num-mFromIndex) {
deleteNum = num-mFromIndex;
}
mGridAdapter.setNum(num-deleteNum);
mGridAdapter.notifyItemRangeRemoved(mFromIndex, deleteNum);
} else {
final int num = mAdapter.getNum();
int deleteNum = mNum;
if (mNum > num-mFromIndex) {
deleteNum = num-mFromIndex;
}
mAdapter.setNum(num-deleteNum);
mAdapter.notifyItemRangeRemoved(mFromIndex, deleteNum);
}
}
return super.onOptionsItemSelected(item);
}
核心api是notifyItemRangeInserted和notifyItemRangeRemoved。
这两个api针对的是批量添加和删除数据之后的通知RecyclerView重绘。
下面再列一下其他通知重绘api。可以看到,相比较ListView的adapter,这个就多了很多。有针对插入数据,移除数据,改变数据和移动数据。当然,它也保留了我们以前在ListView最常用到的notifyDataSetChanged。
接着,再回到代码,添加就是在索引值以后再添加一段数据。删除就是从索引值往前删除数据。在删除那里,做了特殊处理:如果当前索引值往前的数量少于随机产生的删除数量,那把删除数量调整为索引值往前的item的数量。
说到这,该讲的内容基本都讲完,就剩下一个东西还没讲到,那就是动画效果。可以看到,我写的代码里没有涉及到动画效果的创建。那么动画效果是在哪里设置的呢?
这又得去看RecyclerView的源码了。源码中有这样一句:
ItemAnimator mItemAnimator = new DefaultItemAnimator();
也就是说,在初始化RecyclerView时,就会默认的创建一个DefaultItemAnimator动画效果。
动画效果的设置是通过setItemAnimator这个方法的。
public void setItemAnimator(ItemAnimator animator) {
if (mItemAnimator != null) {
mItemAnimator.endAnimations();
mItemAnimator.setListener(null);
}
mItemAnimator = animator;
if (mItemAnimator != null) {
mItemAnimator.setListener(mItemAnimatorListener);
}
}
至于这个DefaultItemAnimator,是google提供给我们直接使用的。
它继承自RecyclerView.ItemAnimator,如果我们要自己实现一个动画效果,也需要继承它。关于这块,我会在后面详细讲解。
ok,今天讲解了关于水平布局和网格布局的细节部分,并讲解了添加和删除的动画效果展示。下篇会接着讲解分隔图等。
Have a good night!