基础使用
-
定义Activity和item布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="270dp" android:layout_height="330dp" android:clickable="true" android:foreground="?android:attr/selectableItemBackground" app:cardBackgroundColor="#393F4E" app:cardCornerRadius="16dp" app:cardPreventCornerOverlap="false"> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/iv" android:layout_width="170dp" android:layout_height="200dp" android:layout_gravity="center_horizontal" android:layout_marginTop="10dp" android:src="@mipmap/ic_launcher" /> <TextView android:id="@+id/tvName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="210dp" android:paddingTop="10dp" android:text="名字" android:textColor="@color/white" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|center_horizontal" android:layout_marginBottom="20dp" android:background="#F05079" android:text="喜欢" /> <TextView android:id="@+id/tvPrecent" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right|bottom" android:layout_margin="3dp" android:textColor="@android:color/white" tools:text="1/13" /> </androidx.cardview.widget.CardView>
-
设置Adapter和ViewHolder
这里使用一个通用模板,不参照按照日常使用那样就行,可跳过
封装的Adapter
public abstract class UniversalAdapter<T> extends RecyclerView.Adapter<ViewHolder> { protected Context mContext; protected int mLayoutId; protected List<T> mDatas; protected LayoutInflater mInflater; protected ViewGroup mRv; private OnItemClickListener mOnItemClickListener; public UniversalAdapter setOnItemClickListener(OnItemClickListener onItemClickListener) { this.mOnItemClickListener = onItemClickListener; return this; } public OnItemClickListener getmOnItemClickListener() { return this.mOnItemClickListener; } public UniversalAdapter(Context context, List<T> datas, int layoutId) { this.mContext = context; this.mInflater = LayoutInflater.from(context); this.mLayoutId = layoutId; this.mDatas = datas; } public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { ViewHolder viewHolder = ViewHolder.get(this.mContext, (View)null, parent, this.mLayoutId); if(null == this.mRv) { this.mRv = parent; } return viewHolder; } protected int getPosition(RecyclerView.ViewHolder viewHolder) { return viewHolder.getAdapterPosition(); } protected boolean isEnabled(int viewType) { return true; } /** @deprecated */ @Deprecated protected void setListener(final ViewGroup parent, final ViewHolder viewHolder, int viewType) { if(this.isEnabled(viewType)) { viewHolder.itemView.setOnClickListener(new OnClickListener() { public void onClick(View v) { if(UniversalAdapter.this.mOnItemClickListener != null) { int position = UniversalAdapter.this.getPosition(viewHolder); UniversalAdapter.this.mOnItemClickListener.onItemClick(parent, v, UniversalAdapter.this.mDatas.get(position), position); } } }); viewHolder.itemView.setOnLongClickListener(new OnLongClickListener() { public boolean onLongClick(View v) { if(UniversalAdapter.this.mOnItemClickListener != null) { int position = UniversalAdapter.this.getPosition(viewHolder); return UniversalAdapter.this.mOnItemClickListener.onItemLongClick(parent, v, UniversalAdapter.this.mDatas.get(position), position); } else { return false; } } }); } } public void onBindViewHolder(ViewHolder holder, int position) { this.setListener(position, holder); this.convert(holder, this.mDatas.get(position)); } protected void setListener(final int position, final ViewHolder viewHolder) { if(this.isEnabled(this.getItemViewType(position))) { viewHolder.itemView.setOnClickListener(new OnClickListener() { public void onClick(View v) { if(UniversalAdapter.this.mOnItemClickListener != null) { UniversalAdapter.this.mOnItemClickListener.onItemClick(UniversalAdapter.this.mRv, v, UniversalAdapter.this.mDatas.get(position), position); } } }); viewHolder.itemView.setOnLongClickListener(new OnLongClickListener() { public boolean onLongClick(View v) { if(UniversalAdapter.this.mOnItemClickListener != null) { int position = UniversalAdapter.this.getPosition(viewHolder); return UniversalAdapter.this.mOnItemClickListener.onItemLongClick(UniversalAdapter.this.mRv, v, UniversalAdapter.this.mDatas.get(position), position); } else { return false; } } }); } } // 将 var2的数据 显示在ViewHolder 的对应 View 中 public abstract void convert(ViewHolder var1, T var2); public int getItemCount() { return this.mDatas != null?this.mDatas.size():0; } public void setDatas(List<T> list) { if(this.mDatas != null) { if(null != list) { ArrayList temp = new ArrayList(); temp.addAll(list); this.mDatas.clear(); this.mDatas.addAll(temp); } else { this.mDatas.clear(); } } else { this.mDatas = list; } this.notifyDataSetChanged(); } public void remove(int i) { if(null != this.mDatas && this.mDatas.size() > i && i > -1) { this.mDatas.remove(i); this.notifyDataSetChanged(); } } public void addDatas(List<T> list) { if(null != list) { ArrayList temp = new ArrayList(); temp.addAll(list); if(this.mDatas != null) { this.mDatas.addAll(temp); } else { this.mDatas = temp; } this.notifyDataSetChanged(); } } public List<T> getDatas() { return this.mDatas; } public T getItem(int position) { return position > -1 && null != this.mDatas && this.mDatas.size() > position?this.mDatas.get(position):null; } }
Adapter要用到的Listener
public interface OnItemClickListener<T> { void onItemClick(ViewGroup var1, View var2, T var3, int var4); boolean onItemLongClick(ViewGroup var1, View var2, T var3, int var4); }
封装的ViewHolder
public class ViewHolder extends RecyclerView.ViewHolder { private SparseArray<View> mViews = new SparseArray(); private int mLayoutId; public ViewHolder(View itemView) { super(itemView); } public static ViewHolder get(Context context, View convertView, ViewGroup parent, int layoutId) { if (convertView == null) { View holder2 = LayoutInflater.from(context).inflate(layoutId, parent, false); ViewHolder holder1 = new ViewHolder(holder2); holder1.mLayoutId = layoutId; return holder1; } else { ViewHolder holder = (ViewHolder) convertView.getTag(); return holder; } } public int getLayoutId() { return this.mLayoutId; } public <T extends View> T getView(int viewId) { View view = (View) this.mViews.get(viewId); if (view == null) { view = this.itemView.findViewById(viewId); this.mViews.put(viewId, view); } return (T) view; } public ViewHolder setText(int viewId, CharSequence text) { TextView tv = (TextView) this.getView(viewId); tv.setText(text); return this; } public ViewHolder setSelected(int viewId, boolean selected) { View v = this.getView(viewId); v.setSelected(selected); return this; } public ViewHolder setImageResource(int viewId, int resId) { ImageView view = (ImageView) this.getView(viewId); view.setImageResource(resId); return this; } public ViewHolder setImageBitmap(int viewId, Bitmap bitmap) { ImageView view = (ImageView) this.getView(viewId); view.setImageBitmap(bitmap); return this; } public ViewHolder setImageDrawable(int viewId, Drawable drawable) { ImageView view = (ImageView) this.getView(viewId); view.setImageDrawable(drawable); return this; } public ViewHolder setBackgroundColor(int viewId, int color) { View view = this.getView(viewId); view.setBackgroundColor(color); return this; } public ViewHolder setBackgroundRes(int viewId, int backgroundRes) { View view = this.getView(viewId); view.setBackgroundResource(backgroundRes); return this; } public ViewHolder setTextColor(int viewId, int textColor) { TextView view = (TextView) this.getView(viewId); view.setTextColor(textColor); return this; } public ViewHolder setTextColorRes(int viewId, int textColorRes) { TextView view = (TextView) this.getView(viewId); view.setTextColor(this.itemView.getContext().getResources().getColor(textColorRes)); return this; } @SuppressLint({"NewApi"}) public ViewHolder setAlpha(int viewId, float value) { if (VERSION.SDK_INT >= 11) { this.getView(viewId).setAlpha(value); } else { AlphaAnimation alpha = new AlphaAnimation(value, value); alpha.setDuration(0L); alpha.setFillAfter(true); this.getView(viewId).startAnimation(alpha); } return this; } public ViewHolder setVisible(int viewId, boolean visible) { View view = this.getView(viewId); view.setVisibility(visible ? View.VISIBLE : View.GONE); return this; } public ViewHolder linkify(int viewId) { TextView view = (TextView) this.getView(viewId); Linkify.addLinks(view, Linkify.ALL); return this; } public ViewHolder setTypeface(Typeface typeface, int... viewIds) { int[] var3 = viewIds; int var4 = viewIds.length; for (int var5 = 0; var5 < var4; ++var5) { int viewId = var3[var5]; TextView view = (TextView) this.getView(viewId); view.setTypeface(typeface); view.setPaintFlags(view.getPaintFlags() | 128); } return this; } public ViewHolder setProgress(int viewId, int progress) { ProgressBar view = (ProgressBar) this.getView(viewId); view.setProgress(progress); return this; } public ViewHolder setProgress(int viewId, int progress, int max) { ProgressBar view = (ProgressBar) this.getView(viewId); view.setMax(max); view.setProgress(progress); return this; } public ViewHolder setMax(int viewId, int max) { ProgressBar view = (ProgressBar) this.getView(viewId); view.setMax(max); return this; } public ViewHolder setRating(int viewId, float rating) { RatingBar view = (RatingBar) this.getView(viewId); view.setRating(rating); return this; } public ViewHolder setRating(int viewId, float rating, int max) { RatingBar view = (RatingBar) this.getView(viewId); view.setMax(max); view.setRating(rating); return this; } public ViewHolder setTag(int viewId, Object tag) { View view = this.getView(viewId); view.setTag(tag); return this; } public ViewHolder setTag(int viewId, int key, Object tag) { View view = this.getView(viewId); view.setTag(key, tag); return this; } public ViewHolder setChecked(int viewId, boolean checked) { Checkable view = (Checkable) this.getView(viewId); view.setChecked(checked); return this; } public ViewHolder setOnClickListener(int viewId, OnClickListener listener) { View view = this.getView(viewId); view.setOnClickListener(listener); return this; } public ViewHolder setOnTouchListener(int viewId, OnTouchListener listener) { View view = this.getView(viewId); view.setOnTouchListener(listener); return this; } public ViewHolder setOnLongClickListener(int viewId, OnLongClickListener listener) { View view = this.getView(viewId); view.setOnLongClickListener(listener); return this; } public void setItemVisible(boolean visible) { if (null != this.itemView) { if (visible) { if (null != this.itemView.getLayoutParams()) { this.itemView.getLayoutParams().width = -1; this.itemView.getLayoutParams().height = -2; } else { this.itemView.setLayoutParams(new LayoutParams(-1, -2)); } this.itemView.setVisibility(View.VISIBLE); } else { if (null != this.itemView.getLayoutParams()) { this.itemView.getLayoutParams().width = -1; this.itemView.getLayoutParams().height = 1; } else { this.itemView.setLayoutParams(new LayoutParams(-1, 1)); } this.itemView.setVisibility(View.GONE); } } } public void setHItemVisible(boolean visible) { if (null != this.itemView) { if (visible) { if (null != this.itemView.getLayoutParams()) { this.itemView.getLayoutParams().width = -2; this.itemView.getLayoutParams().height = -2; } else { this.itemView.setLayoutParams(new LayoutParams(-1, -2)); } this.itemView.setVisibility(View.VISIBLE); } else { if (null != this.itemView.getLayoutParams()) { this.itemView.getLayoutParams().width = -1; this.itemView.getLayoutParams().height = 1; } else { this.itemView.setLayoutParams(new LayoutParams(-1, 1)); } this.itemView.setVisibility(View.GONE); } } } }
-
创建
adapter = object : UniversalAdapter<SwipeCardBean>(this, mDatas, R.layout.item_swipe_card) { override fun convert(holder: ViewHolder?, bean: SwipeCardBean?) { holder?.run { setText(R.id.tvName, bean!!.name) setText(R.id.tvPrecent, "${bean.position}/${mDatas.size}") Glide.with(this@MainActivity) .load(bean.url) .into(getView(R.id.iv) as ImageView) } } } rv.adapter = adapter
自定义LayouManager
-
重写generateDefaultLayoutParams方法
为itemView创建layout parameters
override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams { return RecyclerView.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ) }
-
重写onLayoutChildren方法
用来布局itemView
override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) { // 回收复用 detachAndScrapAttachedViews(recycler!!) // 8个 // 0 1 2 3 4 5 6 7 val bottomPosition = if (itemCount < CardConfig.MAX_SHOW_COUNT) { 0 } else { itemCount - CardConfig.MAX_SHOW_COUNT } for (i in bottomPosition until itemCount) { // 复用 val view = recycler.getViewForPosition(i) addView(view) measureChildWithMargins(view, 0, 0) // 求padding大小 val widthSpace = width - getDecoratedMeasuredWidth(view) val heightSpace = height - getDecoratedMeasuredHeight(view) //布局 layoutDecoratedWithMargins( view, widthSpace / 2, heightSpace / 2, widthSpace / 2 + getDecoratedMeasuredWidth(view), heightSpace / 2 + getDecoratedMeasuredHeight(view) ) val level = itemCount - i - 1 if (level > 0) { if (level < CardConfig.MAX_SHOW_COUNT - 1) { view.translationY = (CardConfig.TRANS_Y_GAP * level).toFloat() view.scaleX = 1 - CardConfig.SCALE_GAP * level view.scaleY = 1 - CardConfig.SCALE_GAP * level } else { // 把第四个弄得跟第三个一样 view.translationY = (CardConfig.TRANS_Y_GAP * (level - 1)).toFloat() view.scaleX = 1 - CardConfig.SCALE_GAP * (level - 1) view.scaleY = 1 - CardConfig.SCALE_GAP * (level - 1) } } } }
-
使用
rv.layoutManager = SwipeCardLayoutManager()
自定义ItemTouchHelper的Callback
-
重写onMove
Called when ItemTouchHelper wants to move the dragged item from its old position to the new position. 这里不处理,直接返回false
override fun onMove( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder ): Boolean { return false }
-
重写onSwipe
Called when a ViewHolder is swiped by the user. 滑出去删除时调用
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { val remove = mDatas.removeAt(viewHolder.layoutPosition) mDatas.add(0, remove) adapter.notifyDataSetChanged() }
-
重写onChildDraw
定义怎么布局子view
override fun onChildDraw( c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean ) { super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) Log.i("jintag", "onChildDraw: actionState: $actionState") val maxDistance = recyclerView.width * 0.5f val distance = sqrt(dX * dX + dY * dY) var fraction = distance / maxDistance.toDouble() if (fraction > 1) { fraction = 1.0 } val itemCount = recyclerView.childCount for (i in 0 until itemCount) { val view = recyclerView.getChildAt(i) val level = itemCount - i - 1 if (level > 0) { view.translationY = (CardConfig.TRANS_Y_GAP * level - fraction * CardConfig.TRANS_Y_GAP).toFloat() view.scaleX = (1 - CardConfig.SCALE_GAP * level + fraction * CardConfig.SCALE_GAP).toFloat() view.scaleY = (1 - CardConfig.SCALE_GAP * level + fraction * CardConfig.SCALE_GAP).toFloat() } } }
-
使用
// 拖拽删除 val callback = SwipeCardCallback(rv, adapter, mDatas) val helper = ItemTouchHelper(callback) helper.attachToRecyclerView(rv)