public class

CircularProgressLayout

extends FrameLayout

 java.lang.Object

↳FrameLayout

↳androidx.wear.widget.CircularProgressLayout

Gradle dependencies

compile group: 'androidx.wear', name: 'wear', version: '1.4.0-alpha01'

  • groupId: androidx.wear
  • artifactId: wear
  • version: 1.4.0-alpha01

Artifact androidx.wear:wear:1.4.0-alpha01 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.wear:wear com.android.support:wear

Androidx class mapping:

androidx.wear.widget.CircularProgressLayout android.support.wear.widget.CircularProgressLayout

Overview

CircularProgressLayout adds a circular countdown timer behind the view it contains, typically used to automatically confirm an operation after a short delay has elapsed.

The developer can specify a countdown interval via CircularProgressLayout.setTotalTime(long) and a listener via CircularProgressLayout.setOnTimerFinishedListener(CircularProgressLayout.OnTimerFinishedListener) to be called when the time has elapsed after CircularProgressLayout.startTimer() has been called. Tap action can be received via CircularProgressLayout and can be used to cancel the timer via CircularProgressLayout.stopTimer() method.

Alternatively, this layout can be used to show indeterminate progress by calling CircularProgressLayout.setIndeterminate(boolean) method.

Summary

Constructors
publicCircularProgressLayout(Context context)

publicCircularProgressLayout(Context context, AttributeSet attrs)

publicCircularProgressLayout(Context context, AttributeSet attrs, int defStyleAttr)

publicCircularProgressLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)

Methods
public intgetBackgroundColor()

Returns the background color of the CircularProgressDrawable.

public int[]getColorSchemeColors()

Returns the color scheme colors of the progress drawable

public CircularProgressLayout.OnTimerFinishedListenergetOnTimerFinishedListener()

Returns the CircularProgressLayout.OnTimerFinishedListener that is registered to this layout.

public CircularProgressDrawablegetProgressDrawable()

Returns the CircularProgressDrawable used as background of this layout.

public floatgetStartingRotation()

Returns the starting rotation of the progress drawable.

public floatgetStrokeWidth()

Returns the stroke width of the progress drawable in pixels.

public longgetTotalTime()

Returns the total time in milliseconds for the timer to countdown to.

public booleanisIndeterminate()

Returns if progress is showing as an indeterminate spinner.

public booleanisTimerRunning()

Returns if the timer is running.

protected voidonDetachedFromWindow()

protected voidonLayout(boolean changed, int left, int top, int right, int bottom)

public voidsetBackgroundColor(int color)

Sets the background color of the CircularProgressDrawable, which is drawn as a circle inside the progress drawable.

public voidsetColorSchemeColors(int[] colors[])

Sets the color scheme colors of the progress drawable, which is equivalent to calling CircularProgressDrawable.setColorSchemeColors(int...) method on background drawable of this layout.

public voidsetIndeterminate(boolean indeterminate)

Sets if progress should be shown as an indeterminate spinner.

public voidsetOnTimerFinishedListener(CircularProgressLayout.OnTimerFinishedListener listener)

Sets the CircularProgressLayout.OnTimerFinishedListener to be notified when timer countdown is finished.

public voidsetStartingRotation(float rotation)

Sets the starting rotation for the progress drawable to start from.

public voidsetStrokeWidth(float strokeWidth)

Sets the stroke width of the progress drawable in pixels.

public voidsetTotalTime(long totalTime)

Sets the total time in milliseconds for the timer to countdown to.

public voidstartTimer()

Starts the timer countdown.

public voidstopTimer()

Stops the timer countdown.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public CircularProgressLayout(Context context)

public CircularProgressLayout(Context context, AttributeSet attrs)

public CircularProgressLayout(Context context, AttributeSet attrs, int defStyleAttr)

public CircularProgressLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)

Methods

protected void onLayout(boolean changed, int left, int top, int right, int bottom)

protected void onDetachedFromWindow()

public void setBackgroundColor(int color)

Sets the background color of the CircularProgressDrawable, which is drawn as a circle inside the progress drawable. Colors are in ARGB format defined in .

Parameters:

color: an ARGB color

public int getBackgroundColor()

Returns the background color of the CircularProgressDrawable.

Returns:

an ARGB color

public CircularProgressDrawable getProgressDrawable()

Returns the CircularProgressDrawable used as background of this layout.

Returns:

CircularProgressDrawable

