AppCompatCompoundButtonHelper.java revision 091b0f935e68ce9bfecc2422e60eada33fa3b09c
1091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes/*
2091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * Copyright (C) 2015 The Android Open Source Project
3091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes *
4091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * you may not use this file except in compliance with the License.
6091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * You may obtain a copy of the License at
7091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes *
8091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes *
10091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * Unless required by applicable law or agreed to in writing, software
11091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * See the License for the specific language governing permissions and
14091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * limitations under the License.
15091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes */
16091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
17091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banespackage android.support.v7.widget;
18091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
19091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.content.res.ColorStateList;
20091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.content.res.TypedArray;
21091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.graphics.PorterDuff;
22091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.graphics.drawable.Drawable;
23091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.os.Build;
24091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.support.annotation.Nullable;
25091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.support.v4.graphics.drawable.DrawableCompat;
26091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.support.v4.widget.CompoundButtonCompat;
27091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.support.v7.appcompat.R;
28091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.support.v7.graphics.drawable.DrawableUtils;
29091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.support.v7.internal.widget.TintManager;
30091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.util.AttributeSet;
31091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.widget.CompoundButton;
32091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
33091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesclass AppCompatCompoundButtonHelper {
34091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
35091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    private final CompoundButton mView;
36091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    private final TintManager mTintManager;
37091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
38091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    private ColorStateList mButtonTintList = null;
39091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    private PorterDuff.Mode mButtonTintMode = null;
40091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    private boolean mHasButtonTint = false;
41091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    private boolean mHasButtonTintMode = false;
42091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
43091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    private boolean mSkipNextApply;
44091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
45091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    /**
46091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes     * Interface which allows us to directly set a button, bypass any calls back to ourselves.
47091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes     */
48091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    interface DirectSetButtonDrawableInterface {
49091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        void setButtonDrawable(Drawable buttonDrawable);
50091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
51091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
52091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    AppCompatCompoundButtonHelper(CompoundButton view, TintManager tintManager) {
53091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        mView = view;
54091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        mTintManager = tintManager;
55091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
56091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
57091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
58091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        TypedArray a = mView.getContext().obtainStyledAttributes(attrs, R.styleable.CompoundButton,
59091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                defStyleAttr, 0);
60091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        try {
61091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            if (a.hasValue(R.styleable.CompoundButton_android_button)) {
62091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                final int resourceId = a.getResourceId(
63091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                        R.styleable.CompoundButton_android_button, 0);
64091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                if (resourceId != 0) {
65091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                    mView.setButtonDrawable(mTintManager.getDrawable(resourceId));
66091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                }
67091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            }
68091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            if (a.hasValue(R.styleable.CompoundButton_buttonTint)) {
69091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                CompoundButtonCompat.setButtonTintList(mView,
70091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                        a.getColorStateList(R.styleable.CompoundButton_buttonTint));
71091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            }
72091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            if (a.hasValue(R.styleable.CompoundButton_buttonTintMode)) {
73091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                CompoundButtonCompat.setButtonTintMode(mView,
74091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                        DrawableUtils.parseTintMode(
75091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                                a.getInt(R.styleable.CompoundButton_buttonTintMode, -1),
76091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                                null));
77091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            }
78091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        } finally {
79091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            a.recycle();
80091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        }
81091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
82091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
83091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    void setSupportButtonTintList(ColorStateList tint) {
84091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        mButtonTintList = tint;
85091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        mHasButtonTint = true;
86091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
87091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        applyButtonTint();
88091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
89091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
90091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    ColorStateList getSupportButtonTintList() {
91091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        return mButtonTintList;
92091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
93091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
94091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    void setSupportButtonTintMode(@Nullable PorterDuff.Mode tintMode) {
95091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        mButtonTintMode = tintMode;
96091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        mHasButtonTintMode = true;
97091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
98091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        applyButtonTint();
99091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
100091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
101091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    PorterDuff.Mode getSupportButtonTintMode() {
102091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        return mButtonTintMode;
103091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
104091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
105091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    void onSetButtonDrawable() {
106091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        if (mSkipNextApply) {
107091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            mSkipNextApply = false;
108091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            return;
109091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        }
110091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
111091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        mSkipNextApply = true;
112091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        applyButtonTint();
113091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
114091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
115091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    void applyButtonTint() {
116091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        Drawable buttonDrawable = CompoundButtonCompat.getButtonDrawable(mView);
117091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
118091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        if (buttonDrawable != null && (mHasButtonTint || mHasButtonTintMode)) {
119091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
120091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            buttonDrawable = buttonDrawable.mutate();
121091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            if (mHasButtonTint) {
122091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                DrawableCompat.setTintList(buttonDrawable, mButtonTintList);
123091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            }
124091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            if (mHasButtonTintMode) {
125091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                DrawableCompat.setTintMode(buttonDrawable, mButtonTintMode);
126091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            }
127091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            // The drawable (or one of its children) may not have been
128091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            // stateful before applying the tint, so let's try again.
129091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            if (buttonDrawable.isStateful()) {
130091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                buttonDrawable.setState(mView.getDrawableState());
131091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            }
132091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            mView.setButtonDrawable(buttonDrawable);
133091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        }
134091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
135091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
136091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    int getCompoundPaddingLeft(int superValue) {
137091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
138091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            // Before JB-MR1 the button drawable wasn't taken into account for padding. We'll
139091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            // workaround that here
140091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            Drawable buttonDrawable = CompoundButtonCompat.getButtonDrawable(mView);
141091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            if (buttonDrawable != null) {
142091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                superValue += buttonDrawable.getIntrinsicWidth();
143091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            }
144091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        }
145091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        return superValue;
146091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
147091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes}
148