public class

RoundedDrawable

extends Drawable

 java.lang.Object

↳Drawable

↳androidx.wear.widget.RoundedDrawable

Gradle dependencies

compile group: 'androidx.wear', name: 'wear', version: '1.3.0-alpha02'

  • groupId: androidx.wear
  • artifactId: wear
  • version: 1.3.0-alpha02

Artifact androidx.wear:wear:1.3.0-alpha02 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.RoundedDrawable android.support.wear.widget.RoundedDrawable

Overview

Maintains and draws a drawable inside rounded rectangular bounds.

The drawable set by the RoundedDrawable.setDrawable(Drawable) method will be drawn within the rounded bounds specified by RoundedDrawable and RoundedDrawable.setRadius(int) when the RoundedDrawable.draw(Canvas) method is called.

By default, RoundedDrawable will apply padding to the drawable inside to fit the drawable into the rounded rectangle. If clipping is enabled by the RoundedDrawable.setClipEnabled(boolean) method, it will clip the drawable to a rounded rectangle instead of resizing it.

The RoundedDrawable.setRadius(int) method is used to specify the amount of border radius applied to the corners of inner drawable, regardless of whether or not the clipping is enabled, border radius will be applied to prevent overflowing of the drawable from specified rounded rectangular area.

RoundedDrawable can be inflated from XML (supported above API level 24) or constructed programmatically. To inflate from XML, use method.

Syntax:

 <?xml version="1.0" encoding="utf-8"?>
 <androidx.wear.widget.RoundedDrawable
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:src="drawable"
   app:backgroundColor="color"
   app:radius="dimension"
   app:clipEnabled="boolean" />

Summary

Constructors
publicRoundedDrawable()

Methods
public voiddraw(Canvas canvas)

public intgetAlpha()

public intgetBackgroundColor()

Returns the background color.

public DrawablegetDrawable()

Returns the drawable that will be rendered.

public intgetOpacity()

public intgetRadius()

Returns the border radius applied when rendering the drawable in pixels.

public voidinflate(Resources r, XmlPullParser parser, AttributeSet attrs, Resources.Theme theme)

public booleanisClipEnabled()

Returns whether the drawable inside is clipped or resized to fit the rounded bounds.

protected voidonBoundsChange(Rect bounds)

public voidsetAlpha(int alpha)

public voidsetBackgroundColor(int color)

Sets the background color of the rounded drawable.

public voidsetClipEnabled(boolean clipEnabled)

Sets whether the drawable inside should be clipped or resized to fit the rounded bounds.

public voidsetColorFilter(ColorFilter cf)

public voidsetDrawable(Drawable drawable)

Sets the drawable to be rendered.

public voidsetRadius(int radius)

Sets the border radius to be applied when rendering the drawable in pixels.

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

Constructors

public RoundedDrawable()

Methods

public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Resources.Theme theme)

public void setDrawable(Drawable drawable)

Sets the drawable to be rendered.

Parameters:

drawable: Drawable to be rendered

public Drawable getDrawable()

Returns the drawable that will be rendered.

Returns:

Drawable that will be rendered.

public void setBackgroundColor(int color)

Sets the background color of the rounded drawable.

Parameters:

color: an ARGB color

public int getBackgroundColor()

Returns the background color.

Returns:

an ARGB color

public void setClipEnabled(boolean clipEnabled)

Sets whether the drawable inside should be clipped or resized to fit the rounded bounds. If the drawable is animated, don't set clipping to true as clipping on animated drawables is not supported.

Parameters:

clipEnabled: true if the drawable should be clipped, false if it should be resized.

public boolean isClipEnabled()

Returns whether the drawable inside is clipped or resized to fit the rounded bounds.

Returns:

true if the drawable is clipped, false if it's resized.

protected void onBoundsChange(Rect bounds)

public void draw(Canvas canvas)

public int getOpacity()

public void setAlpha(int alpha)

public int getAlpha()

public void setColorFilter(ColorFilter cf)

public void setRadius(int radius)

Sets the border radius to be applied when rendering the drawable in pixels.

Parameters:

radius: radius in pixels

public int getRadius()

