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

View觸發(fā)機(jī)制API實(shí)現(xiàn)GestureDetector OverScroller詳解

 更新時(shí)間:2022年11月22日 17:05:21   作者:陳序猿_Android  
這篇文章主要為大家介紹了View觸發(fā)機(jī)制API實(shí)現(xiàn)GestureDetector OverScroller詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

前一篇文章講了View的觸發(fā)反饋機(jī)制的原理,對(duì)于一個(gè)自定義View而言,手勢(shì)的處理都是重寫onTouchEvent函數(shù),或者通過(guò)setOnTouchEventListener方法捕捉手勢(shì)。但是手勢(shì)的處理,如滑動(dòng)、觸摸、雙擊等檢測(cè)對(duì)應(yīng)的檢測(cè)也并不是那么簡(jiǎn)單,自己一個(gè)個(gè)造輪子也過(guò)于麻煩,萬(wàn)幸的是google早已經(jīng)給開(kāi)發(fā)者提供了手勢(shì)捕捉的類- GestureDetector。通過(guò)這個(gè)類我們可以識(shí)別很多的手勢(shì),主要是通過(guò)他的onTouchEvent(event)方法完成了不同手勢(shì)的識(shí)別。雖然他能識(shí)別手勢(shì),但是不同的手勢(shì)要怎么處理,應(yīng)該是提供給程序員實(shí)現(xiàn)的。

GestureDetector

GestureDetector 中一共有三種主要的回調(diào)接口 ,OnGestureListenerOnDoubleTapListener、OnContextClickListener

這三個(gè)接口的方法如下。

public interface OnGestureListener {
        boolean onDown(MotionEvent e);
        void onShowPress(MotionEvent e);
        boolean onSingleTapUp(MotionEvent e);
        boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
        void onLongPress(MotionEvent e);
        boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}
public interface OnDoubleTapListener {
    boolean onSingleTapConfirmed(MotionEvent e);
    boolean onDoubleTap(MotionEvent e);
    boolean onDoubleTapEvent(MotionEvent e);
}
public interface OnContextClickListener {
    boolean onContextClick(MotionEvent e);
}

GestureDetector 使用

GestureDector 負(fù)責(zé)監(jiān)聽(tīng)手勢(shì),而 OnDoubleTapListenerOnGestureListener 用于開(kāi)發(fā)者自己去處理對(duì)應(yīng)手勢(shì)的反饋

package com.example.androidtemp.view;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.OverScroller;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
public class TouchView extends View implements GestureDetector.OnGestureListener,GestureDetector.OnDoubleTapListener{
    private static final String TAG = "TouchView";
    GestureDetector gestureDetector = null;
    public TouchView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        gestureDetector = new GestureDetector(context,this);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }
    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        Log.i(TAG, "onSingleTapConfirmed: ");
        return false;
    }
    @Override
    public boolean onDoubleTap(MotionEvent e) {
        Log.i(TAG, "onDoubleTap: ");
        return false;
    }
    @Override
    public boolean onDoubleTapEvent(MotionEvent e) {
        Log.i(TAG, "onDoubleTapEvent: ");
        return false;
    }
    @Override
    public boolean onDown(MotionEvent e) {
        Log.d(TAG, "onDown: ");
        return true;
    }
    @Override
    public void onShowPress(MotionEvent e) {
        Log.i(TAG, "onShowPress: ");
    }
    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        Log.i(TAG, "onSingleTapUp: ");
        return false;
    }
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        Log.i(TAG, "onScroll: ");
        return false;
    }
    @Override
    public void onLongPress(MotionEvent e) {
        Log.i(TAG, "onLongPress: ");
    }
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        Log.i(TAG, "onFling: ");
        return false;
    }
}

onDown方法

onDown 方法是在ACTION_DOWN 事件時(shí)被調(diào)用的,其的返回值決定了View是否消費(fèi)該事件,一般我們肯定是需要消費(fèi)該事件的,因此其值為true.

public boolean onDown() {
    return true;
}

onShowPress方法

@Override
public void onShowPress(MotionEvent e) {
    //進(jìn)行控件顏色的改變或其他一些動(dòng)作
}

