RecyclerView
》一直這麼用RecyclerView都沒總結過幾次,大概說下RecyclerView這個東西吧
介紹:
1、RecyclerView.Adapter:負責託管資料集,為每一項Item建立佈局並繫結資料。
2、RecyclerView.ItemDecoration,給Item新增分割線。需要繼承該類自定義一個類。
3、RecyclerView.ItemAnimator負責處理Item增加或刪除時的動畫效果,系統提供了一個預設的動畫類DefaultItemAnimator()。
4、RecyclerView.ViewHolder:負責承載Item檢視的子佈局。
先看看正常情況下的RecyclerView.Adapter
public class RecAdapterCommon extends RecyclerView.Adapter<RecAdapterCommon.MyViewHolder> {
private List<Datas> data = new ArrayList<>();
private Context context;
public RecAdapterCommon(Context context) {
this.context = context;
}
public void setData(List<Datas> data) {
if (data != null && data.size() > 0) {
this.data.clear();
this.data.addAll(data);
notifyDataSetChanged();
}
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.item_layout, parent, false);
return new MyViewHolder(v);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
Datas datas = data.get(position);
if (datas != null) {
holder.tv_name.setText(datas.getName());
holder.tv_money.setText(datas.getMoney());
}
}
@Override
public int getItemCount() {
return data.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView tv_name, tv_money;
public MyViewHolder(View itemView) {
super(itemView);
tv_name = (TextView) itemView.findViewById(R.id.name_tv_item);
tv_money = (TextView) itemView.findViewById(R.id.money_tv_item);
}
}
}
- 以上
RecyclerView.Adapter
雖然是屬於優化後的Adapter,可以很好應用,但是要是資料來源List<Datas> data
改變為List<Person> person
呢,(要是佈局不同呢),又要去改Adapter,或者兩個都有是不是也要新增一樣的資料來源不同的Adapter,所以,下來就要做到Adapter的同用,這樣才完美
RecyclerView.Adapter
的通用1.資料怎麼辦?
可以將資料來源改為泛型,傳入什麼資料就是什麼資料,通用
2.佈局怎麼辦?
同Context一樣,在構造方法中實現,在外部傳入
3.繫結怎麼辦?
將該RecyclerView.Adapter
改為抽象類,實現對外的抽象方法
看程式碼:
public abstract class RecAdapterCommon<T> extends RecyclerView.Adapter<MyViewHolder> {
private List<T> data;
private int mLayoutId;
private Context context;
public RecAdapterCommon(Context context,List<T> data, int mLayoutId) {
this.context = context;
this.mLayoutId = mLayoutId;
this.data = data;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//R.layout.item_layout===========>>> mLayoutId
View v = LayoutInflater.from(context).inflate(mLayoutId, parent, false);
return new MyViewHolder(v);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
T datas = data.get(position);
if (datas != null) {
convert(holder, datas);
}
}
/**
* 既然已介面的形式將資料要傳出去,那麼本類RecAdapterCommon也要改為介面形式新增abstract
*
* @param holder
* @param datas
*/
public abstract void convert(MyViewHolder holder, T datas);
@Override
public int getItemCount() {
return data.size();
}
}
通用ViewHolder
既然Adapter都通用了,那麼ViewHolder也不可能獨善其身了,那麼我們可以將所有的控制元件都集中在一個泛型的View中就可以啦
public class MyViewHolder extends RecyclerView.ViewHolder {
// 用來存放子View減少findViewById的次數
private SparseArray<View> mViews;
public MyViewHolder(View itemView) {
super(itemView);
mViews = new SparseArray<>();
}
/**
* 通過id獲取view
*/
public <T extends View> T getView(int viewId) {
// 先從快取中找
View view = mViews.get(viewId);
if (view == null) {
// 直接從ItemView中找
view = itemView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
/**
* 設定TextView文字
*/
public MyViewHolder setText(int viewId, CharSequence text) {
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
/**
* 設定ImageView的資源
*/
public MyViewHolder setImageResource(int viewId, int resourceId) {
ImageView imageView = getView(viewId);
imageView.setImageResource(resourceId);
return this;
}
}
那麼同用完了,要是我們的專案需求改了,客戶現在要求有點選長按事件以及拖動和側滑刪除呢
1.點選長按事件都是很簡單,對外實現介面就行
2.那麼側滑和拖動呢?正好SDK提供了ItemTouchHelper
這樣一個工具類幫助我們實現拖動和側滑
- 那麼我們先建立
CommonItemTouchHelper
類繼承ItemTouchHelper.Callback
public class CommonItemTouchHelper extends ItemTouchHelper.Callback {
private static final float ALPHA_FULL = 1.0f;
private CommonItemTouchHelperAdapter adapter;
private boolean isLongPressDragEnabled = false, isItemViewSwipeEnabled = false;
public CommonItemTouchHelper(CommonItemTouchHelperAdapter adapter) {
this.adapter = adapter;
}
/**
* 支援長按開始拖拽
*
* @return
*/
public void setLongPressDragEnabled(boolean isLongPressDragEnabled) {
this.isLongPressDragEnabled = isLongPressDragEnabled;
}
/**
* 該方法返回true時,表示支援長按拖動,
* 即長按ItemView後才可以拖動,我們遇到的場景一般也是這樣的。預設是返回false。
*
* @return
*/
@Override
public boolean isLongPressDragEnabled() {
return isLongPressDragEnabled;
}
/**
* 支援左右滑動
*
* @return
*/
public void setItemViewSwipeEnabled(boolean itemViewSwipeEnabled) {
isItemViewSwipeEnabled = itemViewSwipeEnabled;
}
/**
* 該方法返回true時,表示如果使用者觸控並左右滑動了View,
* 那麼可以執行滑動刪除操作,即可以呼叫到onSwiped()方法。預設是返回false。
*
* @return
*/
@Override
public boolean isItemViewSwipeEnabled() {
return isItemViewSwipeEnabled;
}
/**
* 持哪個方向的拖拽和滑動
*
* @param recyclerView
* @param viewHolder
* @return
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.START | ItemTouchHelper.END;// 允許上下左右的拖動
int swipeFlags = 0;//無觸發
return makeMovementFlags(dragFlags, swipeFlags);
} else if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
return 0;
}
/**
* 從靜止狀態變為拖拽或者滑動的時候會回撥該方法,引數actionState表示當前的狀態。
*
* @param viewHolder
* @param actionState
*/
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
}
/**
* 拖拽後呼叫
*
* @param recyclerView
* @param viewHolder
* @param target
* @return
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
if (viewHolder.getItemViewType() != target.getItemViewType()) {
return false;
}
adapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return false;
}
/**
* 滑動刪除後呼叫
* <p>
* 當使用者左右滑動Item達到刪除條件時,會呼叫該方法,
* 一般手指觸控滑動的距離達到RecyclerView寬度的一半時,再鬆開手指,
* 此時該Item會繼續向原先滑動方向滑過去並且呼叫onSwiped方法進行刪除,否則會反向滑回原來的位置。
* <p>
* 如果在onSwiped方法內我們沒有進行任何操作,即不刪除已經滑過去的Item,
* 那麼就會留下空白的地方,因為實際上該ItemView還佔據著該位置,只是移出了我們的可視範圍內罷了
*
* @param viewHolder
* @param direction
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
adapter.onItemDelete(viewHolder.getAdapterPosition());
}
/**
* 我們可以在這個方法內實現我們自定義的互動規則或者自定義的動畫效果
*
* @param c
* @param recyclerView
* @param viewHolder
* @param dX
* @param dY
* @param actionState
* @param isCurrentlyActive
*/
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE && dX > 200
|| actionState == ItemTouchHelper.ACTION_STATE_SWIPE && dX < -200) {//滑動,手指沒有離開螢幕
//左右滑動時改變Item的透明度
final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
viewHolder.itemView.setAlpha(alpha);
viewHolder.itemView.setTranslationX(dX);
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
/**
* 當使用者操作完畢某個item並且其動畫也結束後會呼叫該方法,
* 一般我們在該方法內恢復ItemView的初始狀態,防止由於複用而產生的顯示錯亂問題。
*
* @param recyclerView
* @param viewHolder
*/
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
//重置改變,防止由於複用而導致的顯示問題
viewHolder.itemView.setAlpha(ALPHA_FULL);
}
}
以上程式碼中的方法就不一一解釋了,程式碼中都有註釋,在程式碼中我們頭一個private CommonItemTouchHelperAdapter
介面類,主要是對外實現拖動和側滑的方法,既然是對外的,那麼我們的RecAdapterCommon
就要實現它,來注意我們的item的改變
public abstract class RecAdapterCommon<T> extends RecyclerView.Adapter<MyViewHolder> implements CommonItemTouchHelperAdapter {
//...部分程式碼略...
public RecAdapterCommon(Context context,List<T> data, int mLayoutId) {
//...部分程式碼略...
OnItemListener = (RecOnItemListener) context;
}
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//實現點選事件
OnItemListener.onItemClickListener(v, position);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
//實現長按事件
OnItemListener.onItemLongClickListener(v, position);
return false;
}
});
//...部分程式碼略...
}
private RecOnItemListener OnItemListener;
/**
* 自定義對外實現點選、長按的介面
*/
public interface RecOnItemListener {
void onItemClickListener(View view, int position);
void onItemLongClickListener(View view, int position);
}
/**
* 拖動時
*
* @param formPosition
* @param toPosition
*/
@Override
public void onItemMove(int formPosition, int toPosition) {
//拖動交換位置
Collections.swap(data, formPosition, toPosition);
notifyItemMoved(formPosition, toPosition);
}
/**
* 側滑刪除
*
* @param position
*/
@Override
public void onItemDelete(int position) {
//側滑刪除
data.remove(position);
notifyItemRemoved(position);
}
}
如果想監聽點選和長按需要implements ``RecAdapterCommon.RecOnItemListener
介面實現點選和長按的方法
想實現側滑和拖動還需要再MainActivity中這樣
//設定佈局
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// 給每個item新增分割線
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
// 設定item增加和移除的動畫
recyclerView.setItemAnimator(new DefaultItemAnimator());
adapter = new MyAdapter(this, datasList, R.layout.layout_itemssss);
recyclerView.setAdapter(adapter);
//側滑和拖動要配置這些才卡一實現----
//建立CommonItemTouchHelper
CommonItemTouchHelper callback = new CommonItemTouchHelper(adapter);
callback.setItemViewSwipeEnabled(true);//設定可以側滑
callback.setLongPressDragEnabled(true);//可以拖拽
//用Callback構造ItemtouchHelper
ItemTouchHelper itemTouchHelper= new ItemTouchHelper(callback);
//呼叫ItemTouchHelper的attachToRecyclerView方法建立聯絡
itemTouchHelper.attachToRecyclerView(recyclerView);
上面拖動和策劃完了,下來專案有改需求了,客戶想要的側滑是,側滑時先出來個刪除按鈕,然後再點選刪除才會刪除資料否則不刪除,那麼下來我們就自己手動實現側滑出現刪除
- 首先我們自定義一個佈局View ->
CommonSlidingButtonView
繼承HorizontalScrollView
,我這裡給它側滑時出現兩個按鈕
public class CommonSlidingButtonView extends HorizontalScrollView {
private TextView mTextView_Delete;
private TextView mTextView_Save;
private int mScrollWidth;
private IonSlidingButtonListener mIonSlidingButtonListener;
private Boolean isOpen = false;
private Boolean once = false;
private boolean isAllowableSliding = true;//是否允許側滑
public CommonSlidingButtonView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
getAttrs(context, attrs);
}
public CommonSlidingButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.setOverScrollMode(OVER_SCROLL_NEVER);
getAttrs(context, attrs);
}
/**
* 新增可在xml檔案中直接設定是否允許策劃
* @param context
* @param attrs
*/
private void getAttrs(Context context, AttributeSet attrs) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CommonSlidingButtonView);
isAllowableSliding = ta.getBoolean(R.styleable.CommonSlidingButtonView_isAllowableSliding, true);
ta.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!once) {
mTextView_Delete = (TextView) findViewById(R.id.common_delete);
mTextView_Save = (TextView) findViewById(R.id.common_save);
once = true;
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
this.scrollTo(0, 0);
//獲取水平滾動條可以滑動的範圍,即右側按鈕的寬度
mScrollWidth = mTextView_Delete.getWidth() + mTextView_Save.getWidth();
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (isAllowableSliding) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
mIonSlidingButtonListener.onDownOrMove(this);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
changeScrollx();
return true;
default:
break;
}
return super.onTouchEvent(ev);
} else {
return true;
}
}
/**
* 按滾動條被拖動距離判斷關閉或開啟選單
*/
public void changeScrollx() {
if (getScrollX() >= (mScrollWidth / 2)) {
this.smoothScrollTo(mScrollWidth, 0);
isOpen = true;
mIonSlidingButtonListener.onMenuIsOpen(this);
} else {
this.smoothScrollTo(0, 0);
isOpen = false;
}
}
/**
* 開啟選單
*/
public void openMenu() {
if (isOpen) {
return;
}
this.smoothScrollTo(mScrollWidth, 0);
isOpen = true;
mIonSlidingButtonListener.onMenuIsOpen(this);
}
/**
* 關閉選單
*/
public void closeMenu() {
if (!isOpen) {
return;
}
this.smoothScrollTo(0, 0);
isOpen = false;
}
public void setSlidingButtonListener(IonSlidingButtonListener listener) {
mIonSlidingButtonListener = listener;
}
public interface IonSlidingButtonListener {
void onMenuIsOpen(View view);
void onDownOrMove(CommonSlidingButtonView commonSlidingButtonView);
}
}
- 好了,佈局完了,我們就根據這個佈局View自定義一個xml的item(看佈局)
<?xml version="1.0" encoding="utf-8"?>
<com.suchengkeji.android.landroidaunchmodedemo.commonadpter.CommonSlidingButtonView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:isAllowableSliding="true"
android:background="@android:color/white">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:id="@+id/common_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/name_tv_item"
android:lines="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:textColor="@color/colorPrimaryDark"
android:textSize="18sp" />
<TextView
android:lines="1"
android:id="@+id/money_tv_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:textColor="@color/colorPrimaryDark"
android:textSize="18sp" />
</LinearLayout>
</RelativeLayout>
<TextView
android:id="@+id/common_save"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_toRightOf="@+id/common_content"
android:background="#00ff00"
android:gravity="center"
android:text="刪 除"
android:textColor="#DDFFFFFF"/>
<TextView
android:id="@+id/common_delete"
android:layout_width="80dp"
android:layout_height="match_parent"
android:layout_toRightOf="@+id/common_save"
android:background="#ff0000"
android:gravity="center"
android:text="刪 除"
android:textColor="#DDFFFFFF"/>
</RelativeLayout>
</com.suchengkeji.android.landroidaunchmodedemo.commonadpter.CommonSlidingButtonView>
- 佈局完了就該看他的Adapter了,我們讓
CommonRecyclerAdapter
同時也實現介面CommonSlidingButtonView.IonSlidingButtonListener
,先看看關於CommonSlidingButtonView
的方法
/********************* 側滑 *************************/
private CommonSlidingButtonView mMenu = null;
/**
* 刪除選單開啟資訊接收
*
* @param view
*/
@Override
public void onMenuIsOpen(View view) {
mMenu = (CommonSlidingButtonView) view;
}
/**
* 滑動或者點選了Item監聽
*
* @param commonSlidingButtonView
*/
@Override
public void onDownOrMove(CommonSlidingButtonView commonSlidingButtonView) {
if (menuIsOpen()) {
if (mMenu != commonSlidingButtonView) {
closeMenu();
}
}
}
/**
* 關閉選單
*/
public void closeMenu() {
mMenu.closeMenu();
mMenu = null;
}
/**
* 判斷是否有選單開啟
*/
public Boolean menuIsOpen() {
if (mMenu != null) {
return true;
}
return false;
}
public void removeData(int position) {
mData.remove(position);
notifyItemRemoved(position);
}
同樣,和我們的點選和長按事件一樣給他新增策劃的對外介面監聽
private OnItemListener OnItemListener;
public interface OnItemListener {
void onItemClickListener(View view, int position);
void onItemLongClickListener(View view, int position);
//側滑欄中的刪除點選
void onItemSlidingClickListener(View view, int position);
}
下來就簡單了,每一次在點選或長按之前先判斷側滑是否開啟
//判斷是否有刪除選單開啟
if (menuIsOpen()) {
closeMenu();//關閉選單
} else {
OnItemListener.onItemLongClickListener(v, position);
}
同樣,在點選側滑欄裡面的刪除或者其他鍵之後記得關閉側滑欄
if (menuIsOpen()) {
closeMenu();//關閉選單
}
- 到這裡了估計就有人說了,他現在還是不能跑,跑起來碰下就直接crash 了,那是因為我們的ViewHolder還沒配啊,來下來一起看看
ViewHolder
,ViewHolder中要用到CommonSlidingButtonView.IonSlidingButtonListener
,又不在同類,只能在RecAdapterCommon
中給留條後路,如下
private static CommonSlidingButtonView.IonSlidingButtonListener ionSlidingButtonListener;
public static CommonSlidingButtonView.IonSlidingButtonListener getIonSlidingButtonListener() {
return ionSlidingButtonListener;
}
那麼在ViewHolder
中就簡單啦
public MyViewHolder(View itemView) {
super(itemView);
mViews = new SparseArray<>();
((CommonSlidingButtonView) itemView).setSlidingButtonListener((CommonSlidingButtonView.IonSlidingButtonListener) CommonRecyclerAdapter.getIonSlidingButtonListener());
}
在這裡又出現問提了,我的刪除鍵是根據訊息內容顯示的,要是太長畫不出來,太短不劃就出來了,還有點選長按都沒用了怎麼搞。
記得我們在佈局裡面有個父佈局RelativeLayout
包含了所以有的子佈局是吧,我們直接給他設定個手機螢幕寬,點選長按事件也可以通過父佈局設定
/*******ViewHolder 中*******/
public ViewGroup viewGroup;//內容的邊框大小
public TextView commonDelete, commonSave;//刪除、收藏。。。。
public MyViewHolder(View itemView) {
super(itemView);
mViews = new SparseArray<>();
commonDelete = itemView.findViewById(R.id.common_delete);
commonSave = itemView.findViewById(R.id.common_save);
viewGroup = itemView.findViewById(R.id.common_content);
((CommonSlidingButtonView) itemView).setSlidingButtonListener((CommonSlidingButtonView.IonSlidingButtonListener) CommonRecyclerAdapter.getIonSlidingButtonListener());
}
/******RecAdapterCommon中-----onBindViewHolder方法******/
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
//點選
holder.viewGroup.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//判斷是否有刪除選單開啟
if (menuIsOpen()) {
closeMenu();//關閉選單
} else {
//可解決item拖動後讓他的位置點也隨之改變
int adapterPosition = holder.getAdapterPosition();
OnItemListener.onItemClickListener(v, adapterPosition );
}
}
});
//長按
holder.viewGroup.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//判斷是否有刪除選單開啟
if (menuIsOpen()) {
closeMenu();//關閉選單
} else {
OnItemListener.onItemLongClickListener(v, holder.getAdapterPosition());
}
}
});
//刪除
holder.commonDelete.setText("刪除");
holder.commonDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
OnItemListener.onItemSlidingClickListener(v, holder.getAdapterPosition());
if (menuIsOpen()) {
closeMenu();//關閉選單
}
}
});
//其他
holder.commonSave.setText("其他");
holder.commonSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//其他。。。如:置頂、刪除。。。。
if (menuIsOpen()) {
closeMenu();//關閉選單
}
}
});
if (data.get(position) != null) {
convert(holder, data.get(position));
}
}
- 這個是我在執行時發現的一個問題,正常情況沒遇見過不知道為什麼突然會這樣,不過還好改變方式就可以了,上面為什麼不用
postion
而用int adapterPosition = holder.getAdapterPosition();
呢?
因為一個的值會隨拖動改變,一個不會變,會造成刪除時出現crash(比如:)- 一共載入了兩個item(
itemA 原位置postion = 0,itemB 原位置postion = 1
),先拖動itemA
到itemB
的位置,那麼看postion的變化,可以列印看看
- 一共載入了兩個item(
-- | 拖動前 | 拖動後 |
---|---|---|
holder.getAdapterPosition() | 0 | 1 |
position | 0 | 0 |
》注意點*
1.如果自定義的側滑和SDK工具ItemTouchHelper實現的側滑同時開啟,則優先使用ItemTouchHelper實現的側滑,所以想要應用自定義這個必須在
MainActivity
中設定callback.setItemViewSwipeEnabled(false);//設定不可以側滑
同時在CommonSlidingButtonView
佈局中新增上app:isAllowableSliding= true
允許側滑
2.RecyclerView設定時必須設定佈局,不然不會顯示
最終原始碼:
ViewHodler
public class CommonViewHodler extends RecyclerView.ViewHolder {
// 用來存放子View減少findViewById的次數
private SparseArray<View> mViews;
public ViewGroup viewGroup;//內容的邊框大小
public TextView commonDelete, commonSave;//刪除、收藏。。。。
public CommonViewHodler(View itemView) {
super(itemView);
mViews = new SparseArray<>();
commonDelete = itemView.findViewById(R.id.common_delete);
commonSave = itemView.findViewById(R.id.common_save);
viewGroup = itemView.findViewById(R.id.common_content);
((CommonSlidingButtonView) itemView).setSlidingButtonListener((CommonSlidingButtonView.IonSlidingButtonListener) CommonRecyclerAdapter.getIonSlidingButtonListener());
}
/**
* 通過id獲取view
*/
public <T extends View> T getView(int viewId) {
// 先從快取中找
View view = mViews.get(viewId);
if (view == null) {
// 直接從ItemView中找
view = itemView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
/**
* 設定TextView文字
*/
public CommonViewHodler setText(int viewId, CharSequence text) {
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
/**
* 設定ImageView的資源
*/
public CommonViewHodler setImageResource(int viewId, int resourceId) {
ImageView imageView = getView(viewId);
imageView.setImageResource(resourceId);
return this;
}
}
RecyclerView.Adapter
public abstract class CommonRecyclerAdapter<T> extends RecyclerView.Adapter<CommonViewHodler>
implements CommonSlidingButtonView.IonSlidingButtonListener, CommonItemTouchHelperAdapter {
private static CommonSlidingButtonView.IonSlidingButtonListener ionSlidingButtonListener;
protected static Context mContext;
protected LayoutInflater mInflater;
//資料
protected List<T> mData;
// 佈局
private int mLayoutId;
public static CommonSlidingButtonView.IonSlidingButtonListener getIonSlidingButtonListener() {
return ionSlidingButtonListener;
}
public CommonRecyclerAdapter(Context context, List<T> data, int layoutId) {
this.mContext = context;
this.mInflater = LayoutInflater.from(mContext);
this.mData = data;
this.mLayoutId = layoutId;
OnItemListener = (CommonRecyclerAdapter.OnItemListener) context;
ionSlidingButtonListener = this;
}
@Override
public CommonViewHodler onCreateViewHolder(ViewGroup parent, int viewType) {
// 先inflate資料
View itemView = mInflater.inflate(mLayoutId, parent, false);
// 返回ViewHolder
CommonViewHodler holder = new CommonViewHodler(itemView);
return holder;
}
@Override
public void onBindViewHolder(CommonViewHodler holder, int position) {
//設定內容佈局的寬為螢幕寬度
holder.viewGroup.getLayoutParams().width = Utils.getScreenWidth(mContext);
holder.viewGroup.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//判斷是否有刪除選單開啟
if (menuIsOpen()) {
closeMenu();//關閉選單
} else {
//這裡這個postion需要通過 holder.getAdapterPosition()獲取的,
//請勿用上面position,因為拖動後上面的position不會改變,刪除時會崩潰
OnItemListener.onItemClickListener(v, holder.getAdapterPosition());
}
}
});
holder.viewGroup.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//判斷是否有刪除選單開啟
if (menuIsOpen()) {
closeMenu();//關閉選單
} else {
OnItemListener.onItemLongClickListener(v, holder.getAdapterPosition());
}
}
});
holder.commonDelete.setText("刪除");
holder.commonDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
OnItemListener.onItemSlidingClickListener(v, holder.getAdapterPosition());
if (menuIsOpen()) {
closeMenu();//關閉選單
}
}
});
holder.commonSave.setText("置頂");
holder.commonSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (menuIsOpen()) {
closeMenu();//關閉選單
}
}
});
// 繫結怎麼辦?回傳出去
convert(holder, mData.get(position));
}
/**
* ItemTouchHelper實現 拖動交換位置
* 實現介面---CommonItemTouchHelperAdapter
*
* @param formPosition
* @param toPosition
*/
@Override
public void onItemMove(int formPosition, int toPosition) {
if (formPosition < toPosition) {
for (int i = formPosition; i < toPosition; i++) {
Collections.swap(mData, i, i + 1);
}
} else {
for (int i = formPosition; i > toPosition; i--) {
Collections.swap(mData, i, i - 1);
}
}
Collections.swap(mData, formPosition, toPosition);
notifyItemMoved(formPosition, toPosition);
}
/**
* ItemTouchHelper實現側滑刪除
* 實現介面---CommonItemTouchHelperAdapter
*
* @param position
*/
@Override
public void onItemDelete(int position) {
removeData(position);
}
/**
* 利用抽象方法回傳出去,每個不一樣的Adapter去設定
* <p>
* // * @param item 當前的資料
*/
public abstract void convert(CommonViewHodler holder, T item);
@Override
public int getItemCount() {
return mData.size();
}
/*******************點選、長按、側滑s欄裡的刪除*************************/
private OnItemListener OnItemListener;
public interface OnItemListener {
void onItemClickListener(View view, int position);
void onItemLongClickListener(View view, int position);
void onItemSlidingClickListener(View view, int position);
}
/********************* 側滑 *************************/
private CommonSlidingButtonView mMenu = null;
/**
* 刪除選單開啟資訊接收
*
* @param view
*/
@Override
public void onMenuIsOpen(View view) {
mMenu = (CommonSlidingButtonView) view;
}
/**
* 滑動或者點選了Item監聽
*
* @param commonSlidingButtonView
*/
@Override
public void onDownOrMove(CommonSlidingButtonView commonSlidingButtonView) {
if (menuIsOpen()) {
if (mMenu != commonSlidingButtonView) {
closeMenu();
}
}
}
/**
* 關閉選單
*/
public void closeMenu() {
mMenu.closeMenu();
mMenu = null;
}
/**
* 判斷是否有選單開啟
*/
public Boolean menuIsOpen() {
if (mMenu != null) {
return true;
}
return false;
}
public void removeData(int position) {
mData.remove(position);
notifyItemRemoved(position);
}
}
有更好的方法請熱情告知,有不對的地方請多多指教
相關文章
- RecyclerView使用View
- RecyclerView系列View
- RecyclerView Adapter 系列(1):RecyclerView Array AdapterViewAPT
- RecyclerView Adapters 系列(2):RecyclerView Cursor AdapterViewAPT
- RecyclerView.ItemDecorationView
- RecyclerView總結View
- RecyclerView之ItemDecorationView
- RecyclerView的使用View
- 玩轉RecyclerViewView
- 教你玩轉 Android RecyclerView:深入解析 RecyclerView ItemDecoration類AndroidView
- RecyclerView進階(一)RecyclerView實現雙列表聯動View
- 為RecyclerView打造通用Adapter 讓RecyclerView更加好用ViewAPT
- RecyclerView原始碼解析View原始碼
- RecyclerView增刪itemView
- RecyclerView重新整理View
- RecyclerView 的基本使用View
- 構建Recyclerview DSLView
- 六邊形RecyclerViewView
- 與RecyclerView的日常View
- RecyclerView 介紹 01View
- RecyclerView使用體驗View
- RecyclerView定製:通用ItemDecoration及全展開RecyclerView的實現View
- Android中的RecyclerViewAndroidView
- RecyclerView的簡單使用View
- RecyclerView問題彙總View
- 對RecyclerView Item做動畫View動畫
- RecyclerView 原始碼分析(一)View原始碼
- RecyclerView與ListView比較View
- Android RecyclerView詳解AndroidView
- RecyclerView新增動態水印View
- Android RecyclerView的使用AndroidView
- RecyclerView的LinearLayoutManager分析View
- RecyclerView平滑到指定位置View
- RecyclerView之SnapHelper原始碼分析View原始碼
- RecyclerView-->通用的AdapterViewAPT
- RecyclerView的複用機制View
- RecyclerView重新整理機制View
- RecyclerView動畫原始碼淺析View動畫原始碼