您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“Android自定義控件之小說書架怎么實現”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Android自定義控件之小說書架怎么實現”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
通過運行圖可以看出,該程序主要功能包括
1.按照網格布局展示小說信息
2.手指長按單個小說時,可拖拽該小說,并且手指松開時,將拖拽小說插入到該位置,其他小說依次向移動
3.選中要刪除的小說,點擊刪除按鈕刪除
其中有些難度的是小說的拖拽,主要是拖拽需要注意的地方比較多。
我這里是使用RecyclerView實現,只不過layoutManager是使用GridLayoutManager,代碼如下:
<androidx.recyclerview.widget.RecyclerView android:id="@+id/rl_bookshelf" android:layout_width="match_parent" android:layout_height="wrap_content" app:spanCount="3" app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" tools:listitem="@layout/item_bookshelf"/>
RecyclerView想要實現拖拽功能需要寫一個繼承ItemTouchHelper.Callback的類,這里把這個類命名為BookShelfItemTouchHelper。如果想要一個RecyclerView可以實現拖拽,可以給這個RecyclerView添加ItemTouchHelper,binding.rlBookshelf是想要添加拖拽效果的RecyclerView,設置代碼如下:
val itemTouchHelper = ItemTouchHelper(BookShelfItemTouchHelper(books,adapter)) itemTouchHelper.attachToRecyclerView(binding.rlBookshelf)
關于BookShelfItemTouchHelper這個類的實現,需要重寫下面幾個方法:
getMovementFlags:可以拖動和滑動的方向,最后通過 makeMovementFlags 方法將拖拽和滑動方向匯總起來。代碼里面是設置可以上下左右拖動,不設置滑動。
onMove:這個方法是,當手指移動到某個item上時,會觸發這個函數(個人理解這個函數觸發時機是,當手指拖拽item在目標view上停留了一小會),這個方法里面viewHolder參數是手指拖拽item的ViewHolde,target是目標item的ViewHolder。在這里我們是把拖拽item放到目標item位置上,并返回true。方法末尾,還需要調用Adapter的notifyItemMoved()方法,告訴RecyclerView這兩個item發生了變換,并重繪。關于托拽item與目標item位置變換,如果我們把拖動的item位置看為fromPosition,把目標item的位置看為toPosition。我們需要把拖拽item放到目標item位置上,并比較fromPosition和toPosition大小來決定,fromPosition與toPositon之間item是前移還是后移。代碼如下:
@Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { int fromPosition = viewHolder.getAdapterPosition(); int toPosition = target.getAdapterPosition(); bookshelfAdapter.notifyItemRangeChanged(Math.min(fromPosition,toPosition),Math.abs(fromPosition - toPosition) + 1); if (fromPosition < toPosition){ for (int i = fromPosition;i<toPosition;i++){ Collections.swap(books,i,i+1); } }else { for (int i = fromPosition; i > toPosition;i--){ Collections.swap(books,i,i-1); } } bookshelfAdapter.notifyItemMoved(fromPosition,toPosition); return true; }
onMoved:onMove返回true會觸發這個方法,在這個方法里需要調用Adapter的notifyItemRangeChanged()方法來批量更新,item位置變換過程中受影響的數據。
onSelectedChanged():當手指長按選中item時會觸發這個方法,這個方法中,我修改了item的背景色并稍微擴大item的寬高。
clearView:當手指松開時會觸發該方法,在這個方法里面,是恢復item的寬高及背景色。
interpolateOutOfBoundsScroll:這個方法是可以設置滾動速度,如果不修改的話,會發現拖拽item到頂部或底部時候,向上或下的速度很慢,在這里設置快一些。
下面是BookShelfItemTouchHelper的完整代碼:
public class BookShelfItemTouchHelper extends ItemTouchHelper.Callback { private final String TAG = "BookShelfItemTouchHelper"; private List<Book> books; private BookshelfAdapter bookshelfAdapter; public BookShelfItemTouchHelper(List<Book> books, BookshelfAdapter bookshelfAdapter) { this.books = books; this.bookshelfAdapter = bookshelfAdapter; } @Override public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; int swipeFlags = 0;//不響應滑動方向 int flags = makeMovementFlags(dragFlags,swipeFlags); return flags; } @Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { int fromPosition = viewHolder.getAdapterPosition(); int toPosition = target.getAdapterPosition(); bookshelfAdapter.notifyItemRangeChanged(Math.min(fromPosition,toPosition),Math.abs(fromPosition - toPosition) + 1); if (fromPosition < toPosition){ for (int i = fromPosition;i<toPosition;i++){ Collections.swap(books,i,i+1); } }else { for (int i = fromPosition; i > toPosition;i--){ Collections.swap(books,i,i-1); } } bookshelfAdapter.notifyItemMoved(fromPosition,toPosition); return true; } @Override public void onMoved(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, int fromPos, @NonNull RecyclerView.ViewHolder target, int toPos, int x, int y) { super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y); bookshelfAdapter.notifyItemRangeChanged(Math.min(fromPos, toPos), Math.abs(fromPos - toPos) + 1); } /** 選中狀態改變通知 */ @Override public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) { super.onSelectedChanged(viewHolder, actionState); if (actionState == ItemTouchHelper.ACTION_STATE_DRAG){ int bgColor = viewHolder.itemView.getContext().getResources().getColor(R.color.text_blue); viewHolder.itemView.setScaleX(1.2f); viewHolder.itemView.setScaleY(1.2f); viewHolder.itemView.setBackgroundColor(bgColor); } } /** 手指釋放item或者交互動畫結束時調用 viewHolder是釋放的item的ViewHolder對象*/ @Override public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); viewHolder.itemView.setScaleX(1.0f); viewHolder.itemView.setScaleY(1.0f); int color = viewHolder.itemView.getContext().getResources().getColor(R.color.white); viewHolder.itemView.setBackgroundColor(color); } /** 修改滾動速度 下面是固定了劃動速度*/ @Override public int interpolateOutOfBoundsScroll(@NonNull RecyclerView recyclerView, int viewSize, int viewSizeOutOfBounds, int totalSize, long msSinceStartScroll) { final int direction = (int) Math.signum(viewSizeOutOfBounds); return 30 * direction; } }
小說刪除相對簡單,是在Adapter中定義了一個方法,當點擊刪除按鈕時會調用該方法,該方法內部時會判斷當刪除數據長度大于0時,從Adapter接收的數據集里面移除刪除數據,代碼如下:
/** 刪除選中的數據 */ fun deleted(){ if (deletDatas.size>0){ for (data in deletDatas){ datas.remove(data) } deletDatas.clear() notifyDataSetChanged() } }
在實現小說書架功能時,遇到一些需要注意的地方,在此記錄下。
一開始在實現功能時候,將下面的代碼,放到了onMove方法中執行,這樣會有一個問題手指拖拽時,手指沒有松開,拖拽就結束,猜測是因為拖動item時,系統會重繪列表,但下面代碼會重新排序更新位置,就與拖拽產生沖突,導致拖拽結束。
bookshelfAdapter.notifyItemRangeChanged(Math.min(fromPos, toPos), Math.abs(fromPos - toPos) + 1);
另一個地方是,我想通過ItemDecoration給ReyclerView添加裝飾時,在onDrawOver方法給列表每行開頭小說添加裝飾后,在拖動時候發現,不是開頭的小說也會出現這個效果,onDarwOver方法如下:
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.onDrawOver(c, parent, state); int childCount = parent.getChildCount(); for (int i=0;i<childCount;i++){ View child = parent.getChildAt(i); if (child!=null && i%row==0){ int startX = (child.getLeft()+child.getRight())/2 - decorationBmp.getWidth()/2; int startY = child.getTop() + child.getPaddingTop() - decorationBmp.getHeight()/2; c.drawBitmap(decorationBmp,startX,startY,paint); } } }
讀到這里,這篇“Android自定義控件之小說書架怎么實現”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。