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();
}
}
);
Related
I want to scroll RecyclerView with 2 or 3 item/sec when setOnLongClickListener on image view. I searched a lot and tried a countdown timer and handler to scroll RecyclerView by specific time. but without any success. any IDEA how to achieve this.
I tried this :
private Runnable runnable = new Runnable() {
#Override
public void run() {
if (minutesCurrentPosition >= 0 && minutesCurrentPosition < minutesArray.length) {
minutesCurrentPosition++;
if (minutesCurrentPosition == minutesArray.length) {
minutesCurrentPosition--;
}
if (minutesCurrentPosition < minutesArray.length) {
rvMinutes.scrollToPosition(minutesCurrentPosition);
}
}
}
};
ivMinutesUp.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
handler.postDelayed(runnable,500);
return false;
}
});
ivMinutesUp.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
handler.postDelayed(runnable,500);
return false;
}
});
ivMinutesUp.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View pView, MotionEvent pEvent) {
// We're only interested in when the button is released.
if (pEvent.getAction() == MotionEvent.ACTION_DOWN) {
// handler.postDelayed(runnable,300);
}
switch (pEvent.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
handler.removeCallbacks(runnable);
break;
}
pView.onTouchEvent(pEvent);
return false;
}
});
I used this:
private void hoursGoUpFast() {
isButtonReleased = false;
if (thread != null) {
thread.interrupt();
thread = null;
}
thread = new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i < hoursArray.length; i++) {
if (isButtonReleased) {
break;
} else {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
nextHour();
}
}
}
});
thread.start();
}
private void hoursGoDownFast() {
isButtonReleased = false;
if (thread != null) {
thread.interrupt();
thread = null;
}
thread = new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i < hoursArray.length; i++) {
if (isButtonReleased) {
break;
} else {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
previousHour();
}
}
}
});
thread.start();
}
on click :
// hours long click
ivHoursUp.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
hoursGoUpFast();
// handler.postDelayed(runnable, 500);
return false;
}
});
ivHoursDown.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
hoursGoDownFast();
// handler.postDelayed(runnable, 500);
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 make two buttons that will change add or subtract 1 from a value when tapped, and constantly add or subtract 1 ten times per second while the button is held. I can get the value to be changed when the button is tapped, or when it is held, but I can't get the behavior I want. Here's what I have:
btPlus.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
setTempo(mTempo + 1);
mTempo=mTempo+1;
tvTempo.setText(Integer.toString(mTempo));
return true;
}
return false;
}
});
btMinus.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
setTempo(mTempo - 1);
mTempo=mTempo-1;
tvTempo.setText(Integer.toString(mTempo));
return true;
}
return false;
}
});
btPlus.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
int action = motionEvent.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
if (getActivity() == null)
return;
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
tvTempo.setText(Integer.toString(mTempo));
mTempo++;
if (mTempo > 300)
mTempo = 300;
}
});
}
}, 100, 100);
break;
case MotionEvent.ACTION_UP:
timer.cancel();
}
return true;
}
});
btMinus.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
int action = motionEvent.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
if (getActivity() == null)
return;
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
tvTempo.setText(Integer.toString(mTempo));
mTempo--;
if (mTempo < 1)
mTempo = 1;
}
});
}
}, 100, 100);
break;
case MotionEvent.ACTION_UP:
timer.cancel();
}
return true;
}
});
return rootView;
}
Thanks for your help!
You can implement onKeyListener to determine how long button is held, like in this example:
Android long-touch event
or use onLongClickListener to perform a different function from a regular "click" like here:
On long click delete item
Even if I press the back button, my timer is still running and the next activity is executed after the specified time. How do I stop this?
Splash.java :
package com.ultimate.biggboss;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.widget.VideoView;
public class Splash extends Activity{
VideoView vid;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
vid = (VideoView)findViewById(R.id.video);
String urlpath="android.resource://" + getPackageName() + "/" + R.raw.logoanimate;
vid.setVideoURI(Uri.parse(urlpath));
vid.start();
Thread timer = new Thread(){
public void run(){
try{
sleep(4400);
} catch (InterruptedException e){
e.printStackTrace();
}finally{
Intent openHome = new Intent(Splash.this, main.class);
startActivity(openHome);
finish();
}
}
};
timer.start();
}
}
What code do I write and where in this class to stop the app as soon as I press the back button?
you can handle the back key press in the following way:
#Override
public void onBackPressed() {
// your code.
}
and before API 5 use this:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// your code
}
return super.onKeyDown(keyCode, event);
}
Inside this, you can do something like:
if(null != timer){
timer.cancel();
}
Note that the Thread's stop(), suspend() etc., methods have been
deprecated, the only way to safely terminate a thread is to have it
exit its run() method.
Try following:
public class MyThread extends Thread {
private long sleepTime;
private boolean stop;
public MyThread(long sleepTime) {
this.sleepTime = sleepTime;
stop = false;
}
public void run() {
do {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (!stop);
}
// soft stopping of thread
public synchronized void stopThread() {
stop = true;
}
}
To start the thread:
MyThread timer = new MyThread(4400);
timer.start();
To stop the thread:
timer.stopThread();
So you can handle it on pressing back button like:
#Override
public void onBackPressed() {
if(timer != null){
timer.stopThread();
timer = null;
}
}
You should interrupt the Thread
public void onBackPressed()
{
timer.interrupt();
this.finish();
}
I think you can use a flag to stop a thread, below is how i had done it:
public static int flag=0;//whenever flag=1, stop the thread. Flag should be a static global variable
#Override
public void onBackPressed() {
flag=1;
}
//My thread code was somewhat like this:
Thread timer = new Thread(){
public void run(){
try{
while(1)
{
if(flag==0)
//My code here
else
stopThread(this);
}
} catch (InterruptedException e){
e.printStackTrace();
}
}
};
timer.start();
The code for stopThread() is given below:
private synchronized void stopThread(Thread theThread)
{
if (theThread != null)
{
theThread = null;
}
}
This code worked for me. Hope it helps you too.
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.