中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

平臺(tái)類網(wǎng)站有哪些搜索引擎分類

平臺(tái)類網(wǎng)站有哪些,搜索引擎分類,做網(wǎng)站找毛葉子歌,網(wǎng)站建設(shè)賣手機(jī)代碼嵌套滾動(dòng):內(nèi)外兩層均可滾動(dòng),比如上半部分是一個(gè)有限的列表,下半部分是WebView,在內(nèi)層上半部分展示到底的時(shí)候,外部父布局整體滾動(dòng)內(nèi)部View,將底部WevView拉起來(lái),滾動(dòng)到頂部之后再將滾動(dòng)交給內(nèi)部…

嵌套滾動(dòng):內(nèi)外兩層均可滾動(dòng),比如上半部分是一個(gè)有限的列表,下半部分是WebView,在內(nèi)層上半部分展示到底的時(shí)候,外部父布局整體滾動(dòng)內(nèi)部View,將底部WevView拉起來(lái),滾動(dòng)到頂部之后再將滾動(dòng)交給內(nèi)部WebView,之后滾動(dòng)的就是內(nèi)部WebView,如下圖:

image.png

實(shí)現(xiàn):onInterceptTouchEvent或者NestedScroll

按照上下兩部分構(gòu)建父布局,父ViewGroup建議繼承FrameLayout/RelativeLayout來(lái)實(shí)現(xiàn),方便處理測(cè)量[無(wú)需復(fù)寫]與布局,在計(jì)算出全部View高度后,可以計(jì)算最大父布局滾動(dòng)距離:

override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {var top = tvar bottom = bfor (i in 0 until childCount) {getChildAt(i).layout(l, top, r, bottom)top += getChildAt(i).measuredHeightbottom += getChildAt(i).measuredHeighttotalHeight += getChildAt(i).measuredHeight}maxScrollHeight = totalHeight - measuredHeight
}

上述交互有兩種比較常用的方式,一種是onInterceptTouchEvent全局?jǐn)r擊Touch事件來(lái)實(shí)現(xiàn)拖動(dòng)與Fling的處理,另一種是借助后期推出的NestedScroll框架來(lái)實(shí)現(xiàn)。先簡(jiǎn)單看下傳統(tǒng)的onInterceptTouchEvent攔截的方式:核心的處理事兩個(gè)操作,一個(gè)是拖動(dòng)、一個(gè)是UP后的Fling,onInterceptTouchEvent首先要確定攔截的時(shí)機(jī):判斷有效拖動(dòng)

override fun onInterceptTouchEvent(event: MotionEvent): Boolean {when (event.actionMasked) {MotionEvent.ACTION_DOWN -> {mLastY = event.rawYmDownY = event.rawYmDownX = event.rawXmBeDraging = false}MotionEvent.ACTION_MOVE -> if (abs(event.rawY - mDownY) >  ViewConfiguration.get(context).scaledTouchSlop) {mBeDraging = truereturn true} else {mLastY = event.rawY}else -> {}}return super.onInterceptTouchEvent(event)
}

一般而言垂直滾動(dòng)超過(guò)某個(gè)TouchSlop就可以認(rèn)為拖動(dòng)有效,拖動(dòng)開(kāi)始,子View后續(xù)無(wú)法獲取到Touch事件,其實(shí)大多數(shù)場(chǎng)景而言,父布局接管之后,沒(méi)有必要再給子View分發(fā)事件,之后自行處理拖拽與Fling。

onInterceptTouchEvent方式處理拖拽與fling

處理拖拽與fling需要注意銜接,所以需要準(zhǔn)備好GestureDetector,用于將來(lái)的fling,建議放在dispatchTouchEvent整體處理事件的消費(fèi)

override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {if (ev != null) {gestureDetector.onTouchEvent(ev)}if (ev?.action == MotionEvent.ACTION_DOWN)overScrollerNest.abortAnimation()if (ev?.action == MotionEvent.ACTION_MOVE && mBeDraging) {scrollInner((mLastY - ev.rawY).roundToInt())mLastY = ev.rawY}return super.dispatchTouchEvent(ev)
}

