向左拖拽跳轉(zhuǎn)至“更多頁面”的通用控件

Github

https://github.com/uin3566/DragFooterView

效果圖

項目來源

本項目來源于公司的項目需求拢锹,需要做成如下效果:


這是某個App里面的效果卒稳,名字我就不說啦充坑,不打免費廣告匪傍,哈役衡。
我發(fā)現(xiàn)市面上很多app都有這方面的需求手蝎,但基本上都是在右上角添加一個“更多”標(biāo)簽棵介,然后點擊“更多”跳轉(zhuǎn)至另一個頁面邮辽,類似于下面這樣吨述,此圖來自豆瓣App揣云。
douban.png

這種體驗中規(guī)中矩刘莹,但我還是覺得向左拖拽比較点弯。蒲拉。于是就寫下了本項目雌团,為這種向左拖拽的交互方式提供一種通用的解決方案锦援。

介紹

作為一種library灵寺,怎么可以只有以上這一種效果呢略板。因此叮称,可插拔的Footer View是必須的瓤檐。
下面是我定制的另外兩種效果挠蛉,當(dāng)然谴古,你也可以定制任意你想要的效果讥电。


上圖中的字符串轉(zhuǎn)Path功能用的是秋百萬大神的android-Ultra-Pull-To-Refresh下拉刷新庫中的類StoreHousePath瞬测,只不過將字符串從橫向改為了豎向,在此深表感謝恢口,順便膜拜下耕肩。

涉及的知識點

本質(zhì)上說猿诸,這個控件是一個自定義的ViewGroup,需要支持左滑窜觉,需要支持可插拔化的Footer View禀挫,所以View的事件分發(fā)语婴,View的繪制這兩個知識點都必須用到腻格。
View的繪制就不多說了菜职,無非是Canvas酬核,Paint嫡意,Path結(jié)合動畫的用法蔬螟,GcsSloop魔法師的文章講解的又好逼格又高旧巾,推薦下鲁猩,具體的繪制可以看我的代碼搅窿,自以為寫的還是蠻清晰的e男应。
這里看下事件分發(fā)殉了,由于DragContainer支持放置了各種子View薪铜,比如Button隔箍,RecyclerView蜒滩,HorizontalScrollView等。那么竹握,有時候需要子控件響應(yīng)事件啦辐,有時候需要DragContainer響應(yīng)拖拽芹关。本項目通過重寫dispatchTouchEvent實現(xiàn)需求侥衬,代碼分析詳見代碼中的注釋贬媒。

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        //若復(fù)位動畫正在執(zhí)行,則直接return
        if (resetAnimator != null && resetAnimator.isRunning()) {
            return super.dispatchTouchEvent(event);
        }

        //調(diào)用ViewGroup的dispatchTouchEvent方法漂佩,將
        //事件分發(fā)給子View投蝉。
        super.dispatchTouchEvent(event);

        //調(diào)用IDragChecker接口的canDrag方法判斷此時DragContainer 
        //的可拖拽狀態(tài),本項目提供了默認(rèn)實現(xiàn)類DefaultDragChecker 
        //將根據(jù)子View的類型來返回一個boolean庸娱。若返回false熟尉,
        //則該方法直接返回斤儿,返回true才能執(zhí)行后續(xù)的拖拽操作。
        //以RecyclerView為例陕贮,若RecyclerView沒有滑動到最底部
        //則返回false飘蚯,否則返回true局骤,DragContainer將會對后續(xù)事件
        //進(jìn)行處理峦甩。
        if (!dragChecker.canDrag(contentView)) {
            return true;
        }

        //DragContainer處理拖拽事件
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                dragDx = 0;
                downX = event.getX();
                downY = event.getY();
                lastMoveX = downX;
                break;
            case MotionEvent.ACTION_MOVE:
                float dx = Math.abs(event.getX() - downX);
                float dy = Math.abs(event.getY() - downY);
                //dx >= dy時判定為橫向拖動犬辰,不允許parentViewGroup
                //攔截事件幌缝,否則使之?dāng)r截事件。
                if (dx >= dy) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                } else {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }

                //若此時往左拖動轿偎,則開始處理ACTION_MOVE事件
                if (dragDx <= 0 && dragChecker.canDrag(contentView)) {
                    //這里更新拖拽狀態(tài)坏晦,F(xiàn)ooter View的繪制依賴于該狀態(tài)
                    //該函數(shù)判斷此時是往左拖拽還是往右拖拽。
                    updateDragState(event);
                    if (dragDx != 0) {
                        //拖拽時發(fā)送ACTION_CANCEL給子View挖诸,取消View的事件序列多律,否則View將與DragContainer產(chǎn)生事件沖突狼荞。
                        sendCancelEvent(event);
                    }
                    dragDx = event.getX() - downX;
                    //dragDamp是拖拽阻尼,取值范圍是(0,1]丰涉。值越小一死,阻尼越大
                    float realDragDistance = dragDx * dragDamp;
                    //更新子View的位置投慈。
                    setContentView((int) realDragDistance, 0, containerWidth + (int) realDragDistance, containerHeight);
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                //手指抬起時或接收到ACTION_CANCEL時子View恢復(fù)原位伪煤。
                resetContentView();
                break;
        }
        return true;
    }