onShowPress 是用戶按下時(shí)的一種回調(diào),主要作用是用于給用戶一種按壓下的狀態(tài),可以在該回調(diào)中讓控件顏色改變或進(jìn)行一些動(dòng)作。需要注意的是,onShowPress 方法不是立即回調(diào)的,在手指觸碰后,在100ms左右后才會(huì)回調(diào)。在這100ms內(nèi)如果手指抬起或滾動(dòng),該回調(diào)方法不會(huì)被觸發(fā)。在前一篇文章View事件分發(fā)機(jī)制 中提到過(guò)自定義View 默認(rèn)的super.onTouchEvent 實(shí)現(xiàn)中,按壓狀態(tài)也是有一個(gè)預(yù)按壓狀態(tài)的檢測(cè),此處的onShowPress的回調(diào)機(jī)制也是同理。

onLongPress 方法

用于檢測(cè)長(zhǎng)按事件的,即手指按下后不抬起,在一段時(shí)間后會(huì)觸發(fā)該事件。

@Override 
public void onLongPress(MotionEvent e) {
}

onLongPress 回調(diào)被觸發(fā)前 onShowPress 一定會(huì)被觸發(fā)。

需要注意的是 onLongPress一旦被觸發(fā),其他事件都不會(huì)被觸發(fā)了。

不過(guò),onLongPress事件可以被禁止使用,通過(guò)如下代碼設(shè)置,即不會(huì)觸發(fā)長(zhǎng)按事件

gestureDetector.setIsLongpressEnabled(false);

onSingleTapUp 方法

@Override
public boolean onSingleTapUp(MotionEvent e) {
    return false;
}

onSingleTapUP的返回值不是太重要,不過(guò)一般消費(fèi)了就還是返回ture吧。

onSingleTapUp的意思顧名思義,即在 手指抬起時(shí)觸發(fā),不過(guò)他跟一般的onClick、以及onSingleTapConfirmed有一定區(qū)別

單擊事件觸發(fā):

GCS: onSingleTapUp
GCS: onClick
GCS: onSingleTapConfirmed
類型觸發(fā)次數(shù)摘要
onSingleTapUp1單擊抬起
onSingleTapConfirmed1單擊確認(rèn)
onClick1單擊事件

雙擊事件觸發(fā):

onSingleTapUp
onClick
onDoubleTap 
onClick
類型觸發(fā)次數(shù)摘要
onSingleTapUp1在雙擊的第一次抬起時(shí)觸發(fā)
onSingleTapConfirmed0雙擊發(fā)生時(shí)不會(huì)觸發(fā)。
onClick2在雙擊事件時(shí)觸發(fā)兩次。

可以看出來(lái)這三個(gè)事件還是有所不同的,根據(jù)自己實(shí)際需要進(jìn)行使用即可

onScroll

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float 
        distanceY) {
    return true;
}

onScroll 方法是用于監(jiān)聽(tīng)手指的滑動(dòng)的,e1是第一次ACTION_DOWN的事件,e2是當(dāng)前滾動(dòng)事件。distanceX、distanceY記錄了手指在x、y軸滑動(dòng)的距離。

需要注意的時(shí),該滑動(dòng)距離記錄的是上次滑動(dòng)回調(diào)與這次回調(diào)之間的距離差值。且還有一個(gè)有意思的注意事項(xiàng),該差值是 lastEvent-curEvent 得到的,這與正常的邏輯行為不太一致,不過(guò)google就這樣干了,所以當(dāng)我們?cè)谟?jì)算滑動(dòng)偏移量時(shí)需要對(duì) distanceX、distancesY進(jìn)行一個(gè) 相減的操作而不是相加。

onFling

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                       float velocityY) {
    return true;
}

用戶手指在屏幕快速滑動(dòng)后,在抬起時(shí)(ACTION_UP)觸發(fā)該事件。

Fling 中文直接翻譯過(guò)來(lái)就是一扔、拋、甩,最常見(jiàn)的場(chǎng)景就是在 ListView 或者 RecyclerView 上快速滑動(dòng)時(shí)手指抬起后它還會(huì)滾動(dòng)一段時(shí)間才會(huì)停止。onFling 就是檢測(cè)這種手勢(shì)的。

四個(gè)參數(shù)的介紹如下

參數(shù)簡(jiǎn)介
e1手指按下時(shí)的 Event。
e2手指抬起時(shí)的 Event。
velocityX在 X 軸上的運(yùn)動(dòng)速度(像素/秒)。
velocityY在 Y 軸上的運(yùn)動(dòng)速度(像素/秒)。

