欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Android RecyclerView實(shí)現(xiàn)宮格拖拽效果

 更新時(shí)間:2024年03月22日 09:10:31   作者:時(shí)光少年  
在Android發(fā)展的進(jìn)程中,網(wǎng)格布局一直比較有熱度,其中一個(gè)原因是對(duì)用戶來(lái)說(shuō)便捷操作,對(duì)app廠商而言也會(huì)帶來(lái)很多的曝光量,本篇我們會(huì)使用RecyclerView來(lái)實(shí)現(xiàn)網(wǎng)格拖拽,本篇將結(jié)合圖片分片案例,實(shí)現(xiàn)拖拽效果,需要的朋友可以參考下

前言

在Android發(fā)展的進(jìn)程中,網(wǎng)格布局一直比較有熱度,其中一個(gè)原因是對(duì)用戶來(lái)說(shuō)便捷操作,對(duì)app廠商而言也會(huì)帶來(lái)很多的曝光量,對(duì)于很多頭部app,展示網(wǎng)格菜單幾乎是必選項(xiàng)。實(shí)現(xiàn)網(wǎng)格的方式有很多種,比如GridView、GridLayout,TableLayout等,實(shí)際上,由于RecyclerView的靈活性和可擴(kuò)展性很高,這些View基本沒(méi)必要去學(xué)了,為什么這樣說(shuō)呢?主要原因是基于RecyclerView可以實(shí)現(xiàn)很多布局效果,傳統(tǒng)的很多Layout都可以通過(guò)RecyclerView去實(shí)現(xiàn),比如ViewPager、SlingTabLayout、DrawerLayout、ListView等,甚至連九宮格解鎖效果也可以實(shí)現(xiàn)。

當(dāng)然,在很早之前,實(shí)現(xiàn)網(wǎng)格的拖拽效果主要是通過(guò)GridView去實(shí)現(xiàn)的,如果列數(shù)為1的話,那么GridView基本上就實(shí)現(xiàn)了ListView一樣的上下拖拽。

話說(shuō)回來(lái),我們現(xiàn)在基本不用去學(xué)習(xí)這類(lèi)實(shí)現(xiàn)了,因?yàn)镽ecyclerView足夠強(qiáng)大,通過(guò)簡(jiǎn)單的數(shù)據(jù)組裝,是完全可以替代GridView和ListView的。

效果

本篇我們會(huì)使用RecyclerView來(lái)實(shí)現(xiàn)網(wǎng)格拖拽,本篇將結(jié)合圖片分片案例,實(shí)現(xiàn)拖拽效果。

如果要實(shí)現(xiàn)網(wǎng)格菜單的拖拽,也是可以使用這種方式的,只要你的想象豐富,理論上,借助RecyclerView其實(shí)可以做出很多效果。

拖拽效果原理

拖動(dòng)其實(shí)需要處理3個(gè)核心的問(wèn)題,事件、圖像平移、數(shù)據(jù)交換。

事件處理

實(shí)際上無(wú)論傳統(tǒng)的拖拽效果還是最新的拖拽效果,都離不開(kāi)事件處理,不過(guò),好處就是,google為RecyclerView提供了ItemTouchHelper來(lái)處理這個(gè)問(wèn)題,相比傳統(tǒng)的GridView實(shí)現(xiàn)方式,省去了很多事情,如動(dòng)畫(huà)、目標(biāo)查找等。

不過(guò),我們回顧下原理,其實(shí)他們很多方面都是相似的,不同之處就是ItemTouchHelper 設(shè)計(jì)的非常好用,而且接口暴露的非常徹底,甚至能控制那些可以拖動(dòng)、那些不能拖動(dòng)、以及什么方向可以拖動(dòng),如果我們上、下、左、右四個(gè)方向都選中的話,斜對(duì)角拖動(dòng)完全沒(méi)問(wèn)題,

事件處理這里,GridView使用的方式相對(duì)傳統(tǒng),而ItemTouchHelper借助RecyclerView的一個(gè)接口(看樣子是開(kāi)的后門(mén)),通過(guò)View自身去攔截事件.

public interface OnItemTouchListener {
    //是否讓RecyclerView攔截事件
    boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
    //攔截之后處理RecyclerView的事件
    void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
    //監(jiān)聽(tīng)禁止攔截事件的請(qǐng)求結(jié)果
    void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
}

這種其實(shí)相對(duì)GridView來(lái)說(shuō)簡(jiǎn)單的多

圖像平移

