Масштаб проекта
Когда пользователь касается экрана Android двумя пальцами, нарисуйте «Рамку» в каждом месте касания с «курсором» для каждого кадра.Каждый кадр - это пользовательский слайдер, курсор которого будет перемещаться вверх и вниз.Вполне вверх будет 100%, средний будет 0%, а вниз будет -100%.Это будет использоваться для управления маленькими моторами, подобно повороту бака, каждое касание управляет отдельным мотором (посылая сигналы через Bluetooth).После двух касаний и все прорисовано, я хочу иметь возможность оторвать любой палец, НО держать курсор в том месте, где он был в последний раз, в то время как другой палец может свободно перемещать курсор.Когда последний палец снят, все «прячется» и сбрасывается до 0%.
Требуется функциональность
- Нарисуйте двумя пальцами отдельные .pngsпод сенсорным местоположением
- После отрисовки рамок и курсоров отслеживайте их расположение относительно рамки, чтобы определить процентное соотношение.
- Если палец оторван, удерживайте курсор на пальцахв последнем известном месте, но другой палец может перемещать курсор.Также, если палец опустить вниз, он сможет снова переместить курсор.
- Если оба пальца оторваны от экрана, спрячьте все и сбросьте проценты до 0%
Полученные функциональные возможности
- Я могу рисовать рамки и курсоры на мультитач
- Позиции и проценты работают нормально
- Курсоры перемещаются правильно
Что не работает
- Я не уверен, должен ли я иметь один пользовательский класс, который обрабатывает оба события касания, или если у меня должно быть 2 экземпляракаждый пользовательский класс обрабатывает свои собственные сенсорные события (я пробовал оба, единственный способ получить «реальную» функциональность - это 1 пользовательский класс, обрабатывающий оба сенсорных события, другой способ не работает должным образом)
- Когда у меня есть только 1 пользовательский класс, он отлично работает, но он у меня «скрывает» все, если оба пальца не на экране, и иногда Android регистрирует, что я убрал палец с экрана, и это вызываетЕсть много проблем, когда кадры скрываются, а затем появляются в другом месте.
- Когда я использую 2 пользовательских класса, я прикасаюсь, что у каждого пользовательского класса будет свое собственное событие касания, и мне не придется беспокоиться о мультитач, еслия равномерно разделил классы между экранами.Это был не тот случай, все равно нужно разобраться с мультитачем
Может кто-нибудь объяснить мне, как андроид обрабатывает свои сенсорные события.из того, что я сделал, кажется, если я положу палец 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));
}
}
};
}
Любые другие классы, не указанные в списке, я не считаю нужными, но если они необходимы, пожалуйста, дайте мне знать.
Любая помощь очень ценится