40

I've been searching for a KISS example of how to do this, and while they all seem (and I've gone through them ALL!) simple enough, I still cannot get my head around the concept. I'm referring to custom listeners (ones that do not extend anything)... In particular, creating a listener for a boolean variable so that I can set off some methods when its value changes.

this is an example of an example I've tried: android how to make listener to a custom variable?

If someone has the time to explain a custom listener and how it works in plain English, that would be much appreciated. I'm hoping that once I understand exactly how the listener is notified of a change, then I'll be able to create my own and understand how the damn thing works. This is giving me a headache... Thanks in advance

Here is where I'm at at the moment:

public class ChangeListener {
    boolean boo = false;

    public ChangeListener(boolean b){
        boo = b;
    }

    private listener l = null;

    public interface listener{
        public void onChange(boolean b);
    }

    public void setChangeListener(listener mListener){
        l = mListener;
    }

    public void somethingChanged(){
        if(l != null){
            l.onChange(boo);
        }
    }


}

This is a snapshot of my main activity:

public class Main extends Activity implements ChangeListener.listener{
    private ChangeListener listener;
    private boolean boo = false;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        listener = new ChangeListener(boo);
        listener.setChangeListener(this);
        listener.somethingChanged();

    }

    @Override
    public void onChange(boolean b) {
        // TODO Auto-generated method stub
        Toast.makeText(this, "REST = "+b, Toast.LENGTH_SHORT).show();
    }


}

Now, when the activity starts, the Toast displays the state of boo... great. Problem is this only happens within onCreate, which makes sense as .somethingChanged is placed in there. What I would like is for the Toast to be generated every time boo is changed from false to true, and true to false regardless if its within onCreate or not. I.e:

  • Click a button
  • Causes boo to change from false to true
  • Listener picks up on the change and runs whatever is in onChange

I know I could just use the button actionListener to do this, but I would like to stay away from this if possible, which at the moment I'm starting to think is impossible.

Any insight would be much appreciated. Thanks in advance!

Community
  • 1
  • 1
user1977217
  • 401
  • 1
  • 4
  • 4
  • I'm considering using simple setX/getX methods to change the value and setoff other methods. This will probably be enough but I still think my original request has a lot of value if its possible to do in Java. Comments/solutions still appreciated. Thanks – user1977217 Jan 23 '13 at 02:06

3 Answers3

41

You wrap the variable up inside a custom class such that the only access functions also trip the listener. So that the only way to flip the switch is via the functions which toggles and calls the listener.

public class BooVariable {
    private boolean boo = false;
    private ChangeListener listener;

    public boolean isBoo() {
        return boo;
    }

    public void setBoo(boolean boo) {
        this.boo = boo;
        if (listener != null) listener.onChange();
    }

    public ChangeListener getListener() {
        return listener;
    }

    public void setListener(ChangeListener listener) {
        this.listener = listener;
    }

    public interface ChangeListener {
        void onChange();
    }
}

To monitor the change you need to implement BooVariable.ChangeListener and then pass the BooVariable class a copy of "this" then, when you change the variable it calls onChange.

Also, keeping in mind you can just inline the code rather than extend directly:

BooVariable bv = new BooVariable();
bv.setListener(new BooVariable.ChangeListener() {
    @Override
    public void onChange() {
        Toast.makeText(MainActivity.this,"blah", Toast.LENGTH_LONG).show();
     }
});

PS. The toast must be called from the UI thread, so you need to switch the Looper if you're going to change the variable in a different thread. This likely won't come up though.

Tatarize
  • 10,238
  • 4
  • 58
  • 64
32

There is a much easier solution now. Just wrap your variable in a LiveData Specifically MutableLiveData.

Java Version

MutableLiveData<String> listen = new MutableLiveData<>();

listen.setValue("Changed value"); //Initilize with a value

listen.observe(context,new Observer<String>() {
    @Override
    public void onChanged(String changedValue) {
        //Do something with the changed value
    }
});

Kotlin Version

val listen : MutableLiveData<String> =  MutableLiveData<>()

listen.setValue("Changed value") //Initilize with a value

listen.observe(context, Observer {

    //Do something with the changed value -> it

})
GianhTran
  • 3,443
  • 2
  • 22
  • 42
Ratul Bin Tazul
  • 2,121
  • 1
  • 15
  • 23
  • can you please check if this method still works? I am not able to do `listen.setValue()`, do I have to write some extra code apart from above? (java version) – Teekam Suthar Nov 13 '20 at 05:10
  • There is an error on on this line: listen.observe(context, Observer { ... }) Changing the line to: listen.observe(context, { ... }) it works. – Caesar Jan 18 '21 at 15:52
  • I used the Java version of your code and it works perfectly fine.!! Not sure about the Kotlin version though – Juzer Taher Totanawala Jun 17 '21 at 12:10
0

We can achieve this by using MutableLiveData.

MutableLiveData is mutable and is a subclass of LiveData. In MutableLiveData we can observe and set the values using postValue() and setValue() methods (the former being thread-safe) so that we can dispatch values to any live or active observers.

So, create / convert the variable that you require to be watched into a MutableLiveData type.

Eg: String result; into MutableLiveData<String> result = new MutableLiveData<>();

You can set the variable value using setValue() method and get the current value by using getValue() method.

To watch the changes on the required variable, you can call the observe() method and override the onChanged() call back method.

The following code is an example of the same,

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;

import android.os.Bundle;
import android.os.CountDownTimer;
import android.widget.TextView;
import android.widget.Toast;

import java.util.Random;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //MutableLiveData object creation
        MutableLiveData<String> listen = new MutableLiveData<>();
        listen.setValue("Initial value: 0");

        //Listener for listening the changes
        listen.observe(MainActivity.this,new Observer<String>() {
            @Override
            public void onChanged(String changedValue) {
                Toast.makeText(MainActivity.this, listen.getValue(),Toast.LENGTH_LONG).show();
            }
        });

        //Changing values randomly
        new CountDownTimer(30000, 3000) {
            public void onTick(long millisUntilFinished) {
                listen.setValue("Changed value: " + new Random().nextInt(61));
                TextView txtResult = findViewById(R.id.txtResult);
                txtResult.setText(listen.getValue());
            }

            public void onFinish() {
                listen.setValue("Final value: 100");
                TextView txtResult = findViewById(R.id.txtResult);
                txtResult.setText(listen.getValue());
            }
        }.start();
    }
}

In this example the listen variable is a MutableLiveData and we are updating the listen value in each 3 second with the help of CountDownTimer. So, on each update the onChanged() call back is executed and the user will get a Toast message with updated value of the listen variable.

For more:

Codemaker2015
  • 12,190
  • 6
  • 97
  • 81