拖拽的控制方式:自己計(jì)算出可滾動(dòng)的距離,可以利用View的canScrollVertically判斷View是否能消費(fèi),從而決定留給哪個(gè)View

  private fun scrollInner(dy: Int) {var pConsume: Int = 0var cConsume: Int = 0if (dy > 0) {if (scrollY in 1 until maxScrollHeight) {pConsume = dy.coerceAtMost(maxScrollHeight - scrollY)scrollBy(0, dy)cConsume = dy - pConsumeif (bottomView.canScrollVertically(cConsume) && cConsume != 0) {bottomView.scrollBy(0, cConsume)}} else if (scrollY == 0) {bottomView.scrollTo(0, 0)if (upView.canScrollVertically(dy)) {upView.scrollBy(0, dy)} else {if (canScrollVertically(dy)) {scrollBy(0, dy)}}} else if (scrollY >= maxScrollHeight) {scrollTo(0, maxScrollHeight)if (bottomView.canScrollVertically(dy)) {bottomView.scrollBy(0, dy)} else {overScrollerNest.abortAnimation()}}} else {if (scrollY in 1 until maxScrollHeight) {pConsume = Math.max(dy, -scrollY)scrollBy(0, pConsume)cConsume = dy - pConsumeif (bottomView.canScrollVertically(cConsume)) {bottomView.scrollBy(0, cConsume)}} else if (scrollY == maxScrollHeight) {if (bottomView.canScrollVertically(dy)) {bottomView.scrollBy(0, dy)} else {if (canScrollVertically(dy)) {scrollBy(0, dy)}}} else {if (upView.canScrollVertically(dy)) {upView.scrollBy(0, dy)}bottomView.scrollTo(0, 0)}}invalidate()}

拖拽結(jié)束后,fling跟上利用GestureDetector的onFling,直接讓Scroller接上即可 overScroller = OverScroller(context),一般用OverScroller的體驗(yàn)好一些:

private GestureDetector gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {@Overridepublic boolean onFling(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) {if (!(Math.abs(e1.getX() - e2.getX()) > mTouchSlop && Math.abs(velocityX) > Math.abs(velocityY))) {<!--銜接滾動(dòng)-->overScroller.fling(0, 0, 0, (int) velocityY, 0, 0, -10 * ScreenUtil.getDisplayHeight(), 10 * ScreenUtil.getDisplayHeight());<!--必須觸發(fā)一次-->postInvalidate();}return super.onFling(e1, e2, velocityX, velocityY);}
});

在computeScroll里從新計(jì)算應(yīng)該滾動(dòng)的距離,可以看到全局接管,并利用scrollBy自行控制滾動(dòng)的偏移量是這種方案的核心

    var mLastOverScrollerValue = 0override fun computeScroll() {super.computeScroll()if (overScrollerNest.computeScrollOffset()) {scrollInner(overScrollerNest.currY - mLastOverScrollerValue)mLastOverScrollerValue = overScrollerNest.currYinvalidate()}}    

如此就可以利用onInterceptTouchEvent實(shí)現(xiàn)嵌套滾動(dòng),不涉及太多內(nèi)部View【僅僅是獲取了內(nèi)部View的高度及判斷是否可滾】,一切交給父布局即可。

利用NestedScrolling框架實(shí)現(xiàn)嵌套滑動(dòng)

Android5.0推出了嵌套滑動(dòng)機(jī)制NestedScrolling,讓父View和子View在滑動(dòng)時(shí)相互協(xié)調(diào)配合,為了向前兼容又抽離了NestedScrollingChild、NestedScrollingParent、NestedScrollingChildHelper、NestedScrollingParentHelper等支持類,不過(guò)在23年的場(chǎng)景下基本不需要使用這些輔助類了。NestedScrolling的核心是子View一直能收到Move事件,在自己處理之前先交給父View消費(fèi),父View處理完之后,再將余量還給子View,讓子View自己處理,可以看出這套框架必須父子配合,也就是NestedScrollingChild、NestedScrollingParent是配套的。5.0之后View與ViewGroup本身就實(shí)現(xiàn)了NestedScrollingChild+NestedScrollingParent的框架,自定義布局的時(shí)候只需要定制與啟用,也就是必須進(jìn)行二次開(kāi)發(fā),目前Google提供的最好用的就是RecyclerView。有張圖很清晰的描述NestedScrolling框架是如何工作的:

