亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

android學(xué)習(xí)筆記之View的滑動(dòng)

 更新時(shí)間:2023年01月03日 15:02:08   作者:呼嘯  
Android開(kāi)發(fā)中我們常常需要View滑動(dòng)實(shí)現(xiàn)一些絢麗的效果來(lái)優(yōu)化用戶體驗(yàn),下面這篇文章主要給大家介紹了關(guān)于android學(xué)習(xí)筆記之View滑動(dòng)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

其實(shí)不管是哪種滑動(dòng)方式,基本思想都是類(lèi)似的:當(dāng)點(diǎn)擊事件傳遞到View時(shí),系統(tǒng)記下觸摸點(diǎn)的坐標(biāo),手指移動(dòng)的時(shí)候,系統(tǒng)記下移動(dòng)后的坐標(biāo),并計(jì)算出偏移量,并通過(guò)偏移量來(lái)修改View的坐標(biāo)。

下面我們來(lái)講下幾種滑動(dòng)方法:

1.layout方法

大家知道,繪制View的時(shí)候會(huì)調(diào)用onlayout方法來(lái)設(shè)置要顯示的位置。因此我們也可以通過(guò)修改view的left,top,right,bottom這4個(gè)屬性來(lái)控制view的坐標(biāo)。接下來(lái)我們來(lái)自定義一個(gè)View,通過(guò)layout方法來(lái)實(shí)現(xiàn)滑動(dòng):

package com.example.myapplication.views
 
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
 
class CustomView @JvmOverloads constructor(
    context: Context,
    attrs:AttributeSet? = null,
    defStyleAttr:Int= 0
) :View(
    context,attrs,defStyleAttr){
    private var lastX:Int = 0
    private var lastY:Int = 0
 
    override fun onTouchEvent(event: MotionEvent): Boolean {
        //獲取手指觸摸點(diǎn)的橫坐標(biāo)和縱坐標(biāo)
        val x = event.x.toInt()
        val y = event.y.toInt()
        when(event.action) {
            MotionEvent.ACTION_DOWN -> {
                lastX = x
                lastY = y
            }
            MotionEvent.ACTION_MOVE -> {
                //計(jì)算移動(dòng)距離
                val offsetX = x -lastX
                val offsetY = y -lastY
                //調(diào)用layout方法來(lái)重新放置他的位置
                layout(left+offsetX,top+offsetY,right + offsetX, bottom + offsetY)
            }
        }
        return true
    }
}

然后我們把他放到XML里去使用:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <com.example.myapplication.views.CustomView
        android:id="@+id/view_my"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center_horizontal"
        android:background="@mipmap/cai"/>
 
</LinearLayout>

看下效果:

 這個(gè)移動(dòng)的效果實(shí)現(xiàn)。

2.接下來(lái),我們看看offsetLeftAndRight()與offsetTopAndBottom()方法

這兩種方法和layout方法差不多,我們稍加修改就可以。我們替換下ACTION_MOVE中的代碼塊:

MotionEvent.ACTION_MOVE -> {
                //計(jì)算移動(dòng)距離
                val offsetX = x -lastX
                val offsetY = y -lastY
                //對(duì)left和right進(jìn)行偏移
                offsetLeftAndRight(offsetX)
                //對(duì)top和bottom進(jìn)行偏移
                offsetTopAndBottom(offsetY)
 
//                //調(diào)用layout方法來(lái)重新放置他的位置
//                layout(left+offsetX,top+offsetY,right + offsetX, bottom + offsetY)
            }

仍然能夠?qū)崿F(xiàn)。

3.第三個(gè)方法:LayoutParams(改變布局參數(shù))

LayoutParams主要保存了一個(gè)View的布局參數(shù),因此我們可以通過(guò)LayoutParams來(lái)改變View的布局參數(shù)。從而達(dá)到改變View位置的效果。我們同樣替換下ACTION_MOVE中的代碼塊:

  MotionEvent.ACTION_MOVE -> {
                //計(jì)算移動(dòng)距離
                val offsetX = x -lastX
                val offsetY = y -lastY
                val  layoutParams = layoutParams as LinearLayout.LayoutParams
                layoutParams.leftMargin = left + offsetX
                layoutParams.topMargin = top + offsetY
                setLayoutParams(layoutParams)
 
//                //對(duì)left和right進(jìn)行偏移
//                offsetLeftAndRight(offsetX)
//                //對(duì)top和bottom進(jìn)行偏移
//                offsetTopAndBottom(offsetY)
 
//                //調(diào)用layout方法來(lái)重新放置他的位置
//                layout(left+offsetX,top+offsetY,right + offsetX, bottom + offsetY)
            }
        }

不過(guò)經(jīng)過(guò)我實(shí)驗(yàn),這個(gè)有挺大的偏差,總是靠右邊。什么原因?

4.接下來(lái),看看使用第四種方法,使用動(dòng)畫(huà)來(lái)滑動(dòng)。

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
    <translate android:fromXDelta="0"
        android:toXDelta="300"
        />
</set>

使用代碼調(diào)用:

findViewById<View>(R.id.view_my).animation = AnimationUtils.loadAnimation(this,R.anim.translate)

需要注意的是,view動(dòng)畫(huà),并不能改變view的位置參數(shù)。所以我們點(diǎn)擊Button并不會(huì)觸發(fā),因?yàn)樗呢?fù)控件要先判斷點(diǎn)擊事件是否在子view的位置參數(shù)范圍內(nèi)才會(huì)分發(fā)給他,當(dāng)我們點(diǎn)擊原來(lái)的位置的時(shí)候,才會(huì)響應(yīng)。如果我們想讓他在移動(dòng)后的位置響應(yīng),也就是說(shuō)更改view的位置參數(shù),那么可以使用屬性動(dòng)畫(huà)。

  val customView = findViewById<View>(R.id.view_my)
        findViewById<Button>(R.id.btn_move).setOnClickListener {
            ObjectAnimator.ofFloat(customView,"translationX",0f,300f).setDuration(1000).start()
        }

