Android-多点触控的实现示例详解

最近在做一个多点触控的项目,因为之前没有做过相同的,就先写了一个示例代码。其中涉及了一些常用的实现方法,在此分析一下,和大家分享。
这个示例涉及到的内容:

1.静态设置横屏的方法;
2.设置全屏的方法;
3.在onCreate()方法中获取图片高度和宽度的方法;
4.多点触控。

示例比较简单,先把源代码放上。

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class MainMultiTouch extends Activity {

    private ImageView img1;
    private ImageView img2;
    private RelativeLayout rela;

    private int widthImg1;
    private int heightImg1;
    private int widthImg2;
    private int heightImg2;
    private int locImg1X = 0;
    private int locImg1Y = 0;
    private int locImg2X = 0;
    private int locImg2Y = 0;

    private boolean hasMeasured = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(
        WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_main_multi_touch);

        initView();

//      getImagePixHandler.sendEmptyMessageDelayed(0, 200);

    }

    /**
     * initialize the views
     */
    private void initView() {
        img1 = (ImageView) findViewById(R.id.imageView1);
        img2 = (ImageView) findViewById(R.id.imageView2);
        rela = (RelativeLayout) findViewById(R.id.mainlayout);
        rela.setOnTouchListener(new OnLayoutTouchListener());
        //监听绘画完成后测量图片的长宽
        ViewTreeObserver vto = rela.getViewTreeObserver();
        vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            public boolean onPreDraw() {
                if (hasMeasured == false) {

                    widthImg1 = img1.getWidth();
                    heightImg1 = img1.getHeight();
                    widthImg2 = img2.getWidth();
                    heightImg2 = img2.getHeight();

                    locImg1X = (int) img1.getX();
                    locImg2X = (int) img2.getX();
                    locImg1Y = (int) img1.getY();
                    locImg2Y = (int) img2.getY();

                    hasMeasured = true;
                }
                return true;
            }
        });
    }

    /**
     * get the width&height and visual position of imageviews,in pixels
     */
    @SuppressLint("HandlerLeak")
    private Handler getImagePixHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0) {
                // The width of your view, in pixels
                widthImg1 = img1.getWidth();
                widthImg2 = img2.getWidth();
                heightImg1 = img1.getHeight();
                heightImg2 = img2.getHeight();
                // The visual x&y position of imageviews, in pixels.
                locImg1X = (int) img1.getX();
                locImg2X = (int) img2.getX();
                locImg1Y = (int) img1.getY();
                locImg2Y = (int) img2.getY();
                rela.setOnTouchListener(new OnLayoutTouchListener());
            }
        }
    };

    /**
     * 
     * @author dell
     *
     */
    class OnLayoutTouchListener implements OnTouchListener {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            int x;
            int y;

            int idPoint = event.getActionIndex();

            x = (int) event.getX(idPoint);
            y = (int) event.getY(idPoint);

            Boolean flag1 = x > locImg1X && x < (locImg1X + widthImg1)
                    && y > locImg1Y && y < (locImg1Y + heightImg1);
            Boolean flag2 = x > locImg2X && x < (locImg2X + widthImg2)
                    && y > locImg2Y && y < (locImg2Y + heightImg2);

            // switch(event.getAction() & MotionEvent.ACTION_MASK){
            switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                // 第一个手指按下事件
                if (flag1) {
                    // System.out.println("第一个手指的按下1");
                    System.out.println("按下1");
                }
                if (flag2) {
                    // System.out.println("第一个手指的按下2");
                    System.out.println("按下2");
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                // 第二个手指按下事件
                if (flag1) {
                    // System.out.println("第二个手指的按下1");
                    System.out.println("按下1");
                }
                if (flag2) {
                    // System.out.println("第二个手指的按下2");
                    System.out.println("按下2");
                }
                break;
            case MotionEvent.ACTION_UP:
                // 第一个手指抬起事件
                if (flag1) {
                    // System.out.println("第一个手指的抬起1");
                    System.out.println("抬起1");
                }
                if (flag2) {
                    // System.out.println("第一个手指的抬起2");
                    System.out.println("抬起2");
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                // 第二个手指抬起事件
                if (flag1) {
                    // System.out.println("第二个手指的抬起1");
                    System.out.println("抬起1");
                }
                if (flag2) {
                    // System.out.println("第二个手指的抬起2");
                    System.out.println("抬起2");
                }
                break;
            case MotionEvent.ACTION_MOVE:
                // 手指滑动事件
                break;
            }
            return true;
        }
    }

}

然后根据上面的4点一次说明一下。
1.设置横屏的方法
静态设置可以通过在Manifest文件中的对应的Activity的属性中添加如下代码:

android:launchMode="singleTask"
android:screenOrientation="landscape"

OK!
顺便提一下,在电脑编辑layout文件的时候,可以按照下图的点击按钮,即可将布局文件更改为横屏模式。
这里写图片描述

2.设置全屏的方法
设置全屏只需要在onCreate()方法中添加如下代码

requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(
        WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN);

需要注意的是这段代码和setContentView()方法的相互位置。如果放在setContentView()的后面就会报错!

3.在onCreate()方法中获取图片高度和宽度的方法。
可以自己写代码尝试一下:

public class DrawTest extends Activity {

    private ImageView img1;
    private ImageView img2;