用法

1职烧、添加依賴

  • step1:Add it in your root build.gradle at the end of repositories:
    allprojects {
        repositories {
            ...
            maven { url "https://jitpack.io" }
        }
    }
  • step2:Add the dependency:
    dependencies {
        compile 'com.github.uin3566:DragFooterView:v1.0.2'
    }

2、在xml中配置如下 (注意:DragContainer只能有一個子View)

    <com.fangxu.library.DragContainer
        android:id="@+id/drag_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white" />
    </com.fangxu.library.DragContainer>

3前普、在java類中添加事件監(jiān)聽器DragListener

    DragContainer dragContainer = (DragContainer) findViewById(R.id.drag_image_view);
    
    //若需使用自己定制的footer拭卿,需要調(diào)用DragContainer的setFooterDrawer方法設(shè)置定制的footer類峻厚,如下
    //其中ArrowPathFooterDrawer繼承于BaseFooterDrawer
    dragContainer.setFooterDrawer(new ArrowPathFooterDrawer.Builder(this, 0xff444444).setPathColor(0xffffffff).build());
    
    dragContainer.setDragListener(new DragListener() {
        @Override
        public void onDragEvent() {
            //do whatever you want,for example skip to the load more Activity.
            Intent intent = new Intent(HomeActivity.this, ShowMoreActivity.class);
            startActivity(intent);
        }
    });

可自定義屬性

attribute value type defalut value description
dc_footer_color color 0xffcdcdcd footer view的背景顏色
dc_reset_animator_duration integer 700 松開拖拽后復(fù)位動畫的時長
dc_drag_damp float 0.5f 拖拽阻尼系數(shù),取值在(0,1]之間劈狐,取值越小肥缔,阻尼越大

其余footer view的屬性參數(shù)交給BaseFooterDrawer的實現(xiàn)類自己設(shè)置续膳。

小結(jié)

木有小結(jié)收班,我就是來打廣告的坟岔,哈哈,如果你覺得本項目對你的學(xué)習(xí)有幫助闺阱,具有一定的實用價值炮车,那就多多star,多多follow吧!
最后再厚顏無恥地再貼一次github地址:https://github.com/uin3566/DragFooterView

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纪隙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子扛或,更是在濱河造成了極大的恐慌绵咱,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件熙兔,死亡現(xiàn)場離奇詭異悲伶,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)住涉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門麸锉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人舆声,你說我怎么就攤上這事花沉。” “怎么了媳握?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵碱屁,是天一觀的道長。 經(jīng)常有香客問我蛾找,道長娩脾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任打毛,我火速辦了婚禮柿赊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘幻枉。我一直安慰自己闹瞧,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布展辞。 她就那樣靜靜地躺著奥邮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪罗珍。 梳的紋絲不亂的頭發(fā)上洽腺,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機(jī)與錄音覆旱,去河邊找鬼蘸朋。 笑死,一個胖子當(dāng)著我的面吹牛扣唱,可吹牛的內(nèi)容都是我干的藕坯。 我是一名探鬼主播团南,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼炼彪!你這毒婦竟也來了吐根?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤辐马,失蹤者是張志新(化名)和其女友劉穎拷橘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喜爷,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡冗疮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了檩帐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片术幔。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖湃密,靈堂內(nèi)的尸體忽然破棺而出特愿,到底是詐尸還是另有隱情,我是刑警寧澤勾缭,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站目养,受9級特大地震影響俩由,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜癌蚁,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一幻梯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧努释,春花似錦碘梢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至逸邦,卻和暖如春恩沛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缕减。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工雷客, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人桥狡。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓搅裙,卻偏偏與公主長得像皱卓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子部逮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

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