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

Commit 1cb088c1 authored by Alan Viverette's avatar Alan Viverette Committed by Android (Google) Code Review
Browse files

Merge "Support for hollow switch and seek bar thumbs"

parents 03365563 661e6365
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
@@ -34386,9 +34387,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);
  }
@@ -36193,6 +36196,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();
@@ -36201,6 +36205,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