關(guān)于安卓卡片式交互實(shí)現(xiàn)(recyclerview)

背景

對(duì)于安卓卡片式交互,已有很多案例挤渐,前有“探探”卡片滑動(dòng)交互苹享,后有各種各樣的三方軟件,都在互相復(fù)制粘貼浴麻。今項(xiàng)目中也有類(lèi)似需求得问,特此記錄。

H砻狻9场!代碼鏈接在文末8嘞簟@焐А蝌衔!

演示gif

演示

思路

實(shí)現(xiàn)這樣的效果,其實(shí)從宏觀上蝌蹂,就是實(shí)現(xiàn)了一個(gè)layoutmanger以及ItemTouchHelper噩斟。
(一)LayoutManager主要是實(shí)現(xiàn)recyclerview的布局
(二)ItemTouchHelper主要是實(shí)現(xiàn)用戶滑動(dòng)的時(shí)候,卡片的交互過(guò)程

實(shí)現(xiàn)

(一)重寫(xiě)LayoutManger
首先孤个,要確定的是剃允,繪制多少個(gè)層級(jí)的布局。目前需求是顯示三個(gè)疊加的item齐鲤,因此斥废,對(duì)于LayoutManager,我們只需要每次刷新的時(shí)候佳遂,繪制三次即可营袜。(ps:開(kāi)發(fā)過(guò)程應(yīng)當(dāng)把“層級(jí)”變量抽象全局化,適配“層級(jí)”變化的情況)

核心代碼如下:


    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        super.onLayoutChildren(recycler, state);

        detachAndScrapAttachedViews(recycler);
        int maxCount = SlideConfig.SHOW_MAX_COUNT;
        //獲取所有item(包括不可見(jiàn)的)個(gè)數(shù)
        int count = getItemCount();
        //由于我們是倒序擺放丑罪,所以初始索引從后面開(kāi)始
        int initIndex = count - maxCount;
        if (initIndex < 0) {
            initIndex = 0;
        }

        //當(dāng)前順序
        int currentIndex = 0;

        for (int i = initIndex; i < count; i++) {
            //從緩存中獲取view
            View view = recycler.getViewForPosition(i);
            //添加到recyclerView
            addView(view);
            //測(cè)量一下view
            measureChild(view, 0, 0);

            //居中擺放荚板,getDecoratedMeasuredWidth方法是獲取帶分割線的寬度,比直接使用view.getWidth()精確
            int realWidth = getDecoratedMeasuredWidth(view);
            int realHeight = getDecoratedMeasuredHeight(view);
            int widthPadding = (int) ((getWidth() - realWidth) / 2f);
            int heightPadding = (int) ((getHeight() - realHeight) / 2f);

            //擺放child
            layoutDecorated(view, widthPadding, heightPadding,
                    widthPadding + realWidth, heightPadding + realHeight);
            //根據(jù)索引吩屹,來(lái)位移和縮放child
            int measureHeight = view.getMeasuredHeight();
            int trainY = SlideDpUtils.dp2px(SlideConfig.TRANSLATION_Y);


            RecyclerView.LayoutParams viewParams = (RecyclerView.LayoutParams) view.getLayoutParams();
            int paramsHeight = viewParams.height;
            if (paramsHeight == -1) {
                viewParams.height = (measureHeight - trainY * (maxCount + 1));
//                viewParams.height = (500);
                view.setLayoutParams(viewParams);
            }

            float translationY = (maxCount - currentIndex - 1) * trainY;
            if (currentIndex != maxCount - 1) {
                view.setTranslationY(translationY);
            }
            view.setScaleX(1 - (maxCount - currentIndex - 1) * SlideConfig.SCALE);
            currentIndex++;

//            view.setScaleY(1 - level * SlideConfig.SCALE);
        }
    }

可見(jiàn)跪另,,其實(shí)也沒(méi)啥煤搜,就是一些布局中免绿,寬高的適配,以及一些位置計(jì)算罷了擦盾。
其實(shí)這里的繪制邏輯嘲驾,是和recyclerview中的LinearLayoutManager中的處理手段,有著異曲同工之妙迹卢,所以辽故,這里就不在敘述了。

值得注意的是腐碱,在繪制的過(guò)程中誊垢,LayoutManager有個(gè)居中的邏輯,如果布局高度太小症见,則會(huì)出現(xiàn)上下距離過(guò)寬的問(wèn)題喂走。如果布局高度填充滿了,則會(huì)出現(xiàn)顯示不全堆疊效果的問(wèn)題谋作。

這里的解決思路就是芋肠,當(dāng)布局高度未填充滿的情況下,則進(jìn)行控件的高度進(jìn)行重新設(shè)置遵蚜,計(jì)算好誤差距離业栅,然后進(jìn)行Y軸方向的偏移秒咐。

