日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

Android自定义控件:NestedScrolling实现仿魅族flyme6应用市场应用详情弹出式layout

發(fā)布時(shí)間:2024/1/8 Android 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android自定义控件:NestedScrolling实现仿魅族flyme6应用市场应用详情弹出式layout 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在前一篇博文中已經(jīng)實(shí)現(xiàn)過(guò)一個(gè)仿魅族flyme6應(yīng)用市場(chǎng)應(yīng)用詳情彈出式layout: Android自定義控件:從零開(kāi)始實(shí)現(xiàn)魅族flyme6應(yīng)用市場(chǎng)應(yīng)用詳情彈出式layout,主要是通過(guò)viewDragHelper來(lái)實(shí)現(xiàn),大部分效果算是實(shí)現(xiàn)了,但是在最后還是有一些bug。
趁著這段時(shí)間工作比較輕松一點(diǎn),這次再通過(guò)NestedScrolling來(lái)實(shí)現(xiàn)一次這個(gè)自定義控件,對(duì)比前面的實(shí)現(xiàn)方法,通過(guò)NestedScrolling實(shí)現(xiàn)起來(lái)會(huì)簡(jiǎn)單許多。
老規(guī)矩,先看看最終要實(shí)現(xiàn)的效果圖:

()

NestedScrolling

NestedScrolling是個(gè)啥玩意呢?這是Google官方從5.0后引入的滑動(dòng)嵌套解決方案。
看效果圖看的出來(lái),這次我們要實(shí)現(xiàn)的效果的難點(diǎn)就在嵌套滑動(dòng),因?yàn)槭种阜诺絪crollview中,然后實(shí)際滾動(dòng)的是卻外部的ViewGroup,在ViewGroup滾動(dòng)到頂部的時(shí)候呢,內(nèi)部的Scrollview又繼續(xù)滾動(dòng)。按照傳統(tǒng)的View事件攔截和處理方式,那首先要保證ViewGroup攔截事件,否則事件會(huì)被內(nèi)部的scrollview消費(fèi)掉。但是如果攔截了,當(dāng)ViewGroup滾動(dòng)到頂部的時(shí)候又如何讓scrollview又持續(xù)滑動(dòng)呢?按照傳統(tǒng)的方式,一次事件攔截就是一次性處理的事情,ViewGroup如果攔截了這次滑動(dòng)事件,那么scrollview肯定是沒(méi)法繼續(xù)處理這次滑動(dòng)事件的。
我們上篇博文是通過(guò)事件攔截和分發(fā)人為的在ViewGroup中更動(dòng)態(tài)的修改scrollView的滑動(dòng),從視覺(jué)上實(shí)現(xiàn)一次滑動(dòng)事件ViewGroup和子view嵌套的滾動(dòng)效果。實(shí)際上從本質(zhì)上來(lái)講,還是ViewGroup攔截和消費(fèi)了事件,第一次ViewGroup中的事件并沒(méi)有到子view中去處理。

那么NestedScrolling如何實(shí)現(xiàn)嵌套滑動(dòng)呢?
NestedScrollingParent內(nèi)部實(shí)現(xiàn)了NestedScrollingChild接口的子View會(huì)優(yōu)先獲得事件處理權(quán),然后滑動(dòng)的時(shí)候,會(huì)先將dx、dy傳入給NestedScrollingParent,NestedScrollingParent可以決定是否對(duì)其進(jìn)行消耗,也就是說(shuō)NestedScrollingParent可以消費(fèi)部分dx、dy,余下的未消費(fèi)完的dx、dy交還給子view去消費(fèi)。

這樣看實(shí)際上要實(shí)現(xiàn)本次的效果就很簡(jiǎn)單了,話不多說(shuō),貼代碼。

先讓我們的自定義ScrollView實(shí)現(xiàn)NestedScrollingChild接口,并且將NestedScrolling相關(guān)的處理全部交給ScrollingChildHelper處理。

