最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

前言

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

GestureDetector

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

這三個接口的方法如下。

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 負責監(jiān)聽手勢,而 OnDoubleTapListenerOnGestureListener 用于開發(fā)者自己去處理對應(yīng)手勢的反饋

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 事件時被調(diào)用的,其的返回值決定了View是否消費該事件,一般我們肯定是需要消費該事件的,因此其值為true.

public boolean onDown() {
    return true;
}

onShowPress方法

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

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

onLongPress 方法

用于檢測長按事件的,即手指按下后不抬起,在一段時間后會觸發(fā)該事件。

@Override 
public void onLongPress(MotionEvent e) {
}

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

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

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

gestureDetector.setIsLongpressEnabled(false);

onSingleTapUp 方法

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

onSingleTapUP的返回值不是太重要,不過一般消費了就還是返回ture吧。

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

單擊事件觸發(fā):

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

雙擊事件觸發(fā):

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

可以看出來這三個事件還是有所不同的,根據(jù)自己實際需要進行使用即可

onScroll

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

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

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

onFling

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

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

Fling 中文直接翻譯過來就是一扔、拋、甩,最常見的場景就是在 ListView 或者 RecyclerView 上快速滑動時手指抬起后它還會滾動一段時間才會停止。onFling 就是檢測這種手勢的。

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

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

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

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

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)聽單擊事件,而onDoubleTap用于監(jiān)聽雙擊事件。這兩個回調(diào)函數(shù)是互斥的。

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

onSingleTapConfigrmed 適合于在 既檢測單擊事件也檢測雙擊時間時使用。

但是如果只是檢測單擊事件,onSingleTapUp更合適,onSingleTapConfigrmed會讓用戶明顯感覺到延遲。

需要注意的是 onDoubleTap 事件并不是第二次抬起時觸發(fā)的,而是第二次手觸摸到屏幕時即(第二次ACTION_DOWN)事件時就會觸發(fā)該事件,如果要保證在第二次抬起時才觸發(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;
}

雙擊時,onDoubleTapEvent 將會在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

需要注意的是不論是雙擊還是單擊,只要按下長時間未動且未抬起,都會觸發(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 方法中,曾說過 使用velocityX ,velocityY 兩個參數(shù)可以實現(xiàn) View的滑動效果.

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

示例

此處用一個可拖拉滑動的小圓球作為示例.

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 //球直徑長度
    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)
        //中間位置畫個圓
        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
        //移動不能超過圓的一半
        offsetX = min(offsetX,width.toFloat()/2)
        offsetX = max(offsetX,-width.toFloat()/2)
        //移動不能超過圓的一半
        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 {
        //限制滑動不能超過一小圓的一半
        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) {
    //實現(xiàn)邏輯省略,有興趣的可以自己去看代碼
}
參數(shù)簡介
startX、startY開始滑動的X(Y)軸位置
velocityX、velocityY在 X(Y) 軸上的運動速度(像素/秒)。
minX、maxX滑動時X軸的兩個邊界值,滑動時一旦到達邊界值,則立刻停止
minY、maxY滑動時Y軸的兩個邊界值,滑動時一旦到達邊界值,則立刻停止
overX、overY在滑動時,可超出的滑動值,可超過邊界值,不過超過邊界值后,又會重新滑動回來
  • startScroll 方法

startScroll的滾動默認以一種粘性液體的效果進行滾動。

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ù)簡介
startX、startY開始滑動的X(Y)軸位置
dx、dy滾動到達的目標位置
duration滾動花費時間(單位ms),如果不指定默認時250ms

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

相關(guān)文章

  • 在Android項目中使用AspectJ的詳細攻詻

    在Android項目中使用AspectJ的詳細攻詻

    AspectJ是實現(xiàn)AOP的其中一款框架,內(nèi)部通過處理字節(jié)碼實現(xiàn)代碼注入,文章給大家提到AspectJ基礎(chǔ)語法和集成AspectJ的方式,對AspectJ在android中使用教程感興趣的朋友跟隨小編一起看看吧
    2021-06-06
  • Android簡單實現(xiàn)文件下載

    Android簡單實現(xiàn)文件下載

    這篇文章主要為大家詳細介紹了Android簡單實現(xiàn)文件下載,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Kotlin try catch異常處理i詳解

    Kotlin try catch異常處理i詳解

    在 Kotlin 協(xié)程當中,我們通常把異常分為兩大類,一類是取消異常(CancellationException),另一類是其他異常。在 Kotlin 協(xié)程當中,這兩種異常的處理方式是不一樣的
    2022-11-11
  • Android實現(xiàn)靜音檢測功能

    Android實現(xiàn)靜音檢測功能

    這篇文章主要為大家詳細介紹了Android實現(xiàn)靜音檢測功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • Android中socketpair雙向通信詳解

    Android中socketpair雙向通信詳解

    這篇文章主要為大家詳細介紹了Android socketpair雙向通信的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • 基于AnDroid FrameLayout的使用詳解

    基于AnDroid FrameLayout的使用詳解

    本篇文章是對AnDroid FrameLayout的使用進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • 一文帶你了解Android?Flutter中Transform的使用

    一文帶你了解Android?Flutter中Transform的使用

    flutter的強大之處在于,可以對所有的widget進行Transform,因此可以做出非常酷炫的效果。本文就來大家了解一下Transform的具體使用,感興趣的可以了解一下
    2023-01-01
  • Android xmlns 的作用及其自定義實例詳解

    Android xmlns 的作用及其自定義實例詳解

    這篇文章主要介紹了 Android xmlns 的作用及其自定義實例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • Android studio 3.5.2安裝圖文教程詳解

    Android studio 3.5.2安裝圖文教程詳解

    這篇文章主要介紹了Android studio 3.5.2安裝教程,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • Android view更改背景資源與padding消失的問題解決辦法

    Android view更改背景資源與padding消失的問題解決辦法

    這篇文章主要介紹了Android view更改背景資源與padding消失的問題解決辦法的相關(guān)資料,需要的朋友可以參考下
    2017-04-04

最新評論

千阳县| 承德市| 抚宁县| 藁城市| 威远县| 秦皇岛市| 曲水县| 龙岩市| 长治市| 鄂州市| 临沭县| 吉隆县| 七台河市| 饶平县| 邯郸市| 密山市| 凤台县| 南乐县| 普宁市| 阳东县| 独山县| 黎平县| 平罗县| 黎城县| 宁河县| 阳原县| 承德县| 沙雅县| 清苑县| 秦安县| 甘谷县| 百色市| 咸丰县| 德清县| 汾阳市| 华亭县| 团风县| 滦平县| 宣威市| 方城县| 赣州市|