image.png

NestedScrolling只處理拖動(dòng)[target無(wú)法改變],Fling交給Parent處理

在這個(gè)框架中,子View必須主動(dòng)啟動(dòng)嵌套滑動(dòng)、并且在Move的時(shí)候主動(dòng)請(qǐng)求父ViewGroup進(jìn)行處理,這樣才能完成協(xié)同,并非簡(jiǎn)單的打開(kāi)開(kāi)關(guān),所有的定制邏輯仍舊需要開(kāi)發(fā)者自己處理,只是替代了onInterceptTouchEvent,提供了子View回傳事件給父View的能力,不用父View主動(dòng)攔截,也能獲取接管子View事件的能力。

以開(kāi)頭描述的場(chǎng)景為例,如果上部分用ScrollView下部分用WebView,那么必須將兩者都改造成NestedScrollingChild,也就是NestedScrollView與NestedWebView,NestedScrollView谷歌已經(jīng)提供,NestedWebView目前沒(méi)有,需要自己封裝,可以看看如何配合實(shí)現(xiàn)一套嵌套滑動(dòng)交互:

class NetScrollWebView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null,
) : WebView(context, attrs) {private val mTouchSlop = android.view.ViewConfiguration.get(context).scaledTouchSlopprivate val mScrollOffset = IntArray(2)private val mScrollConsumed = IntArray(2)init {<!--啟動(dòng)嵌套滑動(dòng)-->isNestedScrollingEnabled = true}private var mLastY: Float = 0fprivate var dragIng: Boolean = falseoverride fun dispatchTouchEvent(ev: MotionEvent?): Boolean {when (ev?.action) {MotionEvent.ACTION_MOVE -> {if (abs(ev.rawY - mLastY) > mTouchSlop) {dragIng = true} else {super.dispatchTouchEvent(ev)}if (dragIng) {if (parent != null) {parent.requestDisallowInterceptTouchEvent(true)}<!--主動(dòng)調(diào)用dispatchNestedPreScroll,請(qǐng)求父容器處理-->dispatchNestedPreScroll(0, (mLastY - ev.rawY).toInt(), mScrollConsumed, mScrollOffset)mLastY = ev.rawY}}MotionEvent.ACTION_DOWN -> {dragIng = falsesuper.dispatchTouchEvent(ev)<!--startNestedScroll啟動(dòng)嵌套滑動(dòng)-->startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)mScrollConsumed[1] = 0mLastY = ev.rawY}MotionEvent.ACTION_UP -> stopNestedScroll()else -> super.dispatchTouchEvent(ev)}return true}// 強(qiáng)制自己不消費(fèi)moveoverride fun onTouchEvent(ev: MotionEvent?): Boolean {if (dragIng || ev?.action == MotionEvent.ACTION_MOVE)return falsereturn super.onTouchEvent(ev)}}
  1. setNestedScrollingEnabled(true) ,后續(xù)的dispatch都依賴該開(kāi)關(guān)
  2. 子View收到DOWN事件的時(shí)候啟動(dòng)startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL),【無(wú)論有沒(méi)有NestedScrollingParent】
  3. 父布局其實(shí)這個(gè)時(shí)候也會(huì)響應(yīng),只有存在支持嵌套滑動(dòng)的父布局,后續(xù)dispatchNestedPreScroll等函數(shù)才有意義才有意義
  4. 假設(shè)存在支持嵌套滑動(dòng)的父布局,在MOVE的時(shí)候,調(diào)用dispatchNestedPreScroll讓父布局處理
  5. 在MotionEvent.ACTION_UP的時(shí)候,stopNestedScroll ,由于WebView是ViewGroup,所以可以直接在dispatchTouchEvent處理,如果是View可以在onTouchEvent中處理

