I want to refresh a TextView's text per minute when Digital Clock refresh the time with the format hh/mm every minute. I put a TextView named txtView1 in the Activity and create a class Digital Clock.When I run the app,However,the app exits with error.I really don't konw why
here is the important function onAttachedToWindow() about the Digital Clock:
protected void onAttachedToWindow() {
mTickerStopped = false;
super.onAttachedToWindow();
mHandler = new Handler();
/**
* requests a tick on the next hard-second boundary
*/
mTicker = new Runnable() {
public void run() {
if (mTickerStopped) return;
mCalendar.setTimeInMillis(System.currentTimeMillis());
String content = (String) DateFormat.format(mFormat, mCalendar);
if(content.split(" ").length > 1){
content = content.split(" ")[0] + content.split(" ")[1];
}
setText(android.text.Html.fromHtml(content));
//-----Here is the TextView I want to refresh
TextView txtV1 = (TextView)findViewById(R.id.txtView1);
txtV1.setText("Now Fresh");//Just for try,so set a constant string
invalidate();
long now = SystemClock.uptimeMillis();
//refresh each minute
long next = now + (60*1000 - now % 1000);
mHandler.postAtTime(mTicker, next);
}
};
mTicker.run();
}
The system sends a broadcast event at the exact beginning of every minutes based on system clock. The most reliable way is to do it like this :
BroadcastReceiver _broadcastReceiver;
private final SimpleDateFormat _sdfWatchTime = new SimpleDateFormat("HH:mm");
private TextView _tvTime;
#Override
public void onStart() {
super.onStart();
_broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context ctx, Intent intent) {
if (intent.getAction().compareTo(Intent.ACTION_TIME_TICK) == 0)
_tvTime.setText(_sdfWatchTime.format(new Date()));
}
};
registerReceiver(_broadcastReceiver, new IntentFilter(Intent.ACTION_TIME_TICK));
}
#Override
public void onStop() {
super.onStop();
if (_broadcastReceiver != null)
unregisterReceiver(_broadcastReceiver);
}
Don't forget however to initialize your TextView beforehand (to current system time) since it is likely you will pop your UI in the middle of a minute and the TextView won't be updated until the next minute happens.
Related
I have this countdown timer which i modified to count up.The timer will keep on running at the same time give percentage on a textview (tvp). below are the code.
timer = new CountUpTimer(date3) {
public void onTick(int secondt) {
simpleDateFormat = new SimpleDateFormat ("HH:mm:ss");
tv_timer.setText(simpleDateFormat.format (secondt));
t1 = date3 - date;
percentage = Math.round (secondt/t1 *100);
tvp.setText ( percentage+"%" );
}
};timer.start();
The timer is running on background which i use services and broadcast receiver and it work just fine.
The problem is, the percentage textview is not updating and running and stuck on the last value after pressing back button. below are the runnable code used to update percentage textview.
getActivity ().runOnUiThread(new Runnable() {
#Override
public void run() {
tvp.setText ( percentage + "%" );
}
});
below are my timer service code
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
mpref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
mEditor = mpref.edit();
mTimer.scheduleAtFixedRate(new TimeDisplayTimerTask(), 5, NOTIFY_INTERVAL);
intent = new Intent(str_receiver);
}
class TimeDisplayTimerTask extends TimerTask {
#Override
public void run() {
mHandler.post( () -> {
timerprogressupdate ();
} );
}
}
public String timerprogressupdate() {
fn_update ( IFTimer.secondt );
fn_updatep ( IFTimer.percentage );
return timerprogressupdate();
}
#Override
public void onDestroy() {
super.onDestroy();
Log.e("Service finish","Finish");
}
private void fn_update(long str_time){
intent.putExtra("time",str_time);
sendBroadcast(intent);
}
}
broadcast receiver
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String strDate = intent.getStringExtra("time");
}
};
I need the percentage textview updating and running after i press back button. Anyone have a solution on this? Appreciated if anyone could help me.
I have requirement to implement, In my activity, I receive an OTP for login, the OTP has be expired in 90 seconds.
Questions
1> Is Alarm Manager is best way to implement the 90 second time expiry?
2> If I have received OTP and same time I receive a call and when call is ended after 90 seconds and when i come back to original
activity , user should be shown a pop up saying OTP has been expired?
any help will be appreciated.
Thanks
Use CountDownTimer
new CountDownTimer(90000, 1000) {
public void onTick(long millisUntilFinished) {
Log.d("seconds remaining: " , millisUntilFinished / 1000);
}
public void onFinish() {
// Called after timer finishes
}
}.start();
You can use TimerTask like below sample :
public class AndroidTimerTaskExample extends Activity {
Timer timer;
TimerTask timerTask;
//we are going to use a handler to be able to run in our TimerTask
final Handler handler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
super.onResume();
//onResume we start our timer so it can start when the app comes from the background
startTimer();
}
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, after the first 5000ms the TimerTask will run every 10000ms
timer.schedule(timerTask, 5000, 10000); //
}
public void stoptimertask(View v) {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
//use a handler to run a toast that shows the current timestamp
handler.post(new Runnable() {
public void run() {
//get the current timeStamp
Calendar calendar = Calendar.getInstance();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd:MMMM:yyyy HH:mm:ss a");
final String strDate = simpleDateFormat.format(calendar.getTime());
//show the toast
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(getApplicationContext(), strDate, duration);
toast.show();
}
});
}
};
}}
You can change start and stop of Task as per your call and initialise too whenever you want.
Good afternoon everyone
So, I'm trying to dynamically change textview's properties.
basically, I defined a Duration. and I want to my handler/runnable to append text to a textView until I reach the duration.
public class Dynamic_testActivity extends Activity
{
public Context context = null;
public TextView view = null;
public Handler mHandler = null;
public long startTime = 0L;
public final int duration_millis = 10000;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
context = getApplicationContext();
view = (TextView) findViewById(R.id.textView1);
view.append("\n");
mHandler = new Handler();
}
#Override
protected void onStart() {
super.onStart();
startTime = System.currentTimeMillis();
mHandler.post(new Runnable() {
#Override
public void run() {
view.append("Hell_yeah_!\n");
// 10 character lenght
}
});
}
}
So yes, it append the text once, because the run do so.
But how could I make some kind of loop, without blocking the UI Thread, and append text until the end of the duration.
That was the first step ...
The second part now ... In fact, I Want to change the color of the text.
using
Spannable WordtoSpan = new SpannableString(view.getText());
WordtoSpan.setSpan(new ForegroundColorSpan(Color.BLUE), 0, view.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
I want to the color changing to be dynamic for the duration ... like a karaoke ...
So, is it possible to make that in the runnable without having the UI Thread blocked until the end of the duration ? and How ?
If anyone could explain the complet process ? or post some source code
Solved.
Here is a basic example...
There is still a little trouble ... at the very beginning of the application, the whole textview is yellow, and after a second, it updates the display as it should be .
If any one knows why, advices are welcome =)
Note : there's only two simple Textview in the layout... Duration is in milliseconds... and there is 10 character in the dynamic textview to fit the duration ... So basically, one char = one second ...
public class Dynamic_testActivity extends Activity
{
public Context context = null;
public TextView view = null;
public TextView view2 = null;
public Handler handler = null;
public long start_time, current_time, elapsed_time = 0L;
public final int duration = 10000;
public int end = 0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
context = getApplicationContext();
view = (TextView) findViewById(R.id.textView1);
view2 = (TextView) findViewById(R.id.textView2);
handler = new Handler();
}
#Override
protected void onStart() {
super.onStart();
start_time = Long.valueOf( System.currentTimeMillis() );
current_time = start_time;
handler.postDelayed(new Runnable() {
#Override
public void run() {
current_time = Long.valueOf( System.currentTimeMillis() );
elapsed_time = Long.valueOf(current_time) - Long.valueOf(start_time);
if ( elapsed_time >= duration + 30 ) {
Toast.makeText(context, "Done", Toast.LENGTH_LONG).show();
//finish();
} else {
end = (int) (elapsed_time / 1000);
Spannable WordtoSpan = new SpannableString(view.getText());
WordtoSpan.setSpan(new ForegroundColorSpan(Color.YELLOW), 0, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
view.setText(WordtoSpan);
view2.setText("time : " + elapsed_time);
handler.postDelayed(this, 10);
}
}
}, 10);
}
}
In your run() methond, you can call mHandler.post(this) (or use postDelayed to delay it)
There is property change animation in API level 14, but if you are targetting a lower version, use postDelayed repetively to change progressively the color of the text.
So I have a TextSwitcher that I want to update every second with the number of seconds it has been since the activity opened. Here is my code
public class SecondActivity extends Activity implements ViewFactory
{
private TextSwitcher counter;
private Timer secondCounter;
int elapsedTime = 0;
#Override
public void onCreate(Bundle savedInstanceState)
{
// Create the layout
super.onCreate(savedInstanceState);
setContentView(R.layout.event);
// Timer that keeps track of elapsed time
counter = (TextSwitcher) findViewById(R.id.timeswitcher);
Animation in = AnimationUtils.loadAnimation(this,
android.R.anim.fade_in);
Animation out = AnimationUtils.loadAnimation(this,
android.R.anim.fade_out);
counter.setFactory(this);
counter.setInAnimation(in);
counter.setOutAnimation(out);
secondCounter = new Timer();
secondCounter.schedule(new TimerUpdate(), 0, 1000);
}
/**
* Updates the clock timer every second
*/
public void updateClock()
{
//Update time
elapsedTime++;
int hours = elapsedTime/360;
int minutes = elapsedTime/60;
int seconds = elapsedTime%60;
// Format the string based on the number of hours, minutes and seconds
String time = "";
if (!hours >= 10)
{
time += "0";
}
time += hours + ":";
if (!minutes >= 10)
{
time += "0";
}
time += minutes + ":";
if (!seconds >= 10)
{
time += "0";
}
time += seconds;
// Set the text to the textview
counter.setText(time);
}
private class TimerUpdate extends TimerTask
{
#Override
public void run()
{
updateClock();
}
}
#Override
public View makeView()
{
Log.d("MakeView");
TextView t = new TextView(this);
t.setTextSize(40);
return t;
}
}
So basically, I have a Timer that every second adds another second and them formats the way I want to be displayed and set the text of the TextSwitcher, which I thought called makeView, but makeView only gets called once and the time stays as 00:00:01. Did I miss a step, I dont think this UI object is very well documented.
Thanks, Jake
You can only update the UI in the UI thread. So in your example you could do something like this.
private Handler mHandler = new Handler() {
void handleMessage(Message msg) {
switch(msg.what) {
CASE UPDATE_TIME:
// set text to whatever, value can be put in the Message
}
}
}
And call
mHandler.sendMessage(msg);
in the run() method of the TimerTask.
This is a solution to your current issue but there is probably a better way to do it without using TimerTasks.
I do have one service running in the background. Whenever it starts I store in memory the starting time in milliseconds:
startingTime = new Date().getTime();
I want to display a chronometer that starts counting when the service starts and never stops until the user presses a button. I want to allow the user to leave the activity rendering the chronometer, do some stuff and then return. But the idea is that when the user returns I dont want the chronometer to go to 0:00 again. Insted I want it to show the exact time that has passed ever since the service has started.
I can calculate elapsedTime every time the user return to the chronometer activity:
elapsedTime = new Date().getTime() - startingTime;
The thing is that i dont know how to tell the chronometer to start counting from that time!
Setting it as the chronometer base does not work. Can someon explain what exactly "base" means or how to accomplish this?
thanks a lot!
BYE
You can use Chronometer.
You should also check this thread.
EDIT: The solution:
public class ChronoExample extends Activity {
Chronometer mChronometer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
mChronometer = new Chronometer(this);
// Set the initial value
mChronometer.setText("00:10");
layout.addView(mChronometer);
Button startButton = new Button(this);
startButton.setText("Start");
startButton.setOnClickListener(mStartListener);
layout.addView(startButton);
Button stopButton = new Button(this);
stopButton.setText("Stop");
stopButton.setOnClickListener(mStopListener);
layout.addView(stopButton);
Button resetButton = new Button(this);
resetButton.setText("Reset");
resetButton.setOnClickListener(mResetListener);
layout.addView(resetButton);
setContentView(layout);
}
private void showElapsedTime() {
long elapsedMillis = SystemClock.elapsedRealtime() - mChronometer.getBase();
Toast.makeText(ChronoExample.this, "Elapsed milliseconds: " + elapsedMillis,
Toast.LENGTH_SHORT).show();
}
View.OnClickListener mStartListener = new OnClickListener() {
public void onClick(View v) {
int stoppedMilliseconds = 0;
String chronoText = mChronometer.getText().toString();
String array[] = chronoText.split(":");
if (array.length == 2) {
stoppedMilliseconds = Integer.parseInt(array[0]) * 60 * 1000
+ Integer.parseInt(array[1]) * 1000;
} else if (array.length == 3) {
stoppedMilliseconds = Integer.parseInt(array[0]) * 60 * 60 * 1000
+ Integer.parseInt(array[1]) * 60 * 1000
+ Integer.parseInt(array[2]) * 1000;
}
mChronometer.setBase(SystemClock.elapsedRealtime() - stoppedMilliseconds);
mChronometer.start();
}
};
View.OnClickListener mStopListener = new OnClickListener() {
public void onClick(View v) {
mChronometer.stop();
showElapsedTime();
}
};
View.OnClickListener mResetListener = new OnClickListener() {
public void onClick(View v) {
mChronometer.setBase(SystemClock.elapsedRealtime());
showElapsedTime();
}
};
}
To start the chronometer you should use the setBase() method of your chronometer with SystemClock.elapsedRealTime(). Just like this:
mChronometer.setBase(SystemClock.elapsedRealTime())
But if you want to start at another time, you have to subtract the time you want in milliseconds. For example, you want to start your Chronometer at 10 seconds:
mChronometer.setBase(SystemClock.elapsedRealTime() - 10*1000);
At 2 minutes:
mChronometer.setBase(SystemClock.elapsedRealTime() - 2*60*1000);
But the problem here is that the Chronometer will show "00:00" time before it start to count, to change it to your time you have to do that :
mChronometer.setText("02:00");
The base time is the time that the Chronometer started ticking at. You can set it using Chronometer.setBase(). You should get the base time by using SystemClock.getElapsedTime(). Call setBase() with the start time each time the Chronometer is started. If there is potential for the Activity to be destroyed and recreated while the timer is still active then you will need to hold the base time somewhere outside of the Activity that owns the Chronometer.
This works for me :
Date now = new Date();
long elapsedTime = now.getTime() - startTime.getTime(); //startTime is whatever time you want to start the chronometer from. you might have stored it somwehere
myChronometer.setBase(SystemClock.elapsedRealtime() - elapsedTime);
myChronometer.start();
Some strange with SystemClock.getElapsedTime(), I did some changes for normal using with start date, like
myChron.setBase(startDate.getTime());
Here child of Chronometer below, TimeView
import android.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.Chronometer;
import android.widget.RemoteViews;
import java.util.Formatter;
import java.util.IllegalFormatException;
import java.util.Locale;
#RemoteViews.RemoteView
public class TimeView extends Chronometer {
private static final String TAG = "TimeView";
private long mBase;
private boolean mVisible;
private boolean mStarted;
private boolean mRunning;
private boolean mLogged;
private String mFormat;
private Formatter mFormatter;
private Locale mFormatterLocale;
private Object[] mFormatterArgs = new Object[1];
private StringBuilder mFormatBuilder;
private OnChronometerTickListener mOnChronometerTickListener;
private StringBuilder mRecycle = new StringBuilder(8);
private static final int TICK_WHAT = 2;
/**
* Initialize this Chronometer object.
* Sets the base to the current time.
*/
public TimeView(Context context) {
this(context, null, 0);
}
/**
* Initialize with standard view layout information.
* Sets the base to the current time.
*/
public TimeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* Initialize with standard view layout information and style.
* Sets the base to the current time.
*/
public TimeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mBase = System.currentTimeMillis();
updateText(mBase);
}
public void setBase(long base) {
mBase = base;
dispatchChronometerTick();
updateText(System.currentTimeMillis());
}
/**
* Return the base time as set through {#link #setBase}.
*/
public long getBase() {
return mBase;
}
public void start() {
mStarted = true;
updateRunning();
}
/**
* Stop counting up. This does not affect the base as set from {#link #setBase}, just
* the view display.
* <p/>
* This stops the messages to the handler, effectively releasing resources that would
* be held as the chronometer is running, via {#link #start}.
*/
public void stop() {
mStarted = false;
updateRunning();
}
/**
* The same as calling {#link #start} or {#link #stop}.
*
* #hide pending API council approval
*/
public void setStarted(boolean started) {
mStarted = started;
updateRunning();
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
updateRunning();
}
#Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == VISIBLE;
updateRunning();
}
private synchronized void updateText(long now) {
long seconds = now - mBase;
seconds /= 1000;
String text = DateUtils.formatElapsedTime(mRecycle, seconds);
if (mFormat != null) {
Locale loc = Locale.getDefault();
if (mFormatter == null || !loc.equals(mFormatterLocale)) {
mFormatterLocale = loc;
mFormatter = new Formatter(mFormatBuilder, loc);
}
mFormatBuilder.setLength(0);
mFormatterArgs[0] = text;
try {
mFormatter.format(mFormat, mFormatterArgs);
text = mFormatBuilder.toString();
} catch (IllegalFormatException ex) {
if (!mLogged) {
Log.w(TAG, "Illegal format string: " + mFormat);
mLogged = true;
}
}
}
setText(text);
}
private void updateRunning() {
boolean running = mVisible && mStarted;
if (running != mRunning) {
if (running) {
updateText(System.currentTimeMillis());
dispatchChronometerTick();
mHandler.sendMessageDelayed(Message.obtain(mHandler, TICK_WHAT), 1000);
} else {
mHandler.removeMessages(TICK_WHAT);
}
mRunning = running;
}
}
private Handler mHandler = new Handler() {
public void handleMessage(Message m) {
if (mRunning) {
updateText(System.currentTimeMillis());
dispatchChronometerTick();
sendMessageDelayed(Message.obtain(this, TICK_WHAT), 1000);
}
}
};
void dispatchChronometerTick() {
if (mOnChronometerTickListener != null) {
mOnChronometerTickListener.onChronometerTick(this);
}
}
}
Just copy and use, it works for me
When you set the basetime with .setBase(SystemClock.elapsedRealTime()) the chronomether starts to count from 00.00 but the time it stores is the amount of milisecs from boot.
When you use .stop the internal count does not stop, just the time you see on the clock. So, if you use .start again, the clock count jumps to the real count. If you want to store the time that has passed from the start, you have to get again the System elapsed time and make the difference with the .setTime
How to set starting time? What is Chronometer “Base”?
Use SystemClock.elapsedRealtime() for this purpose:
myChronometer.setBase(SystemClock.elapsedRealtime());