课程学习笔记
张鸿洋大神的课程, 明日之星-RecyclerView
https://www.imooc.com/learn/424
代码直接看张鸿洋大神的blog即可: https://blog.csdn.net/lmj623565791/article/details/45059587
详细的代码可以看: https://github.com/carrys17/RecyclerViewTest
为什么叫做RecyclerView
RecyclerView在功能上和ListView是相似的, 那么为什么把它的名字起名叫做RecyclerView呢? 有没有想过这个问题?
以ListView为例, 它使用的Adapter通常是继承自BaseAdapter. 在它的核心方法 getView() 中, 有经验的开发人员都知道要使用ViewHolder的模式, 避免反射layout file, 來提高代码的执行效率.
但我们也知道, 在ListView中, 并没有强制要求开发者必须使用ViewHolder的模式. 这就造成了使用ListView的话, 如果开发者的经验不足, 可能会写出一些效率低的ListView代码出来.
而RecyclerView使用的Adapter, 并不是继承自BaseAdapter, 而是RecyclerView自己的內部类, RecyclerView.Adapter
public abstract static class Adapter<VH extends ViewHolder> {
}
这里的ViewHolder, 也是RecyclerView自己的内部类, RecyclerView.ViewHolder
public abstract static class ViewHolder {
}
这样, 如果要使用RecyclerView的话, 就等于强制要求开发人员必须使用ViewHolder的模式, 保证了代码的执行效率. 这就是RecyclerView, 即"回收View"这个名字的由来.
相比ListView来说最大的好处.
- 是强制开发人员使用ViewHolder的模式, 保证代码的执行效率.
- 动态的改变布局样式, 在ListView, GridView, 瀑布流这几种样式之间, 可以非常方便的进行动态切换.
简洁的代码
HomeAdaper.java
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
{
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
MyViewHolder holder = new MyViewHolder(LayoutInflater.from(
HomeActivity.this).inflate(R.layout.item_home, parent,
false));
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position)
{
holder.tv.setText(mDatas.get(position));
}
@Override
public int getItemCount()
{
return mDatas.size();
}
class MyViewHolder extends ViewHolder
{
TextView tv;
public MyViewHolder(View view)
{
super(view);
tv = (TextView) view.findViewById(R.id.id_num);
}
}
}
这里的onCreateViewHolder()和onBindViewHolder(), 实际上就是把ListView BaseAdapter中的getView()的实现, 强制分解成了2步来完成.
onCreateViewHolder()的目的是, 获取到每个item的view 对象.
onBindViewHolder()的目的是, 给每个item view 去绑定数据.
public class HomeActivity extends Activity
{
private RecyclerView mRecyclerView;
private List<String> mDatas;
private HomeAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_single_recyclerview);
initData();
mRecyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mAdapter = new HomeAdapter());
}
protected void initData()
{
mDatas = new ArrayList<String>();
for (int i = 'A'; i < 'z'; i++)
{
mDatas.add("" + (char) i);
}
}
}
如何设置具体的布局样式, 重点是如何实现瀑布流的效果.
//设置为线性布局, 样子就和普通的ListView一样了.
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
//设置为gridView的样式, eg. 下面这样设置就是每行展示4个小宫格.
mRecyclerView.setLayoutManager(new GridLayoutManager(this,4));
//设置为瀑布流的效果, 直接使用StaggeredGridLayoutManager就行. eg. 下面这样设置, 就是每行展示4个小宫格.
//瀑布流的实现本质就是给每个item view都设置一个随机的高度值,
//然后再使用StaggeredGridLayoutManager, 这样就实现了瀑布流的效果.
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.VERTICAL));
设置随机高度值的代码在这里:
在onBindViewHolder()中, 也就是在给每个item view 绑定数据时, 给每个item view设置一个新的随机的高度值.
public class StaggeredAdapter extends MyViewAdapter{
private List<Integer>mHeights;
public StaggeredAdapter(Context context, List<String>datas){
super(context,datas);
mHeights = new ArrayList<>();
for (int i = 0; i<mDatas.size();i++){
//高度取值在100~400px之间的一个随机值.
mHeights.add((int) (100+Math.random()*300));
}
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.mItemTX.setText(mDatas.get(position));
ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
lp.height = mHeights.get(position);
//在这里, 从新设置每个item view的高度值, 给它设置一个随机值.
holder.itemView.setLayoutParams(lp);
setUpItemEvent(holder);
}
}
如何设置每行item view 之间的分割线的样式
我们可以通过该方法添加分割线:
mRecyclerView.addItemDecoration()
该方法的参数为RecyclerView.ItemDecoration,该类为抽象类,官方目前并没有提供默认的实现类.
github上有一些现成的实现类, eg. DividerItemDecoration
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
}
这个类实现的是一个颜色渐变的分割线的效果.
具体的实现就先不看了, 用到的时候再说.
如何设置每个 item增加、删除时的动画
ItemAnimator也是一个抽象类,好在系统为我们提供了一种默认的实现类, DefaultItemAnimator.
github上有一些ItemAnimator的实现类, 实现了更好的动画效果, 可以这里找到.
https://github.com/gabrielemariotti/RecyclerViewItemAnimators
// 设置item动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
public void addData(int position) {
mDatas.add(position, "Insert One");
notifyItemInserted(position);
}
public void removeData(int position) {
mDatas.remove(position);
notifyItemRemoved(position);
}
有一点细节要注意, 这里更新数据集不是用adapter.notifyDataSetChanged()而是
notifyItemInserted(position)与notifyItemRemoved(position)
否则没有动画效果。
如何给RecyclerView设置item onClick listener 和 onLongClickListener.
RecyclerView.Adapter 中并没有提供一个默认的ClickListener和LongClickListener.
因此需要自己去写一个接口.
public interface OnItemClickLitener
{
void onItemClick(View view, int position);
void onItemLongClick(View view , int position);
}
class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
{
//...
public interface OnItemClickLitener
{
void onItemClick(View view, int position);
void onItemLongClick(View view , int position);
}
private OnItemClickLitener mOnItemClickLitener;
public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
{
this.mOnItemClickLitener = mOnItemClickLitener;
}
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position)
{
holder.tv.setText(mDatas.get(position));
// 如果用户设置了回调对象进来,则设置点击事件
if (mOnItemClickLitener != null)
{
// 在这里给item view设置点击回调.
holder.itemView.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
int pos = holder.getLayoutPosition();
mOnItemClickLitener.onItemClick(holder.itemView, pos);
}
});
holder.itemView.setOnLongClickListener(new OnLongClickListener()
{
@Override
public boolean onLongClick(View v)
{
int pos = holder.getLayoutPosition();
mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
return false;
}
});
}
}
//...
}
一点意外的收获
大神讲课用的是eclipse, 里面有个log filter 挺好用的, 我在android studio的插件库中找到了个logviewer, 也可以实现类似的效果.
logviewer的用法总结到简书的文章中去了.
---DONE.---
网友评论