利用 velocityX、velocityY 參數(shù)可以實(shí)現(xiàn)一個(gè)具有一定初速度的滑動(dòng),之后該速度隨著滑動(dòng)衰減,直到停止。

一般onFling 可以結(jié)合 OverScroller 實(shí)現(xiàn)一個(gè)均勻減速的滑動(dòng)效果。

overScroller的用法在后方介紹。

onSingleTapConfirmed 和onDoubleTap

public boolean onSingleTapConfirmed(MotionEvent e) {
    return false;
}
public boolean onDoubleTap(MotionEvent e) {
    return false;
}
public boolean onDoubleTapEvent(MotionEvent e) {
    return false;
}

onSingleTapConfirmed用于監(jiān)聽(tīng)單擊事件,而onDoubleTap用于監(jiān)聽(tīng)雙擊事件。這兩個(gè)回調(diào)函數(shù)是互斥的。

onSingleTapConfigrmed的調(diào)用是延遲的,其在 手指按下300ms后觸發(fā)。

onSingleTapConfigrmed 適合于在 既檢測(cè)單擊事件也檢測(cè)雙擊時(shí)間時(shí)使用。

但是如果只是檢測(cè)單擊事件,onSingleTapUp更合適,onSingleTapConfigrmed會(huì)讓用戶明顯感覺(jué)到延遲。

需要注意的是 onDoubleTap 事件并不是第二次抬起時(shí)觸發(fā)的,而是第二次手觸摸到屏幕時(shí)即(第二次ACTION_DOWN)事件時(shí)就會(huì)觸發(fā)該事件,如果要保證在第二次抬起時(shí)才觸發(fā)該事件,就需要使用onDoubleTapEvent方法了

onDoubleTapEvent

@Override
public boolean onDoubleTapEvent(MotionEvent e) {
    Log.i(TAG, "onDoubleTapEvent: event:" + e.getActionMasked());
    switch (e.getActionMasked()) {
        case MotionEvent.ACTION_UP:
            Log.i(TAG, "onDoubleTapEvent: ACTION_UP");
            break;
    }
    return true;
}

雙擊時(shí),onDoubleTapEvent 將會(huì)在onDoubleTap 后觸發(fā).

雙擊觸發(fā)日志:

TouchView: onDown: 
TouchView: onSingleTapUp: 
TouchView: onDoubleTap: 
TouchView: onDoubleTapEvent: event:0(ACTION_DOWN)
TouchView: onDown: 
TouchView: onDoubleTapEvent: event:2(ACTION_MOVE)
TouchView: onDoubleTapEvent: event:2(ACTION_MOVE)
TouchView: onDoubleTapEvent: event:1(ACTION_UP)
TouchView: onDoubleTapEvent: ACTION_UP

需要注意的是不論是雙擊還是單擊,只要按下長(zhǎng)時(shí)間未動(dòng)且未抬起,都會(huì)觸發(fā)onLongPress。

第二次按下后常按再抬起日志

TouchView: onDown: 
TouchView: onSingleTapUp: 
TouchView: onDoubleTap: 
TouchView: onDoubleTapEvent: event:0
TouchView: onDown: 
TouchView: onDoubleTapEvent: event:2
TouchView: onDoubleTapEvent: event:2
TouchView: onDoubleTapEvent: event:2
TouchView: onShowPress: 
TouchView: onDoubleTapEvent: event:2
TouchView: onDoubleTapEvent: event:2
TouchView: onDoubleTapEvent: event:2
TouchView: onLongPress: 
ouchView: onDoubleTapEvent: event:1
TouchView: onDoubleTapEvent: ACTION_UP

OverScroller

onFling 方法中,曾說(shuō)過(guò) 使用velocityX ,velocityY 兩個(gè)參數(shù)可以實(shí)現(xiàn) View的滑動(dòng)效果.

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
                       float velocityY) {
    return true;
}

示例

此處用一個(gè)可拖拉滑動(dòng)的小圓球作為示例.

scroll效果圖

Fling效果圖

代碼如下