如此一個(gè)簡(jiǎn)單的NestedScrollingChild就完成了,但是只有這個(gè)并不能完成上述需求,還需要一個(gè)NestedScrollingParent來(lái)配合,其實(shí)這里大部分的功能跟上述onInterceptTouchEvent的實(shí)現(xiàn)的類似,只不過(guò)

	class NestUpDownTwoPartsScrollView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0,defStyleRes: Int = 0,
) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {private val gestureDetector: GestureDetector =GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {override fun onFling(與onInterceptTouchEvent一致...})...<!--標(biāo)志父布局支持垂直的嵌套滑動(dòng)-->override fun onStartNestedScroll(child: View, target: View, nestedScrollAxes: Int): Boolean {return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL}<!--被NestedScrollingChild回調(diào)-->override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray) {overScrollerNest.abortAnimation()scrollInner(dy)<!--完全給消費(fèi)-->consumed[1] = dy}
<!--攔截子View們的fling-->	override fun onNestedPreFling(target: View, velocityX: Float, velocityY: Float): Boolean {//  獲取的fling速度有差異,原因不詳return true}var mLastOverScrollerValue = 0<!--自己處理fling-->override fun computeScroll() {super.computeScroll()<!--自己處理fling-->if (overScrollerNest.computeScrollOffset()) {scrollInner(overScrollerNest.currY - mLastOverScrollerValue)mLastOverScrollerValue = overScrollerNest.currYinvalidate()}}private lateinit var overScrollerNest: OverScrolleroverride fun computeVerticalScrollRange(): Int {return totalHeight}private fun scrollInner(dy: Int) {...	  與onInterceptTouchEvent一致}override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {if (ev != null) {gestureDetector.onTouchEvent(ev)}if (ev?.action == MotionEvent.ACTION_DOWN)overScrollerNest.abortAnimation()return super.dispatchTouchEvent(ev)}
}

父布局的操作如下

  1. onStartNestedScroll 返回true 啟動(dòng)
  2. 被子View調(diào)用onNestedPreScroll開(kāi)始協(xié)同滑動(dòng)
  3. onNestedPreFling接管Fling
  4. 利用GestureDetector+OverScroller自行處理Fling

可以看到,在這個(gè)框架下,可以比較靈活的接管拖動(dòng),不用自己攔截,而且消費(fèi)多少,可以父子協(xié)商,關(guān)于Fling,可以處理成一致,而且有兩個(gè)滾動(dòng)布局銜接的時(shí)候,交給外部統(tǒng)一處理應(yīng)該也是最合理的做法,防止兩個(gè)View的Scroller不一致,而且嵌套滑動(dòng)也無(wú)法處理target切換的問(wèn)題。

NestedScrolling框架 使用注意點(diǎn)

  1. fling處理:盡量不使用內(nèi)層GestureDetector來(lái)獲取,因?yàn)閮?nèi)外側(cè)獲取MotionEvent不是統(tǒng)一的,所以內(nèi)外層獲取的fling初始速度可能不同,銜接易出問(wèn)題,還是統(tǒng)一給外層自己做
  2. move處理拖拽:盡量使用rawY,因?yàn)镸otionEvent獲取的Y在嵌套滾動(dòng)時(shí)候不如rawY直觀,rawY始終是相對(duì)屏幕,而Y是相對(duì)自己View,在父View進(jìn)行滾動(dòng)的時(shí)候,target的Y幾乎是不動(dòng)的

強(qiáng)大的RecyclerView

