前言
在其他App上看到了這樣的一個(gè)效果,感覺有點(diǎn)意思荆几,于是決定實(shí)現(xiàn)一個(gè)類似的效果。
(其實(shí)是iOS的同學(xué)在實(shí)現(xiàn)功能的時(shí)候隨意發(fā)揮了一下)
效果大概值這樣子的:
UI看完后
“這個(gè)效果不錯(cuò)啊”
“要不你們Android也么做赊时?” 于是~~
作為一個(gè)有追求的程序員吨铸,決定也要實(shí)現(xiàn)一個(gè)這樣的效果(滿腦子都是草泥馬在奔騰)
思路
這樣的效果嘛~~
利用自定義的ViewGroup,通過對手勢的處理祖秒,應(yīng)該就能實(shí)現(xiàn)了吧诞吱?
主要應(yīng)該分兩部分:
- 判斷手勢舟奠,如果為下拉操作,獲取下拉的距離來實(shí)現(xiàn)View的放大
- 當(dāng)手松開的時(shí)候房维,重置View的高度
比較麻煩的應(yīng)該是在第一部分沼瘫,需要對事件的分發(fā)有一些理解。
關(guān)于事件分發(fā)
說到手勢的判斷咙俩,難免需要對事件分發(fā)進(jìn)行處理耿戚。
下拉部分
1、在onInterceptTouchEvent
中對事件進(jìn)行處理阿趁,如果為下拉事件溅话,則將該事件攔截,交給onTouchEvent
處理歌焦;
2飞几、在onTouchEvent
中通過計(jì)算得到下拉的距離,然后動態(tài)改變Header的配置独撇,實(shí)現(xiàn)放大的效果屑墨。
重置部分
在onTouchEvent
的ACTION_UP中重置Header,實(shí)現(xiàn)回彈
對事件分發(fā)不了解的纷铣,這邊有兩篇不錯(cuò)的文章
Android事件分發(fā)機(jī)制 詳解攻略卵史,您值得擁有
圖解 Android 事件分發(fā)機(jī)制
實(shí)現(xiàn)
知道思路以后,實(shí)現(xiàn)起來就比較簡單了
攔截事件
創(chuàng)建一個(gè)ViewGroup(這么命名為FlexibleLayout)繼承LinearLayout搜立。
onInterceptTouchEvent的處理
- 在
onInterceptTouchEvent
中的DOWN事件中記錄觸摸位置 - 在MOVE事件中判斷是否為下拉動作以躯,若為下拉事件則進(jìn)行攔截。
public class FlexibleLayout extends LinearLayout{
/**
* true:開始下拽
*/
private boolean mIsBeingDragged;
/**
* 初始坐標(biāo)
*/
private float mInitialY, mInitialX;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialX = ev.getX();
mInitialY = ev.getY();
mIsBeingDragged = false;
break;
case MotionEvent.ACTION_MOVE:
float diffY = ev.getY() - mInitialY;
float diffX = ev.getX() - mInitialX;
if (diffY > 0 && diffY / Math.abs(diffX) > 2) {
mIsBeingDragged = true;
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
}
先通過兩個(gè)條件判斷是否為下拉事件:
- diffY > 0 : Y軸向下滑動
- diffY / Math.abs(diffX) > 2: Y軸滑動的距離超過X軸的兩倍
然后通過mIsBeingDragged
來標(biāo)記開始拖拽
onTouchEvent的處理
- 在
onTouchEvent
中的MOVE事件中獲取Y軸移動的距離啄踊,動態(tài)改變頭部的大小 - 在UP或CANCEL事件中忧设,重置頭部
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
if (mIsBeingDragged) {
//得到下拉的距離
float diffY = ev.getY() - mInitialY;
changeHeader((int) diffY);
return true;
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
//重置頭部
resetHeader();
return true;
}
break;
}
return super.onTouchEvent(ev);
}
修改頭部大小
得到下拉的距離后,就可以來改變Header的大小颠通,實(shí)現(xiàn)放大效果了址晕。
放大、重置的部分大家可以自由發(fā)揮
這里利用Math.pow(offsetY, 0.8)
得到實(shí)際需要增加的高度顿锰,通過計(jì)算得到對應(yīng)的寬度以及偏移(類似阻尼效果)谨垃。
/**
* 改變頭部大小
* @param offsetY
*/
private void changeHeader(int offsetY) {
int pullOffset = (int) Math.pow(offsetY, 0.8);
int newHeight = pullOffset + mHeaderHeight;
int newWidth = (int) ((((float) newHeight / mHeaderHeight)) * mHeaderWidth);
mHeaderView.getLayoutParams().height = newHeight;
mHeaderView.getLayoutParams().width = newWidth;
int margin = (newWidth - mHeaderWidth) / 2;
mHeaderView.setTranslationX(-margin);
mHeaderView.requestLayout();
}
重置頭部
直接將寬高以及偏移設(shè)置成原來的參數(shù)即可。
(如果覺得這樣重置過程不夠絲滑硼控,可以通過動畫來完成一個(gè)流暢的重置效果刘陶,這里就不演示了)
/**
* 重置頭部
*/
private void resetHeader() {
mHeaderView.getLayoutParams().height = mHeaderHeight;
mHeaderView.getLayoutParams().width = mHeaderWidth;
mHeaderView.setTranslationX(0);
mHeaderView.requestLayout();
}
到這里,一個(gè)簡易拉下放大的效果就做完了牢撼。試試效果
使用
直接在需要下拉放大的布局外面套上FlexibleLayout即可匙隔,例如ScrollView
<com.gavin.view.flexible.FlexibleLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
... >
<LinearLayout
... >
<ImageView
android:id="@+id/iv_header"
... />
</LinearLayout>
</ScrollView>
</com.gavin.view.flexible.FlexibleLayout>
效果
ScrollVIew:
RecyclerView:
CoordinatorLayout:
大功告成!@四牡直!
當(dāng)然里面還有一些細(xì)節(jié)的處理,比如下拉的條件纳决、回彈的動畫碰逸、最大高度等,具體內(nèi)容的可以在源碼中看到阔加。
完善
完成下拉放大后饵史,貌似把一個(gè)很重要的功能遺忘了下拉刷新 ?胜榔?
光顧這下拉放大胳喷,刷新怎么辦?【黑人問號】
這個(gè)功能留著下周實(shí)現(xiàn)吧夭织,我的7小時(shí)睡眠已遙遙無期~~
雖然沒有直接實(shí)現(xiàn)下拉刷新的功能吭露,不過源碼中已經(jīng)暴露了一個(gè)下拉的監(jiān)聽,你也可以通過這個(gè)監(jiān)聽實(shí)現(xiàn)下拉刷新的操作
public interface OnPullListener {
/**
* 下拉
* @param offset
*/
void onPull(int offset);
/**
* 松開
*/
void onRelease();
}
到這里就結(jié)束了
下拉刷新(2018-6-24補(bǔ)充)
(來還上周欠下的債~~)
和下拉放大類似尊惰,通過希手指下滑的監(jiān)聽讲竿,利用View的translationY和rotation實(shí)現(xiàn)移動和旋轉(zhuǎn)。
具體的實(shí)現(xiàn)過程這里就不貼出來了弄屡,直接看效果吧
有興趣的可以直接去Github上看源碼以及用法题禀。
源碼
參考
PullZoomView
Android事件分發(fā)機(jī)制 詳解攻略,您值得擁有
以上有錯(cuò)誤之處膀捷,感謝指出