package com.example.androidtemp.view
import android.view.View
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.util.Log
import android.view.GestureDetector
import android.view.MotionEvent
import android.widget.OverScroller
import kotlin.math.max
import kotlin.math.min
private const val TAG = "SmallBallView"
class SmallBallView(context: Context?, attrs:AttributeSet?) :View(context,attrs) ,GestureDetector.OnGestureListener{
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val BALL_DIAMETER_SIZE = 100 //球直徑長(zhǎng)度
    private var originOffsetX = 0f
    private var originOffsetY = 0f
    private var offsetX = 0f
    private var offsetY = 0f
    private val gestureDetector = GestureDetector(this.context,this)
    private val scroller = OverScroller(this.context)
    override fun onTouchEvent(event: MotionEvent): Boolean {
        return gestureDetector.onTouchEvent(event);
    }
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        originOffsetX = (w - BALL_DIAMETER_SIZE)/2f
        originOffsetY = (h - BALL_DIAMETER_SIZE)/2f
    }
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        // 偏移
        canvas.translate(offsetX,offsetY)
        //中間位置畫個(gè)圓
        canvas.drawArc(originOffsetX,originOffsetY,originOffsetX + BALL_DIAMETER_SIZE.toFloat(),originOffsetY + BALL_DIAMETER_SIZE.toFloat(),0f,360f,false,paint)
    }
    override fun onDown(e: MotionEvent?): Boolean = true
    override fun onShowPress(e: MotionEvent?) {}
    override fun onSingleTapUp(e: MotionEvent?): Boolean {
        return false
    }
    override fun onLongPress(e: MotionEvent?) {}
    override fun onScroll(
        e1: MotionEvent?,
        e2: MotionEvent?,
        distanceX: Float,
        distanceY: Float
    ): Boolean  {
        Log.i(TAG, "onScroll: ")
        offsetX -= distanceX
        offsetY -= distanceY
        //移動(dòng)不能超過(guò)圓的一半
        offsetX = min(offsetX,width.toFloat()/2)
        offsetX = max(offsetX,-width.toFloat()/2)
        //移動(dòng)不能超過(guò)圓的一半
        offsetY = min(offsetY,height.toFloat()/2)
        offsetY = max(offsetY,-height.toFloat()/2)
        invalidate()
        return true;
    }
    override fun onFling(
        e1: MotionEvent?,
        e2: MotionEvent?,
        velocityX: Float,
        velocityY: Float
    ): Boolean {
        //限制滑動(dòng)不能超過(guò)一小圓的一半
        scroller.fling(offsetX.toInt(),offsetY.toInt(),velocityX.toInt(),velocityY.toInt(),-width/2,width/2,-height/2,height/2)
        postOnAnimation(scrollerRunnable)
        return true;
    }
    private val scrollerRunnable = object :Runnable {
        override fun run() {
            if (scroller.computeScrollOffset()) {
                offsetX = scroller.currX.toFloat()
                offsetY = scroller.currY.toFloat()
                invalidate()
                postOnAnimation(this)
            }
        }
    }
}

OverScroller方法介紹

  • fling 方法
public void fling(int startX, int startY, int velocityX, int velocityY,
            int minX, int maxX, int minY, int maxY) {
    fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
}
public void fling(int startX, int startY, int velocityX, int velocityY,
            int minX, int maxX, int minY, int maxY, int overX, int overY) {
    //實(shí)現(xiàn)邏輯省略,有興趣的可以自己去看代碼
}
參數(shù)簡(jiǎn)介
startX、startY開(kāi)始滑動(dòng)的X(Y)軸位置
velocityX、velocityY在 X(Y) 軸上的運(yùn)動(dòng)速度(像素/秒)。
minX、maxX滑動(dòng)時(shí)X軸的兩個(gè)邊界值,滑動(dòng)時(shí)一旦到達(dá)邊界值,則立刻停止
minY、maxY滑動(dòng)時(shí)Y軸的兩個(gè)邊界值,滑動(dòng)時(shí)一旦到達(dá)邊界值,則立刻停止
overX、overY在滑動(dòng)時(shí),可超出的滑動(dòng)值,可超過(guò)邊界值,不過(guò)超過(guò)邊界值后,又會(huì)重新滑動(dòng)回來(lái)
  • startScroll 方法

startScroll的滾動(dòng)默認(rèn)以一種粘性液體的效果進(jìn)行滾動(dòng)。

