AppCompatSeekBarHelper.java revision 3de8a4e8305507475d7890205184946a25cf45e7
110e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes/*
210e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes * Copyright (C) 2015 The Android Open Source Project
310e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes *
410e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes * Licensed under the Apache License, Version 2.0 (the "License");
510e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes * you may not use this file except in compliance with the License.
610e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes * You may obtain a copy of the License at
710e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes *
810e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes *      http://www.apache.org/licenses/LICENSE-2.0
910e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes *
1010e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes * Unless required by applicable law or agreed to in writing, software
1110e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes * distributed under the License is distributed on an "AS IS" BASIS,
1210e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1310e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes * See the License for the specific language governing permissions and
1410e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes * limitations under the License.
1510e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes */
1610e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.appcompat.widget;
1810e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
19b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banesimport android.content.res.ColorStateList;
20b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banesimport android.graphics.Canvas;
21b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banesimport android.graphics.PorterDuff;
2210e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banesimport android.graphics.drawable.Drawable;
233de8a4e8305507475d7890205184946a25cf45e7Aurimas Liutikasimport android.util.AttributeSet;
243de8a4e8305507475d7890205184946a25cf45e7Aurimas Liutikasimport android.widget.SeekBar;
253de8a4e8305507475d7890205184946a25cf45e7Aurimas Liutikas
26ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.Nullable;
27ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RequiresApi;
283de8a4e8305507475d7890205184946a25cf45e7Aurimas Liutikasimport androidx.appcompat.R;
29ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.graphics.drawable.DrawableCompat;
30ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.view.ViewCompat;
3110e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
3210e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banesclass AppCompatSeekBarHelper extends AppCompatProgressBarHelper {
3310e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
3410e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes    private final SeekBar mView;
3510e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
36b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    private Drawable mTickMark;
37b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    private ColorStateList mTickMarkTintList = null;
38b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    private PorterDuff.Mode mTickMarkTintMode = null;
39b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    private boolean mHasTickMarkTint = false;
40b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    private boolean mHasTickMarkTintMode = false;
41b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
424c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes    AppCompatSeekBarHelper(SeekBar view) {
434c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes        super(view);
4410e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        mView = view;
4510e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes    }
4610e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
47dc8e099ed130434c0238f558277c5bcb6a533121Aurimas Liutikas    @Override
4810e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
4910e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        super.loadFromAttributes(attrs, defStyleAttr);
5010e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes
5110e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
52b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                R.styleable.AppCompatSeekBar, defStyleAttr, 0);
53b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        final Drawable drawable = a.getDrawableIfKnown(R.styleable.AppCompatSeekBar_android_thumb);
5410e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        if (drawable != null) {
5510e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes            mView.setThumb(drawable);
5610e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        }
57b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
58b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        final Drawable tickMark = a.getDrawable(R.styleable.AppCompatSeekBar_tickMark);
59b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        setTickMark(tickMark);
60b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
61b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        if (a.hasValue(R.styleable.AppCompatSeekBar_tickMarkTintMode)) {
62b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            mTickMarkTintMode = DrawableUtils.parseTintMode(a.getInt(
63b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                    R.styleable.AppCompatSeekBar_tickMarkTintMode, -1), mTickMarkTintMode);
64b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            mHasTickMarkTintMode = true;
65b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        }
66b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
67b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        if (a.hasValue(R.styleable.AppCompatSeekBar_tickMarkTint)) {
68b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            mTickMarkTintList = a.getColorStateList(R.styleable.AppCompatSeekBar_tickMarkTint);
69b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            mHasTickMarkTint = true;
70b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        }
71b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
7210e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes        a.recycle();
73b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
74b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        applyTickMarkTint();
75b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    }
76b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
77b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    void setTickMark(@Nullable Drawable tickMark) {
78b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        if (mTickMark != null) {
79b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            mTickMark.setCallback(null);
80b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        }
81b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
82b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        mTickMark = tickMark;
83b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
84b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        if (tickMark != null) {
85b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            tickMark.setCallback(mView);
86b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            DrawableCompat.setLayoutDirection(tickMark, ViewCompat.getLayoutDirection(mView));
87b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            if (tickMark.isStateful()) {
88b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                tickMark.setState(mView.getDrawableState());
89b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            }
90b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            applyTickMarkTint();
91b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        }
92b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
93b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        mView.invalidate();
94b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    }
95b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
96b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    @Nullable
97b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    Drawable getTickMark() {
98b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        return mTickMark;
99b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    }
100b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
101b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    void setTickMarkTintList(@Nullable ColorStateList tint) {
102b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        mTickMarkTintList = tint;
103b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        mHasTickMarkTint = true;
104b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
105b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        applyTickMarkTint();
106b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    }
107b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
108b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    @Nullable
109b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    ColorStateList getTickMarkTintList() {
110b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        return mTickMarkTintList;
11110e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes    }
112b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
113b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    void setTickMarkTintMode(@Nullable PorterDuff.Mode tintMode) {
114b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        mTickMarkTintMode = tintMode;
115b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        mHasTickMarkTintMode = true;
116b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
117b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        applyTickMarkTint();
118b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    }
119b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
120b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    @Nullable
121b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    PorterDuff.Mode getTickMarkTintMode() {
122b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        return mTickMarkTintMode;
123b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    }
124b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
125b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    private void applyTickMarkTint() {
126b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        if (mTickMark != null && (mHasTickMarkTint || mHasTickMarkTintMode)) {
127168fbe2c9b213338bce447c8825303c86b5b9281Chris Banes            mTickMark = DrawableCompat.wrap(mTickMark.mutate());
128b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
129b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            if (mHasTickMarkTint) {
130168fbe2c9b213338bce447c8825303c86b5b9281Chris Banes                DrawableCompat.setTintList(mTickMark, mTickMarkTintList);
131b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            }
132b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
133b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            if (mHasTickMarkTintMode) {
134168fbe2c9b213338bce447c8825303c86b5b9281Chris Banes                DrawableCompat.setTintMode(mTickMark, mTickMarkTintMode);
135b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            }
136b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
137b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            // The drawable (or one of its children) may not have been
138b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            // stateful before applying the tint, so let's try again.
139b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            if (mTickMark.isStateful()) {
140b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                mTickMark.setState(mView.getDrawableState());
141b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            }
142b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        }
143b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    }
144b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
1458f886fe8c7e23fe6ccb8734167c960c2ed3429c3Alan Viverette    @RequiresApi(11)
146b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    void jumpDrawablesToCurrentState() {
147b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        if (mTickMark != null) {
148b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            mTickMark.jumpToCurrentState();
149b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        }
150b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    }
151b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
152b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    void drawableStateChanged() {
153b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        final Drawable tickMark = mTickMark;
154b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        if (tickMark != null && tickMark.isStateful()
155b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                && tickMark.setState(mView.getDrawableState())) {
156b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            mView.invalidateDrawable(tickMark);
157b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        }
158b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    }
159b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
160b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    /**
161b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes     * Draw the tick marks.
162b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes     */
163b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    void drawTickMarks(Canvas canvas) {
164b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        if (mTickMark != null) {
165b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            final int count = mView.getMax();
166b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            if (count > 1) {
167b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                final int w = mTickMark.getIntrinsicWidth();
168b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                final int h = mTickMark.getIntrinsicHeight();
169b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                final int halfW = w >= 0 ? w / 2 : 1;
170b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                final int halfH = h >= 0 ? h / 2 : 1;
171b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                mTickMark.setBounds(-halfW, -halfH, halfW, halfH);
172b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
173b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                final float spacing = (mView.getWidth() - mView.getPaddingLeft()
174b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                        - mView.getPaddingRight()) / (float) count;
175b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                final int saveCount = canvas.save();
176b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                canvas.translate(mView.getPaddingLeft(), mView.getHeight() / 2);
177b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                for (int i = 0; i <= count; i++) {
178b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                    mTickMark.draw(canvas);
179b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                    canvas.translate(spacing, 0);
180b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                }
181b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes                canvas.restoreToCount(saveCount);
182b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes            }
183b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes        }
184b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes    }
185b19cba1c83a68efb5c18ef74e4e5d376fa166625Chris Banes
18610e2dbc1ad1c01d2824d921a8b0f070859d6f146Chris Banes}
187