無(wú)論是RecyclerView和傳統(tǒng)GridView拖動(dòng),都需要圖像平移。我們知道,RecyclerView和GridView本身是通過(guò)子View的邊界(left\top\right\bottom)來(lái)移動(dòng)的,那么,在平移圖像的時(shí)候必然不能選擇這種方式,只能選擇Matrix 變化,也就是transitionX和transitionY的等。不同點(diǎn)是GridView的子View本身并不移動(dòng),而是將圖像繪制到一個(gè)GridView之外的View上,當(dāng)然,實(shí)現(xiàn)上是比較復(fù)雜的。

但是,ItemTouchHelper設(shè)計(jì)比較巧妙的一點(diǎn)是,通過(guò)RecyclerView#ItemDecoration來(lái)實(shí)現(xiàn),在捕獲可以滑動(dòng)的View之后,在繪制時(shí)對(duì)View進(jìn)行偏移。

class ItemTouchUIUtilImpl implements ItemTouchUIUtil {
    static final ItemTouchUIUtil INSTANCE =  new ItemTouchUIUtilImpl();

    @Override
    public void onDraw(Canvas c, RecyclerView recyclerView, View view, float dX, float dY,
            int actionState, boolean isCurrentlyActive) {
        if (Build.VERSION.SDK_INT >= 21) {
            if (isCurrentlyActive) {
                Object originalElevation = view.getTag(R.id.item_touch_helper_previous_elevation);
                if (originalElevation == null) {
                    originalElevation = ViewCompat.getElevation(view);
                    float newElevation = 1f + findMaxElevation(recyclerView, view);
                    ViewCompat.setElevation(view, newElevation);
                    view.setTag(R.id.item_touch_helper_previous_elevation, originalElevation);
                }
            }
        }

        view.setTranslationX(dX);
        view.setTranslationY(dY);
    }
     //省略一些有關(guān)或者無(wú)關(guān)的代碼
}

不過(guò),我們看到,Android 5.0的版本借助了setElevation 使得被拖拽View不被其他順序的View遮住,那Android 5.0之前是怎么實(shí)現(xiàn)的呢?

其實(shí),做過(guò)TV app的都比較清楚,子View繪制順序可以通過(guò)下面方式調(diào)整,借助下面的方法,在TV上某個(gè)View獲取焦點(diǎn)之后,就不會(huì)被后面的View蓋住。

View#getChildDrawingOrder

ItemTouchHelper 同樣借助了此方法,為什么不統(tǒng)一一種呢,主要原因是getChildDrawingOrder是protected,總的來(lái)說(shuō),沒(méi)有通過(guò)setElevation方便。

private void addChildDrawingOrderCallback() {
    if (Build.VERSION.SDK_INT >= 21) {
        return; // we use elevation on Lollipop
    }
    if (mChildDrawingOrderCallback == null) {
        mChildDrawingOrderCallback = new RecyclerView.ChildDrawingOrderCallback() {
            @Override
            public int onGetChildDrawingOrder(int childCount, int i) {
                if (mOverdrawChild == null) {
                    return i;
                }
                int childPosition = mOverdrawChildPosition;
                if (childPosition == -1) {
                    childPosition = mRecyclerView.indexOfChild(mOverdrawChild);
                    mOverdrawChildPosition = childPosition;
                }
                if (i == childCount - 1) {
                    return childPosition;
                }
                return i < childPosition ? i : i + 1;
            }
        };
    }
    mRecyclerView.setChildDrawingOrderCallback(mChildDrawingOrderCallback);
}

數(shù)據(jù)更新

數(shù)據(jù)更新這里其實(shí)ReyclerView的優(yōu)勢(shì)更加明顯,我們知道RecyclerView可以做到無(wú)requestLayout的局部刷新,性能更好。

@Override
public boolean onItemMove(int fromPosition, int toPosition) {
    Collections.swap(mDataList, fromPosition, toPosition);
    notifyItemMoved(fromPosition, toPosition);
    return true;
}

不過(guò),數(shù)據(jù)交換后還有一點(diǎn)需要處理,對(duì)Matrix相關(guān)屬性清理,防止無(wú)法落到指定區(qū)域。

@Override
public void clearView(View view) {
    if (Build.VERSION.SDK_INT >= 21) {
        final Object tag = view.getTag(R.id.item_touch_helper_previous_elevation);
        if (tag instanceof Float) {
            ViewCompat.setElevation(view, (Float) tag);
        }
        view.setTag(R.id.item_touch_helper_previous_elevation, null);
    }

    view.setTranslationX(0f);
    view.setTranslationY(0f);
}

本篇實(shí)現(xiàn)

以上基本都是對(duì)ItemTouchHelper的原理梳理了,當(dāng)然,如果你沒(méi)時(shí)間看上面的話,就看實(shí)現(xiàn)部分吧。

圖片分片

下面我們把多張圖片分割成 [行數(shù) x 列數(shù)]數(shù)量的圖片。

