jp.tkgktyk.xposed.forcetouchdetector.app.util.fab.CircularBorderDrawable.java Source code

Java tutorial

Introduction

Here is the source code for jp.tkgktyk.xposed.forcetouchdetector.app.util.fab.CircularBorderDrawable.java

Source

/*
 * Copyright (C) 2015 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 jp.tkgktyk.xposed.forcetouchdetector.app.util.fab;

import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.LinearGradient;
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.support.v4.graphics.ColorUtils;

/**
 * A drawable which draws an oval 'border'.
 */
class CircularBorderDrawable extends Drawable {

    /**
     * We actually draw the stroke wider than the border size given. This is to reduce any
     * potential transparent space caused by anti-aliasing and padding rounding.
     * This value defines the multiplier used to determine to draw stroke width.
     */
    private static final float DRAW_STROKE_WIDTH_MULTIPLE = 1.3333f;

    final Paint mPaint;
    final Rect mRect = new Rect();
    final RectF mRectF = new RectF();

    float mBorderWidth;

    private int mTopOuterStrokeColor;
    private int mTopInnerStrokeColor;
    private int mBottomOuterStrokeColor;
    private int mBottomInnerStrokeColor;

    private int mTintColor;

    private boolean mInvalidateShader = true;

    public CircularBorderDrawable() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
    }

    void setGradientColors(int topOuterStrokeColor, int topInnerStrokeColor, int bottomOuterStrokeColor,
            int bottomInnerStrokeColor) {
        mTopOuterStrokeColor = topOuterStrokeColor;
        mTopInnerStrokeColor = topInnerStrokeColor;
        mBottomOuterStrokeColor = bottomOuterStrokeColor;
        mBottomInnerStrokeColor = bottomInnerStrokeColor;
    }

    /**
     * Set the border width
     */
    void setBorderWidth(float width) {
        if (mBorderWidth != width) {
            mBorderWidth = width;
            mPaint.setStrokeWidth(width * DRAW_STROKE_WIDTH_MULTIPLE);
            mInvalidateShader = true;
            invalidateSelf();
        }
    }

    @Override
    public void draw(Canvas canvas) {
        if (mInvalidateShader) {
            mPaint.setShader(createGradientShader());
            mInvalidateShader = false;
        }

        final float halfBorderWidth = mPaint.getStrokeWidth() / 2f;
        final RectF rectF = mRectF;

        // We need to inset the oval bounds by half the border width. This is because stroke draws
        // the center of the border on the dimension. Whereas we want the stroke on the inside.
        copyBounds(mRect);
        rectF.set(mRect);
        rectF.left += halfBorderWidth;
        rectF.top += halfBorderWidth;
        rectF.right -= halfBorderWidth;
        rectF.bottom -= halfBorderWidth;

        // Draw the oval
        canvas.drawOval(rectF, mPaint);
    }

    @Override
    public boolean getPadding(Rect padding) {
        final int borderWidth = Math.round(mBorderWidth);
        padding.set(borderWidth, borderWidth, borderWidth, borderWidth);
        return true;
    }

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

    void setTintColor(int tintColor) {
        mTintColor = tintColor;
        mInvalidateShader = true;
        invalidateSelf();
    }

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

    @Override
    public int getOpacity() {
        return mBorderWidth > 0 ? PixelFormat.TRANSLUCENT : PixelFormat.TRANSPARENT;
    }

    @Override
    protected void onBoundsChange(Rect bounds) {
        mInvalidateShader = true;
    }

    /**
     * Creates a vertical {@link LinearGradient}
     * @return
     */
    private Shader createGradientShader() {
        final Rect rect = mRect;
        copyBounds(rect);

        final float borderRatio = mBorderWidth / rect.height();

        final int[] colors = new int[6];
        colors[0] = ColorUtils.compositeColors(mTopOuterStrokeColor, mTintColor);
        colors[1] = ColorUtils.compositeColors(mTopInnerStrokeColor, mTintColor);
        colors[2] = ColorUtils.compositeColors(ColorUtils.setAlphaComponent(mTopInnerStrokeColor, 0), mTintColor);
        colors[3] = ColorUtils.compositeColors(ColorUtils.setAlphaComponent(mBottomInnerStrokeColor, 0),
                mTintColor);
        colors[4] = ColorUtils.compositeColors(mBottomInnerStrokeColor, mTintColor);
        colors[5] = ColorUtils.compositeColors(mBottomOuterStrokeColor, mTintColor);

        final float[] positions = new float[6];
        positions[0] = 0f;
        positions[1] = borderRatio;
        positions[2] = 0.5f;
        positions[3] = 0.5f;
        positions[4] = 1f - borderRatio;
        positions[5] = 1f;

        return new LinearGradient(0, rect.top, 0, rect.bottom, colors, positions, Shader.TileMode.CLAMP);
    }
}