public class MyScrollView extends ScrollView implements NestedScrollingChild{private boolean isScrollToTop = true;private boolean isScrollToBottom = false;private OnScrollLimitListener mOnScrollLimitListener;private NestedScrollingChildHelper mScrollingChildHelper;public MyScrollView(Context context) {this(context, null);}public MyScrollView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overridepublic void setNestedScrollingEnabled(boolean enabled) {getScrollingChildHelper().setNestedScrollingEnabled(enabled);}@Overridepublic boolean isNestedScrollingEnabled() {return getScrollingChildHelper().isNestedScrollingEnabled();}@Overridepublic boolean startNestedScroll(int axes) {return getScrollingChildHelper().startNestedScroll(axes);}@Overridepublic void stopNestedScroll() {getScrollingChildHelper().stopNestedScroll();}@Overridepublic boolean hasNestedScrollingParent() {return getScrollingChildHelper().hasNestedScrollingParent();}@Overridepublic boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,int dyUnconsumed, int[] offsetInWindow) {return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,dxUnconsumed, dyUnconsumed, offsetInWindow);}@Overridepublic boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);}@Overridepublic boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);}@Overridepublic boolean dispatchNestedPreFling(float velocityX, float velocityY) {return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);}private NestedScrollingChildHelper getScrollingChildHelper() {if (mScrollingChildHelper == null) {mScrollingChildHelper = new NestedScrollingChildHelper(this);setNestedScrollingEnabled(true);}return mScrollingChildHelper;}/*** 設(shè)置ScrollView滑動(dòng)到邊界監(jiān)聽(tīng)** @param onScrollLimitListener ScrollView滑動(dòng)到邊界監(jiān)聽(tīng)*/public void setOnScrollLimitListener(OnScrollLimitListener onScrollLimitListener) {mOnScrollLimitListener = onScrollLimitListener;}@Overrideprotected void onScrollChanged(int l, int t, int oldl, int oldt) {super.onScrollChanged(l, t, oldl, oldt);if (getScrollY() == 0) {//滑動(dòng)到頂部isScrollToTop = true;isScrollToBottom = false;isScrollToBottom = false;} else if (getScrollY() + getHeight() - getPaddingTop() - getPaddingBottom() ==getChildAt(0).getHeight()) {// 小心踩坑: 這里不能是 >=// 小心踩坑:這里最容易忽視的就是ScrollView上下的padding isScrollToTop = false;isScrollToBottom = true;} else {isScrollToTop = false;isScrollToBottom = false;}notifyScrollChangedListeners();}/*** 回調(diào)*/private void notifyScrollChangedListeners() {if (isScrollToTop) {if (mOnScrollLimitListener != null) {mOnScrollLimitListener.onScrollTop();}} else if (isScrollToBottom) {if (mOnScrollLimitListener != null) {mOnScrollLimitListener.onScrollBottom();}} else {if (mOnScrollLimitListener != null) {mOnScrollLimitListener.onScrollOther();}}}/*** scrollview滑動(dòng)到邊界監(jiān)聽(tīng)接口*/public interface OnScrollLimitListener {/*** 滑動(dòng)到頂部*/void onScrollTop();/*** 滑動(dòng)到頂部和底部之間的位置(既不是頂部也不是底部)*/void onScrollOther();/*** 滑動(dòng)到底部*/void onScrollBottom();} }

然后是我們的PopupLayout,上一篇博文是通過(guò)自定義FrameLayout的方式實(shí)現(xiàn)的,這次由于是通過(guò)NestedScrolling實(shí)現(xiàn),所以一次滑動(dòng)事件其實(shí)是針對(duì)整個(gè)ViewGroup的,所以本次采取自定義LinearLayout的方式去實(shí)現(xiàn)。

在這里我們重點(diǎn)看下面幾個(gè)方法,首先是onMeasure方法。因?yàn)槌跏紶顟B(tài)下ContentView是在界面之外的,所以要確定ContentView的高度。

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);Log.e("tag", "onMeasure");ViewGroup.LayoutParams params = contentView.getLayoutParams();params.height = darkView.getMeasuredHeight() - mOrginY;setMeasuredDimension(getMeasuredWidth(), contentView.getMeasuredHeight() + darkView.getMeasuredHeight());}