5.接下來(lái)我們看看第五種方法,scrollTo與ScrollBy

scrollTo(x,y)表示移動(dòng)到一個(gè)具體的坐標(biāo)點(diǎn),而scrollBy(dx,dy)表示移動(dòng)的增量為dx,dy,其實(shí)我們看源碼就知道scorllBy最終也是計(jì)算出最終坐標(biāo),最終調(diào)用scrollTo的。我們可以看下源碼:

 public void scrollBy(int x, int y) {
        scrollTo(mScrollX + x, mScrollY + y);
    }

需要注意的是,scrollTo和ScrollBy是移動(dòng)的內(nèi)容。我們?nèi)绻O(shè)置了Bacgroud會(huì)發(fā)現(xiàn)看不出效果??梢愿南拢?/p>

 <com.example.myapplication.views.CustomView
        android:id="@+id/view_my"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center_horizontal"
        android:foreground="@mipmap/cai"/>
  findViewById<Button>(R.id.btn_move).setOnClickListener {
            customView.scrollBy(50,100)
//            ObjectAnimator.ofFloat(customView,"translationX",0f,300f).setDuration(1000).start()
        }

使用foreground。接著看下效果:

最開(kāi)始是這樣的,然后我們點(diǎn)擊下按鈕,變成了這樣:

 是不是發(fā)現(xiàn)了神奇的現(xiàn)象。再點(diǎn)擊一次:

好家伙,快看不到了。什么原因呢?這里有兩個(gè)奇怪的現(xiàn)象,第一個(gè)是他似乎移動(dòng)的方向是相反的,第二個(gè)是是越來(lái)越小。準(zhǔn)確說(shuō),是好像并不是縮小,而是只能看到局部。

這是因?yàn)閰⒄瘴锏牟煌瑢?dǎo)致的。假設(shè),我們把手機(jī)屏幕比喻為放大鏡。下面的內(nèi)容當(dāng)作報(bào)紙。我們調(diào)用這個(gè)方法,實(shí)際是使放大鏡相對(duì)于這個(gè)view,往下移動(dòng)了,放大鏡看不到的地方不并是不存在了。所以他才看起來(lái)像是往左上移動(dòng)了。然后,因?yàn)槲覀兪莾?nèi)容在移動(dòng),所以View本身并沒(méi)有移動(dòng),所以他的前景色就看不到了。

我們?nèi)绻M茏龅诫S手指移動(dòng),可以在move方法里這樣修改:

MotionEvent.ACTION_MOVE -> {
                //計(jì)算移動(dòng)距離
                val offsetX = x -lastX
                val offsetY = y -lastY
 
                val parentView = parent as View
                parentView.scrollBy(-offsetX,-offsetY)
}

這里,取相反的值,崗剛說(shuō)了,是因?yàn)閰⒖嘉?,?huì)導(dǎo)致視覺(jué)相反。而要取他的parent,是因?yàn)檫@個(gè)view就屬于他的parent的內(nèi)容。所以,他就可以進(jìn)行相關(guān)的動(dòng)作。

6.來(lái)看第六種方法Scroller

我們?cè)谑褂胹crollTo/scrollBy方法進(jìn)行滑動(dòng)的時(shí)候,這個(gè)過(guò)程是瞬間就完成的。所以體驗(yàn)就不是太好。我們可以使用scroller來(lái)實(shí)現(xiàn)有過(guò)度效果的滑動(dòng)。但是scroller本身是不能實(shí)現(xiàn)View的滑動(dòng),它需要與View的computeScroller 方法配合才能實(shí)現(xiàn)彈性滑動(dòng)效果。來(lái)看代碼實(shí)現(xiàn):

首先,定義個(gè)成員變量:

 private val mScroller = Scroller(context)

接下來(lái),我們重寫(xiě)computeScroll方法,系統(tǒng)會(huì)繪制View的時(shí)候,在draw方法里調(diào)用該方法。

我們先在CustomView里定義個(gè)方法:

 fun smoothScrollTo(destX:Int, destY:Int){
        val scrollX = scrollX
        val delta  = destX - scrollX
        mScroller.startScroll(scrollX,0,delta,2,2000)
        invalidate()
    }

這個(gè)方法是提供給外部調(diào)用的,參數(shù)是想偏移的X和Y。

scrollX是目前已經(jīng)滑動(dòng)值,拿目的要偏移的減去已經(jīng)滑動(dòng)的,就是還剩下的。調(diào)用scroller.startScroll()方法。設(shè)置一個(gè)duration。我們調(diào)用invalidate()就開(kāi)始重繪。這個(gè)時(shí)候就會(huì)調(diào)用view的computeScroller方法。這里面我們調(diào)用父空間viewGroup的scroollTo方法,來(lái)獲取當(dāng)前的一小段滑動(dòng)值,然后行成小的滑動(dòng)。接著調(diào)用invalidate()方法不斷的重繪。

 override fun computeScroll() {
        super.computeScroll()
        if (mScroller.computeScrollOffset()) {
            (parent as View).scrollTo(mScroller.currX,mScroller.currY)
            invalidate()
        }
    }

最后,我們?cè)赼ctivity里調(diào)用:

 customView.smoothScrollTo(-400,0)

向右邊平移400。

總結(jié)

到此這篇關(guān)于android學(xué)習(xí)筆記之View的滑動(dòng)的文章就介紹到這了,更多相關(guān)android View的滑動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論