I am using Android Things v1 and trying to use an on-screen Button to activate a motor as long as the button is pressed (tap & long press).
The problem I have is once the button is pressed the motor will not stop. I would like it to stop once the button is released.
Here is the button code:
mtrbtnGD.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
try {
mtrGpio = manager.openGpio("BCM24");
mtrGpio.setEdgeTriggerType(Gpio.EDGE_NONE);
mtrGpio.setActiveType(Gpio.ACTIVE_HIGH);
mtrGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
mtrGpio.setValue(true);
Log.i(TAG, "Motor started");
}
catch (IOException e) {
Log.w(TAG, "Unable to access GPIO", e);
}
return true;
}
});
EDIT:
Here is the new code from Sam's reply:
public class MainActivity extends Activity {
private Gpio mtrGpio;
private GestureDetector mtrbtnGD;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button mtrbtnGD = (Button) findViewById(R.id.mtrbtn);
Button closebtn = (Button) findViewById(R.id.closebtn);
Button stopmtrbtn = (Button) findViewById(R.id.stopmtrbtn);
final PeripheralManager manager = PeripheralManager.getInstance();
List<String> portList = manager.getGpioList();
if (portList.isEmpty()) {
Log.i(TAG, "No GPIO port available on this device.");
} else {
Log.i(TAG, "List of available ports: " + portList);
}
mtrbtnGD.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
try {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
// PRESSED
mtrGpio = manager.openGpio("BCM24");
mtrGpio.setEdgeTriggerType(Gpio.EDGE_NONE);
mtrGpio.setActiveType(Gpio.ACTIVE_HIGH);
mtrGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
mtrGpio.setValue(true);
Log.i(TAG, "Motor started");
return true; // if you want to handle the touch event
case MotionEvent.ACTION_UP:
// RELEASED
mtrGpio.close();
return true; // if you want to handle the touch event
}
}
catch (IOException e) {
Log.w(TAG, "Unable to access GPIO", e);
}
return true;
}
});
The motor still doesn't stop when I release the on-screen button.
close() closes your connection to the GPIO peripheral, it does not change the value of that connection before closing it. You need to use setValue(false); as so:
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
// PRESSED
mtrGpio = manager.openGpio("BCM24");
mtrGpio.setEdgeTriggerType(Gpio.EDGE_NONE);
mtrGpio.setActiveType(Gpio.ACTIVE_HIGH);
mtrGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
mtrGpio.setValue(true);
Log.i(TAG, "Motor started");
return true; // if you want to handle the touch event
case MotionEvent.ACTION_UP:
// RELEASED
mtrGpio.setValue(false); // ADD THIS
mtrGpio.close();
return true; // if you want to handle the touch event
}
Ideally, if you expect the motor to be turned on and off a lot, you should keep the connection open.
public class MainActivity extends Activity {
private Gpio mtrGpio;
private GestureDetector mtrbtnGD;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button mtrbtnGD = (Button) findViewById(R.id.mtrbtn);
try {
PeripheralManager manager = PeripheralManager.getInstance();
mtrGpio = manager.openGpio("BCM24");
mtrGpio.setEdgeTriggerType(Gpio.EDGE_NONE);
mtrGpio.setActiveType(Gpio.ACTIVE_HIGH);
mtrGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
} catch (IOException e) {
throw new IllegalStateException("cannot open gpio", e);
}
mtrbtnGD.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
try {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
// PRESSED
mtrGpio.setValue(true);
Log.i(TAG, "Motor started");
return true;
case MotionEvent.ACTION_UP:
// RELEASED
mtrGpio.setValue(false);
return true;
}
} catch (IOException e) {
Log.w(TAG, "Unable to access GPIO", e);
}
return true;
}
});
}
#Override
protected void onDestroy() {
try {
mtrGpio.close();
} catch (IOException ignore) {
Log.w(TAG, "Unable to close GPIO", ignore);
}
super.onDestroy();
}
You can use this code inside onTouch method and Check event.getAction() :
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
// PRESSED
return true; // if you want to handle the touch event
case MotionEvent.ACTION_UP:
// RELEASED
return true; // if you want to handle the touch event
}
Related
I´m just trying to program a simple code that shows a message when the button is pressed and another one when is released.
I try this but it didn´t work:
btnOn.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
mmHandler.postDelayed(mAction1, 10);
break;
case MotionEvent.ACTION_UP:
mmHandler.removeCallbacksAndMessages(null);
mmHandler.postDelayed(mAction1, 10);
txtdebug.setText("Message 2");
break;
}
return true;
}
Runnable mAction1 = new Runnable() {
#Override
public void run() {
txtdebug.setText("Message 1");
//mHandler.postDelayed(this, 100);
}
};
Any idea of why the removeCallbacksAndMessages is not working. It displays for instance Message 2 when I released but then it appears again message 1. It didn´t keep like that.
I don't know If I understand You correctly but if You want to execute function every X milliseconds when the button is pressed and then when the user release button stop it You can do it like this (You were very close to this solution but You run Hendler again in UP event):
public class MainActivity extends AppCompatActivity
{
Button btnOn;
Handler handler;
#SuppressLint("ClickableViewAccessibility")
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnOn = findViewById(R.id.btnOn);
handler = new Handler();
final Runnable action = new Runnable()
{
#Override
public void run()
{
Log.d("MyTag", "Run action");
handler.postDelayed(this, 100); // Run action every 100 ms
}
};
btnOn.setOnTouchListener(new View.OnTouchListener()
{
#Override
public boolean onTouch(View view, MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
Log.d("MyTag", "Down");
handler.postDelayed(action, 10);
return true;
case MotionEvent.ACTION_UP:
Log.d("MyTag", "Up");
handler.removeCallbacksAndMessages(null);
return true;
default:
return false;
}
}
});
}
}
I need to change image every 5 seconds, then when we touch the ImageView it will stop changing until we release the touch.
When the application go to OnPause state it pauses the auto changing of the image.
I save the KEY_ID and stop the runnable in onSaveInstanceState
How can I make this change automatically ? Below is my code in java.
public class MainActivity extends AppCompatActivity {
private ImageView im_car;
private Handler mHandler = new Handler();
public static Integer[] mThumbeId = {R.drawable.first, R.drawable.second, R.drawable.thard};
public int i = 0, id = 0;
private String KEY_ID = "key_id";
#SuppressLint("ClickableViewAccessibility")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
im_car = findViewById(R.id.im_car);
mThumbRun.run();
if (savedInstanceState != null) {
i = savedInstanceState.getInt(KEY_ID);
}
im_car.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mHandler.removeCallbacks(mThumbRun);
break;
case MotionEvent.ACTION_UP:
if (i!=0)
{
i = i-1;
}
else i=2;
mThumbRun.run();
break;
}
return false;
}
});
}
#Override
protected void onRestart() {
super.onRestart();
if (i!=0)
{
i = i-1;
}
else i=2;
mThumbRun.run();
}
Runnable mThumbRun = new Runnable() {
#Override
public void run() {
im_car.setImageResource(mThumbeId[i]);
i++;
if (i >= mThumbeId.length) {
i = 0;
}
mHandler.postDelayed(this, 5000);
}
};
}
You can create a touch listener in your imageview.
Keep a boolean to check wether image should change.
boolean imageChangePermission = true;
yourImageView.setOnTouchListener(new OnTouchListener () {
public boolean onTouch(View view, MotionEvent event) {
if (event.getAction() == android.view.MotionEvent.ACTION_DOWN) {
Log.d("TouchTest", "Touch down");
}
else if (event.getAction() == android.view.MotionEvent.ACTION_UP) {
Log.d("TouchTest", "Touch up");
imageChangePermission = false;
}
}
}
ACTION_DOWN - when you first touch
ACTION_MOVE - when you are moving your finger on screen
ACTION_UP - when you remove your finger from screen
Create a Handler to periodically call the change Image method of yours.
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if(imageChangePermission) {
changeImage();
}
}
},5000); //Run after every 5 second
I'm trying to detect a user long click using a TimerTask. The code isnide the runnable should be executed whenthe user holds the button for longer than the LONG_PRESS_TIMEOUT variable. The short click event works, however when executing the below code, when the TimerTask is invoked, I get the error Only the original thread that created a view hierarchy can touch its views.
View.OnTouchListener detectClickAndHoldListener = new View.OnTouchListener() {
private Timer timer = new Timer();
private long LONG_PRESS_TIMEOUT = 1337; // TODO: your timeout here
private boolean wasLong = false;
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(getClass().getName(), "touch event: " + event.toString());
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// touch & hold started
timer.schedule(new TimerTask() {
#Override
public void run() {
wasLong = true;
snap.setBackgroundResource(R.drawable.cam_rec);
try {
initRecorder(mCameraView.getHolder().getSurface());
mMediaRecorder.start();
} catch (IOException e) {
e.printStackTrace();
}
// touch & hold was long
}
}, LONG_PRESS_TIMEOUT);
return true;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
// touch & hold stopped
timer.cancel();
if(!wasLong){
mCamera.takePicture(shutterCallback, rawCallback, jpegCallback);
snap.setBackgroundResource(R.drawable.filled_cam);
}
else {
mMediaRecorder.stop();
mMediaRecorder.reset();
}
timer = new Timer();
return true;
}
return false;
}
};
YourActivity.this.runOnUiThread(new Runnable(){
#Override
public void run(){
try {
initRecorder(mCameraView.getHolder().getSurface());
mMediaRecorder.start();
} catch (IOException e) {
e.printStackTrace();
}
}
);
I wanna make a button that can show me how long time it gets pressed. If the button stops to get pressed the time still stays there, then you can keep pressing it. How can i do this?
With this code, I made a 5 second button-press open a new activity :)
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1=(Button)findViewById(R.id.button1);
final Handler handel = new Handler();
button1.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View arg0, MotionEvent arg1) {
switch (arg1.getAction()) {
case MotionEvent.ACTION_DOWN:
handel.postDelayed(run, 5000/* OR the amount of time you want */);
break;
default:
handel.removeCallbacks(run);
break;
}
return true;
}
Runnable run = new Runnable() {
#Override
public void run() {
Intent i = new Intent(MainActivity.this, SecActivity.class);
startActivity(i);
}
};});
}}
if your mean a Button count the times it's pressed you can use:
int count = 0;
(your button).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
count++;
}
});
if you mean the longer you hold it you can use:
long time = 0;
(your button).setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if(event.getAction() == event.ACTION_DOWN){
time = System.currentTimeMillis();
}else if( event.getAcion() == event.ACTION_UP){
time = System.currentTimeMillis() - time;
}
return true;
}
});
note: that your variables like (int count) and (long time) should be declared in the body of your class not into a method;
below code is a example code for recognizing double click on a view with Custom time;
boolean isPressed = false;
long delayed = 1000;
final Hanlder handler = new Handler();
Button b;
b.setOnClickListener(new View.onClickListener(){
public void onClick(View v){
if(isPressed == true){
// recognize double click;
}else{
Toast.makeText(getApplicationContext(),"tap again to exit",1).show();
isPressed = true;
handler.postDelayed(new Runnable(){
public void run(){
isPressed = false;
}
},delayed);
}
});
Try TouchListener:
Handler handel = new Handler();
b.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View arg0, MotionEvent arg1) {
switch (arg1.getAction()) {
case MotionEvent.ACTION_DOWN:
handel.postDelayed(run, 5000);
break;
default:
handel.removeCallbacks(run);
break;
}
return true;
}
});
Later define run:
Runnable run = new Runnable() {
#Override
public void run() {
// Your code to run on long click
}
};
Set a listener for when the button is pressed and when the button is released. Create a timer that records after the button is pressed and stops when the button is released. There should be listeners for both if you are using JFrame or Android as your GUI.
I have a video view that is full screen and would like to trigger a popupMenu when a certain key is pressed. The log shows that the key event is captured, but the popup view will not show. Any ideas why?
#Override
public boolean onKeyDown(int keyCode , KeyEvent event ){
switch(keyCode){
case KeyEvent.KEYCODE_DPAD_CENTER:
Log.i("POP uP MENU","Show");
mVideoView.pause();
showPopupMenu(new View(VideoPlayer.this));
case KeyEvent.KEYCODE_D:
popupMenu.dismiss();
}
return true;
}
private void showPopupMenu(View v){
popupMenu = new PopupMenu(VideoPlayer.this, v);
if(bitRates != null){
for(int i=0;i<bitRates.size();i++){
int menuItem = i;
popupMenu.getMenu().add(0,menuItem, 0,qualityList.get(i)+" : "+bitRates.get(i));
}
}
popupMenu.show();
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(final MenuItem item) {
progressDialog = ProgressDialog.show(VideoPlayer.this, "", "Loading video...");
final int position = mVideoView.getCurrentPosition();
new Thread() {
public void run() {
try{
runOnUiThread(new Runnable() {
public void run() {
play(streamUrls.get(item.getItemId()),position);
}
});
}
catch (Exception e)
{
Log.e("tag", e.getMessage());
}
progressDialog.dismiss();
}
}.start();
return true;
}
});
}
It seems that
KeyEvent.KEYCODE_DPAD_CENTER
is an event that is set to be triggered by the VideoView by default. So when I pressed that key VideoView triggered its event event instead of the event I was requesting it to. I changed the KeyEvent KEYCODE to another key(One not used by Videoview) and it worked.