接下來(lái)看看重寫(xiě)的NestedScrollingParent幾個(gè)方法。

@Overridepublic boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {Log.e(TAG, "onStartNestedScroll");return true;}@Overridepublic void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {Log.e(TAG, "onNestedScrollAccepted");}@Overridepublic void onStopNestedScroll(View target) {Log.e(TAG, "onStopNestedScroll");if (mDarkViewHeight - mOrginY - getScrollY() > mDragRange) {//向下拖拽,超出拖拽限定距離dismiss();} else if (mDarkViewHeight - mOrginY - getScrollY() > 0) {//向下拖拽,但是沒(méi)有超出拖拽限定距離springback();}}@Overridepublic void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, intdyUnconsumed) {Log.e(TAG, "onNestedScroll");}@Overridepublic void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {boolean patchDown = dy < 0 && mIsScrollInTop;//下滑boolean patchUp = dy > 0 && getScrollY() < (mDarkViewHeight - UIUtils.getStatusBarHeight(target));//上滑if (patchDown || patchUp) {scrollBy(0, dy);consumed[1] = dy;}}@Overridepublic boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {return true;}@Overridepublic boolean onNestedPreFling(View target, float velocityX, float velocityY) {//不做攔截 可以傳遞給子Viewreturn false;}@Overridepublic int getNestedScrollAxes() {Log.e(TAG, "getNestedScrollAxes");return 0;}

onNestedPreScroll中,我們判斷,如果是上滑且contentView未滑動(dòng)到頂部,則消耗掉dy,即consumed[1]=dy。如果是下滑且內(nèi)部scrollview已經(jīng)滑動(dòng)到頂,則消耗掉dy,即consumed[1]=dy,消耗掉的意思,就是自己去執(zhí)行scrollBy,實(shí)際上就是滑動(dòng)PopupLayout本身。

onStopNestedScroll中,我們判斷向下滑動(dòng)的距離,來(lái)確定是dismiss PopupLayout還是回彈到初始位置。

最后由于需要更新TitleBar的狀態(tài),所以重寫(xiě)了scrollTo方法,在scrollTo方法中更新TitleBar的狀態(tài)。

@Overridepublic void scrollTo(int x, int y) {if (y >= mDarkViewHeight - UIUtils.getStatusBarHeight(this)) {y = mDarkViewHeight - UIUtils.getStatusBarHeight(this);darkView.setBackgroundColor(Color.WHITE);//拖動(dòng)到頂部時(shí)darkview背景設(shè)置白色titleBar.setBackImageResource(R.mipmap.back);} else {darkView.setBackgroundResource(R.color.dark);//沒(méi)有拖動(dòng)到頂部時(shí)darkview背景設(shè)置暗色titleBar.setBackImageResource(R.mipmap.close);}if (y != getScrollY()) {super.scrollTo(x, y);}}

本次的要點(diǎn)基本就這么多,總的來(lái)說(shuō)相較上一篇博文各種絞盡腦汁想著事件處理,這次通過(guò)NestedScrolling就重寫(xiě)幾個(gè)方法,然后根據(jù)自己的實(shí)際需求做一些判斷,實(shí)現(xiàn)起來(lái)還是很簡(jiǎn)單的。

最后附上源碼鏈接:https://github.com/Horrarndoo/PopupLayoutNew

總結(jié)

以上是生活随笔為你收集整理的Android自定义控件:NestedScrolling实现仿魅族flyme6应用市场应用详情弹出式layout的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。