public void setIndeterminate(boolean indeterminate)

Sets if progress should be shown as an indeterminate spinner.

Parameters:

indeterminate: true if indeterminate spinner should be shown, false otherwise.

public boolean isIndeterminate()

Returns if progress is showing as an indeterminate spinner.

Returns:

true if indeterminate spinner is shown, false otherwise.

public void setTotalTime(long totalTime)

Sets the total time in milliseconds for the timer to countdown to. Calling this method while the timer is already running will not change the duration of the current timer.

Parameters:

totalTime: total time in milliseconds

public long getTotalTime()

Returns the total time in milliseconds for the timer to countdown to.

Returns:

total time in milliseconds

public void startTimer()

Starts the timer countdown. Once the countdown is finished, if there is an CircularProgressLayout.OnTimerFinishedListener registered by CircularProgressLayout.setOnTimerFinishedListener(CircularProgressLayout.OnTimerFinishedListener) method, its CircularProgressLayout.OnTimerFinishedListener.onTimerFinished(CircularProgressLayout) method is called. If this method is called while there is already a running timer, it will restart the timer.

public void stopTimer()

Stops the timer countdown. If there is no timer running, calling this method will not do anything.

public boolean isTimerRunning()

Returns if the timer is running.

Returns:

true if the timer is running, false otherwise

public void setStartingRotation(float rotation)

