如何在android中正确处理触摸事件?

时间:2011-05-31 01:14:08

标签: java android user-interface multi-touch

项目范围

当用户用两根手指触摸Android屏幕时,在每个触摸位置绘制一个“帧”,每个帧都有一个“光标”。每个帧都是一个自定义滑块,光标将上下移动。一路上涨将是100%,中间将是0%,一直下跌将是-100%。这将用于控制小型电机,类似于油箱转动,每个触摸控制一个单独的电机(通过蓝牙发送信号)。在两次触摸并且绘制了所有内容之后,我希望能够抬起任一个手指,但是将光标保持在最后一个位置,而另一个手指可以自由移动光标。当最后一根手指被抬起时,一切都“隐藏”并重置为0%。

需要的功能

  1. 在两个手指触摸时,在触摸位置下绘制单独的.pngs
  2. 绘制帧和光标后,跟踪它们相对于帧的位置以确定百分比。
  3. 如果手指被抬起,请将手指光标放在最后的已知位置,但另一根手指可以移动它的光标。此外,如果手指放下,它应该能够再次移动光标。
  4. 如果两个手指都从屏幕上抬起,请隐藏所有内容并将百分比重置为0%
  5. 获得的功能

    • 我可以在多点触控上绘制帧和光标
    • 职位和百分比工作正常
    • 游标确实正常移动

    什么行不通

    • 我不确定我是否应该有一个处理触摸事件的自定义类,或者我应该有两个自定义类的实例,每个实例都处理他们自己的触摸事件(我已经尝试了两个,唯一的方法是我得到任何“真实的”功能是1个自定义类处理两个触摸事件,另一个方法不能按预期工作)
    • 当我只有一个自定义类时,它工作得很好,但如果两个手指都不在屏幕上,我就会“隐藏”所有内容,有时候我注意到我已经将一根手指从屏幕上移开,这导致我当框架隐藏然后重新出现在不同的位置时出现很多问题
    • 当我使用2个自定义类时,我触摸每个自定义类都有自己的触摸事件,如果我在屏幕之间均匀分割类,我就不必担心多点触控。事实并非如此,仍然需要处理多点触控

    有人可以向我解释android如何处理他们的触摸事件。从我所做的,似乎如果我放下手指1,手指2,第一个手指将注册“ACTION_DOWN”,第二个手指将注册“ACTION_POINTER_2_DOWN”,但如果我的生命离开我的第一根手指,我的第二根手指是“降级”,现在我的第二个手指注册的所有事件都与“ACTION_POINTER_2”无关,而是“ACTION_DOWN,ACTION_UP等”。这是对的吗?

    TouchUI.java

        package com.robota.android;
    
        import android.content.Context;
        import android.graphics.Bitmap;
        import android.graphics.BitmapFactory;
        import android.graphics.Canvas;
        import android.util.AttributeSet;
        import android.util.Log;
        import android.view.MotionEvent;
        import android.widget.ImageView;
    
    public class TouchUI extends ImageView {
    
    public static final String LEFT_TOUCHUI = "com.robota.android:id/leftTouchUI";
    public static final String RIGHT_TOUCHUI = "com.robota.android:id/rightTouchUI";
    private String whoAmI = new String();
    private MyPoints framePts = new MyPoints();
    private MyPoints cursorPts = new MyPoints();
    private Bitmap frame;
    private Bitmap cursor;
    private int frameWidth;
    private int frameHeight;
    private int cursorHeight;
    private boolean pointerDown = false;
    private int dy;
    
    public TouchUI(final Context context, final AttributeSet as){
        super(context, as);
        Log.d("TouchUI", getResources().getResourceName(this.getId()));
        whoAmI = new String(getResources().getResourceName(this.getId()));
        if(whoAmI.equals(LEFT_TOUCHUI)){
            frame = BitmapFactory.decodeResource(getResources(), R.drawable.tank_left);
        }else if(whoAmI.equals(RIGHT_TOUCHUI)){
            frame = BitmapFactory.decodeResource(getResources(), R.drawable.tank_right);
        }
        cursor = BitmapFactory.decodeResource(getResources(), R.drawable.cursor);
        frameWidth = frame.getWidth();
        frameHeight = frame.getHeight();
        cursorHeight = cursor.getHeight();
    }
    
    public void determinePointers(int x, int y){
            framePts.setOrigin(x-frameWidth/2, y-frameHeight/2);
            cursorPts.setOrigin(x-frameWidth/2, y-frameHeight/2);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent e){
        int x = 0;
        int y = 0;
        Log.d("TouchUI", ">>>>> " + whoAmI);
        if(e.getAction() == MotionEvent.ACTION_DOWN){
            determinePointers(x,y);
            pointerDown = true;
        }else if(e.getAction() == MotionEvent.ACTION_UP){
            pointerDown = false;
        }else if(e.getAction() == MotionEvent.ACTION_MOVE){
            dy = (int)e.getY()-framePts.getY();
            if(dy <= 0){
                dy=0;
            }else if(dy+cursorHeight/2 >= frameHeight){
                dy=frameHeight;
            }
            sendMotorSpeed(dy);
        }
        return true;
    }
    
    public void sendMotorSpeed(int dy){
        float motor = dy;
        motor-=frameHeight;
        motor*=-1;
    
        motor = (motor/frameHeight)*255;
    
        PacketController.updateMotorSpeeds(whoAmI, (int)motor);
    }
    
    public void onDraw(Canvas canvas){
        if(pointerDown){//twoDown){
            canvas.drawBitmap(frame, framePts.getX(), framePts.getY(), null);
            canvas.drawBitmap(cursor, cursorPts.getX(), (cursorPts.getY()+dy), null);
        }
        invalidate();
    }
    
    private class MyPoints{
    
        private int x = -100;
        private int y = -100;
        private int deltaY = 0;;
    
        public MyPoints(){
            this.x = 0;
            this.y = 0;
        }
    
        public int getX(){
            return this.x;
        }
    
        public int getY(){
            return this.y;
        }
    
        public void setOrigin(int x, int y){
            this.x = x;
            this.y = y;
        }
    
        public int getDeltaY(){
            return deltaY;
        }
    
        public void setDeltaY(int newY){
            deltaY = (newY-y);
            Log.d("TouchUI", "DY: " + deltaY);
        }
    }
    }
    

    main.xml中

        <?xml version="1.0" encoding="utf-8"?>
        <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/parentLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <LinearLayout android:orientation="horizontal"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <com.robota.android.TouchUI xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/leftTouchUI"
            android:background="#0000"
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:layout_weight="1">
        </com.robota.android.TouchUI>
        <com.robota.android.TouchUI xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/rightTouchUI"
            android:background="#0000"
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:layout_weight="1">
        </com.robota.android.TouchUI>
    </LinearLayout>
    

    RobotController.java(主要活动类)

        package com.robota.android;
    
        import android.app.Activity;
        import android.bluetooth.BluetoothAdapter;
        import android.bluetooth.BluetoothDevice;
        import android.content.ActivityNotFoundException;
        import android.content.Intent;
        import android.os.Bundle;
        import android.os.Handler;
        import android.os.Message;
        import android.util.Log;
        import android.view.Menu;
        import android.view.MenuInflater;
        import android.view.MenuItem;
        import android.view.View;
        import android.view.Window;
        import android.widget.ScrollView;
        import android.widget.TextView;
        import android.widget.Toast;
    
    public class RobotController extends Activity {
    // Tag used to keep track of class in the Log
    private static final String TAG = "robotController_new";
    // Boolean to debugging
    private static final boolean D = true;
    
    // Intent request codes
    private static final int DISCONNECT_DEVICE = 1;
    private static final int CONNECT_DEVICE = 2;
    private static final int REQUEST_ENABLE_BT = 3;
    
    // Handler Codes
    public static final int MESSAGE_READ = 1;
    public static final int MESSAGE_WRITE = 2;
    
    // Local Bluetooth Adapter
    private BluetoothAdapter bluetoothAdapter = null;
    // Bluetooth Discovery and Datahandler
    private BluetoothComm btComm = null;
    
    // Debug's TextView, this is where strings will be written to display
    private TextView tv;
    private ScrollView sv;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        if(D) Log.d(TAG, "++ON CREATE++");
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.main);
    
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    
        if(bluetoothAdapter == null){
            if(D) Log.d(TAG, "NO BLUETOOTH DEVICE");
            Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_SHORT).show();
            finish();
            return;
        }
    
        PacketController.controller = this;
    }
    
    
    public void onStart(){
        super.onStart();
        if(D) Log.d(TAG, "++ON START++");
    
        if(!bluetoothAdapter.isEnabled()){
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
        }else{
            // Start BluetoothComm
            if(btComm == null){
                setupComm();
            }
        }
    }
    
    /**
     * Creates new Bluetooth Communication
     */
    private void setupComm(){
        if(D) Log.d(TAG, "+++setupComm+++");
        btComm = new BluetoothComm(this, handler);
    }
    
    private void connectDevice(Intent data){
        if(D) Log.d(TAG, "+++connectDevice+++");
        String addr = data.getExtras()
            .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(addr);
        if(D) Log.d(TAG,"REMOTE ADDR: "+ addr);
        btComm.connect(device);
    }
    
    private void disconnectDevice(){
        if(D) Log.d(TAG, "---disconnectDevice---");
        if(btComm.getState() == btComm.STATE_CONNECTED){
            btComm.disconnect();
        }
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        //super.onCreateOptionsMenu(menu);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        Intent serverIntent = null;
        switch(item.getItemId()){       
        case R.id.insecure_connect_scan:
            // Launch the DeviceListActivity to see devices and do scan
            serverIntent = new Intent(this, DeviceListActivity.class);
            try{
                startActivityForResult(serverIntent, CONNECT_DEVICE);
            }catch(ActivityNotFoundException activityNotFound){
                Log.e(TAG, "Could not start DeviceListActivity(Insecure)");
            }
            return true;
        }
        return false;
    }
    
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data){
        switch(requestCode){
        case CONNECT_DEVICE:
            if(resultCode == Activity.RESULT_OK){
                connectDevice(data);
            }
            break;
        case DISCONNECT_DEVICE:
            if(resultCode == Activity.RESULT_OK){
                disconnectDevice();
            }
            break;
        }
    }
    
    public Handler getHandler(){
        return this.handler;
    }
    
    public BluetoothComm getBtComm(){
        return this.btComm;
    }
    
    // The Handler that gets information back from the BluetoothChatService
    private final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if(D) Log.d(TAG, "check message");
            switch (msg.what) {
            case MESSAGE_READ:
                if(D) Log.d(TAG, "trying to read message");
                byte[] readBuf = (byte[]) msg.obj;
                // construct a string from the valid bytes in the buffer
                String readMessage = new String(readBuf, 0, msg.arg1);
                if(D) Log.d(TAG, "bytes: " + readBuf + " arg1: " + msg.arg1 + " Message: " + readMessage);
                tv.append(readMessage);
                break;
            case MESSAGE_WRITE:
                if(D) Log.d(TAG, "trying to send message");
                String sendMessage = new String(String.valueOf(msg.obj));
            }
        }
    };
    }
    

    未列出的任何其他课程我认为不需要,但如果需要,请告诉我。

    非常感谢任何帮助

1 个答案:

答案 0 :(得分:2)

您将需要保存每个点的pointerId并将它们与每个MotionEvent给出的新Id进行比较。解释起来有点棘手,所以我会指出这个ADB Post比我更好地解释它。长话短说?多点触控可能很棘手,但它并不像乍看之下那么糟糕。