Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 661e6365 authored by Alan Viverette's avatar Alan Viverette
Browse files

Support for hollow switch and seek bar thumbs

Also cleans up AbsSeekBar.

Change-Id: I996ea90c732c1da6a801aad3079f6bb3692d5c67
parent b7a6e198
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -1047,6 +1047,7 @@ package android {
    field public static final int spinnerStyle = 16842881; // 0x1010081
    field public static final int spinnersShown = 16843595; // 0x101034b
    field public static final int splitMotionEvents = 16843503; // 0x10102ef
    field public static final int splitTrack = 16843858; // 0x1010452
    field public static final int src = 16843033; // 0x1010119
    field public static final int ssp = 16843747; // 0x10103e3
    field public static final int sspPattern = 16843749; // 0x10103e5
@@ -34345,9 +34346,11 @@ package android.widget {
    ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int);
    ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int, int);
    method public int getKeyProgressIncrement();
    method public boolean getSplitTrack();
    method public android.graphics.drawable.Drawable getThumb();
    method public int getThumbOffset();
    method public void setKeyProgressIncrement(int);
    method public void setSplitTrack(boolean);
    method public void setThumb(android.graphics.drawable.Drawable);
    method public void setThumbOffset(int);
  }
@@ -36150,6 +36153,7 @@ package android.widget {
    ctor public Switch(android.content.Context, android.util.AttributeSet);
    ctor public Switch(android.content.Context, android.util.AttributeSet, int);
    ctor public Switch(android.content.Context, android.util.AttributeSet, int, int);
    method public boolean getSplitTrack();
    method public int getSwitchMinWidth();
    method public int getSwitchPadding();
    method public java.lang.CharSequence getTextOff();
@@ -36158,6 +36162,7 @@ package android.widget {
    method public int getThumbTextPadding();
    method public android.graphics.drawable.Drawable getTrackDrawable();
    method public void onMeasure(int, int);
    method public void setSplitTrack(boolean);
    method public void setSwitchMinWidth(int);
    method public void setSwitchPadding(int);
    method public void setSwitchTextAppearance(android.content.Context, int);
+138 −81
Original line number Diff line number Diff line
@@ -19,7 +19,9 @@ package android.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
@@ -32,8 +34,11 @@ import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.R;

public abstract class AbsSeekBar extends ProgressBar {
    private final Rect mTempRect = new Rect();

    private Drawable mThumb;
    private int mThumbOffset;
    private boolean mSplitTrack;

    /**
     * On touch, this offset plus the scaled value from the position of the
@@ -76,12 +81,16 @@ public abstract class AbsSeekBar extends ProgressBar {

        TypedArray a = context.obtainStyledAttributes(
                attrs, com.android.internal.R.styleable.SeekBar, defStyleAttr, defStyleRes);
        Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
        setThumb(thumb); // will guess mThumbOffset if thumb != null...
        // ...but allow layout to override this
        int thumbOffset = a.getDimensionPixelOffset(

        final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
        setThumb(thumb);

        // Guess thumb offset if thumb != null, but allow layout to override.
        final int thumbOffset = a.getDimensionPixelOffset(
                com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset());
        setThumbOffset(thumbOffset);

        mSplitTrack = a.getBoolean(com.android.internal.R.styleable.SeekBar_splitTrack, false);
        a.recycle();

        a = context.obtainStyledAttributes(attrs,
@@ -132,7 +141,7 @@ public abstract class AbsSeekBar extends ProgressBar {
        mThumb = thumb;
        invalidate();
        if (needUpdate) {
            updateThumbPos(getWidth(), getHeight());
            updateThumbAndTrackPos(getWidth(), getHeight());
            if (thumb != null && thumb.isStateful()) {
                // Note that if the states are different this won't work.
                // For now, let's consider that an app bug.
@@ -170,6 +179,25 @@ public abstract class AbsSeekBar extends ProgressBar {
        invalidate();
    }

    /**
     * Specifies whether the track should be split by the thumb. When true,
     * the thumb's optical bounds will be clipped out of the track drawable,
     * then the thumb will be drawn into the resulting gap.
     *
     * @param splitTrack Whether the track should be split by the thumb
     */
    public void setSplitTrack(boolean splitTrack) {
        mSplitTrack = splitTrack;
        invalidate();
    }

    /**
     * Returns whether the track should be split by the thumb.
     */
    public boolean getSplitTrack() {
        return mSplitTrack;
    }

    /**
     * Sets the amount of progress changed via the arrow keys.
     *
@@ -218,78 +246,84 @@ public abstract class AbsSeekBar extends ProgressBar {
    protected void drawableStateChanged() {
        super.drawableStateChanged();

        Drawable progressDrawable = getProgressDrawable();
        final Drawable progressDrawable = getProgressDrawable();
        if (progressDrawable != null) {
            progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
        }

        if (mThumb != null && mThumb.isStateful()) {
            int[] state = getDrawableState();
            mThumb.setState(state);
        final Drawable thumb = mThumb;
        if (thumb != null && thumb.isStateful()) {
            thumb.setState(getDrawableState());
        }
    }

    @Override
    void onProgressRefresh(float scale, boolean fromUser) {
        super.onProgressRefresh(scale, fromUser);
        Drawable thumb = mThumb;

        final Drawable thumb = mThumb;
        if (thumb != null) {
            setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
            /*
             * Since we draw translated, the drawable's bounds that it signals
             * for invalidation won't be the actual bounds we want invalidated,
             * so just invalidate this whole view.
             */

            // Since we draw translated, the drawable's bounds that it signals
            // for invalidation won't be the actual bounds we want invalidated,
            // so just invalidate this whole view.
            invalidate();
        }
    }

    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        updateThumbPos(w, h);

        updateThumbAndTrackPos(w, h);
    }

    private void updateThumbPos(int w, int h) {
        Drawable d = getCurrentDrawable();
        Drawable thumb = mThumb;
        int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
        // The max height does not incorporate padding, whereas the height
        // parameter does
        int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom);
    private void updateThumbAndTrackPos(int w, int h) {
        final Drawable track = getCurrentDrawable();
        final Drawable thumb = mThumb;

        int max = getMax();
        float scale = max > 0 ? (float) getProgress() / (float) max : 0;
        // The max height does not incorporate padding, whereas the height
        // parameter does.
        final int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom);
        final int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();

        // Apply offset to whichever item is taller.
        final int trackOffset;
        final int thumbOffset;
        if (thumbHeight > trackHeight) {
            if (thumb != null) {
                setThumbPos(w, thumb, scale, 0);
            }
            int gapForCenteringTrack = (thumbHeight - trackHeight) / 2;
            if (d != null) {
                // Canvas will be translated by the padding, so 0,0 is where we start drawing
                d.setBounds(0, gapForCenteringTrack, 
                        w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - gapForCenteringTrack
                        - mPaddingTop);
            }
            trackOffset = (thumbHeight - trackHeight) / 2;
            thumbOffset = 0;
        } else {
            if (d != null) {
                // Canvas will be translated by the padding, so 0,0 is where we start drawing
                d.setBounds(0, 0, w - mPaddingRight - mPaddingLeft, h - mPaddingBottom
                        - mPaddingTop);
            trackOffset = 0;
            thumbOffset = (trackHeight - thumbHeight) / 2;
        }

        if (track != null) {
            track.setBounds(0, trackOffset, w - mPaddingRight - mPaddingLeft,
                    h - mPaddingBottom - trackOffset - mPaddingTop);
        }
            int gap = (trackHeight - thumbHeight) / 2;

        if (thumb != null) {
                setThumbPos(w, thumb, scale, gap);
            setThumbPos(w, thumb, getScale(), thumbOffset);
        }
    }

    private float getScale() {
        final int max = getMax();
        return max > 0 ? getProgress() / (float) max : 0;
    }

    /**
     * @param gap If set to {@link Integer#MIN_VALUE}, this will be ignored and
     * Updates the thumb drawable bounds.
     *
     * @param w Width of the view, including padding
     * @param thumb Drawable used for the thumb
     * @param scale Current progress between 0 and 1
     * @param offset Vertical offset for centering. If set to
     *            {@link Integer#MIN_VALUE}, the current offset will be used.
     */
    private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
    private void setThumbPos(int w, Drawable thumb, float scale, int offset) {
        int available = w - mPaddingLeft - mPaddingRight;
        final int thumbWidth = thumb.getIntrinsicWidth();
        final int thumbHeight = thumb.getIntrinsicHeight();
@@ -301,13 +335,13 @@ public abstract class AbsSeekBar extends ProgressBar {
        final int thumbPos = (int) (scale * available + 0.5f);

        final int top, bottom;
        if (gap == Integer.MIN_VALUE) {
        if (offset == Integer.MIN_VALUE) {
            final Rect oldBounds = thumb.getBounds();
            top = oldBounds.top;
            bottom = oldBounds.bottom;
        } else {
            top = gap;
            bottom = gap + thumbHeight;
            top = offset;
            bottom = offset + thumbHeight;
        }

        final int left = (isLayoutRtl() && mMirrorForRtl) ? available - thumbPos : thumbPos;
@@ -342,6 +376,33 @@ public abstract class AbsSeekBar extends ProgressBar {
    protected synchronized void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        drawThumb(canvas);
    }

    @Override
    void drawTrack(Canvas canvas) {
        final Drawable thumbDrawable = mThumb;
        if (thumbDrawable != null && mSplitTrack) {
            final Insets insets = thumbDrawable.getOpticalInsets();
            final Rect tempRect = mTempRect;
            thumbDrawable.copyBounds(tempRect);
            tempRect.offset(mPaddingLeft - mThumbOffset, mPaddingTop);
            tempRect.left += insets.left;
            tempRect.right -= insets.right;

            final int saveCount = canvas.save();
            canvas.clipRect(tempRect, Op.DIFFERENCE);
            super.drawTrack(canvas);
            canvas.restoreToCount(saveCount);
        } else {
            super.drawTrack(canvas);
        }
    }

    /**
     * Draw the thumb.
     */
    void drawThumb(Canvas canvas) {
        if (mThumb != null) {
            canvas.save();
            // Translate the padding. For the x, we need to allow the thumb to
@@ -595,17 +656,13 @@ public abstract class AbsSeekBar extends ProgressBar {
    public void onRtlPropertiesChanged(int layoutDirection) {
        super.onRtlPropertiesChanged(layoutDirection);

        int max = getMax();
        float scale = max > 0 ? (float) getProgress() / (float) max : 0;

        Drawable thumb = mThumb;
        final Drawable thumb = mThumb;
        if (thumb != null) {
            setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
            /*
             * Since we draw translated, the drawable's bounds that it signals
             * for invalidation won't be the actual bounds we want invalidated,
             * so just invalidate this whole view.
             */
            setThumbPos(getWidth(), thumb, getScale(), Integer.MIN_VALUE);

            // Since we draw translated, the drawable's bounds that it signals
            // for invalidation won't be the actual bounds we want invalidated,
            // so just invalidate this whole view.
            invalidate();
        }
    }
+16 −5
Original line number Diff line number Diff line
@@ -1066,21 +1066,30 @@ public class ProgressBar extends View {
    protected synchronized void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Drawable d = mCurrentDrawable;
        drawTrack(canvas);
    }

    /**
     * Draws the progress bar track.
     */
    void drawTrack(Canvas canvas) {
        final Drawable d = mCurrentDrawable;
        if (d != null) {
            // Translate canvas so a indeterminate circular progress bar with padding
            // rotates properly in its animation
            canvas.save();
            final int saveCount = canvas.save();

            if(isLayoutRtl() && mMirrorForRtl) {
                canvas.translate(getWidth() - mPaddingRight, mPaddingTop);
                canvas.scale(-1.0f, 1.0f);
            } else {
                canvas.translate(mPaddingLeft, mPaddingTop);
            }
            long time = getDrawingTime();

            final long time = getDrawingTime();
            if (mHasAnimation) {
                mAnimation.getTransformation(time, mTransformation);
                float scale = mTransformation.getAlpha();
                final float scale = mTransformation.getAlpha();
                try {
                    mInDrawing = true;
                    d.setLevel((int) (scale * MAX_LEVEL));
@@ -1089,8 +1098,10 @@ public class ProgressBar extends View {
                }
                postInvalidateOnAnimation();
            }

            d.draw(canvas);
            canvas.restore();
            canvas.restoreToCount(saveCount);

            if (mShouldStartAnimationDrawable && d instanceof Animatable) {
                ((Animatable) d).start();
                mShouldStartAnimationDrawable = false;
+68 −22
Original line number Diff line number Diff line
@@ -22,9 +22,11 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.StaticLayout;
@@ -85,6 +87,7 @@ public class Switch extends CompoundButton {
    private int mThumbTextPadding;
    private int mSwitchMinWidth;
    private int mSwitchPadding;
    private boolean mSplitTrack;
    private CharSequence mTextOn;
    private CharSequence mTextOff;

@@ -174,13 +177,13 @@ public class Switch extends CompoundButton {
        super(context, attrs, defStyleAttr, defStyleRes);

        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        Resources res = getResources();

        final Resources res = getResources();
        mTextPaint.density = res.getDisplayMetrics().density;
        mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);

        final TypedArray a = context.obtainStyledAttributes(
                attrs, com.android.internal.R.styleable.Switch, defStyleAttr, defStyleRes);

        mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb);
        mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track);
        mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn);
@@ -191,15 +194,16 @@ public class Switch extends CompoundButton {
                com.android.internal.R.styleable.Switch_switchMinWidth, 0);
        mSwitchPadding = a.getDimensionPixelSize(
                com.android.internal.R.styleable.Switch_switchPadding, 0);
        mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false);

        int appearance = a.getResourceId(
        final int appearance = a.getResourceId(
                com.android.internal.R.styleable.Switch_switchTextAppearance, 0);
        if (appearance != 0) {
            setSwitchTextAppearance(context, appearance);
        }
        a.recycle();

        ViewConfiguration config = ViewConfiguration.get(context);
        final ViewConfiguration config = ViewConfiguration.get(context);
        mTouchSlop = config.getScaledTouchSlop();
        mMinFlingVelocity = config.getScaledMinimumFlingVelocity();

@@ -468,6 +472,29 @@ public class Switch extends CompoundButton {
        return mThumbDrawable;
    }

    /**
     * Specifies whether the track should be split by the thumb. When true,
     * the thumb's optical bounds will be clipped out of the track drawable,
     * then the thumb will be drawn into the resulting gap.
     *
     * @param splitTrack Whether the track should be split by the thumb
     *
     * @attr ref android.R.styleable#Switch_splitTrack
     */
    public void setSplitTrack(boolean splitTrack) {
        mSplitTrack = splitTrack;
        invalidate();
    }

    /**
     * Returns whether the track should be split by the thumb.
     *
     * @attr ref android.R.styleable#Switch_splitTrack
     */
    public boolean getSplitTrack() {
        return mSplitTrack;
    }

    /**
     * Returns the text displayed when the button is in the checked state.
     *
@@ -518,13 +545,15 @@ public class Switch extends CompoundButton {

        mTrackDrawable.getPadding(mTempRect);

        final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());
        final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
                + mThumbTextPadding * 2;
        mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth());

        final int switchWidth = Math.max(mSwitchMinWidth,
                maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);
                2 * mThumbWidth + mTempRect.left + mTempRect.right);
        final int switchHeight = Math.max(mTrackDrawable.getIntrinsicHeight(),
                mThumbDrawable.getIntrinsicHeight());

        mThumbWidth = maxTextWidth + mThumbTextPadding * 2;

        mSwitchWidth = switchWidth;
        mSwitchHeight = switchHeight;
@@ -777,7 +806,7 @@ public class Switch extends CompoundButton {
        final Drawable trackDrawable = mTrackDrawable;
        final Drawable thumbDrawable = mThumbDrawable;

        // Draw the switch
        // Layout the track.
        final int switchLeft = mSwitchLeft;
        final int switchTop = mSwitchTop;
        final int switchRight = mSwitchRight;
@@ -793,9 +822,10 @@ public class Switch extends CompoundButton {
        // Relies on mTempRect, MUST be called first!
        final int thumbPos = getThumbOffset();

        // Layout the thumb.
        thumbDrawable.getPadding(tempRect);
        int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
        int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
        final int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
        final int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
        thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);

        final Drawable background = getBackground();
@@ -805,20 +835,32 @@ public class Switch extends CompoundButton {

        super.onDraw(canvas);

        if (mSplitTrack) {
            final Insets insets = thumbDrawable.getOpticalInsets();
            thumbDrawable.copyBounds(tempRect);
            tempRect.left += insets.left;
            tempRect.right -= insets.right;

            final int saveCount = canvas.save();
            canvas.clipRect(tempRect, Op.DIFFERENCE);
            trackDrawable.draw(canvas);
            canvas.restoreToCount(saveCount);
        } else {
            trackDrawable.draw(canvas);
        }

        final int saveCount = canvas.save();
        canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
        thumbDrawable.draw(canvas);

        final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
        if (switchText != null) {
            final int drawableState[] = getDrawableState();
            if (mTextColors != null) {
                mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
            }
            mTextPaint.drawableState = drawableState;

        final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
        if (switchText != null) {
            final int left = (thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2;
            final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2;
            canvas.translate(left, top);
@@ -889,12 +931,16 @@ public class Switch extends CompoundButton {
    protected void drawableStateChanged() {
        super.drawableStateChanged();

        int[] myDrawableState = getDrawableState();
        final int[] myDrawableState = getDrawableState();

        // Set the state of the Drawable
        // Drawable may be null when checked state is set from XML, from super constructor
        if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState);
        if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState);
        if (mThumbDrawable != null && mThumbDrawable.setState(myDrawableState)) {
            // Handle changes to thumb width and height.
            requestLayout();
        }

        if (mTrackDrawable != null) {
            mTrackDrawable.setState(myDrawableState);
        }

        invalidate();
    }
−109 B (232 B)
Loading image diff...
Loading