RecyclerView適配一切,利用RecyclerView內(nèi)嵌WebView也能實(shí)現(xiàn)上述效果:但需要主動(dòng)控制內(nèi)部可滾動(dòng)Item。RecyclerView自身實(shí)現(xiàn)了onInterceptTouchEvent邏輯,理論上內(nèi)部子View是無(wú)法獲取到攔截之后的事件,只能依賴外部主動(dòng)控制,否則WebView被拖到頂部就結(jié)束了,內(nèi)部無(wú)法繼續(xù)拖拽。但是RecyclerView本身實(shí)現(xiàn)了NestedScrollingChild3,可以看做是一個(gè)支持嵌套滑動(dòng)的Child,在NestedScrolling框架中Move事件時(shí)一般會(huì)直接調(diào)用dispatchNestedPreScroll,之后dispatchNestedPreScroll會(huì)區(qū)分是否能啟用嵌套滑動(dòng)。因此除了借助 onInterceptTouchEvent邏輯,還可以借助dispatchNestedPreScroll來(lái)處理,一種很猥瑣的做法:繼承RecyclerView,復(fù)寫dispatchNestedPreScroll,這個(gè)時(shí)候繼承類先父類RecyclerView獲取事件處理的優(yōu)先權(quán),在一定程度上看做實(shí)現(xiàn)了onInterceptTouchEvent的NestedScrollingChild,在復(fù)寫的dispatchNestedPreScroll種處理子View的滾動(dòng)。即可自身滾動(dòng),也能控制內(nèi)部可滾動(dòng)View的滾動(dòng),但很難做到那么通用。不過(guò)在做業(yè)務(wù)的時(shí)候,思路有時(shí)候勝過(guò)單純的技術(shù),尤其嵌套滑動(dòng),不需要過(guò)分追求通用型控件。

對(duì)于上述交互場(chǎng)景,只需在dispatchNestedPreScroll做如下處理:只需要主動(dòng)接手內(nèi)部子View的操控,外部的操控?zé)o需處理

 override fun dispatchNestedPreScroll( dx: Int,  dy: Int,  consumed: IntArray?,  offsetInWindow: IntArray?,  type: Int): Boolean {var consumedSelf = false// 先讓父布局處理,還是后處理?val parentScrollConsumed = mParentScrollConsumedval parentConsumed = super.dispatchNestedPreScroll(    dx,  dy,   parentScrollConsumed,   offsetInWindow,   type)consumed?.let {consumed[1] += parentScrollConsumed[1]}<!--再交給自己已處理-->if (type == ViewCompat.TYPE_TOUCH) {<!--對(duì)于向上滾動(dòng),如果自身可是滾動(dòng)就直接滾動(dòng)自身,說(shuō)明還沒(méi)到頂部RecyclerView會(huì)自己處理,自己攔截過(guò)了無(wú)需外部干預(yù),如果自身不能滾,就滾動(dòng)內(nèi)部的可滾動(dòng)target-->if (!canScrollVertically(1)) {//外部自身的操控?zé)o需處理<!--fetchNestedChild是用來(lái)獲取內(nèi)部的可滾動(dòng)View,這個(gè)看具體業(yè)務(wù)操作-->val remain = dy - (consumed?.get(1) ?: 0)if (remain > 0) {//  已經(jīng)到頂了if (!canScrollVertically(1)) {val target = fetchBottomNestedScrollChild()target?.apply {this.scrollBy(0, remain)consumed?.let {it[1] += remain}consumedSelf = true}}}// down 其實(shí)還是自己控制,而不是底層控制if (remain < 0) {val target = fetchBottomNestedScrollChild()target?.apply {if (this.canScrollVertically(-1)) {this.scrollBy(0, remain)//   消耗完,不給底層機(jī)會(huì)consumed?.let {it[1] += remain}consumedSelf = true}}}}return consumedSelf || parentConsumed
}

拖拽是比較容易處理的,比較棘手的是對(duì)于fling的處理,fling是一次性的,如果Recycleview繼承類dispatchNestedPreFling自己處理了fling后,父布局就獲取不到,銜接就比較麻煩

   override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean {fling(velocityY)return true}

如果Recycleview自身通過(guò)OverScroller處理完畢后,還有盈余,就需要將盈余給外部,先處理內(nèi)部,還是先處理外部都是可選的,看用戶自己

