LoginSignup
4
1

More than 3 years have passed since last update.

*Android*ピンチイン・ピンチアウトを検知する

Posted at

はじめに

こんにちは.今回は,Androidでピンチ操作を検知しようと思います.ピンチイン・ピンチアウトを判別する方法について解説します.

前提

開発環境は以下の通りです.
*Android Studio 4.0.1
*targetSdkVersion 28
*Google Nexus 5x

ピンチイン・ピンチアウトの判別

ピンチ操作の検知には,ScaleGestureDetectorを使用します.第一引数にContext,第二引数には,OnScaleGestureListenerインタフェースを実装したクラスのオブジェクトを指定します.今回は,匿名クラスを使用して,インタフェースを実装します.

this.scaleGestureDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.OnScaleGestureListener() {
    // ピンチ操作中に繰り返し呼ばれる
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        return true;
    }
    // ピンチ操作を開始したときに呼ばれる
    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }
    //  ピンチ操作を終了したときに呼ばれる
    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
    }
});

次に,onTouchEventメソッドを用意し,onTouchEventメソッドが呼ばれた際に、ScaleGestureDetector.onTouchEventが呼ばれるようにすることで,ピンチ操作を検知することができます.

@Override
public boolean onTouchEvent(MotionEvent motionEvent){
    this.scaleGestureDetector.onTouchEvent(motionEvent);
    return true;

ピンチ操作を検知できたら,次にピンチイン・ピンチアウトを判別します.ピンチ操作では,画面に2本の指を触れています.指間の距離を測定するメソッドとしてgetCurrentSpan()が用意されているので,それを使用します.指間の距離の変化を利用することで,ピンチイン・ピンチアウトを判別することができます.ピンチイン・ピンチアウトの誤判定を防ぐために,閾値として指間の距離xを指定し,x以上変化した場合にピンチイン・ピンチアウトの判定を行います.

@Override
public boolean onScale(ScaleGestureDetector detector) {          
    distance_current = detector.getCurrentSpan();
    return true;
}

ピンチ操作中の指間の距離の正確性

ピンチ操作中の指間の距離を取得するにはgetCurrentSpan()を使用します.取得した指間の距離は,スマートフォンの画面の対角線の長さより小さいはずです.誤った距離を取得してしまった場合に取り除くために,スマートフォンの対角線の長さと比較して,それより小さいもののみを指間の距離とします.ここでは,Navigation bar を除いた画面の領域から,対角線の長さを求めます.画面の対角線の長さを取得するには以下のようにします.

WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
Point size = new Point();
Display disp = wm.getDefaultDisplay();
disp.getSize(size);
screen_width = this.size.x;
screen_height = this.size.y;
//対角線の長さを求める
int screen_diagonal = (int) Math.sqrt((int)(Math.pow(screen_width, 2)) + (int)(Math.pow(screen_height, 2)));

サンプルコード

AndroidManifest.xml
?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
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">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java
package com.example.myapplication3;

import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Point;
import android.os.Bundle;
import android.view.Display;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.WindowManager;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private ScaleGestureDetector scaleGestureDetector;
    private long time_elapsed;
    private long time_start;
    private long time_current;
    private float distance_current;
    private float distance_start;
    private Boolean flg_pinch_out;
    private Boolean flg_pinch_in;
    private WindowManager wm;
    private Display disp;
    private Point size;
    private int screen_width;
    private int screen_height;
    //画面の対角線の長さ
    private int screen_diagonal;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        this.size = new Point();
        this.disp = wm.getDefaultDisplay();
        this.disp.getSize(size);
        screen_width = this.size.x;
        screen_height = this.size.y;
        //対角線の長さを求める
        screen_diagonal = (int) Math.sqrt((int)(Math.pow(screen_width, 2)) + (int)(Math.pow(screen_height, 2)));
        this.scaleGestureDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.OnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                time_current = detector.getEventTime();
                time_elapsed = time_current - time_start;
                if (time_elapsed >= 0.5){
                    distance_current = detector.getCurrentSpan();
                    if (distance_start == 0){
                        distance_start = distance_current;
                    }
                    flg_pinch_out = (distance_current - distance_start) > 300;
                    flg_pinch_in = (distance_start - distance_current) > 300;
                    if (flg_pinch_out){
                        Toast.makeText(getApplicationContext(), "Pinch out", Toast.LENGTH_LONG).show();
                        time_start = time_current;
                        distance_start = distance_current;
                    }
                    else if (flg_pinch_in){
                        Toast.makeText(getApplicationContext(), "Pinch in", Toast.LENGTH_LONG).show();
                        time_start = time_current;
                        distance_start = distance_current;
                    }
                    else {
                        //pass
                    }
                }
                return true;
            }


            @Override
            public boolean onScaleBegin(ScaleGestureDetector detector) {
                distance_start = detector.getEventTime();
                if (distance_start > screen_diagonal){
                    distance_start = 0;
                }
                time_start = detector.getEventTime();
                return true;
            }

            @Override
            public void onScaleEnd(ScaleGestureDetector detector) {
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent motionEvent){
        this.scaleGestureDetector.onTouchEvent(motionEvent);
        return true;
    }
}
4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1