(二)重寫(xiě)ItemTouchHelper
對(duì)于ItemTouchHelper,需要在swipe方法進(jìn)行數(shù)據(jù)的移除碘裕,界面刷新以及監(jiān)聽(tīng)的回調(diào)携取,核心代碼如下:


    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
        //通知adapter
        if (mAdapter != null) {
            int currentPosition = viewHolder.getLayoutPosition();
            //由于是限定了永遠(yuǎn)只有三個(gè),所以帮孔,該current position的值永遠(yuǎn)少于等于
            mAdapter.getDataList().remove(currentPosition);
//        mAdapter.getDataList().add(0, s);
            mAdapter.notifyDataSetChanged();
            mAdapter.notifyOutSideRefresh();
            mAdapter.notifyPageChange();
        }
    }

而對(duì)于觸摸的場(chǎng)景下雷滋,卡片的寬高,縮放等設(shè)置文兢,則在onChildDraw方法實(shí)現(xiàn)即可晤斩,核心代碼如下:


    @Override
    public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView
            recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY,
                            int actionState, boolean isCurrentlyActive) {
        try {
            //計(jì)算移動(dòng)距離
            float distance = (float) Math.hypot(dX, dY);
            float maxDistance = recyclerView.getWidth() / 2f;
            //比例
            float fraction = distance / maxDistance;
            if (fraction > 1) {
                fraction = 1;
            }
            int maxCount = SlideConfig.SHOW_MAX_COUNT;
            int trainY = SlideDpUtils.dp2px(SlideConfig.TRANSLATION_Y);
            //為每個(gè)child執(zhí)行動(dòng)畫(huà)
            int count = recyclerView.getChildCount();
            int adapterCount = mAdapter.getDataList().size();
            for (int i = 0; i < count; i++) {
                if (i != count - 1 && adapterCount > maxCount) {
                    //不是第一層--且數(shù)量大于3
                    View view = recyclerView.getChildAt(i);
                    view.setScaleX(1 - (maxCount - i - 1) * SlideConfig.SCALE + fraction * SlideConfig.SCALE);
                    view.setTranslationY((maxCount - i - 1) * trainY - fraction * trainY);
                }


//            int level = SlideConfig.SHOW_MAX_COUNT - i - 1;
//            if (level != SlideConfig.SHOW_MAX_COUNT)
//            //獲取的view從下層到上層
//            View view = recyclerView.getChildAt(i);
//            int level = SlideConfig.SHOW_MAX_COUNT - i - 1;
//            //level范圍(SlideConfig.SHOW_MAX_COUNT-1)-0,每個(gè)child最大只移動(dòng)一個(gè)SlideConfig.TRANSLATION_Y和放大SlideConfig.SCALE
//
//            if (level == SlideConfig.SHOW_MAX_COUNT - 1) { // 最下層的不動(dòng)和最后第二層重疊
//                view.setTranslationY(SlideConfig.TRANSLATION_Y * (level - 1));
//                view.setScaleX(1 - SlideConfig.SCALE * (level - 1));
//                view.setScaleY(1 - SlideConfig.SCALE * (level - 1));
//            } else if (level > 0) {
//                view.setTranslationY(level * SlideConfig.TRANSLATION_Y - fraction * SlideConfig.TRANSLATION_Y);
//                view.setScaleX(1 - level * SlideConfig.SCALE + fraction * SlideConfig.SCALE);
//                view.setScaleY(1 - level * SlideConfig.SCALE + fraction * SlideConfig.SCALE);
//            }
            }
        } catch (Exception e) {

        }
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }

這里姆坚,就實(shí)現(xiàn)了recyclerview卡片堆疊效果了澳泵。
代碼地址--庫(kù)libslidrecyclerview

that's all---------------------------------------------------------------------

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市兼呵,隨后出現(xiàn)的幾起案子兔辅,更是在濱河造成了極大的恐慌,老刑警劉巖击喂,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件维苔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡懂昂,警方通過(guò)查閱死者的電腦和手機(jī)介时,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)凌彬,“玉大人沸柔,你說(shuō)我怎么就攤上這事〔玻” “怎么了褐澎?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)原探。 經(jīng)常有香客問(wèn)我乱凿,道長(zhǎng)顽素,這世上最難降的妖魔是什么咽弦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮胁出,結(jié)果婚禮上型型,老公的妹妹穿的比我還像新娘。我一直安慰自己全蝶,他們只是感情好闹蒜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布寺枉。 她就那樣靜靜地躺著,像睡著了一般绷落。 火紅的嫁衣襯著肌膚如雪姥闪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天砌烁,我揣著相機(jī)與錄音筐喳,去河邊找鬼亡电。 笑死佩抹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的笨奠。 我是一名探鬼主播管呵,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼梳毙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了捐下?” 一聲冷哼從身側(cè)響起账锹,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蔑担,沒(méi)想到半個(gè)月后牌废,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡啤握,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年鸟缕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片排抬。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡懂从,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蹲蒲,到底是詐尸還是另有隱情番甩,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布届搁,位于F島的核電站缘薛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏卡睦。R本人自食惡果不足惜宴胧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望表锻。 院中可真熱鬧恕齐,春花似錦、人聲如沸瞬逊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至士骤,卻和暖如春范删,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拷肌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工瓶逃, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人廓块。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓厢绝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親带猴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子昔汉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容