美文网首页
Android 手势(Gesture)

Android 手势(Gesture)

作者: gaookey | 来源:发表于2022-02-18 23:22 被阅读0次
    image.png
    public class MainActivity extends AppCompatActivity implements GestureDetector.OnGestureListener {
        // 定义手势检测器变量
        private GestureDetector detector;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // 创建手势检测器
            detector = new GestureDetector(this, this);
        }
    
        // 将该Activity上的触碰事件交给GestureDetector处理
        @Override
        public boolean onTouchEvent(MotionEvent me) {
            return detector.onTouchEvent(me);
        }
    
        // 当触碰事件按下时触发该方法
        @Override
        public boolean onDown(MotionEvent e) {
            Toast.makeText(this, "onDown",
                    Toast.LENGTH_SHORT).show();
            return false;
        }
    
        // 当用户手指在触摸屏上按下,而且还未移动和松开时触发该方法
        @Override
        public void onShowPress(MotionEvent e) {
            Toast.makeText(this, "onShowPress",
                    Toast.LENGTH_SHORT).show();
        }
    
        // 用户手指在触摸屏上的轻击事件将会触发该方法
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            Toast.makeText(this, "onSingleTapUp",
                    Toast.LENGTH_SHORT).show();
            return false;
        }
    
        // 当用户手指在屏幕上“滚动”时触发该方法。
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            Toast.makeText(this, "onScroll",
                    Toast.LENGTH_SHORT).show();
            return false;
        }
    
        // 当用户手指在屏幕上长按时触发该方法
        @Override
        public void onLongPress(MotionEvent e) {
            Toast.makeText(this, "onLongPress",
                    Toast.LENGTH_SHORT).show();
        }
    
        // 当用户手指在触摸屏上“拖过”时触发该方法。其中 velocityX、velocityY 代表“拖过”动作在横向、纵向上的速度。
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            Toast.makeText(this, "onFling",
                    Toast.LENGTH_SHORT).show();
            return false;
        }
    }
    
    实例:通过手势缩放图片
    public class MainActivity extends AppCompatActivity {
        // 定义手势检测器变量
        private GestureDetector detector;
        private ImageView imageView;
        // 初始的图片资源
        private Bitmap bitmap;
        // 定义图片的宽、高
        private int width;
        private int height;
        // 记录当前的缩放比
        private float currentScale = 1.0f;
        // 控制图片缩放的Matrix对象
        private Matrix matrix;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // 创建手势检测器
            detector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onFling(MotionEvent event1, MotionEvent event2,
                                       float velocityX, float velocityY)  // ②
                {
                    float vx = velocityX > 4000 ? 4000f : velocityX;
                    vx = velocityX < -4000 ? -4000f : velocityX;
                    // 根据手势的速度来计算缩放比,如果vx>0,则放大图片;否则缩小图片
                    currentScale += currentScale * vx / 4000.0f;
                    // 保证currentScale不会等于0
                    currentScale = currentScale > 0.01 ? currentScale : 0.01f;
                    // 重置Matrix
                    matrix.reset();
                    // 缩放Matrix
                    matrix.setScale(currentScale, currentScale, 160f, 200f);
                    BitmapDrawable tmp = (BitmapDrawable) imageView.getDrawable();
                    // 如果图片还未回收,先强制回收该图片
                    if (!tmp.getBitmap().isRecycled()) // ①
                    {
                        tmp.getBitmap().recycle();
                    }
                    // 根据原始位图和Matrix创建新图片
                    Bitmap bitmap2 = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
                    // 显示新的位图
                    imageView.setImageBitmap(bitmap2);
                    return true;
                }
            });
            imageView = findViewById(R.id.show);
            matrix = new Matrix();
            // 获取被缩放的源图片
            bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.flower);
            // 获得位图宽
            width = bitmap.getWidth();
            // 获得位图高
            height = bitmap.getHeight();
            // 设置ImageView初始化时显示的图片
            imageView.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.flower));
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent me) {
            // 将该Activity上的触碰事件交给GestureDetector处理
            return detector.onTouchEvent(me);
        }
    }
    
    实例:通过多点触碰缩放 TextView
    public class TouchZoomView extends androidx.appcompat.widget.AppCompatTextView {
        // 保存TextView当前的字体大小
        private float textSize;
        // 保存两个手指前一次的距离
        private float prevDist;
    
        public TouchZoomView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        public TouchZoomView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public TouchZoomView(Context context) {
            super(context);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // 只处理触碰点大于等于2(必须是多点触碰)的情形
            if (event.getPointerCount() >= 2) {
                // 获取该TextView默认的字体大小
                if (textSize == 0) {
                    textSize = this.getTextSize();
                }
                // 对于多点触碰事件,需要使用getActionMasked来获取触摸事件类型
                switch (event.getActionMasked()) {
                    // 处理手指按下的事件
                    case MotionEvent.ACTION_POINTER_DOWN:
                        // 计算两个手指之间的距离
                        prevDist = calSpace(event);
                        break;
                    // 处理手指移动的事件
                    case MotionEvent.ACTION_MOVE:
                        // 实时计算两个手指之间的距离
                        float curVDist = calSpace(event);
                        // 根据两个手指之间的距离计算缩放比
                        zoom(curVDist / prevDist);
                        // 为下一次移动的缩放做准备
                        prevDist = curVDist;
                        break;
                }
            }
            return true;
        }
    
        // 缩放字体
        private void zoom(float f) {
            textSize *= f;
            this.setTextSize(px2sp(getContext(), textSize));
        }
    
        // 将px值转换为sp值,保证文字大小不变
        public static int px2sp(Context context, float pxValue) {
            float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
            return (int) (pxValue / fontScale + 0.5f);
        }
    
        // 计算两个手指之间的距离
        private float calSpace(MotionEvent event) {
            // 获取两个点之间X坐标的差值
            float x = event.getX(0) - event.getX(1);
            // 获取两个点之间Y坐标的差值
            float y = event.getY(0) - event.getY(1);
            // 计算两点距离
            return (float) Math.sqrt(x * x + y * y);
        }
    }
    
    实例:通过多点触碰缩放图片
    public class TouchZoomImageView extends View {
        // 定义手势检测器变量
        private GestureDetector detector;
        // 保存该组件所绘制的位图的变量
        private Bitmap mBitmap;
        // 定义对位图进行变换的Matrix
        private Matrix matrix = new Matrix();
        private float prevDist;
        private float totalScaleRadio = 1.0f;
        private float totalTranslateX = 0.0f;
        private float totalTranslateY = 0.0f;
    
        // 为TouchZoomImageView定义不同场景下的构造器
        public TouchZoomImageView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            initDetector();
        }
    
        public TouchZoomImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initDetector();
        }
    
        public TouchZoomImageView(Context context) {
            super(context);
            initDetector();
        }
    
        public void initDetector() {
            detector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onFling(MotionEvent event1, MotionEvent event2,
                                       float velocityX, float velocityY) {
                    // 根据手势的滑动距离来计算图片的位移距离
                    float translateX = event2.getX() - event1.getX();
                    float translateY = event2.getY() - event1.getY();
                    totalTranslateX += translateX;
                    totalTranslateY += translateY;
                    postInvalidate();
                    return false;
                }
            });
        }
    
        // 根据传入的位图资源ID来设置位图
        public void setImage(int resourceId) {
            // 根据位图资源ID来解析图片
            Bitmap bm = BitmapFactory.decodeResource(getResources(),
                    resourceId);
            setImage(bm);
        }
    
        public void setImage(Bitmap bm) {
            this.mBitmap = bm;
            // 当传递过位图来之后,对位图进行初始化操作
            postInvalidate();
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // 获取当前触摸点的个数,如果触碰点大于2,说明是缩放行为
            if (event.getPointerCount() >= 2) {
                // 使用getActionMasked来处理多点触碰事件
                switch (event.getActionMasked()) {  // ①
                    // 处理手指按下的事件
                    case MotionEvent.ACTION_POINTER_DOWN:
                        // 计算两个手指的距离
                        prevDist = calSpace(event);
                        break;
                    // 处理手指移动的事件
                    case MotionEvent.ACTION_MOVE:
    
                        float curDist = calSpace(event);
                        // 计算出当前的缩放值
                        float scaleRatio = curDist / prevDist;
                        totalScaleRadio *= scaleRatio;
                        // 调用onDraw方法,重新绘制界面
                        postInvalidate();
                        // 准备处理下一次缩放行为
                        prevDist = curDist;
                        break;
                }
            }
            // 对于单点触碰,触碰事件交给GestureDetector处理
            else {
                detector.onTouchEvent(event);  // ②
            }
            return true;
        }
    
        @Override
        public void onDraw(Canvas canvas) {
            matrix.reset();
            // 处理缩放
            matrix.postScale(totalScaleRadio, totalScaleRadio);
            // 处理位移
            matrix.postTranslate(totalTranslateX, totalTranslateY);
            canvas.drawBitmap(mBitmap, matrix, null);
        }
    
        // 计算两个手指之间的距离
        private float calSpace(MotionEvent event) {
            // 获取两个点之间X坐标的差值
            float x = event.getX(0) - event.getX(1);
            // 获取两个点之间Y坐标的差值
            float y = event.getY(0) - event.getY(1);
            // 计算两点距离
            return (float) Math.sqrt(x * x + y * y);
        }
    }
    
    通过手势实现翻页效果
    public class MainActivity extends AppCompatActivity {
        //  ViewFlipper实例
        private ViewFlipper flipper;
        // 定义手势检测器变量
        private GestureDetector detector;
        // 定义一个动画数组,用于为ViewFlipper指定切换动画效果
        private Animation[] animations = new Animation[4];
        // 定义手势动作两点之间的最小距离
        private float flipDistance = 0f;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            flipDistance = getResources().getDimension(R.dimen.flip_distance);
            // 创建手势检测器
            detector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onFling(MotionEvent event1, MotionEvent event2,
                                       float velocityX, float velocityY) {
                    // 如果第一个触点事件的X坐标大于第二个触点事件的X坐标超过flipDistance
                    // 也就是手势从右向左滑
                    if (event1.getX() - event2.getX() > flipDistance) {
                        // 为flipper设置切换的动画效果
                        flipper.setInAnimation(animations[0]);
                        flipper.setOutAnimation(animations[1]);
                        flipper.showPrevious();
                        return true;
                    }
                    // 如果第二个触点事件的X坐标大于第一个触点事件的X坐标超过flipDistance
                    // 也就是手势从左向右滑
                    else if (event2.getX() - event1.getX() > flipDistance) {
                        // 为flipper设置切换的动画效果
                        flipper.setInAnimation(animations[2]);
                        flipper.setOutAnimation(animations[3]);
                        flipper.showNext();
                        return true;
                    }
                    return false;
                }
            });
            // 获得ViewFlipper实例
            flipper = this.findViewById(R.id.flipper);
            // 为ViewFlipper添加6个ImageView组件
            flipper.addView(addImageView(R.drawable.java));
            flipper.addView(addImageView(R.drawable.javaee));
            flipper.addView(addImageView(R.drawable.ajax));
            flipper.addView(addImageView(R.drawable.android));
            flipper.addView(addImageView(R.drawable.html));
            flipper.addView(addImageView(R.drawable.swift));
            // 初始化Animation数组
            animations[0] = AnimationUtils.loadAnimation(this, R.anim.left_in);
            animations[1] = AnimationUtils.loadAnimation(this, R.anim.left_out);
            animations[2] = AnimationUtils.loadAnimation(this, R.anim.right_in);
            animations[3] = AnimationUtils.loadAnimation(this, R.anim.right_out);
        }
    
        // 定义添加ImageView的工具方法
        private View addImageView(int resId) {
            ImageView imageView = new ImageView(this);
            imageView.setImageResource(resId);
            imageView.setScaleType(ImageView.ScaleType.CENTER);
            return imageView;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // 将该Activity上的触碰事件交给GestureDetector处理
            return detector.onTouchEvent(event);
        }
    }
    

    anim/left_in.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate
            android:duration="5000"
            android:fromXDelta="100%p"
            android:toXDelta="0" />
        <alpha
            android:duration="5000"
            android:fromAlpha="0.1"
            android:toAlpha="1.0" />
    </set>   
    

    anim/left_out.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate
            android:duration="5000"
            android:fromXDelta="0"
            android:toXDelta="-100%p" />
        <alpha
            android:duration="5000"
            android:fromAlpha="0.1"
            android:toAlpha="1.0" />
    </set>  
    

    anim/right_in.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate
            android:duration="500"
            android:fromXDelta="-100%p"
            android:toXDelta="0" />
        <alpha
            android:duration="500"
            android:fromAlpha="0.1"
            android:toAlpha="1.0" />
    </set>  
    

    anim/right_out.xml

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate
            android:duration="500"
            android:fromXDelta="0"
            android:toXDelta="100%p" />
        <alpha
            android:duration="500"
            android:fromAlpha="0.1"
            android:toAlpha="1.0" />
    </set>   
    

    增加手势

    AndroidManifest.xml 添加权限

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    MainActivity

    public class MainActivity extends AppCompatActivity {
        private GestureOverlayView gestureView;
        private Gesture gesture;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // 获取手势编辑视图
            gestureView = findViewById(R.id.gesture);
            // 设置手势的绘制颜色
            gestureView.setGestureColor(Color.RED);
            // 设置手势的绘制宽度
            gestureView.setGestureStrokeWidth(4f);
            // 为gesture的手势完成事件绑定事件监听器
            gestureView.addOnGesturePerformedListener((source, gesture) -> {
                this.gesture = gesture;
                // 请求访问写入SD卡的权限
                requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0x123);
            });
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode,
                                               String[] permissions, int[] grantResults) {
            // 如果确认允许访问
            if (requestCode == 0x123 && grantResults != null && grantResults[0] == 0) {
                // 加载dialog_save.xml界面布局代表的视图
                LinearLayout saveDialog = (LinearLayout) getLayoutInflater().inflate(R.layout.dialog_save, null);
                // 获取saveDialog里的show组件
                ImageView imageView = saveDialog.findViewById(R.id.show);
                // 获取saveDialog里的gesture_name组件
                EditText gestureName = saveDialog.findViewById(R.id.gesture_name);
                // 根据Gesture包含的手势创建一个位图
                Bitmap bitmap = gesture.toBitmap(128, 128, 10, -0x10000);
                imageView.setImageBitmap(bitmap);
                // 使用对话框显示saveDialog组件
                new AlertDialog.Builder(MainActivity.this).setView(saveDialog)
                        .setPositiveButton(R.string.bn_save, (dialog, which) -> {
                            // 获取指定文件对应的手势库
                            GestureLibrary gestureLib = GestureLibraries.fromFile(
                                    Environment.getExternalStorageDirectory().getPath() + "/mygestures");
                            // 添加手势
                            gestureLib.addGesture(gestureName.getText().toString(), gesture);
                            // 保存手势库
                            gestureLib.save();
                        }).setNegativeButton(R.string.bn_cancel, null).show();
            }
        }
    }
    

    layout/activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/gestureTip" />
        <!-- 使用手势绘制组件 -->
        <android.gesture.GestureOverlayView
            android:id="@+id/gesture"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gestureStrokeType="multiple" />
    
    </LinearLayout>
    

    layout/dialog_save.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">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="4dp"
                android:text="@string/gesture_name" />
            <!-- 定义一个文本框来让用户输入手势名 -->
            <EditText
                android:id="@+id/gesture_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="4dp" />
        </LinearLayout>
        <!-- 定义一个图片框来显示手势 -->
        <ImageView
            android:id="@+id/show"
            android:layout_width="128dp"
            android:layout_height="128dp"
            android:layout_marginTop="10dp" />
    </LinearLayout>
    
    image.png

    识别用户手势

    public class MainActivity extends AppCompatActivity {
        // 定义手势编辑组件
        private GestureOverlayView gestureView;
        // 记录手机上已有的手势库
        private GestureLibrary gestureLibrary;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0x123);
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode,
                                               String[] permissions, int[] grantResults) {
            // 如果用户授权访问SD卡
            if (requestCode == 0x123 && grantResults != null
                    && grantResults[0] == 0) {
                // 读取上一个程序所创建的手势库
                gestureLibrary = GestureLibraries.fromFile(
                        Environment.getExternalStorageDirectory().getPath() + "/mygestures");
                if (gestureLibrary.load()) {
                    Toast.makeText(MainActivity.this, "手势文件装载成功!",
                            Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(MainActivity.this, "手势文件装载失败!",
                            Toast.LENGTH_SHORT).show();
                }
                // 获取手势编辑组件
                gestureView = findViewById(R.id.gesture);
                // 为手势编辑组件绑定事件监听器
                gestureView.addOnGesturePerformedListener((source, gesture) -> {
                    // 识别用户刚刚所绘制的手势
                    List<Prediction> predictions = gestureLibrary.recognize(gesture);
                    List<String> result = new ArrayList<>();
                    // 遍历所有找到的Prediction对象
                    for (Prediction pred : predictions) {
                        // 只有相似度大于2.0的手势才会被输出
                        if (pred.score > 2.0) {
                            result.add("与手势【" + pred.name + "】相似度为" + pred.score);
                        }
                    }
                    if (result.size() > 0) {
                        ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this,
                                android.R.layout.simple_dropdown_item_1line, result);
                        // 使用一个带List的对话框来显示所有匹配的手势
                        new AlertDialog.Builder(MainActivity.this).setAdapter(adapter, null)
                                .setPositiveButton("确定", null).show();
                    } else {
                        Toast.makeText(MainActivity.this, "无法找到能匹配的手势!",
                                Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }
    }
    

    摘抄至《疯狂Android讲义(第4版)》

    相关文章

      网友评论

          本文标题:Android 手势(Gesture)

          本文链接:https://www.haomeiwen.com/subject/alrmlrtx.html