Bitmap srcInputBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.image_4);
Bitmap source = Bitmap.createScaledBitmap(srcInputBitmap, width, height, true);
srcInputBitmap.recycle();

int colCount = spanCount;
int rowCount = 6;

int spanImageWidthSize = source.getWidth() / colCount;
int spanImageHeightSize = (source.getHeight() - rowCount * padding/2) / rowCount;

Bitmap[] bitmaps = new Bitmap[rowCount * colCount];
for (int i = 0; i < rowCount; i++) {
    for (int j = 0; j < colCount; j++) {
        int y = i * spanImageHeightSize;
        int x = j * spanImageWidthSize;
        Bitmap bitmap = Bitmap.createBitmap(source, x, y, spanImageWidthSize, spanImageHeightSize);
        bitmaps[i * colCount + j] = bitmap;
    }
}

在這種過(guò)程我們一定要處理一個(gè)問(wèn)題,如果我們對(duì)網(wǎng)格設(shè)置了邊界線(ItemDecoration)且是縱向布局的話,那么,縱向總高度要減去rowCount * bottomPadding,這里bottomPadding == padding/2,如下面代碼。

為什么要這么做呢?因?yàn)镽ecyclerView計(jì)算高度的時(shí)候,需要考慮這個(gè)高度,如果不去處理,那么ReyclerView可能不是禁止不動(dòng),而是會(huì)滑動(dòng),雖然影響不大,但是如果實(shí)現(xiàn)全屏效果,還能上下滑的話體驗(yàn)比較差。

public class SimpleItemDecoration extends RecyclerView.ItemDecoration {

    public int delta;
    public SimpleItemDecoration(int padding) {
        delta = padding;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view,
                               RecyclerView parent, RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view);
        RecyclerView.Adapter adapter = parent.getAdapter();
        int viewType = adapter.getItemViewType(position);
        if(viewType== Bean.TYPE_GROUP){
            return;
        }
        GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
         //列數(shù)量
        int cols = layoutManager.getSpanCount(); 
        //position轉(zhuǎn)為在第幾列
        int current =  layoutManager.getSpanSizeLookup().getSpanIndex(position,cols); 
        //可有可無(wú)
        int currentCol = current % cols;


        int bottomPadding = delta / 2;

        if (currentCol == 0) {  //第0列左側(cè)貼邊
            outRect.left = 0;
            outRect.right = delta / 4;
            outRect.bottom = bottomPadding;
        } else if (currentCol == cols - 1) {
            outRect.left = delta / 4;
            outRect.right = 0;
            outRect.bottom = bottomPadding;
             //最后一列右側(cè)貼邊
        } else {
            outRect.left = delta / 4;
            outRect.right = delta / 4;
            outRect.bottom = bottomPadding;
        }
    }
}

更新數(shù)據(jù)

這部分是常規(guī)操作,主要目的是設(shè)置LayoutManager、Decoration、Adapter以及ItemTouchHelper,當(dāng)然,ItemTouchHelper比較特殊,因?yàn)槠鋬?nèi)部試下是ItemTouchHelper、OnItemTouchListener、Gesture的組合,因此封裝為attachToRecyclerView 來(lái)調(diào)用。

mLinearLayoutManager = new GridLayoutManager(this, spanCount, LinearLayoutManager.VERTICAL, false);
mLinearLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup(){
    @Override
    public int getSpanSize(int position) {
        if(mAdapter.getItemViewType(position) == Bean.TYPE_GROUP){
            return spanCount;
        }
        return 1;
    }
});
mAdapter = new RecyclerViewAdapter();
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mRecyclerView.addItemDecoration(new SimpleItemDecoration(padding));
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new GridItemTouchCallback(mAdapter));
itemTouchHelper.attachToRecyclerView(mRecyclerView);

這里,我們主要還是關(guān)注ItemTouchHelper,在初始化的時(shí)候,我們給了一個(gè)GridItemTouchCallback,用于監(jiān)聽(tīng)相關(guān)處理邏輯,最終通知Adapter調(diào)用notifyXXX更新View。

public class GridItemTouchCallback extends ItemTouchHelper.Callback {
    private final ItemTouchCallback mItemTouchCallback;
    public GridItemTouchCallback(ItemTouchCallback itemTouchCallback) {
        mItemTouchCallback = itemTouchCallback;
    }
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        // 上下左右拖動(dòng),但允許觸發(fā)刪除
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        return makeMovementFlags(dragFlags, 0);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        // 通知Adapter移動(dòng)View
        return mItemTouchCallback.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
    }
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        // 通知Adapter刪除View
        mItemTouchCallback.onItemRemove(viewHolder.getAdapterPosition());
    }

    @Override
    public void onChildDraw(@NonNull Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }
    @Override
    public void onChildDrawOver(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        Log.d("GridItemTouch","dx="+dX+", dy="+dY);
        super.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }
}

