Bear with me, I'm still a little new to android.
So I am in the process of making a complex button listener. This listener should recognize a short press, flash in some way (like the normal onClick functionality), as well recognize a long press.
Upon a short press, it would update a (integer) textView by increasing/decreasing the value. If the user holds the button down the textView, it will continuously increase/decrease until the button is released.
Thus far I have found 2 popular threads regarding this sort of issue:
The problem I have with the the answers in these threads is I feel they can be broken down further. Instead of creating a single class to hold everything regarding the button listener, I would like to separate the physical "Task" that would run on the handler thread into it's own class. In fact, I believe this is what the android documentation recommends doing
https://developer.android.com/training/multiple-threads/communicate-ui#java
Right now I have a working implementation for the normal passing of data between the task on the handler and the UI thread for a single alteration. However, once I start adding in loops, it stops working properly. If I do a for loop (i.e. from 0 to 9), it seems to start ignoring the "delay" part of the post, and does all of the increments, and updates the UI when it's all finished. If I try a while loop, it never updates the textView for similar reasons as why the for loop might have failed.
Here are the classes I have implemented, I'll explain bellow:
public class AutoUpdatingListener implements View.OnTouchListener, View.OnClickListener, View.OnLongClickListener {
private TextView textView;
private int textNumber;
private String decision;
private AutoUpdateTask updateTask;
private static final String MSG_KEY = "textData";
private final Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
Bundle bundle = msg.getData();
int data = bundle.getInt(MSG_KEY);
textView.setText(String.format(Locale.getDefault(), "%d", data));
}
};
public AutoUpdatingListener(TextView textView, String decision) {
this.decision = decision;
this.textView = textView;
this.textNumber = Integer.parseInt(textView.getText().toString());
updateTask = new AutoUpdateTask(handler, textNumber, decision, MSG_KEY);
}
public void onClick(View v) {
textNumber = (decision.equals("increase")) ? ++textNumber : (decision.equals("decrease")) ? --textNumber : textNumber;
textView.setText(String.format(Locale.getDefault(),"%d", textNumber));
}
public boolean onLongClick(View v) {
autoUpdate = true;
handler.postDelayed(updateTask, 500);
return true;
}
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
autoUpdate = false;
handler.removeCallbacks(updateTask);
}
return false;
}
}
Here is the Task Object:
public class AutoUpdateTask implements Runnable {
private Handler handler;
private int textNumber;
private final String decision;
private final String MSG_KEY;
public AutoUpdateTask(Handler handler, int tempo, String decision, final String MSG_KEY) {
this.handler = handler;
this.tempo = tempo;
this.decision = decision;
this.MSG_KEY = MSG_KEY;
}
@Override
public void run() {
// 2nd variation would be to place the loop here
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
for (int i = 0; i < 10; i++) {
textNumber = decision.equals("increase") ? ++tempo : decision.equals("decrease") ? --tempo : tempo;
Message msg = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putInt(MSG_KEY, tempo);
msg.setData(bundle);
handler.sendMessage(msg);
SystemClock.sleep(500);
}
}
}
This is all being used in the following context within the onCreate
method:
AutoIncrementingListener customListener = new AutoIncrementingListener(textView, "increase");
upArrow.setOnClickListener(customListener);
upArrow.setOnLongClickListener(customListener);
upArrow.setOnTouchListener(customListener);
So essentially if the user does a short click, it updates the field and the action is done. If the user holds the button, it will go to the longClick listener first, and create a task consisting of a reference to the handler, the text to increment, the "increase" determination, and a unique message key.
The task is then posted with a delay onto the handler thread, and it is executed (the number is incremented), and it then creates a message to send back to the UI thread and updates the textView. This process continues until the user releases the button.
This is all in theory, as I've explained above, it currently doesn't quite work as defined.
Update
I have since put the loop into the task's run()
method. In this loop I have added a sleep(500)
call to stop the increments from firing at the same time.
However, the issue of the UI not updating still persists (despite the sent messages and implemented handleMessage()
function). I.E: after about 5 seconds when it completes the loop it finally updates the textView.
Conclusion
I am abandoning this implementation, so sorry for those who have come across this question seeking answers. I hope this thread has given some insight towards your own implementations.
With the above update, I realized through debugging that regardless of execution time, the messages were being sent all together after the loop was done. For those seeking this implementation, they will have to find out how to short-cut this phenomenon.