「Android」MotionEventで設定したTouch On/off機能


📌 概要
PCとは異なり、携帯電話やタブレットで最も一般的な操作方法はタッチです.この場合、指やペンはタッチしなくても認識できないが、マウスなどのツールは画面にカーソルを表示するので、タッチすることなく認識できる.このときTouchON/OFFをするといつも特定のボタンを押さなくても絵が描けますよね?
📌 コード#コード#
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.touchonoff.DrawingView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/drawingView"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">
    </com.example.touchonoff.DrawingView>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/switchButton"
        android:text="switch"
        app:layout_constraintVertical_bias="0.9"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent">
    </Button>

</androidx.constraintlayout.widget.ConstraintLayout>
これは、DrawingViewと呼ばれるカスタムビューを画面全体に配置し、ユーザーが画面上でマウスの電源オン/オフ操作を行うことができるボタンを含む簡単な構造です.
DrawingView.java
package com.example.touchonoff;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

import java.util.ArrayList;

public class DrawingView extends View {
    private Path drawPath;
    private Paint drawPaint;
    private Paint canvasPaint;
    private int paintColor;
    private Canvas drawCanvas;
    private Bitmap canvasBitmap;

    private ArrayList<Path> paths = new ArrayList<Path>();
    private float mX, mY;
    private static final float BRUSH_SIZE = 20;
    private static final float TOUCH_TOLERANCE = 4;

    public DrawingView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public void init() {
        paintColor = 0xFF000000;
        drawPath = new Path();
        drawPaint = new Paint();
        drawPaint.setColor(Color.BLACK);
        drawPaint.setStrokeWidth(BRUSH_SIZE);
        drawPaint.setAntiAlias(true);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);
        canvasPaint = new Paint(Paint.DITHER_FLAG);
    }

    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
        canvas.drawPath(drawPath, drawPaint);
    }

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        drawCanvas = new Canvas(canvasBitmap);
    }

    public boolean onTouchEvent(MotionEvent motionEvent) {
        float touchX = motionEvent.getX();
        float touchY = motionEvent.getY();

        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchStart(touchX, touchY);
                invalidate();
                break;

            case MotionEvent.ACTION_MOVE:
                touchMove(touchX, touchY);
                invalidate();
                break;

            case MotionEvent.ACTION_UP:
                touchUp();
                invalidate();
                break;

            default:
                return false;
        }
        invalidate();
        return true;
    }

    public void touchStart(float x, float y) {
        drawPath.reset();
        drawPath.moveTo(x, y);
        mX = x;
        mY = y;
    }

    private void touchUp() {
        drawPath.lineTo(mX, mY);
        drawCanvas.drawPath(drawPath, drawPaint);
        paths.add(drawPath);
        drawPath = new Path();
    }

    private void touchMove(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
            drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
            mX = x;
            mY = y;
        }
    }
}
ビューに画像を描画できるCustom Viewが作成されました.タッチペイントコードはここです。です.TouchEventの動作に慣れていない場合は、onTouchEvent()に注目してください.onTouchEvent()は、Androidが特定のビュー内のタッチポイントを識別する方法の1つである.1本の指または1本のペンでタッチする一般的なonTouchEvent()を使用して、次のアクティビティを1つのグループに設定します.
  • MotionEvent.ACTION_DOWN:移動イベント
  • 、指タッチスクリーンで認識
  • MotionEvent.ACTION_MOVE:指をタッチして画面に移動することで、動作イベント
  • を認識することができる.
  • MotionEvent.ACTION_UP:イベント
  • を移動し、画面から指を離すと認識できます.
    タッチすると、システムは動きイベントのタイプを自動的に区別し、パラメータとしてonTouchEvent()を実行する.ただし、タッチを必要とせずにタッチ入力を行う場合は、3段階のアクティブイベントを独立して設定し、TouchEventに反映する必要があります.
    MainActivity.java
    package com.example.touchonoff;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.os.Bundle;
    import android.os.SystemClock;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.Button;
    
    public class MainActivity extends AppCompatActivity {
    
        DrawingView drawingView;
        Button switchButton;
    
        private float touchX;
        private float touchY;
        private long currentTime;
        private boolean isTouchMode = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            drawingView = findViewById(R.id.drawingView);
            switchButton = findViewById(R.id.switchButton);
    
            drawingView.setOnGenericMotionListener(new View.OnGenericMotionListener() {
                @Override
                public boolean onGenericMotion(View v, MotionEvent event) {
                    touchX = event.getX(); // 커서가 이동한 지점의 X좌표
                    touchY = event.getY(); // 커서가 이동한 지점의 Y좌표
                    if (event.getAction() == MotionEvent.ACTION_HOVER_MOVE) {
                        if (isTouchMode) {
                            currentTime = SystemClock.uptimeMillis(); // 이벤트가 발생한 시간
                            MotionEvent moveMotionEvent = MotionEvent.obtain(currentTime, currentTime+1000,
                            	MotionEvent.ACTION_MOVE, touchX, touchY, 0); // MotionEvent 생성
                            drawingView.dispatchTouchEvent(moveMotionEvent); // MotionEvent 수동 반영
                        }
                    }
                    return false;
                }
            });
    
            switchButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    isTouchMode ^= true;
                    if (isTouchMode) {
                        currentTime = SystemClock.uptimeMillis();
                        MotionEvent downMotionEvent = MotionEvent.obtain(currentTime, currentTime+1000, 
                        	MotionEvent.ACTION_DOWN, touchX, touchY, 0);
                        drawingView.dispatchTouchEvent(downMotionEvent);
                    }
                    else {
                        MotionEvent upMotionEvent = MotionEvent.obtain(downTime, eventTime+1000, 
                        	MotionEvent.ACTION_UP, touchX, touchY, 0);
                        drawingView.dispatchTouchEvent(upMotionEvent);
                    }
                }
            });
        }
    
    }
    TouchがOnの場合、カーソルが移動するたびにACTION_MOVEのように位置を感知して画像を描きますよね?したがって、drawingViewは、カーソルの移動を検出するためにGenericMotionListerを設定する必要がある.event.getX()およびevent.getY()により、ビュー内でカーソルが移動するX座標およびY座標をロードすることができる.
    モーションイベントを直接作成するには、モーションの位置に加えて、モーションの開始時刻、終了時刻、モーションタイプ、および準安定状態が必要です.アプリケーションの現在の進捗をSystemClock.uptimeMillis()で取得し、開始時間を現在の進捗に設定し、終了時間を現在の進捗から0.1秒後に設定します.必要な動作MotionEvent.ACTION_MOVEおよび準安定性(metadataを使用しないため0に設定された)を新しいアニメーションイベントに追加し、dispatchEvent()によって手動でイベントをdrawingViewに適用する.これは、DrawingViewのonTouchEvent()によって作成されたアニメーションイベントをパラメータとして実行します.
    Touchが特定のトリガによってのみオン/オフである場合、指が最初に接触したACTION_DOWNおよび指が最終的に離れたACTION_UPは、トリガによって達成されなければならない.したがって、switchButtonでは、ClickListerをクリックするたびに、ACTION_DOWNまたはACTION_UPの動作イベントがdrawingViewに適用されるようにタッチモードが変更される.
    📌 の最後の部分
    ここです。では、プロジェクト全体を表示できます.
    記事作成時に参照するサイト.
    Tistory
    Stackoverflow
    Android