這里,主要是對(duì)Flag的關(guān)注需要處理,第一參數(shù)是拖拽方向,第二個(gè)是刪除方向,我們本篇不刪除,因此,第二個(gè)參數(shù)為0即可。

public static int makeMovementFlags(int dragFlags, int swipeFlags) {
    return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags)
            | makeFlag(ACTION_STATE_SWIPE, swipeFlags)
            | makeFlag(ACTION_STATE_DRAG, dragFlags);
}

總結(jié)

本篇到這里就結(jié)束了,我們利用RecyclerView實(shí)現(xiàn)了宮格圖片的拖拽效果,主要是借助ItemTouchHelper實(shí)現(xiàn),從ItemTouchHelper中我們能看到很多巧妙的的設(shè)計(jì),里面有很多值得我們學(xué)習(xí)的技巧,特別是對(duì)事件的處理、繪制順序調(diào)整的方式,如果做吸頂,未嘗不是一種方案。

以上就是基于Android RecyclerView實(shí)現(xiàn)宮格拖拽效果的詳細(xì)內(nèi)容,更多關(guān)于Android RecyclerView宮格拖拽的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Flutter常用的布局和事件示例詳解

    Flutter常用的布局和事件示例詳解

    這篇文章主要給大家介紹了關(guān)于Flutter常用的布局和事件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • 淺談Android App開(kāi)發(fā)中Fragment的創(chuàng)建與生命周期

    淺談Android App開(kāi)發(fā)中Fragment的創(chuàng)建與生命周期

    這篇文章主要介紹了Android App開(kāi)發(fā)中Fragment的創(chuàng)建與生命周期,文中詳細(xì)地介紹了Fragment的概念以及一些常用的生命周期控制方法,需要的朋友可以參考下
    2016-02-02
  • Popupwindow 的簡(jiǎn)單實(shí)用案例(顯示在控件下方)

    Popupwindow 的簡(jiǎn)單實(shí)用案例(顯示在控件下方)

    下面小編就為大家?guī)?lái)一篇Popupwindow 的簡(jiǎn)單實(shí)用案例(顯示在控件下方)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-04-04
  • 一篇文章揭開(kāi)Kotlin協(xié)程的神秘面紗

    一篇文章揭開(kāi)Kotlin協(xié)程的神秘面紗

    最近看了下Kotlin的協(xié)程,覺(jué)得挺好的,寫(xiě)篇文章總結(jié)總結(jié),所以下面這篇文章主要給大家介紹了關(guān)于Kotlin協(xié)程的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • android dialog根據(jù)彈窗等級(jí)排序顯示的示例代碼

    android dialog根據(jù)彈窗等級(jí)排序顯示的示例代碼

    這篇文章主要介紹了android dialog根據(jù)彈窗等級(jí)排序顯示,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Android開(kāi)發(fā)學(xué)習(xí)之WallPaper設(shè)置壁紙?jiān)敿?xì)介紹與實(shí)例

    Android開(kāi)發(fā)學(xué)習(xí)之WallPaper設(shè)置壁紙?jiān)敿?xì)介紹與實(shí)例

    這篇文章主要介紹了Android開(kāi)發(fā)學(xué)習(xí)之WallPaper設(shè)置壁紙?jiān)敿?xì)介紹與實(shí)例,有需要的朋友可以參考一下
    2013-12-12
  • Android 一鍵清理、內(nèi)存清理功能實(shí)現(xiàn)

    Android 一鍵清理、內(nèi)存清理功能實(shí)現(xiàn)

    這篇文章主要介紹了Android 一鍵清理、內(nèi)存清理功能實(shí)現(xiàn),非常具有實(shí)用價(jià)值,需要的朋友可以參考下。
    2017-01-01
  • Flutter 實(shí)現(xiàn)下拉刷新上拉加載的示例代碼

    Flutter 實(shí)現(xiàn)下拉刷新上拉加載的示例代碼

    這篇文章主要介紹了Flutter 實(shí)現(xiàn)下拉刷新上拉加載的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • Android自定義控件ListView下拉刷新的代碼

    Android自定義控件ListView下拉刷新的代碼

    今天小編就為大家分享一篇關(guān)于Android自定義控件ListView下拉刷新的代碼,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • Android判斷軟鍵盤(pán)彈出并隱藏的簡(jiǎn)單完美解決方法(推薦)

    Android判斷軟鍵盤(pán)彈出并隱藏的簡(jiǎn)單完美解決方法(推薦)

    下面小編就為大家?guī)?lái)一篇Android判斷軟鍵盤(pán)彈出并隱藏的簡(jiǎn)單完美解決方法(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-10-10

最新評(píng)論