Returns the border radius applied when rendering the drawable in pixels.

Returns:

radius in pixels

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.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Xml;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.wear.R;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.Objects;

/**
 * Maintains and draws a drawable inside rounded rectangular bounds.
 *
 * <p>The drawable set by the {@link #setDrawable(Drawable)} method will be drawn within the rounded
 * bounds specified by {@link #setBounds(Rect)} and {@link #setRadius(int)} when the
 * {@link #draw(Canvas)} method is called.
 *
 * <p>By default, RoundedDrawable will apply padding to the drawable inside to fit the drawable into
 * the rounded rectangle. If clipping is enabled by the {@link #setClipEnabled(boolean)} method, it
 * will clip the drawable to a rounded rectangle instead of resizing it.
 *
 * <p>The {@link #setRadius(int)} method is used to specify the amount of border radius applied to
 * the corners of inner drawable, regardless of whether or not the clipping is enabled, border
 * radius will be applied to prevent overflowing of the drawable from specified rounded rectangular
 * area.
 *
 * <p>RoundedDrawable can be inflated from XML (supported above API level 24) or constructed
 * programmatically. To inflate from XML, use {@link android.content.Context#getDrawable(int)}
 * method.
 *
 * <h4>Syntax:</h4>
 *
 * <pre>
 * &lt;?xml version="1.0" encoding="utf-8"?&gt;
 * &lt;androidx.wear.widget.RoundedDrawable
 *   xmlns:android="http://schemas.android.com/apk/res/android"
 *   xmlns:app="http://schemas.android.com/apk/res-auto"
 *   android:src="drawable"
 *   app:backgroundColor="color"
 *   app:radius="dimension"
 *   app:clipEnabled="boolean" /&gt;</pre>
 */
public class RoundedDrawable extends Drawable {

    @VisibleForTesting
    final Paint mPaint;
    final Paint mBackgroundPaint;

    @Nullable
    private Drawable mDrawable;
    private int mRadius; // Radius applied to corners in pixels
    private boolean mIsClipEnabled;

    // Used to avoid creating new Rect objects every time draw() is called
    private final Rect mTmpBounds = new Rect();
    private final RectF mTmpBoundsF = new RectF();