    private int widthImg1;
    private int heightImg1;
    private int widthImg2;
    private int heightImg2;
    private int locImg1X = 0;
    private int locImg1Y = 0;
    private int locImg2X = 0;
    private int locImg2Y = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_multi_touch);
        img1 = (ImageView) findViewById(R.id.imageView1);
        img2 = (ImageView) findViewById(R.id.imageView2);
        img1.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(DrawTest.this,MainMultiTouch.class);
                startActivity(intent);
            }
        });
        printInfo(0);

    }

    @Override
    protected void onStart() {
        super.onStart();
        printInfo(1);
    }

    @Override
    protected void onRestart() {
        super.onRestart();
    }
    @Override
    protected void onResume() {

        super.onResume();
        printInfo(2);
    }
    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        printInfo(3);
    }
    @Override
    protected void onStop() {
        super.onStop();
        printInfo(4);
    }
    @Overridz
    protected void onDestroy() {
        super.onDestroy();
    }
    private void printInfo(int i){

        switch(i){
        case 0:
            //onCreate
            System.out.print("onCreate->");
            break;
        case 1:
            System.out.print("onStart->");
            break;
        case 2:
            System.out.print("onResume->");
            break;
        case 3:
            System.out.print("onPause->");
            break;
        case 4:
            System.out.print("onStop->");
            break;
        }
        widthImg1 = img1.getWidth();
        heightImg1 = img1.getHeight();
        widthImg2 = img2.getWidth();
        heightImg2 = img2.getHeight();

        locImg1X = (int) img1.getX();
        locImg2X = (int) img2.getX();
        locImg1Y = (int) img1.getY();
        locImg2Y = (int) img2.getY();
        System.out.println("widthImg1 = " + widthImg1);
        System.out.println("heightImg1 = " + heightImg1);
        System.out.println("widthImg2 = " + widthImg2);
        System.out.println("heightImg2 = " + heightImg2);
        System.out.println("locImg1X = " + locImg1X);
        System.out.println("locImg2X = " + locImg2X);
        System.out.println("locImg1Y = " + locImg1Y);
        System.out.println("locImg2Y = " + locImg2Y);
    }
}

运行结果如下图:
获取图片高度实验结果
这里写图片描述

由结果可以看出,只有在onResume()方法后才能获取图片的高度,否则为0。是因为在onResume()方法中会调用onDraw()方法。
有两个方法可以获取图片的高度和宽度:、
(1)使用线程延时:即将获取图片高度的方法放到另外一个线程,等主线程运行一段足够长的时间以确保其能运行onDraw()方法。
具体实现方法如上面方法中的handler。这种实现方法的最大问题是,多长的时间算是足够长的时间?因为不同的编译器运行速度不同,为了确保程序在各个设备都能正常运行,这个时间一般取得比较大,例如我取值为200(ms).
(2)监听onDraw()方法,
参考下面网站:
http://www.cnblogs.com/wt616/archive/2012/05/11/2496180.html
如上面程序中的,通过设置一个监听器,可以实现。

4.多点触控
对于这个实现方法,主要集中下面这几对方法或常数的意义:
(1)getAciton() & getActionMasked()
(2)getX & getX(index)
(3)ACTION_DOWN & ACTION_POINTER_DOWN
首先我们先来看看其官方文档的解释:

Some devices can report multiple movement traces at the same time. Multi-touch screens emit one movement trace for each finger. The individual fingers or other objects that generate movement traces are referred to as pointers. Motion events contain information about all of the pointers that are currently active even if some of them have not moved since the last event was delivered.

The number of pointers only ever changes by one as individual pointers go up and down, except when the gesture is canceled.
Each pointer has a unique id that is assigned when it first goes down(indicated by ACTION_DOWN or ACTION_POINTER_DOWN). A pointer id remains valid until the pointer eventually goes up(indicated by ACTION_UP or ACTION_POINTER_UP) or when the gesture is canceled (indicated by ACTION_CANCEL).

The order in which individual pointers appear within a motion event is undefined. Thus the pointer index of a pointer can change from one event to the next but the pointer id of a pointer is guaranteed to remain constant as long as the pointer remains active. Use the getPointerId(int) method to obtain the pointer id of a pointerto track it acro ss all subsequent motion events in a gesture. Then for successive motion events, use the findPointerIndex(int) method to obtain the pointer index for a given pointer id in that motion event.>

这段英文是官方API 对于MotionEvent的概述内容。其中涉及到两个重要的概念,就是“Motion Event”和 “Pointer”。
(1)Pointers:系统对每个引起引动追踪的手指或者其它物品称作“触点”。
而这个点的位置就是接触面积的中心。而后面的讨论都是建立在“触点”的概念基础上的。
(2)Motion events:包含目前正处于活动状态的各个点的信息。

这些信息具体指什么呢?

(1)pointer index
(2)pointer id
(3)pointer position
对于这三个属性,pointer position描述的是触点在整个屏幕的绝对坐标位置,坐标系的原点是屏幕的左上角,单位是pixel即像素点。
而index和id就比较容易混淆。
index在每个动作都有可能发生变化。比方说,你手指按下后抬起,按下时和抬起时的index就可能不一样。
id则是一旦这个点生成就一直保持不变,直到手指抬起之后才会失效。也就是说,你的手指一旦落下,然后可以移动,最后抬起,这一系列过程中,这个随着你手指的点的id是保持不变的。但是相反的,每次event的index就可能不相同。
那么index是如何编号的呢?系统会在每次动作发生时从0开始的空闲的整数中的最小的数赋值给index。

如何获得这些信息呢?

pointer index:

int getActionIndex ();
int findPointerIndex(int pointerId);

pointer id:

int getPointerId(int pointerIndex);

pointer position

getX(); getY();
getX(int index); getY(int index);

getX() 与getX(int)有什么区别呢?
看一下API就了然了!

getX(int):
Returns the X coordinate of this event for the given pointer index;
getX(i):
getX(int) for the first pointer index

最后就是getAction() 与getActionMasked()的区别了。这个可以参考我上篇转载的文章,那篇作者讲解的很详细了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值