override fun computeScroll() {if (overScroller.computeScrollOffset()) {val current = overScroller.currYval dy = current - mCurrentFlingmCurrentFling = currentval target = fetchBottomNestedScrollChild()if (dy > 0) {if (canScrollVertically(1)) {scrollBy(0, dy)} else {if (target?.canScrollVertically(1) == true) {target.scrollBy(0, dy)} else {if (!overScroller.isFinished) {overScroller.abortAnimation()// fling 先內(nèi)部,給上面接管一部分startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)dispatchNestedFling(0f, overScroller.currVelocity, false)stopNestedScroll()}

處理方式就是內(nèi)部不可fling之后,主動(dòng)通過(guò)startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)與 dispatchNestedFling再次交給父布局。

總結(jié)

流暢交互全靠微調(diào)

http://www.risenshineclean.com/news/9131.html

相關(guān)文章:

  • wordpress圖片css鄭州seo地址
  • 國(guó)展做網(wǎng)站的公司網(wǎng)絡(luò)推廣哪個(gè)好
  • 學(xué)做網(wǎng)站論壇vip賬戶如何注冊(cè)百度賬號(hào)
  • 北京工程建設(shè)交易網(wǎng)標(biāo)題優(yōu)化
  • 建網(wǎng)站 免費(fèi)搜索引擎營(yíng)銷分析
  • 北京市保障性住房建設(shè)投資中心網(wǎng)站6搜索引擎下載安裝
  • ASP動(dòng)態(tài)網(wǎng)站制作國(guó)外b站不收費(fèi)免費(fèi)2023
  • 大連市建委培訓(xùn)官方網(wǎng)站微信公眾號(hào)營(yíng)銷
  • 行業(yè)網(wǎng)站開(kāi)發(fā)運(yùn)營(yíng)方案免費(fèi)推廣平臺(tái)排行
  • 煙臺(tái)h5網(wǎng)站制作sem優(yōu)化和seo的區(qū)別
  • 什么網(wǎng)站做簡(jiǎn)歷比較好seo營(yíng)銷服務(wù)
  • js導(dǎo)入wordpress汕頭seo排名收費(fèi)
  • 做銀行設(shè)計(jì)有好的網(wǎng)站參考嗎關(guān)鍵詞工具
  • 怎么樣把以前做的網(wǎng)站刪除希愛(ài)力雙效片騙局
  • 網(wǎng)站建設(shè)shzanen百度競(jìng)價(jià)查詢
  • 萬(wàn)網(wǎng)域名管理平臺(tái)登錄廣州seo網(wǎng)站推廣平臺(tái)
  • 深圳外文網(wǎng)站制作交換免費(fèi)連接
  • ps做網(wǎng)站大小尺寸短視頻seo推廣隱迅推專業(yè)
  • 上海做網(wǎng)絡(luò)推廣濰坊自動(dòng)seo
  • 24小時(shí)自助下單網(wǎng)站搜索引擎付費(fèi)推廣
  • 帝國(guó)cms網(wǎng)站遷移頁(yè)面seo是什么意思
  • 小程序制作開(kāi)發(fā)關(guān)鍵詞排名優(yōu)化教程
  • wordpress 搜索 分類seo站長(zhǎng)助手
  • wordpress企業(yè)主題教程seo優(yōu)化員
  • 做 b2b平臺(tái)的網(wǎng)站公眾號(hào)推廣引流
  • dw做網(wǎng)站投票網(wǎng)址收錄入口
  • 比較好的推廣平臺(tái)seo有名氣的優(yōu)化公司
  • 運(yùn)城 網(wǎng)站制作企業(yè)郵箱賬號(hào)
  • 網(wǎng)站建設(shè)公司業(yè)務(wù)在哪里來(lái)手機(jī)怎么創(chuàng)建自己的網(wǎng)站平臺(tái)
  • b2b電子商務(wù)網(wǎng)站推廣銷售新手怎么找客源