    public RoundedDrawable() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mBackgroundPaint = new Paint();
        mBackgroundPaint.setAntiAlias(true);
        mBackgroundPaint.setColor(Color.TRANSPARENT);
    }

    @Override
    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
            @NonNull AttributeSet attrs, @Nullable Resources.Theme theme)
            throws XmlPullParserException, IOException {
        super.inflate(r, parser, attrs, theme);
        TypedArray a = r.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.RoundedDrawable);
        if (a.hasValue(R.styleable.RoundedDrawable_android_src)) {
            setDrawable(a.getDrawable(R.styleable.RoundedDrawable_android_src));
        }
        setRadius(a.getDimensionPixelSize(R.styleable.RoundedDrawable_radius, 0));
        setClipEnabled(a.getBoolean(R.styleable.RoundedDrawable_clipEnabled, false));
        setBackgroundColor(
                a.getColor(R.styleable.RoundedDrawable_backgroundColor, Color.TRANSPARENT));
        a.recycle();
    }

    /**
     * Sets the drawable to be rendered.
     *
     * @param drawable {@link Drawable} to be rendered
     * {@link android.R.attr#src}
     */
    public void setDrawable(@Nullable Drawable drawable) {
        if (Objects.equals(mDrawable, drawable)) {
            return;
        }
        mDrawable = drawable;
        mPaint.setShader(null); // Clear the shader so it can be reinitialized
        invalidateSelf();
    }

    /**
     * Returns the drawable that will be rendered.
     *
     * @return {@link Drawable} that will be rendered.
     */
    @Nullable
    public Drawable getDrawable() {
        return mDrawable;
    }

    /**
     * Sets the background color of the rounded drawable.
     *
     * @param color an ARGB color
     * {@link androidx.wear.R.attr#backgroundColor}
     */
    public void setBackgroundColor(@ColorInt int color) {
        mBackgroundPaint.setColor(color);
        invalidateSelf();
    }

    /**
     * Returns the background color.
     *
     * @return an ARGB color
     */
    @ColorInt
    public int getBackgroundColor() {
        return mBackgroundPaint.getColor();
    }

    /**
     * Sets whether the drawable inside should be clipped or resized to fit the rounded bounds. If
     * the drawable is animated, don't set clipping to {@code true} as clipping on animated
     * drawables is not supported.
     *
     * @param clipEnabled {@code true} if the drawable should be clipped, {@code false} if it
     *                    should be resized.
     * {@link androidx.wear.R.attr#clipEnabled}
     */
    public void setClipEnabled(boolean clipEnabled) {
        mIsClipEnabled = clipEnabled;
        if (!clipEnabled) {
            mPaint.setShader(null); // Clear the shader so it's garbage collected
        }
        invalidateSelf();
    }

    /**
     * Returns whether the drawable inside is clipped or resized to fit the rounded bounds.
     *
     * @return {@code true} if the drawable is clipped, {@code false} if it's resized.
     */
    public boolean isClipEnabled() {
        return mIsClipEnabled;
    }

    @Override
    protected void onBoundsChange(Rect bounds) {
        mTmpBounds.right = bounds.width();
        mTmpBounds.bottom = bounds.height();
        mTmpBoundsF.right = bounds.width();
        mTmpBoundsF.bottom = bounds.height();
        mPaint.setShader(null); // Clear the shader so it can be reinitialized
    }

    @Override
    public void draw(@NonNull Canvas canvas) {
        Rect bounds = getBounds();
        if (mDrawable == null || bounds.isEmpty()) {
            return;
        }
        canvas.save();
        canvas.translate(bounds.left, bounds.top);
        // mTmpBoundsF is bounds translated to (0,0) and converted to RectF as drawRoundRect
        // requires.
        canvas.drawRoundRect(mTmpBoundsF, (float) mRadius, (float) mRadius,
                mBackgroundPaint);
        if (mIsClipEnabled) {
            // Update the shader if it's not present.
            if (mPaint.getShader() == null) {
                updateBitmapShader();
            }
            // Clip to a rounded rectangle
            canvas.drawRoundRect(mTmpBoundsF, (float) mRadius, (float) mRadius, mPaint);
        } else {
            // Scale to fit the rounded rectangle
            int minEdge = Math.min(bounds.width(), bounds.height());
            int padding = (int) Math.ceil(
                    Math.min(mRadius, minEdge / 2) * (1 - 1 / (float) Math.sqrt(2.0)));
            mTmpBounds.inset(padding, padding);
            mDrawable.setBounds(mTmpBounds);
            mDrawable.draw(canvas);
            mTmpBounds.inset(-padding, -padding);
        }
        canvas.restore();
    }

    @SuppressWarnings("deprecation")
    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
        mBackgroundPaint.setAlpha(alpha);
    }

    @Override
    public int getAlpha() {
        return mPaint.getAlpha();
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        mPaint.setColorFilter(cf);
    }

    /**
     * Sets the border radius to be applied when rendering the drawable in pixels.
     *
     * @param radius radius in pixels
     * {@link androidx.wear.R.attr#radius}
     */
    public void setRadius(int radius) {
        mRadius = radius;
    }

    /**
     * Returns the border radius applied when rendering the drawable in pixels.
     *
     * @return radius in pixels
     */
    public int getRadius() {
        return mRadius;
    }

    /**
     * Updates the shader of the paint. To avoid scaling and creation of a BitmapShader every time,
     * this method should be called only if the drawable or the bounds has changed.
     */
    private void updateBitmapShader() {
        if (mDrawable == null) {
            return;
        }
        Rect bounds = getBounds();
        if (!bounds.isEmpty()) {
            Bitmap bitmap = drawableToBitmap(mDrawable, bounds.width(), bounds.height());

            Shader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
            mPaint.setShader(shader);
        }
    }

    /** Converts a drawable to a bitmap of specified width and height. */
    private Bitmap drawableToBitmap(Drawable drawable, int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, width, height);
        drawable.draw(canvas);
        return bitmap;
    }
}