public void startScroll(int startX, int startY, int dx, int dy) {
    startScroll(startX, startY, dx, dy, DEFAULT_DURATION);//DEFAULT_DURATION 250 ms
}
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
    mMode = SCROLL_MODE;
    mScrollerX.startScroll(startX, dx, duration);
    mScrollerY.startScroll(startY, dy, duration);
}
參數(shù)簡(jiǎn)介
startX、startY開(kāi)始滑動(dòng)的X(Y)軸位置
dx、dy滾動(dòng)到達(dá)的目標(biāo)位置
duration滾動(dòng)花費(fèi)時(shí)間(單位ms),如果不指定默認(rèn)時(shí)250ms

以上就是View觸發(fā)機(jī)制API實(shí)現(xiàn)GestureDetector OverScroller詳解的詳細(xì)內(nèi)容,更多關(guān)于View觸發(fā)機(jī)制API的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Flutter系列重學(xué)Container示例詳解

    Flutter系列重學(xué)Container示例詳解

    這篇文章主要為大家介紹了Flutter系列重學(xué)Container示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • Android自定義滑動(dòng)接聽(tīng)電話控件組實(shí)例

    Android自定義滑動(dòng)接聽(tīng)電話控件組實(shí)例

    這篇文章主要介紹了Android自定義滑動(dòng)接聽(tīng)電話控件組,接聽(tīng)電話可以左右滑動(dòng),感興趣的小伙伴們可以參考一下。
    2016-10-10
  • 使用Android系統(tǒng)提供的DownloadManager來(lái)下載文件

    使用Android系統(tǒng)提供的DownloadManager來(lái)下載文件

    本篇文章主要介紹了使用Android系統(tǒng)提供的DownloadManager來(lái)下載文件,可以將長(zhǎng)時(shí)間的下載任務(wù)交給系統(tǒng),完全由系統(tǒng)管理,有需要的可以了解下。
    2016-11-11
  • Android 實(shí)現(xiàn)滑動(dòng)方法總結(jié)

    Android 實(shí)現(xiàn)滑動(dòng)方法總結(jié)

    這篇文章主要介紹了Android 實(shí)現(xiàn)滑動(dòng)方法總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • Android 仿支付寶密碼輸入框效果

    Android 仿支付寶密碼輸入框效果

    模仿支付寶輸入效果,實(shí)現(xiàn)很簡(jiǎn)單,就是畫個(gè)矩形框和圓形,其他的通過(guò)組合view來(lái)實(shí)現(xiàn)所有功能,雖然簡(jiǎn)單但是封裝起來(lái),方便以后使用,也分享一下
    2016-12-12
  • Android 修改Preferences默認(rèn)樣式的步驟

    Android 修改Preferences默認(rèn)樣式的步驟

    這篇文章主要介紹了Android 修改Preferences默認(rèn)樣式的步驟,幫助大家更好的理解和學(xué)習(xí)使用Android開(kāi)發(fā),感興趣的朋友可以了解下
    2021-04-04
  • Android手勢(shì)密碼實(shí)現(xiàn)實(shí)例代碼

    Android手勢(shì)密碼實(shí)現(xiàn)實(shí)例代碼

    本篇文章主要介紹了Android手勢(shì)密碼實(shí)現(xiàn)實(shí)例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-04-04
  • 自定義View系列之kotlin繪制手勢(shì)設(shè)置溫度控件的方法

    自定義View系列之kotlin繪制手勢(shì)設(shè)置溫度控件的方法

    這篇文章主要給大家介紹了關(guān)于自定義View系列之kotlin繪制手勢(shì)設(shè)置溫度控件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • Android7.0中關(guān)于ContentProvider組件詳解

    Android7.0中關(guān)于ContentProvider組件詳解

    本文描述了Android7.0中關(guān)于ContentProvider組件實(shí)現(xiàn)原理以及ContentProvider發(fā)布者和調(diào)用者這兩在Framework層是如何實(shí)現(xiàn)的。
    2017-11-11
  • Android架構(gòu)組件Room指南

    Android架構(gòu)組件Room指南

    Room是Google推出的Android架構(gòu)組件庫(kù)中的數(shù)據(jù)持久化組件庫(kù), 也可以說(shuō)是在SQLite上實(shí)現(xiàn)的一套ORM解決方案。下面通過(guò)本文給大家介紹Android架構(gòu)組件Room指南,需要的朋友參考下吧
    2017-12-12

最新評(píng)論