RecyclerView
RecyclerView只管回收和复用View,其他的自己去设置。
整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活
常用操作
RecyclerView.Adapter
处理数据集+绑定视图
ViewHolder
装载view
RecyclerView.LayoutManager
布局管理器,用于控制item的摆放
必须为RecyclerView指定LayoutManager,否则会出现异常
RecyclerView.ItemDecoration
设置分割线以及item之间的间隔
RecyclerView.ItemAnimator
设置item增删动画效果
RcyclerView.OnItemTouchListener
ItemTouchHelper是一个处理RecyclerView的滑动删除和拖拽的辅助类
1.RecyclerView.Adapter
public class SimplerItemAdapter extends RecyclerView.Adapter<SimplerItemAdapter.SimpleItemViewHolder> {
private List<String> items;
public SimplerItemAdapter(@NonNull List<String> dateItems) {
this.items = (dateItems != null ? dateItems : new ArrayList<String>());
}
//创建ViewHolder并关联item视图
@Override
public SimpleItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
return new SimpleItemViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false));
}
//设置数据
@Override
public void onBindViewHolder(SimpleItemViewHolder viewHolder, int position) {
viewHolder.textView.setText(items.get(position));
}
@Override
public int getItemCount() {
return (this.items != null) ? this.items.size() : 0;
}
protected final static class SimpleItemViewHolder extends RecyclerView.ViewHolder {
protected TextView textView;
public SimpleItemViewHolder(View itemView) {
super(itemView);
this.textView = (TextView) itemView.findViewById(R.id.text);
}
}
}
2.RecyclerView.LayoutManager
有如下三种布局:
- LinearLayoutManager 水平或者垂直的Item视图
- GridLayoutManager 网格Item视图
- StaggeredGridLayoutManager 瀑布流
LayoutManager 提供了以下方法:
findFirstVisibleItemPosition() 返回当前第一个可见Item的position
findFirstCompletelyVisibleItemPosition() 返回当前第一个完全可见Item的position
findLastVisibleItemPosition() 返回当前最后一个可见Item的position
findLastCompletelyVisibleItemPosition() 返回当前最后一个完全可见Item的position
3.RecyclerView.ItemDecoration
这个写起来有点麻烦,若是没有强制需求的时候,还是设置item的margin方便
通过重写以下三个方法,来实现Item之间的偏移量或者装饰效果:
* public void onDraw(Canvas c, RecyclerView parent)
* 在Item条目绘制之前调用,所以这有可能被Item的内容所遮挡
* public void onDrawOver(Canvas c, RecyclerView parent)
* 在Item条目绘制之后调用,因此装饰将浮于Item之上
* public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)
* 可以通过outRect.set()为每个Item设置一定的偏移量,主要用于绘制Decorator。
ItemDecoration是可以叠加的,因此你可以为每个子view装饰多次
添加分割线
recyclerView.addItemDecoration(new DividerDecoration(this,DividerItemDecoration.HORIZONTAL_LIST))
DividerDecoration是ItemDecoration的一个实现类,通过读取系统主题中
Android.R.attr.listDivider
作为Item间的分割线,并且支持横向和纵向获取到listDivider以后,该属性的值是个Drawable,在getItemOffsets中,outRect设置了绘制的范围,在onDraw中实现了真正的绘制
设置分割线的样式
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
.......
<item name="android:listDivider">@drawable/divider_bg</item>
</style>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:centerColor="#ff00ff00"
android:endColor="#ff0000ff"
android:startColor="#ffff0000"
android:type="linear"/>
<size android:height="4dp"/>
</shape>
注意:当每个Item一行,这样是没问题的,但是当布局为GridLayoutManager、流式布局时,就会出现问题,然后就需要自己去写一个分割线,好难过
4.RecyclerView.ItemAnimator
设置item增删动画效果
这个就好多了,Android给了一个DefaultItemAnimator
,我们可以通过以下代码设置item增删动画效果:
recyclerView.setItemAnimator(new DefaultItemAnimator());
在ListView中,当集合数据发生改变的时候,通过调用notifyDataSetChanged刷新界面,这样做只是对界面进行重绘,并没有任何动画效果,因此在RecyclerVIew中提供了以下方法:
public final void notifyItemInserted(int position) 向指定位置插入Item
public final void notifyItemRemoved(int position) 移除指定位置Item
public final void notifyItemChanged(int position) 更新指定位置Item
常用方法
//如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
mRecyclerView.setHasFixedSize(true);
拓展
A.设置item点击,长按事件
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
holder.tv.setText(mDatas.get(position));
if (mOnItemClickLitener != null) {
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;
}
});
}
}
注意:这里使用了ViewHolder的getLayoutPosition
方法,此方法返回的pos值与onBindViewHolder方法传入的position值有可能不同。
根据SDK中的解释,在Recyclerview 进行添加、移除item等操作时,position位置可能会变化,而所有的adapter的刷新并不总是及时的,只有这个方法返回的才是当前item经过一些变换后所处的真正位置
B.多布局
getItemViewType(int position)+onCreateViewHolder(ViewGroup parent, int viewType)
//标题
private int TYPE_TITLE = 0;
//内容
private int TYPE_SUB = 1;
@Override
public int getItemViewType(int position) {
return typeList.get(position) instanceof String ? TYPE_TITLE : TYPE_SUB;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_TITLE) {
//标题类型
return new TitleViewHolder(LayoutInflater.from(mActivity).inflate(R.layout.adapter_category_title, parent, false));
} else {
return new SubViewHolder(LayoutInflater.from(mActivity).inflate(R.layout.adapter_category_sub, parent, false));
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
if(viewType==TYPE_TITLE){
String title = (String) typeList.get(position);
((TitleViewHolder) holder).mTitle.setText(title);
}else {
CategoryInfo.ListBean.InfosBean infosBean = (CategoryInfo.ListBean.InfosBean) typeList.get(position);
SubViewHolder subViewHolder = (SubViewHolder) holder;
subViewHolder.tv_name1.setText(infosBean.name1);
Glide.with(mActivity).load(NetUrl.IMG_PREFIX+infosBean.url1).into(subViewHolder.iv_image1);
}
}
@Override
public int getItemCount() {
return typeList.size();
}
//titleViewHolder
public class TitleViewHolder extends RecyclerView.ViewHolder {
public TextView mTitle;
public TitleViewHolder(View itemView) {
super(itemView);
mTitle = (TextView) itemView.findViewById(R.id.tv_title);
}
}
//subVieHOlder
public class SubViewHolder extends RecyclerView.ViewHolder {
public ImageView iv_image1;
public TextView tv_name1;
public SubViewHolder(View itemView) {
super(itemView);
this.iv_image1 = (ImageView) itemView.findViewById(R.id.iv_image1);
this.tv_name1 = (TextView) itemView.findViewById(R.id.tv_name1);
//在这里可以设置item点击事件
}
}
C. RecyclerView的滑动删除和拖拽
这个实现的核心主要由ItemTouchHelper来完成
ItemTouchHelper简介:
实现RecyclerView滑动删除和拖拽的工具类
是RecyclerView.ItemDecoration的子类
常用方法
private class MyCallBack1 extends ItemTouchHelper.Callback {
/**
* 设置滑动或者拖拽的方向
* drag - 表示拖拽的方向,有六个类型的值:LEFT、RIGHT、START、END、UP、DOWN
* swipe - 表示滑动的方向,有六个类型的值:LEFT、RIGHT、START、END、UP、DOWN
* 如果为0,则不执行拖动或者滑动
* @param recyclerView
* @param viewHolder
* @return
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = 0, swipeFlags = 0;
if (recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager || recyclerView.getLayoutManager() instanceof GridLayoutManager) {
//网格式布局有4个方向
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
} else if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
//线性式布局有2个方向
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END; //设置侧滑方向为从两个方向都可以
}
//makeMovementFlags./.
return makeMovementFlags(dragFlags, swipeFlags);
}
/**
* item拖拽时调用的方法
*
* @param recyclerView
* @param viewHolder 拖动的ViewHolder
* @param target 目标位置的ViewHolder
* @return
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//拿到起始/目标位置Position
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
Collections.swap(list, from, to);
myAdapter.notifyItemMoved(from, to);
//返回true表示执行拖动
return true;
}
/**
* item滑动时调用的方法
*
* @param viewHolder 滑动的ViewHolder
* @param direction 滑动的方向
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
list.remove(position);
myAdapter.notifyItemRemoved(position);
}
/**
* 当item绘制的时候调用的方法
*
* @param c
* @param recyclerView
* @param viewHolder
* @param dX
* @param dY
* @param actionState 三种状态SWIPE(滑动)、IDLE(静止)、DRAG(拖动)
* @param isCurrentlyActive
*/
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
//滑动时改变item的透明度
final float alpha = 1 - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
viewHolder.itemView.setAlpha(alpha);
viewHolder.itemView.setTranslationX(dX);
}
}
//当选中Item时候会调用该方法,重写此方法可以实现选中时候的一些动画逻辑
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
}
//当动画已经结束的时候调用该方法,重写此方法可以实现恢复Item的初始状态
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
}
/**
* 是否可以长按拖拽排序
* @return
*/
@Override
public boolean isLongPressDragEnabled() {
return super.isLongPressDragEnabled();
}
/**
* Item是否可以被滑动(H:左右滑动,V:上下滑动)
*/
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
}
使用
//1.创建ItemTouchHelper实例
MyCallback callback = new MyCallback(
ItemTouchHelper.UP|ItemTouchHelper.DOWN,
ItemTouchHelper.START|ItemTouchHelper.END
);
ItemTouchHelper helper = new ItemTouchHelper(callback);
//2.与recyclerView进行绑定
helper.attachToRecyclerView(recyclerView);
//3.在callback中实现具体逻辑
Android提供了一个默认的实现类SimpleCallback
,简化了操作。
CardView
CardLayout是一个拥有高度和阴影,以及轮廓裁剪,圆角的布局
添加依赖:
compile 'com.android.support:cardview-v7:23.2.1'
各属性说明:
1.设置圆角:card_view:cardCornerRadius="10dp"
2.设置高度:card_view:cardElevation="10dp"
3.设置内边距:card_view:contentPadding="10dp"
4.设置背景色:card_view:cardBackgroundColor="?android:attr/colorPrimary"
布局文件使用
<!--cardCornerRadius : 圆角
contentPadding : 距离内边框的距离
cardBackgroundColor : 背景色
cardElevation: 海拔 决定 阴影效果
cardUseCompatPadding打开设备兼容性
-->
<android.support.v7.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
app:cardCornerRadius="10dp"
app:contentPadding="10dp"
app:cardBackgroundColor="#0000FF"
app:cardElevation="50dp"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="30dp"
android:layout_height="30dp"
android:background="#FF0000"
/>
<TextView
android:layout_width="30dp"
android:layout_height="30dp"
android:background="#00FF00"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
可在布局文件设置多个显示多种效果