How to do sample counter in Activity? This is not working.
public class MainActivity extends AppCompatActivity implements Runnable {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
run();
}
#Override
public void run() {
while (true) {
updateTv();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void updateTv() {
int counter = 100;
final TextView tv = (TextView) findViewById(R.id.tv);
tv.setText(String.valueOf(counter));
counter--;
}
}
In onCreate() you're starting an infinite loop inside of the UI thread, blocking it completely. Alternatively you could use a Handler for periodic updates. Maybe using a bigger delay and stop it sometime.
public class MainActivity extends AppCompatActivity implements Runnable {
private final Handler mHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
run();
}
#Override
public void run() {
updateTv();
mHandler.postDelayed(this, 17);
}
public void updateTv() {
int counter = 100;
final TextView tv = (TextView) findViewById(R.id.tv);
tv.setText(String.valueOf(counter));
counter--;
}
}
Anyway you should read What is the Android UiThread (UI thread) for sure.
Consider using Timer class which allows you to define a callback method that will be invoked at specified rate.
An example that fits your needs:
public class CounterActivity extends AppCompatActivity {
private TextView mCounterTextView;
private Timer mTimer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_counter);
mCounterTextView = (TextView) findViewById(R.id.counterTextView);
mTimer = new Timer();
mTimer.scheduleAtFixedRate(
new CounterTask(100), 0, TimeUnit.SECONDS.toMillis(1));
}
protected class CounterTask extends TimerTask {
protected int mCounter;
CounterTask(int initial) {
mCounter = initial;
}
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
mCounterTextView.setText(String.valueOf(mCounter));
}
});
--mCounter;
}
}
}
One more thing that should be noticed. As Timer executes it's own thread - it prevents you from updating your UI from outside of the main thread. In that case
you have to register a Runnable using runOnUiThread method.
Also, calling findViewById in a loop is not the best idea.
Related
I'm very new to Android development. Can anyone help me with this snippet, I don't know why it works perfectly although I'm updating my TextView from the worker thread.
When I say works perfectly, I mean the TextView shows the value count without any problem.
So, My question is - "Is it really possible to update the UI from background thread and if not, where I'm wrong"
public class MainActivity extends AppCompatActivity {
TextView textView ;
private int count;
Button btn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textview);
btn = findViewById(R.id.startbtn);
btn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
listen();
}
});
}
public void listen(){
new Thread(new Runnable() {
#Override
public void run() {
long time = System.currentTimeMillis();
while(System.currentTimeMillis()<=time +10000) {
count++;
}
textView.setText(count+"");
}
}).start();
}
}
Use
runOnUiThread(new Runnable() {
#Override
public void run() {
// Add UI code here
}
});
All UI updates should be done on the main thread.
TextView output;
int i;
Random random=new Random();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
output=findViewById(R.id.textView);
new Thread(new mythread()).start();
}
class mythread implements Runnable{
#Override
public void run() {
try {
while(true) {
i = random.nextInt(100);
output.setText(i + "");
Thread.sleep(500);
}
}catch (Exception e){}
}
}
}
it just showing one number in text view
but requirement is ,it should generate random number and keep updating textview after 500ms
Thank You!
The main problem, in your code, is that you can update UI only in the main thread and you are using a custom thread.
The second problem is that you are using Thread.sleep that is a very bad practise.
I suggest you to use Handler
Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
Log.d("Handler", "Running Handler");
handler.postDelayed(this, 500);
}
}
handler.postDelayed(runnable, 0);
and here the kotlin version
var handler = Handler()
var runnable = object : Runnable {
override fun run() {
Log.d("Handler", "Running Handler");
handler.postDelayed(this, 500)
}
}
handler.postDelayed(runnable, 500)
Try this, I think it will solved your problem.
public class MainActivity extends AppCompatActivity {
private Random random;
private Handler handler;
private TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.tv_number);
displayRandomNumber();
}
/**
* Display random number in text view
*/
private void displayRandomNumber()
{
random = new Random();
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
int value = random.nextInt();
textView.setText(String.valueOf(value));
handler.postDelayed(this,2000);
}
}, 2000);
}
}
The 'setRequestedOrientation' method restarts the activity normally on any version of the android higher than Kitkat.
But in Kitkat, even using if, the activity continues to restart.
int orientation = getResources().getConfiguration().orientation;
// Doesn't work
if (orientation != ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
}
// Doesn't work
if (orientation != Configuration.ORIENTATION_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
}
EDIT 1 - StackTrace:
https://gist.github.com/sshnakamoto/11ef6179a561054e54ec4d41a03238f0
Sorry, my log is too long to post here. I've created a gist. But essentially you will see a loop between onCreate() and onStart() methods.
EDIT 2 - ActivityCode:
public class TestActivity extends AppCompatActivity {
private static final String TAG = "TimerActivityCLONE";
private TextView textView;
private ConstraintLayout parentView;
private boolean isColorChanged;
private int textColor;
private int parentColor;
private Handler handler;
private Runnable runnable;
private Timer timer;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate: started ");
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate: super called ");
setContentView(R.layout.activity_timer);
Log.d(TAG, "onCreate: setContentView called ");
/* Find on layout*/
parentView = findViewById(R.id.parent);
textView = findViewById(R.id.textView);
textColor = Color.WHITE;
parentColor = Color.BLACK;
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
}
private void initTimer() {
handler = new Handler();
runnable = new Runnable() {
#Override
public void run() {
changeColors();
}
};
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
handler.post(runnable);
}
};
timer = new Timer();
timer.schedule(timerTask, 0, 1000);
}
private void changeColors() {
Log.d(TAG, "changeColors: size " + textView.getTextSize() / getResources().getDisplayMetrics().scaledDensity);
if (isColorChanged){
textView.setTextColor(parentColor);
parentView.setBackgroundColor(textColor);
isColorChanged = false;
} else {
textView.setTextColor(textColor);
parentView.setBackgroundColor(parentColor);
isColorChanged = true;
}
}
#Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart: ");
/* Start to show */
initTimer();
}
#Override
protected void onStop() {
super.onStop();
killTaskAndFinish();
}
#Override
public void onBackPressed() {
super.onBackPressed();
killTaskAndFinish();
}
private void killTaskAndFinish() {
/* Kill background Thread */
timer.cancel();
timer.purge();
handler.removeCallbacks(runnable);
/* Restore user screen orientation */
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
}
}
I've found the bug, it occurs when calling method killTaskAndFinish() inside onStop() due ResquestedOrientation() method restart activity.
But why does this loop only occur on Kitkat (emulator?)? Testing Lollipop it does not happen
I don't know why only occurs on KikKat, but I was able to fix removing handler use. Only TimerTask was need in my case.
This fixes that bug and prevent memory leaks.
#Override
protected void onStart() {
super.onStart();
timerTask = new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
changeColors();
}
});
}
};
timer = new Timer();
timer.schedule(timerTask, 0, speed);
}
#Override
protected void onStop() {
super.onStop();
timerTask.cancel();
timer.cancel();
timer.purge();
}
i'm working on an Android application in which i want the background image to be changed after every 5 seconds. i have all the images in my drawable folder.
i am giving the code which i am using but i am not getting the output. As an output i am getting a still image which is not changing.
Please help
Thanks
[CODE]
public class Home extends Activity {
public static int count=0;
int[] drawablearray=new int[]{R.drawable.slider_1,R.drawable.slider_2,R.drawable.slider_3,R.drawable.slider_4,R.drawable.slider_5};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
new Handler().postDelayed(new Runnable() {
public void run() {
if(count<drawablearray.length){
Home.this.getWindow().
setBackgroundDrawableResource(drawablearray[count]);
count++; //<<< increment counter here
}
else{
// reset counter here
count=0;
}
}
}, 5000);
}
}
You can achieve this using Timer
public class Home extends Activity {
public static int count=0;
int[] drawablearray=new int[]{R.drawable.slider_1,R.drawable.slider_2,R.drawable.slider_3,R.drawable.slider_4,R.drawable.slider_5};
Timer _t;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
lnMain = (LinearLayout) findViewById(R.id.lnMain);
_t = new Timer();
_t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() // run on ui thread
{
public void run() {
if (count < drawablearray.length) {
lnMain.setBackgroundDrawable(drawablearray[count]);
count = (count + 1) % drawablearray.length;
}
}
});
}
}, 5000, 5000);
}
}
Why don't you have a look at the ViewFlipper class
http://developer.android.com/reference/android/widget/ViewFlipper.html
final Handler h = new Handler();
Runnable r = new Runnable() {
public void run() {
Home.this.getWindow().setBackgroundDrawableResource(drawablearray[count]);
count += (count+1)%drawablearray.length; //<<< increment counter here
h.postDelayed(this, 5000);
}
};
now call like
h.postDelayed(r, 5000);
I think that would be easier to work with the xmls. You can change the background of the main layout of the activity.
Try something like this:
public class Home extends Activity {
public static int count=0;
int[] drawablearray=new int[]{R.drawable.slider_1,R.drawable.slider_2,R.drawable.slider_3,R.drawable.slider_4,R.drawable.slider_5};
LinearLayout ll;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
ll = (LinearLayout) findViewByID(R.id.mainlayout) //It depends of the name that you gave to it
new Handler().postDelayed(new Runnable() {
public void run() {
ll.setBackgroundDrawable(drawablearray[count]);
// or ll.setBackgroundResource(resid) if you want.
count += (count+1)%drawablearray.length;
}
}, 5000);
}
}
I know you can only change the text in tTxtViews from the UI thread but i cant seem to find a way to work with that.
I'll go in to a bit more details: I'm trying to have a TextView that displays the time passed, but I can't do it in a thread, or a method that keeps getting called.
can you help me with a way of doing this? because I'm pretty much out of ideas.
Thanks.
public class MainActivity extends Activity {
protected static final long TIMER_DELAY = 100;
private TextView tv;
protected Handler handler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView)findViewById(R.id.helloWorld);
handler = new Handler();
handler.post(timerTask);
}
private Runnable timerTask = new Runnable() {
public void run() {
Calendar now = Calendar.getInstance();
//format date time
tv.setText(String.format("%02d:%02d:%02d", now.get(Calendar.HOUR_OF_DAY), now.get(Calendar.MINUTE), now.get(Calendar.SECOND)));
//run again with delay
handler.postDelayed(timerTask, TIMER_DELAY);
}
};
}
I forgot add this, sorry. Don't forget do this:
#Override
public void onPause() {
if (handler != null)
handler.removeCallbacks(timerTask);
super.onPause();
}
And if you want resume app try this
#Override
public void onResume() {
super.onResume();
if (handler != null)
handler.post(timerTask);
}
Use this
new Thread(new Runnable() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
// Do what you want.
}
});
}
}).start();
or use a Handler:
Runnable r = new Runnable() {
#Override
public void run() {
// Do what you want.
}
};
Handler mHandler = new Handler();
mHandler.post(r);