Github
https://github.com/uin3566/DragFooterView
效果圖
![](https://github.com/uin3566/DragFooterView/blob/master/screenshot/demo.gif?raw=true)
項目來源
本項目來源于公司的項目需求拢锹,需要做成如下效果:
![](https://github.com/uin3566/DragFooterView/blob/master/screenshot/inspiration.gif?raw=true)
這是某個App里面的效果卒稳,名字我就不說啦充坑,不打免費廣告匪傍,哈役衡。
我發(fā)現(xiàn)市面上很多app都有這方面的需求手蝎,但基本上都是在右上角添加一個“更多”標(biāo)簽棵介,然后點擊“更多”跳轉(zhuǎn)至另一個頁面邮辽,類似于下面這樣吨述,此圖來自豆瓣App揣云。
這種體驗中規(guī)中矩刘莹,但我還是覺得向左拖拽比較爽点弯。蒲拉。于是就寫下了本項目雌团,為這種向左拖拽的交互方式提供一種通用的解決方案锦援。
介紹
作為一種library灵寺,怎么可以只有以上這一種效果呢略板。因此叮称,可插拔的Footer View是必須的瓤檐。
下面是我定制的另外兩種效果挠蛉,當(dāng)然谴古,你也可以定制任意你想要的效果讥电。
![](https://github.com/uin3566/DragFooterView/blob/master/screenshot/custom2.gif?raw=true)
上圖中的字符串轉(zhuǎn)Path功能用的是秋百萬大神的android-Ultra-Pull-To-Refresh下拉刷新庫中的類StoreHousePath瞬测,只不過將字符串從橫向改為了豎向,在此深表感謝恢口,順便膜拜下耕肩。
![](https://github.com/uin3566/DragFooterView/blob/master/screenshot/custom1.gif?raw=true)
涉及的知識點
本質(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