溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android自定義控件之小說書架怎么實(shí)現(xiàn)

發(fā)布時(shí)間:2023-04-18 10:03:15 來源:億速云 閱讀:92 作者:iii 欄目:開發(fā)技術(shù)

本文小編為大家詳細(xì)介紹“Android自定義控件之小說書架怎么實(shí)現(xiàn)”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“Android自定義控件之小說書架怎么實(shí)現(xiàn)”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識(shí)吧。

    功能分析

    通過運(yùn)行圖可以看出,該程序主要功能包括
    1.按照網(wǎng)格布局展示小說信息
    2.手指長按單個(gè)小說時(shí),可拖拽該小說,并且手指松開時(shí),將拖拽小說插入到該位置,其他小說依次向移動(dòng)
    3.選中要?jiǎng)h除的小說,點(diǎn)擊刪除按鈕刪除
    其中有些難度的是小說的拖拽,主要是拖拽需要注意的地方比較多。

    代碼實(shí)現(xiàn)

    小說的展示

    我這里是使用RecyclerView實(shí)現(xiàn),只不過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"/>

    小說拖拽BookShelfItemTouchHelper實(shí)現(xiàn)

    RecyclerView想要實(shí)現(xiàn)拖拽功能需要寫一個(gè)繼承ItemTouchHelper.Callback的類,這里把這個(gè)類命名為BookShelfItemTouchHelper。如果想要一個(gè)RecyclerView可以實(shí)現(xiàn)拖拽,可以給這個(gè)RecyclerView添加ItemTouchHelper,binding.rlBookshelf是想要添加拖拽效果的RecyclerView,設(shè)置代碼如下:

    val itemTouchHelper = ItemTouchHelper(BookShelfItemTouchHelper(books,adapter))
    itemTouchHelper.attachToRecyclerView(binding.rlBookshelf)

    關(guān)于BookShelfItemTouchHelper這個(gè)類的實(shí)現(xiàn),需要重寫下面幾個(gè)方法:

    getMovementFlags:可以拖動(dòng)和滑動(dòng)的方向,最后通過 makeMovementFlags 方法將拖拽和滑動(dòng)方向匯總起來。代碼里面是設(shè)置可以上下左右拖動(dòng),不設(shè)置滑動(dòng)。

    onMove:這個(gè)方法是,當(dāng)手指移動(dòng)到某個(gè)item上時(shí),會(huì)觸發(fā)這個(gè)函數(shù)(個(gè)人理解這個(gè)函數(shù)觸發(fā)時(shí)機(jī)是,當(dāng)手指拖拽item在目標(biāo)view上停留了一小會(huì)),這個(gè)方法里面viewHolder參數(shù)是手指拖拽item的ViewHolde,target是目標(biāo)item的ViewHolder。在這里我們是把拖拽item放到目標(biāo)item位置上,并返回true。方法末尾,還需要調(diào)用Adapter的notifyItemMoved()方法,告訴RecyclerView這兩個(gè)item發(fā)生了變換,并重繪。關(guān)于托拽item與目標(biāo)item位置變換,如果我們把拖動(dòng)的item位置看為fromPosition,把目標(biāo)item的位置看為toPosition。我們需要把拖拽item放到目標(biāo)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會(huì)觸發(fā)這個(gè)方法,在這個(gè)方法里需要調(diào)用Adapter的notifyItemRangeChanged()方法來批量更新,item位置變換過程中受影響的數(shù)據(jù)。

    onSelectedChanged():當(dāng)手指長按選中item時(shí)會(huì)觸發(fā)這個(gè)方法,這個(gè)方法中,我修改了item的背景色并稍微擴(kuò)大item的寬高。

    clearView:當(dāng)手指松開時(shí)會(huì)觸發(fā)該方法,在這個(gè)方法里面,是恢復(fù)item的寬高及背景色。

    interpolateOutOfBoundsScroll:這個(gè)方法是可以設(shè)置滾動(dòng)速度,如果不修改的話,會(huì)發(fā)現(xiàn)拖拽item到頂部或底部時(shí)候,向上或下的速度很慢,在這里設(shè)置快一些。

    下面是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;//不響應(yīng)滑動(dòng)方向
            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);
        }
        /** 選中狀態(tài)改變通知 */
        @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或者交互動(dòng)畫結(jié)束時(shí)調(diào)用 viewHolder是釋放的item的ViewHolder對(duì)象*/
        @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);
        }
        /** 修改滾動(dòng)速度 下面是固定了劃動(dòng)速度*/
        @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;
        }
    }

    小說刪除

    小說刪除相對(duì)簡(jiǎn)單,是在Adapter中定義了一個(gè)方法,當(dāng)點(diǎn)擊刪除按鈕時(shí)會(huì)調(diào)用該方法,該方法內(nèi)部時(shí)會(huì)判斷當(dāng)刪除數(shù)據(jù)長度大于0時(shí),從Adapter接收的數(shù)據(jù)集里面移除刪除數(shù)據(jù),代碼如下:

    /** 刪除選中的數(shù)據(jù) */
    fun deleted(){
        if (deletDatas.size&gt;0){
            for (data in deletDatas){
                datas.remove(data)
            }
            deletDatas.clear()
            notifyDataSetChanged()
        }
    }

    總結(jié)

    在實(shí)現(xiàn)小說書架功能時(shí),遇到一些需要注意的地方,在此記錄下。
    一開始在實(shí)現(xiàn)功能時(shí)候,將下面的代碼,放到了onMove方法中執(zhí)行,這樣會(huì)有一個(gè)問題手指拖拽時(shí),手指沒有松開,拖拽就結(jié)束,猜測(cè)是因?yàn)橥蟿?dòng)item時(shí),系統(tǒng)會(huì)重繪列表,但下面代碼會(huì)重新排序更新位置,就與拖拽產(chǎn)生沖突,導(dǎo)致拖拽結(jié)束。

    bookshelfAdapter.notifyItemRangeChanged(Math.min(fromPos, toPos), Math.abs(fromPos - toPos) + 1);

    另一個(gè)地方是,我想通過ItemDecoration給ReyclerView添加裝飾時(shí),在onDrawOver方法給列表每行開頭小說添加裝飾后,在拖動(dòng)時(shí)候發(fā)現(xiàn),不是開頭的小說也會(huì)出現(xiàn)這個(gè)效果,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&lt;childCount;i++){
            View child = parent.getChildAt(i);
            if (child!=null &amp;&amp; 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自定義控件之小說書架怎么實(shí)現(xiàn)”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI