Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 5989939
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 22, 20262026-05-22T23:10:21+00:00 2026-05-22T23:10:21+00:00

Scope of the project When a user touches the Android screen with two fingers,

  • 0

Scope of the project

When a user touches the Android screen with two fingers, draw a “Frame” at each touch location with a “cursor” for each frame. Each frame is a custom slider that the cursor will move up and down. All the way up will be 100%, middle will be 0% and all the way down will be -100%. This will be used to control small motors, similar to tank turning, each touch controls a separate motor (sending signals over bluetooth). After a two touch and everything is drawn, I want to be able to lift off either finger, BUT keep the cursor at what ever location it was last at, while the other finger is free to move its cursor. When the last finger is lifted off, everything “hides” and resets to 0%.

Functionality Wanted

  1. On two finger touch, draw separate .pngs under the touch location
  2. After the frames and cursors are drawn, keep track of where they are relative to the frame to determine the percentage.
  3. If a finger is lifted off, keep that fingers cursor at last known location, but the other finger can move it’s cursor. Also if the finger is put back down it should be able to move its cursor again.
  4. If both fingers are lifted off of the screen, hide everything and reset percentages to 0%

Functionality Obtained

  • I can draw the frames and cursors on multitouch
  • Positions and percentages work fine
  • Cursors do move properly

What doesn’t work

  • I am unsure if I should have one custom class that handles both touch event or if i should have 2 instances of the custom class each handling their own touch events (I have tried both, the only way i get any “real” functionality is with 1 custom class handling both touch events, the other way doesn’t work as intended)
  • When I only have 1 custom class, It works great, but I have it “hide” everything if both fingers are not on the screen, and sometimes android registers that I have lifted a finger off the screen and this causes me a lot of issues when the frames hide then re appear in a different location
  • When I use 2 custom classes I touch each custom class would have its own touch event, and i wouldn’t have to worry about multitouch if i split the classes evenly between the screen. This was not the case, still need to deal with multitouch

Can someone explain to me how android handles their touch events. from what I have done, it seems if i lay down finger 1, the finger 2, the first finger will register a “ACTION_DOWN” and the second will register a “ACTION_POINTER_2_DOWN”, BUT if i life off my first finger, my second finger is “demoted” and now all of the events my second finger registers does not related to “ACTION_POINTER_2” and instead will be “ACTION_DOWN, ACTION_UP, etc”. Is this correct?

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 (Main Activity Class)

    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));
        }
    }
};
}

Any other classes not listed I didn’t believe needed to be, but if they are needed please let me know.

Any help is much appreciated

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-22T23:10:22+00:00Added an answer on May 22, 2026 at 11:10 pm

    You’re going to need to save the pointerId’s of each point and compare them to the new Id’s given with each MotionEvent. It’s slightly tricky to explain, so I’ll point you to this ADB Post that explains it much better than I could. Long story short? Multitouch can be tricksy, but it’s not as bad as it looks at first glance.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

Interesting problem. In a project scope change, I broke one application into two applications.
I have these tables and relationships: user has_many projects project has_many tasks task has_many
Given: class ThreadParticipations scope :unread, lambda{ |user| where(:user_id => user.id, :read => false) }
in my project.rb model, I'm trying to create a scope with a dynamic variable:
I've alluded to this project before, in this question, but the scope of redesign
In my project, I have two type of users: job seekers and hiring managers.
The scope of this is that we have three main projects. Some of the
Global scope allows you to use a variable in a function that was defined
The scope of my question is solely ASP.NET, as the answer may be different
Within the scope of a Rails controller or a view: How can I query

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.