I'm trying the LiveData and ViewModel architecture. I have a simple counter in the ViewModel which is observed on MainActivity. I am able to confirm that the data is acquired in onChanged of the observer, but the problem is the textView is not updating every increment and I'm only getting the last count which 9.
Here is my code.
MainActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setNumber(model);
binding.setLifecycleOwner(this);
model = ViewModelProviders.of(this).get(MainActivityViewModel.class);
textView = findViewById(R.id.textView);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
model.incrementNumber();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
model.getNumber().observe(this, new Observer<String>() {
#Override
public void onChanged(final String s) {
Log.d(TAG, s);
textView.setText(s);
}
});
}
And here is the viewModel
public class MainActivityViewModel extends AndroidViewModel {
MutableLiveData<String> number;
public MainActivityViewModel(#NonNull Application application) {
super(application);
number = new MutableLiveData<String>("0");
}
public MutableLiveData<String> getNumber(){
return number;
}
public void incrementNumber() throws InterruptedException {
for(int x = 0; x < 10 ; x++){
Thread.sleep(500);
number.setValue(String.valueOf(x));
}
}
}
Try using a main thread Handler.postDelayed that will increment the number and post again to the Handler instead of Thread.sleep.
Related
I am trying to read a value from a database but does not seem to be working. The purpose of the code is to take in a counter value from a user's UID in Firebase, that will change the source of an image. The counter is changed in another activity and works fine. I feel I am close but not sure what do do next. I have tried to print out the value but it does not print in the console.
Full disclaimer, I am new to android development and firebase so I apologise in advance if it is a simple mistake/correction.
Activity calling the database UPDATED:
public class Menu extends AppCompatActivity {
private static final String TAG = "Menu";
Button btn;
public ImageView image;
public static int counter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);
// Add an image
image = findViewById(R.id.imageView2);
image.setImageResource(R.drawable.g1);
//image.setImageResource(getIntent().getIntExtra("myImageResource",R.drawable.g1));
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("counter");
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
menuCounter uCount = dataSnapshot.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).getValue(menuCounter.class);
System.out.println("Simon says" + uCount.counter);
counter = uCount.counter;
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("The read failed: " + databaseError.getCode());
}
});
System.out.println("Counter is: " + counter);
if (counter == 1) {
image.setImageResource(R.drawable.g2);
}
btn = findViewById(R.id.button1);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent WIP = new Intent(Menu.this, WIP.class);
startActivity(WIP);
}
});
}
}
Getter class:
package com.alanlyne.tbm;
public class menuCounter {
int counter;
public menuCounter() {
}
public menuCounter(int counter) {
this.counter = counter;
}
public int getCounter() {
return counter;
}
}
Activity changing the counter value:
public class WIP extends AppCompatActivity {
Button btn;
DatabaseReference databaseName;
FirebaseAuth mFirebaseAuth;
private FirebaseAuth.AuthStateListener mAuthStateListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wip);
databaseName = FirebaseDatabase.getInstance().getReference("counter");
btn = findViewById(R.id.button1);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Menu.counter = 1;
Intent act2= new Intent(WIP.this,Menu.class);
// act2.putExtra("myImageResource", R.drawable.g2);
startActivity(act2);
addCounter();
}
});
}
private void addCounter() {
Menu.counter = 1;
String id = databaseName.push().getKey();
menuCounter counter = new menuCounter(Menu.counter);
databaseName.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).setValue(counter);
}
}
Firebase image:
I have listed the code below, What i am trying to achieve is the int i to be updated from a dialog box (dialog2). I then want to check in the main activity if it has changed and if it has changed then call a method. How would i do this?
Main Activity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int i = 0;
Dialog1 dialog1 = new Dialog1(this, i);
dialog1.show();
}
//Want to call this method whenever I is modified
private void iModified(){
}
}
Dialog 1 Class (This dialog just passes it to dialog2)
public class Dialog1 extends Dialog{
int integerI;
Button button;
public Dialog1(final Activity activity, final int i){
super(activity);
setContentView(R.layout.dialog1);
integerI = i;
button = findViewById(R.id.dialog1Button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Dialog2 dialog2 = new Dialog2(activity ,integerI);
dialog2.show();
closeDialog();
}
});
}
private void closeDialog(){
this.dismiss();
}
}
Dialog 2: This is where the integer is going to be changed and then i want it to be sent to the main activity and checked if it has changed and if so then replace the old integer with the new one.
public class Dialog2 extends Dialog {
int newI;
Button button;
public Dialog2(Activity activity, int i) {
super(activity);
setContentView(R.layout.dialog2);
newI= i + 12345;
button = findViewById(R.id.dialog2Button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
close();
}
});
}
private void close(){
this.dismiss();
}
}
You can use interface.
your activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int i = 0;
Dialog1 dialog1 = new Dialog1(this, i, new ModifiedListener() {
#Override
public void notify(int i) {
iModified();
}
});
dialog1.show();
}
//Want to call this method whenever I is modified
private void iModified(){
}
}
your dialog1:
public class Dialog1 extends Dialog {
int integerI;
Button button;
public Dialog1(final Activity activity, final int i, final ModifiedListener listener) {
super(activity);
setContentView(R.layout.dialog1);
integerI = i;
button = findViewById(R.id.dialog1Button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Dialog2 dialog2 = new Dialog2(activity, integerI);
dialog2.show();
closeDialog();
}
});
}
private void closeDialog() {
this.dismiss();
}
}
your dialog 2:
public class Dialog2 extends Dialog {
int newI;
Button button;
public Dialog2(Activity activity, int i, final ModifiedListener listener) {
super(activity);
setContentView(R.layout.dialog2);
newI = i + 12345;
//when you modify int you have to call
listener.notify(newI);
button = findViewById(R.id.dialog2Button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
close();
}
});
}
private void close() {
this.dismiss();
}
}
define interface similar to class:
public interface ModifiedListener {
public void notify(int i);
}
I'm new to android but based on my understanding that onPostExecute has to run on the main UI thread to be able to access Views and so on which blocks the UI until it finishes. But the application looks ugly -as if it's crashing- when I try to rotate the device while onPostExecute is running (I know it should be a light weight task but I keep in mind slow phones so this might actually happen in my HBO)
Now, here's my code and I know I believe I should use interfaces for communication between my Task, Fragment, and Activity but it's just a proof of concept for now.
//MovieTask Class
public class MovieTask extends AsyncTask<Void, Void, String> {
private Activity activity;
public MovieTask(Activity activity) {
onAttach(activity);
}
//should be an interface
public void onAttach(Activity activit) {
this.activity = activit;
}
//should be an interface
public void onDetach() {
this.activity = null;
}
#Override
protected String doInBackground(Void... params) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
Log.e("ASYNC TASK", "DONE");
return "DONE: FROM DO TO POST";
}
#Override
protected void onPostExecute(String s) {
if(this.activity != null)
{
((MainActivity) activity).ShowResult(s);
Log.e("MovieTask", "Result Received");
}else
Log.e("MovieTask", "Activity is null (1)");
}
}
//My Non-UI Fragment to decouple the Task from the Activity
public class NonUIFragment extends Fragment {
private MovieTask myTask;
private Activity activity;
public NonUIFragment() {
// Required empty public constructor
}
public void BeginTask() {
if (activity != null) {
myTask = new MovieTask(activity);
myTask.execute();
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
//check that the passed context is an Activity first then,
if (context instanceof Activity) {
this.activity = (Activity) context;
if(myTask != null) {
myTask.onAttach((Activity) context);
}
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance
setRetainInstance(true);
}
#Override
public void onDetach() {
super.onDetach();
if(myTask != null) {
myTask.onDetach();
}
}
//Main Activity (Task Consumer)
public class MainActivity extends AppCompatActivity {
NonUIFragment nonUIFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
nonUIFragment = new NonUIFragment();
getSupportFragmentManager()
.beginTransaction()
.add(nonUIFragment, "nonUIFragment")
.commit();
}
else
{
nonUIFragment = (NonUIFragment) getSupportFragmentManager().findFragmentByTag("nonUIFragment");
}
Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
nonUIFragment.BeginTask();
}
});
}
//should be the consumer interface
public void ShowResult(String result)
{
try {
Thread.sleep(5000);
TextView txtVw = (TextView) findViewById(R.id.txtVw);
txtVw.setText(result);
} catch (InterruptedException e) {
Log.e("MovieTask", "mCallbacks is null (2)");
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
TextView txtVw = (TextView) findViewById(R.id.txtVw);
String result = txtVw.getText().toString();
outState.putString("result", result);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
String result = savedInstanceState.getString("result");
TextView txtVw = (TextView) findViewById(R.id.txtVw);
txtVw.setText(result);
}
}
UPDATE 1
In the chat Jigs suggested trying 'runOnUiThread', however onPostExecute already runs on the UI Thread so unfortunately it's kind of irrelevant. I guess what I'm trying to do is not block the UI while the behaviour of onPostExecute is a UI-Blocking in nature which makes it kind of impossible. I'll leave the question around in case anybody has different thoughts!
I have a simple Android program that calculates how long it takes the phone to compute a certain mathematical problem. I want the mathematical problem to start when I hit the button, and while it is running I want a spinning progress bar to be displayed, and I want it to disappear after the math problem is done. This is the code I currently have:
public class main extends AppCompatActivity {
private TextView mScore;
private Button mRunButton;
private TextView mScoreText;
private ProgressBar mSpinner;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mScore = (TextView) findViewById(R.id.score);
mRunButton = (Button) findViewById(R.id.runbutton);
mScoreText = (TextView) findViewById(R.id.scoreText);
mSpinner = (ProgressBar) findViewById(R.id.progress);
mSpinner.setVisibility(View.GONE);
View.OnClickListener listener = new View.OnClickListener(){
#Override
public void onClick(View v) {
mSpinner.setVisibility(View.VISIBLE);
long startTime = System.nanoTime();
long start = System.currentTimeMillis();
long count = 0l;
for(long x=0;x<Integer.MAX_VALUE ;x++){
count+=1;
}
long end = System.currentTimeMillis();
long endTime = System.nanoTime();
long duration = ((endTime - startTime) / 1000000);
mScore.setText(duration + "");
mSpinner.setVisibility(View.GONE);
}
};
mRunButton.setOnClickListener(listener);
}
}
From what I can tell, nothing in the view of the app updates until after the phone is done with the entire onClick method, which is not what I want it to do. I want the progress bar to be displayed ONLY while the program is 'working'. How would I go about doing this?
Thank you
As Blackbelt and vilpe89 mentioned, you have to separate the threads. You can do this by having another class that extends ASyncTask which will handle the calculations. The problem with that is that the progress dialog needs to run on the UI thread. You can have an interface that changes the progress dialog in the main class.
Calculator class:
public final class Calculator extends AsyncTask<Void, Void, Void> {
Context context;
calcCallback mCallback;
public Calculator(Context c) {
this.context = c;
this.mCallback = (calcCallback) c;
}
//The main class needs to implement this interface
public interface calcCallback {
Void calcDone();
Void calcStarted();
//Other methods if necessary
}
#Override
protected Boolean doInBackground(Void... params) {
mCallback.calcStarted();
//Your calculations here
mCallback.calcDone();
return null;
}
}
MainActivity
public class MainActivity extends Activity implements Calculator.calcCallback, {
private TextView mScore;
private Button mRunButton;
private TextView mScoreText;
private ProgressBar mSpinner;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mScore = (TextView) findViewById(R.id.score);
mRunButton = (Button) findViewById(R.id.runbutton);
mScoreText = (TextView) findViewById(R.id.scoreText);
mSpinner = (ProgressBar) findViewById(R.id.progress);
mSpinner.setVisibility(View.GONE);
View.OnClickListener listener = new View.OnClickListener(){
#Override
public void onClick(View v) {
Calculator calculator = new Calculator(MainActivity.this);
calculator.execute();
}
};
mRunButton.setOnClickListener(listener);
}
#Override
public Void calcStarted() {
runOnUiThread(new Runnable() {
#Override
public void run() {
mSpinner.setVisibility(View.VISIBLE);
}
});
}
return null;
}
#Override
public Void calcDone() {
runOnUiThread(new Runnable() {
#Override
public void run() {
mSpinner.setVisibility(View.GONE);
}
});
}
return null;
}
}
You can also set up your calcDone() as calcDone(int duration) so that you can pass the calculated duration back to the main thread.
I am writing my first android application, and am trying to write an app where you simply click a button to display a message, and can do so as many times as you want. So far I have:
public class MyProject extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final TextView tv = new TextView(this);
Button startButton = (Button) findViewById(R.id.startbutton);
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
tv.setText("Hello World!");
setContentView(tv);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {setContentView(R.layout.main);}}, 2000);
}
});
}
}
However doing it this way, when I get back to my main screen and click on the button again...nothing happens. How can I get repeated button clicks to repeat the behaviour?
Your example is very simple and, from what I understand, no multithreading is needed.
You just have to initialize your layout one time, after that you can update the content of the single views.
So... this will set "hello world" string on every button click, you will not notice any difference because the string is always the same :D
public class MyProject extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// this is needed one time only
setContentView(R.layout.main);
// add your textview in xml
final TextView tv = (TextView) findViewById(R.id.textView);
final Button startButton = (Button) findViewById(R.id.startbutton);
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
tv.setText("Hello World!");
}
}
}
}
To do something more fun you can set a counter to update on every click, this way the textview change will be noticeable are more fun!!
int i = 0;
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
tv.setText("you have reached " + i);
i++;
}
}
Here, you are calling setContentView to replace the entire current view with a TextView containing "Hello World!" Then after two seconds you setContentView again to set it back to the main layout. Since OnCreate is only called once when the activity is created, the OnClickListener is never set again.
This code will do what you are looking for.
public class MyActivity extends Activity
implements View.OnClickListener{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
}
private void init() {
setContentView(R.layout.main);
Button startButton = (Button) findViewById(R.id.startbutton);
startButton.setOnClickListener(this);
}
#Override
public void onClick(View v) {
TextView tv = new TextView(this);
tv.setText("Hello World!");
setContentView(tv);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
init();
}
}, 2000);
}
}
You may notice that OnClick is a separate method in my example. This is because I implemented the interface View.OnClickListener. I was able to write the implemented method as part of my class and then pass this as an argument to setOnClickListener. I'm explaining this because you mentioned you are new to Java.
TextView txt;
// our handler
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
//display each item in a single line
txt.setText(txt.getText()+"Item "+System.getProperty("line.separator"));
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
txt=(TextView)findViewById(R.id.txt);
}
#Override
protected void onStart() {
super.onStart();
// create a new thread
Thread background=new Thread(new Runnable() {
#Override
public void run() {
for(int i=0;i<10;i++)
{
try {
Thread.sleep(1000);
b.putString("My Key", "My Value: "+String.valueOf(i));
// send message to the handler with the current message handler
handler.sendMessage(handler.obtainMessage());
} catch (Exception e) {
Log.v("Error", e.toString());
}
}
}
});
background.start();
}
}
Note:Handler one type of thread not udpate any UI here
Take an example from here