Sets the starting rotation for the progress drawable to start from. Default starting rotation is 0.75 and it corresponds clockwise geometric 270 degrees (12 o'clock on a watch)

Parameters:

rotation: starting rotation from [0..1]

public float getStartingRotation()

Returns the starting rotation of the progress drawable.

Returns:

starting rotation from [0..1]

public void setStrokeWidth(float strokeWidth)

Sets the stroke width of the progress drawable in pixels.

Parameters:

strokeWidth: stroke width in pixels

public float getStrokeWidth()

Returns the stroke width of the progress drawable in pixels.

Returns:

stroke width in pixels

public void setColorSchemeColors(int[] colors[])

Sets the color scheme colors of the progress drawable, which is equivalent to calling CircularProgressDrawable.setColorSchemeColors(int...) method on background drawable of this layout.

Parameters:

colors: list of ARGB colors

public int[] getColorSchemeColors()

Returns the color scheme colors of the progress drawable

Returns:

list of ARGB colors

public CircularProgressLayout.OnTimerFinishedListener getOnTimerFinishedListener()

Returns the CircularProgressLayout.OnTimerFinishedListener that is registered to this layout.

Returns:

registered CircularProgressLayout.OnTimerFinishedListener

public void setOnTimerFinishedListener(CircularProgressLayout.OnTimerFinishedListener listener)

Sets the CircularProgressLayout.OnTimerFinishedListener to be notified when timer countdown is finished.

Parameters:

listener: CircularProgressLayout.OnTimerFinishedListener to be notified, or null to clear

Source

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.wear.widget;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.swiperefreshlayout.widget.CircularProgressDrawable;
import androidx.wear.R;

/**
 * {@link CircularProgressLayout} adds a circular countdown timer behind the view it contains,
 * typically used to automatically confirm an operation after a short delay has elapsed.
 *
 * <p>The developer can specify a countdown interval via {@link #setTotalTime(long)} and a listener
 * via {@link #setOnTimerFinishedListener(OnTimerFinishedListener)} to be called when the time has
 * elapsed after {@link #startTimer()} has been called. Tap action can be received via {@link
 * #setOnClickListener(OnClickListener)} and can be used to cancel the timer via {@link
 * #stopTimer()} method.
 *
 * <p>Alternatively, this layout can be used to show indeterminate progress by calling {@link
 * #setIndeterminate(boolean)} method.
 */
public class CircularProgressLayout extends FrameLayout {

    /**
     * Update interval for 60 fps.
     */
    private static final long DEFAULT_UPDATE_INTERVAL = 1000 / 60;

    /**
     * Starting rotation for the progress indicator. Geometric clockwise [0..360] degree range
     * correspond to [0..1] range. 0.75 corresponds to 12 o'clock direction on a watch.
     */
    private static final float DEFAULT_ROTATION = 0.75f;

    /**
     * Used as background of this layout.
     */
    private CircularProgressDrawable mProgressDrawable;

    /**
     * Used to control this layout.
     */
    private CircularProgressLayoutController mController;

    /**
     * Angle for the progress to start from.
     */
    private float mStartingRotation = DEFAULT_ROTATION;

    /**
     * Duration of the timer in milliseconds.
     */
    private long mTotalTime;


    /**
     * Interface to implement for listening to {@link
     * OnTimerFinishedListener#onTimerFinished(CircularProgressLayout)} event.
     */
    public interface OnTimerFinishedListener {

        /**
         * Called when the timer started by {@link #startTimer()} method finishes.
         *
         * @param layout {@link CircularProgressLayout} that calls this method.
         */
        void onTimerFinished(CircularProgressLayout layout);
    }

    public CircularProgressLayout(Context context) {
        this(context, null);
    }

    public CircularProgressLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircularProgressLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public CircularProgressLayout(Context context, AttributeSet attrs, int defStyleAttr,
            int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        mProgressDrawable = new CircularProgressDrawable(context);
        mProgressDrawable.setProgressRotation(DEFAULT_ROTATION);
        mProgressDrawable.setStrokeCap(Paint.Cap.BUTT);
        setBackground(mProgressDrawable);

        // If a child view is added, make it center aligned so it fits in the progress drawable.
        setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
            @Override
            public void onChildViewAdded(View parent, View child) {
                // Ensure that child view is aligned in center
                LayoutParams params = (LayoutParams) child.getLayoutParams();
                params.gravity = Gravity.CENTER;
                child.setLayoutParams(params);
            }

            @Override
            public void onChildViewRemoved(View parent, View child) {

            }
        });

        mController = new CircularProgressLayoutController(this);

        Resources r = context.getResources();
        TypedArray a = r.obtainAttributes(attrs, R.styleable.CircularProgressLayout);

        if (a.getType(R.styleable.CircularProgressLayout_colorSchemeColors) == TypedValue
                .TYPE_REFERENCE || !a.hasValue(
                R.styleable.CircularProgressLayout_colorSchemeColors)) {
            int arrayResId = a.getResourceId(R.styleable.CircularProgressLayout_colorSchemeColors,
                    R.array.circular_progress_layout_color_scheme_colors);
            setColorSchemeColors(getColorListFromResources(r, arrayResId));
        } else {
            setColorSchemeColors(a.getColor(R.styleable.CircularProgressLayout_colorSchemeColors,
                    Color.BLACK));
        }

        setStrokeWidth(a.getDimensionPixelSize(R.styleable.CircularProgressLayout_strokeWidth,
                r.getDimensionPixelSize(
                        R.dimen.circular_progress_layout_stroke_width)));

        setBackgroundColor(a.getColor(R.styleable.CircularProgressLayout_backgroundColor,
                ContextCompat.getColor(context,
                        R.color.circular_progress_layout_background_color)));

        setIndeterminate(a.getBoolean(R.styleable.CircularProgressLayout_indeterminate, false));

        a.recycle();
    }

    private int[] getColorListFromResources(Resources resources, int arrayResId) {
        TypedArray colorArray = resources.obtainTypedArray(arrayResId);
        int[] colors = new int[colorArray.length()];
        for (int i = 0; i < colorArray.length(); i++) {
            colors[i] = colorArray.getColor(i, 0);
        }
        colorArray.recycle();
        return colors;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (getChildCount() != 0) {
            View childView = getChildAt(0);
            // Wrap the drawable around the child view
            mProgressDrawable.setCenterRadius(
                    Math.min(childView.getWidth(), childView.getHeight()) / 2f);
        } else {
            // Fill the bounds if no child view is present
            mProgressDrawable.setCenterRadius(0f);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mController.reset();
    }

    /**
     * Sets the background color of the {@link CircularProgressDrawable}, which is drawn as a circle
     * inside the progress drawable. Colors are in ARGB format defined in {@link Color}.
     *
     * @param color an ARGB color
     */
    @Override
    public void setBackgroundColor(@ColorInt int color) {
        mProgressDrawable.setBackgroundColor(color);
    }

    /**
     * Returns the background color of the {@link CircularProgressDrawable}.
     *
     * @return an ARGB color
     */
    @ColorInt
    public int getBackgroundColor() {
        return mProgressDrawable.getBackgroundColor();
    }

    /**
     * Returns the {@link CircularProgressDrawable} used as background of this layout.
     *
     * @return {@link CircularProgressDrawable}
     */
    @NonNull
    public CircularProgressDrawable getProgressDrawable() {
        return mProgressDrawable;
    }

    /**
     * Sets if progress should be shown as an indeterminate spinner.
     *
     * @param indeterminate {@code true} if indeterminate spinner should be shown, {@code false}
     *                      otherwise.
     */
    public void setIndeterminate(boolean indeterminate) {
        mController.setIndeterminate(indeterminate);
    }

    /**
     * Returns if progress is showing as an indeterminate spinner.
     *
     * @return {@code true} if indeterminate spinner is shown, {@code false} otherwise.
     */
    public boolean isIndeterminate() {
        return mController.isIndeterminate();
    }

    /**
     * Sets the total time in milliseconds for the timer to countdown to. Calling this method while
     * the timer is already running will not change the duration of the current timer.
     *
     * @param totalTime total time in milliseconds
     */
    public void setTotalTime(long totalTime) {
        if (totalTime <= 0) {
            throw new IllegalArgumentException("Total time should be greater than zero.");
        }
        mTotalTime = totalTime;
    }

    /**
     * Returns the total time in milliseconds for the timer to countdown to.
     *
     * @return total time in milliseconds
     */
    public long getTotalTime() {
        return mTotalTime;
    }

    /**
     * Starts the timer countdown. Once the countdown is finished, if there is an {@link
     * OnTimerFinishedListener} registered by {@link
     * #setOnTimerFinishedListener(OnTimerFinishedListener)} method, its
     * {@link OnTimerFinishedListener#onTimerFinished(CircularProgressLayout)} method is called. If
     * this method is called while there is already a running timer, it will restart the timer.
     */
    public void startTimer() {
        mController.startTimer(mTotalTime, DEFAULT_UPDATE_INTERVAL);
        mProgressDrawable.setProgressRotation(mStartingRotation);
    }

    /**
     * Stops the timer countdown. If there is no timer running, calling this method will not do
     * anything.
     */
    public void stopTimer() {
        mController.stopTimer();
    }

    /**
     * Returns if the timer is running.
     *
     * @return {@code true} if the timer is running, {@code false} otherwise
     */
    public boolean isTimerRunning() {
        return mController.isTimerRunning();
    }

    /**
     * Sets the starting rotation for the progress drawable to start from. Default starting rotation
     * is {@code 0.75} and it corresponds clockwise geometric 270 degrees (12 o'clock on a watch)
     *
     * @param rotation starting rotation from [0..1]
     */
    public void setStartingRotation(float rotation) {
        mStartingRotation = rotation;
    }

    /**
     * Returns the starting rotation of the progress drawable.
     *
     * @return starting rotation from [0..1]
     */
    public float getStartingRotation() {
        return mStartingRotation;
    }

    /**
     * Sets the stroke width of the progress drawable in pixels.
     *
     * @param strokeWidth stroke width in pixels
     */
    public void setStrokeWidth(float strokeWidth) {
        mProgressDrawable.setStrokeWidth(strokeWidth);
    }

    /**
     * Returns the stroke width of the progress drawable in pixels.
     *
     * @return stroke width in pixels
     */
    public float getStrokeWidth() {
        return mProgressDrawable.getStrokeWidth();
    }

    /**
     * Sets the color scheme colors of the progress drawable, which is equivalent to calling {@link
     * CircularProgressDrawable#setColorSchemeColors(int...)} method on background drawable of this
     * layout.
     *
     * @param colors list of ARGB colors
     */
    public void setColorSchemeColors(int... colors) {
        mProgressDrawable.setColorSchemeColors(colors);
    }

    /**
     * Returns the color scheme colors of the progress drawable
     *
     * @return list of ARGB colors
     */
    public int[] getColorSchemeColors() {
        return mProgressDrawable.getColorSchemeColors();
    }

    /**
     * Returns the {@link OnTimerFinishedListener} that is registered to this layout.
     *
     * @return registered {@link OnTimerFinishedListener}
     */
    @Nullable
    public OnTimerFinishedListener getOnTimerFinishedListener() {
        return mController.getOnTimerFinishedListener();
    }

    /**
     * Sets the {@link OnTimerFinishedListener} to be notified when timer countdown is finished.
     *
     * @param listener {@link OnTimerFinishedListener} to be notified, or {@code null} to clear
     */
    public void setOnTimerFinishedListener(@Nullable OnTimerFinishedListener listener) {
        mController.setOnTimerFinishedListener(listener);
    }
}