Chronometer

Chronometer is a ** “simple timer” component inherited from TextView. However, the Chronometer “does not show the current time” **, it shows how much time has passed since a certain start time.

The main XML attributes are as follows:

  • Whether “Android :countDown” counts down. Default is false.
  • Android: Format Sets the time display format. If specified, the first “%s” “is replaced with the current timer value in the form “MM:SS” or “H:MM:SS”.

Commonly used method

  • Start () : starts the timer
  • “Stop ()” : stops the timer
  • SetBase (long) : sets the start time of the timer.
  • SetFormat (String) : sets the display time format
  • “SetCountDown (Boolean)” : sets whether to countdown (SDK version is greater than 23).
  • “SetOnChronometerTickListener (OnChronometerTickListener)” : for the timer, binding event listeners when the timer changes trigger this listener.

The sample

Look at the above introduction is very simple, let’s do an example to understand it, first look at the effect diagram.

Main interface layout file

Keep only the Chronometer related layout


      
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="@dimen/dimen_20">
    <Chronometer
        android:id="@+id/chronometer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="@dimen/dimen_20"
        android:padding="@dimen/dimen_10"
        android:layout_gravity="center_horizontal"
        android:textColor="@color/color_188FFF" />
    <Chronometer
        android:id="@+id/ch_format"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="@dimen/dimen_20"
        android:padding="@dimen/dimen_10"
        android:layout_gravity="center_horizontal"
        android:textColor="@color/color_ff0000" />
</LinearLayout>
Copy the code

Main interface code

public class ChronometerActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn_start,btn_stop,btn_reset,btn_format_1;
    private Chronometer chronometer,ch_format;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_textview_chronometer);// Load the layout file
        initView();
    }
    private void initView(a) {
        btn_start = findViewById(R.id.btn_start);
        btn_stop = findViewById(R.id.btn_stop);
        btn_reset = findViewById(R.id.btn_reset);
        chronometer = findViewById(R.id.chronometer);
        btn_start.setOnClickListener(this);
        btn_stop.setOnClickListener(this);
        btn_reset.setOnClickListener(this);
        chronometer.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
            @RequiresApi(api = Build.VERSION_CODES.N)
            @Override
            public void onChronometerTick(Chronometer chronometer) {
                MLog.e(String.valueOf(chronometer.getBase()));
                // Current time - baseline time >20 seconds
                if(SystemClock.elapsedRealtime()-chronometer.getBase()>20*1000)
                {
                    chronometer.setCountDown(true); }}}); btn_format_1 = findViewById(R.id.btn_format_1); ch_format = findViewById(R.id.ch_format); btn_format_1.setOnClickListener(this);
        ch_format.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
            @Override
            public void onChronometerTick(Chronometer chronometer) {
                // Current time - baseline time
                long time = SystemClock.elapsedRealtime()-chronometer.getBase();
                Date d = new Date(time);
                SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.US);
                sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
                // Set the format to HH:mm:ssch_format.setText(sdf.format(d)); }}); }@RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_start:
                / / SystemClock elapsedRealtime (), after the number of milliseconds since boot.
                // Set the baseline time
                chronometer.setBase(SystemClock.elapsedRealtime());
                / / true, countdown
                chronometer.setCountDown(false);
                chronometer.setFormat("Timing: %s seconds");
                // Start the timer
                chronometer.start();
                break;
            case R.id.btn_stop:
                // End the timer
                chronometer.stop();
                // Text display
                ch_format.setText("Prefer");
                break;
            case R.id.btn_reset:
                // Reset the baseline time
                chronometer.setBase(SystemClock.elapsedRealtime());
                break;
            case R.id.btn_format_1:
                ch_format.setBase(SystemClock.elapsedRealtime());
                ch_format.setCountDown(false);
                ch_format.start();
                break; }}}Copy the code

Format Format modification

Change Format from the default 00:00(MM:SS) to 00:00:00(H:MM:SS).

Whenever the Chronometer changes, ** “onChronometerTick” ** will be triggered, so we can deal with it after it has been triggered. Get the style we want to display.

        ch_format.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
            @Override
            public void onChronometerTick(Chronometer chronometer) {
                // Current time - baseline time
                long time = SystemClock.elapsedRealtime()-chronometer.getBase();
                Date d = new Date(time);
                SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.US);
                sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
                // Set the format to HH:mm:ssch_format.setText(sdf.format(d)); }});Copy the code

Source code analysis

setBase()

public void setBase(long base) {
        // Set the baseline time
        mBase = base;
        // Triggers the listener event
        dispatchChronometerTick();
        // Modify the current interface
        updateText(SystemClock.elapsedRealtime());
    }
Copy the code

dispatchChronometerTick()

// Triggers the listener event
void dispatchChronometerTick(a) {
        if(mOnChronometerTickListener ! =null) {
            mOnChronometerTickListener.onChronometerTick(this); }}Copy the code

updateText()

“Modify the current interface”, first “compare the current time with the mBase time”, second is the “difference” between the two **. DateUtils formats second, typically “MM:SS” or “H:MM:SS”, and outputs text.

If format is defined, use Formatter to further format text.

/ / modify
private synchronized void updateText(long now) {
        mNow = now;
        long seconds = mCountDown ? mBase - now : now - mBase;
        seconds /= 1000;
        boolean negative = false;
        if (seconds < 0) {
            seconds = -seconds;
            negative = true;
        }
        String text = DateUtils.formatElapsedTime(mRecycle, seconds);
        if (negative) {
            text = getResources().getString(R.string.negative_duration, text);
        }

        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);
    }
Copy the code

start()

public void start(a) {
        mStarted = true;
        updateRunning();
    }
Copy the code

stop()

public void stop(a) {
        mStarted = false;
        updateRunning();
    }
Copy the code

updateRunning()

The “start()” and ** “stop()” methods change the state of ** “mStarted” and then call “updateRunning()”. The Chronometer state consists of three parts: “mVisible” (whether the Window is visible), “mStarted” (when the Chronometer starts to time), and “isShown” **(whether the View is visible).

If the state changes, modify the current control.

  • UpdateText (Long) Modify interface.
  • DispatchChronometerTick () Triggers listening events.
  • PostDelayed (Runnable, Long) Changes the screen after 1 second.
private void updateRunning(a) {
        boolean running = mVisible && mStarted && isShown();
        if(running ! = mRunning) {if (running) {
                updateText(SystemClock.elapsedRealtime());
                dispatchChronometerTick();
                postDelayed(mTickRunnable, 1000);
            } else{ removeCallbacks(mTickRunnable); } mRunning = running; }}Copy the code

setFormat(String)

    // The default is "MM:SS" or "H:MM:SS"
    public void setFormat(String format) {
        mFormat = format;
        if(format ! =null && mFormatBuilder == null) {
            mFormatBuilder = new StringBuilder(format.length() * 2); }}Copy the code

setCountDown()

public void setCountDown(boolean countDown) {
        mCountDown = countDown;
        updateText(SystemClock.elapsedRealtime());
    }
Copy the code

There are updateText ()

When you setCountDown(true), add a “-” to the current timing value. Such as:

  • SetCountDown (false) shows: timer: ** “20” ** seconds,
  • SetCountDown (true) Displays: timer: ** “-20” ** seconds,

That’s all for this article, hopefully to help and inspire you to learn the Android Chronometer.