I\'m new to Android so sorry if the question is easy to answer. I have two buttons, a decrease and an increase button, and in the middle of them a TextView which displays a
Just wanna share my own solution that worked out for me really well.
First, create a handler in your activity
private Handler mHandler = new Handler();
Then, create the runnables that will increment/decrement and display your number. In here, we will check if your button is still in its pressed state and increment then re-run the runnable if it is.
private Runnable incrementRunnable = new Runnable() {
@Override
public void run() {
mHandler.removeCallbacks(incrementRunnable); // remove our old runnable, though I'm not really sure if this is necessary
if(IncrementButton.isPressed()) { // check if the button is still in its pressed state
// increment the counter
// display the updated value here, if necessary
mHandler.postDelayed(incrementRunnable, 100); // call for a delayed re-check of the button's state through our handler. The delay of 100ms can be changed as needed.
}
}
}
Finally, use it in our button's onLongClickListener
IncrementButton.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
mHandler.postDelayed(incrementRunnable, 0); // initial call for our handler.
return true;
}
});
That's it!
Another way of doing it is declaring both the handler and runnable inside the OnLongClickListener itself although I myself am not sure if this is a good practice.
IncrementButton.setOnLongClickListener(new View.OnLongClickListener() {
private Handler mHandler = Handler();
private Runnable incrementRunnable = new Runnable() {
@Override
public void run() {
mHandler.removeCallbacks(incrementRunnable);
if(IncrementButton.isPressed()) {
// increment the counter
// display the updated value here, if necessary
mHandler.postDelayed(incrementRunnable, 100);
}
}
};
@Override
public boolean onLongClick(View view) {
mHandler.postDelayed(incrementRunnable, 0);
return true;
}
});
When doing this continuous increment, I would suggest to increase the increment value after a certain time/number of increments. E.g. If the number_of_increment made is less than 10, we increment by 1. Otherwise, we increment by 3.
It seems there is no perfect solution to this question and there will always be some complexity involved.
This is my attempt, which incorporates Wiktor's answer, but gives a whole MainActivity that you can cut/paste.
In my example, the complex part is the onLongClickListener, and how deep it goes and how many levels of anonymous classes there are.
However, on the other hand, the simplicity is that everything is included in one relatively short class (MainActivity), and there is only one major block of code -- the onLongClickListener -- which is defined only once and it's very clear where the "action" code is:
package com.example.boober.aalongclickoptimizationunit;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends AppCompatActivity {
TextView valueDisplay;
Button minusButton;
Button plusButton;
Button[] arrayOfControlButtons;
Integer currentDisplayValue = 500;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
valueDisplay = findViewById(R.id.value);
minusButton = findViewById(R.id.minusButton);
plusButton = findViewById(R.id.plusButton);
arrayOfControlButtons = new Button[]{plusButton, minusButton}; // this could be a large set of buttons
updateDisplay(); // initial setting of display
for (Button b : arrayOfControlButtons) {
b.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(final View v) {
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
if (v.isPressed()) { // important: checking if button still pressed
runOnUiThread(new Runnable() {
@Override
public void run() {
// --------------------------------------------------
// this is code that runs each time the
// long-click timer "goes off."
switch (v.getId()) {
// which button was pressed?
case R.id.plusButton: {
currentDisplayValue = currentDisplayValue + 10;
break;
}
case R.id.minusButton: {
currentDisplayValue = currentDisplayValue - 10;
break;
}
}
updateDisplay();
// --------------------------------------------------
}
});
} else
timer.cancel();
}
}, 100, 200);
// if set to false, then long clicks will propagate into single-clicks
// also, and we don't want that.
return true;
}
});
}
}
// ON-CLICKS (referred to from XML)
public void minusButtonPressed(View ignored) {
currentDisplayValue--;
updateDisplay();
}
public void plusButtonPressed(View ignored) {
currentDisplayValue++;
updateDisplay();
}
// INTERNAL
private void updateDisplay() {
valueDisplay.setText(currentDisplayValue.toString());
}
}
for kotlin user
myButton.setOnLongClickListener {
val handler = Handler(Looper.myLooper()!!)
val runnable : Runnable = object : Runnable {
val number = 0
override fun run() {
handler.removeCallbacks(this)
if (myButton.isPressed) {
val newNumber= number + 1
textView.text = "$newNumber Items"
handler.postDelayed(this, 100)
}
}
}
handler.postDelayed(runnable,0)
true
}
I am late to answer this question but it may help any one who's looking for a better answer.
I created a CounterHandler
class and its insanely easy to use to achieve the above mentioned continuous counter functionality.
You can find the class in following gist with a "how to use" example. https://gist.github.com/nomanr/d142f4ccaf55ceba22e7f7122b55b9b6
Sample code
new CounterHandler.Builder()
.incrementalView(buttonPlus)
.decrementalView(buttonMinus)
.minRange(-50) // cant go any less than -50
.maxRange(50) // cant go any further than 50
.isCycle(true) // 49,50,-50,-49 and so on
.counterDelay(200) // speed of counter
.counterStep(2) // steps e.g. 0,2,4,6...
.listener(this) // to listen counter results and show them in app
.build();
Thats all. :)
import java.awt.; import java.awt.event.; public class LabelNumber extends Frame implements ActionListener {
Button badd,bsub;
TextField t1;
void display()
{
setTitle("Label number change");
setSize(400,500);
setLayout(new FlowLayout());
setVisible(true);
badd=new Button("+");
t1=new TextField("0",6);
bsub= new Button("-");
add(bsub);add(t1);add(badd);
badd.addActionListener(this);
bsub.addActionListener(this);
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}
);
}
public void actionPerformed(ActionEvent e)
{
int count=0,add=0,sub,n1,n2;
count=Integer.parseInt(t1.getText());
if(e.getSource()==badd)
{
if(count<10)
{
count=count+1;
t1.setText(""+count);
}
else
{
t1.setText("limit:10");
}
}
if(e.getSource()==bsub)
{
if(count<10)
{
count=count-1;
t1.setText(""+count);
}
else
{
t1.setText("limit:10");
}
}
}
public static void main(String args[])
{
LabelNumber obj =new LabelNumber();
obj.display();
}
}
For that to work, you need a thread that will update the integer value when you long press on a button.
Create a handler in your activity:
private Handler repeatUpdateHandler = new Handler();
And 2 vars which will state: is it increment or decrement? Only one set at a time.
private boolean mAutoIncrement = false;
private boolean mAutoDecrement = false;
And the present number value
public int mValue;
And a class that will run in another thread:
class RptUpdater implements Runnable {
public void run() {
if( mAutoIncrement ){
increment();
repeatUpdateHandler.postDelayed( new RptUpdater(), REP_DELAY );
} else if( mAutoDecrement ){
decrement();
repeatUpdateHandler.postDelayed( new RptUpdater(), REP_DELAY );
}
}
}
Add a long press listener to your button:
mBTIncrement.setOnLongClickListener(
new View.OnLongClickListener(){
public boolean onLongClick(View arg0) {
mAutoIncrement = true;
repeatUpdateHandler.post( new RptUpdater() );
return false;
}
}
);
mBTIncrement.setOnTouchListener( new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if( (event.getAction()==MotionEvent.ACTION_UP || event.getAction()==MotionEvent.ACTION_CANCEL)
&& mAutoIncrement ){
mAutoIncrement = false;
}
return false;
}
});
In the above case the button is the increment one. Create another button which will set mAutoDecrement to true.
And decrement() will be a function, which will set your instance int variable like this:
public void decrement(){
mValue--;
_value.setText( ""+mValue );
}
You figure the increment out. Oh and REP_DELAY is a static int variable set to 50.
I see this is an excerpt from Jeffrey Cole's open source NumberPicker available at http://www.technologichron.net/ Proper author's attribution must be added.