1f933672168f6deff5e3636584bec735416b5a93bChris Banes/*
2f933672168f6deff5e3636584bec735416b5a93bChris Banes * Copyright (C) 2015 The Android Open Source Project
3f933672168f6deff5e3636584bec735416b5a93bChris Banes *
4f933672168f6deff5e3636584bec735416b5a93bChris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5f933672168f6deff5e3636584bec735416b5a93bChris Banes * you may not use this file except in compliance with the License.
6f933672168f6deff5e3636584bec735416b5a93bChris Banes * You may obtain a copy of the License at
7f933672168f6deff5e3636584bec735416b5a93bChris Banes *
8f933672168f6deff5e3636584bec735416b5a93bChris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9f933672168f6deff5e3636584bec735416b5a93bChris Banes *
10f933672168f6deff5e3636584bec735416b5a93bChris Banes * Unless required by applicable law or agreed to in writing, software
11f933672168f6deff5e3636584bec735416b5a93bChris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12f933672168f6deff5e3636584bec735416b5a93bChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f933672168f6deff5e3636584bec735416b5a93bChris Banes * See the License for the specific language governing permissions and
14f933672168f6deff5e3636584bec735416b5a93bChris Banes * limitations under the License.
15f933672168f6deff5e3636584bec735416b5a93bChris Banes */
16f933672168f6deff5e3636584bec735416b5a93bChris Banes
17f933672168f6deff5e3636584bec735416b5a93bChris Banespackage android.support.v7.widget;
18f933672168f6deff5e3636584bec735416b5a93bChris Banes
19f933672168f6deff5e3636584bec735416b5a93bChris Banes
2010c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banesimport android.content.Context;
21f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.content.res.Resources;
22f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.content.res.Resources.Theme;
2310c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banesimport android.support.annotation.NonNull;
24f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.support.annotation.Nullable;
2510c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banesimport android.support.v7.internal.view.ContextThemeWrapper;
2610c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banesimport android.view.LayoutInflater;
27f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.view.View;
28f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.view.ViewGroup;
29f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.widget.SpinnerAdapter;
30f933672168f6deff5e3636584bec735416b5a93bChris Banes
31f933672168f6deff5e3636584bec735416b5a93bChris Banes/**
32f933672168f6deff5e3636584bec735416b5a93bChris Banes * An extension of SpinnerAdapter that is capable of inflating drop-down views
33f933672168f6deff5e3636584bec735416b5a93bChris Banes * against a different theme than normal views.
34f933672168f6deff5e3636584bec735416b5a93bChris Banes * <p>
35f933672168f6deff5e3636584bec735416b5a93bChris Banes * Classes that implement this interface should use the theme provided to
36f933672168f6deff5e3636584bec735416b5a93bChris Banes * {@link #setDropDownViewTheme(Theme)} when creating views in
37f933672168f6deff5e3636584bec735416b5a93bChris Banes * {@link SpinnerAdapter#getDropDownView(int, View, ViewGroup)}.
3810c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes *
3910c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes * <p>The {@link Helper} class is provided to aide implementation in a backwards compatible way.
4010c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes * </p>
41f933672168f6deff5e3636584bec735416b5a93bChris Banes */
42f933672168f6deff5e3636584bec735416b5a93bChris Banespublic interface ThemedSpinnerAdapter extends SpinnerAdapter {
43f933672168f6deff5e3636584bec735416b5a93bChris Banes    /**
44f933672168f6deff5e3636584bec735416b5a93bChris Banes     * Sets the {@link Resources.Theme} against which drop-down views are
45f933672168f6deff5e3636584bec735416b5a93bChris Banes     * inflated.
46f933672168f6deff5e3636584bec735416b5a93bChris Banes     *
47f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @param theme the context against which to inflate drop-down views, or
48f933672168f6deff5e3636584bec735416b5a93bChris Banes     *              {@code null} to use the default theme
49f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @see SpinnerAdapter#getDropDownView(int, View, ViewGroup)
50f933672168f6deff5e3636584bec735416b5a93bChris Banes     */
51f933672168f6deff5e3636584bec735416b5a93bChris Banes    void setDropDownViewTheme(@Nullable Resources.Theme theme);
52f933672168f6deff5e3636584bec735416b5a93bChris Banes
53f933672168f6deff5e3636584bec735416b5a93bChris Banes    /**
54f933672168f6deff5e3636584bec735416b5a93bChris Banes     * Returns the value previously set by a call to
55f933672168f6deff5e3636584bec735416b5a93bChris Banes     * {@link #setDropDownViewTheme(Theme)}.
56f933672168f6deff5e3636584bec735416b5a93bChris Banes     *
57f933672168f6deff5e3636584bec735416b5a93bChris Banes     * @return the {@link Resources.Theme} against which drop-down views are
58f933672168f6deff5e3636584bec735416b5a93bChris Banes     *         inflated, or {@code null} if one has not been explicitly set
59f933672168f6deff5e3636584bec735416b5a93bChris Banes     */
60f933672168f6deff5e3636584bec735416b5a93bChris Banes    @Nullable
61f933672168f6deff5e3636584bec735416b5a93bChris Banes    Resources.Theme getDropDownViewTheme();
6210c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes
6310c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes    /**
6410c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     * A helper class which allows easy integration of {@link ThemedSpinnerAdapter} into existing
6510c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     * {@link SpinnerAdapter}s in a backwards compatible way.
6610c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *
6710c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     * <p>An example {@link android.widget.BaseAdapter BaseAdapter} implementation would be:</p>
6810c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *
6910c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     * <pre>
7010c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     * public class MyAdapter extends BaseAdapter implements ThemedSpinnerAdapter {
7110c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *     private final ThemedSpinnerAdapter.Helper mDropDownHelper;
7210c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *
7310c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *     public CheeseAdapter(Context context) {
7410c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *         mDropDownHelper = new ThemedSpinnerAdapter.Helper(context);
7510c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *         // ...
7610c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *     }
7710c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *
7810c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *     &#064;Override
7910c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *     public View getDropDownView(int position, View convertView, ViewGroup parent) {
8010c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *         View view;
8110c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *
8210c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *         if (convertView == null) {
8310c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *             // Inflate the drop down using the helper's LayoutInflater
8410c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *             LayoutInflater inflater = mDropDownHelper.getDropDownViewInflater();
8510c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *             view = inflater.inflate(R.layout.my_dropdown, parent, false);
8610c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *         }
8710c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *
8810c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *         // ...
8910c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *     }
9010c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *
9110c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *     &#064;Override
9210c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *     public void setDropDownViewTheme(@Nullable Resources.Theme theme) {
9310c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *         // Pass the new theme to the helper
9410c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *         mDropDownHelper.setDropDownViewTheme(theme);
9510c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *     }
9610c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *
9710c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *     &#064;Override
9810c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *     public Resources.Theme getDropDownViewTheme() {
9910c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *         // Return the helper's value
10010c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *         return mDropDownHelper.getDropDownViewTheme();
10110c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     *     }
10210c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     * }
10310c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     * </pre>
10410c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes     */
10510c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes    public final static class Helper {
10610c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        private final Context mContext;
10710c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        private final LayoutInflater mInflater;
10810c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        private LayoutInflater mDropDownInflater;
10910c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes
11010c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        public Helper(@NonNull Context context) {
11110c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes            mContext = context;
11210c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes            mInflater = LayoutInflater.from(context);
11310c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        }
11410c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes
11510c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        /**
11610c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         * Should be called from your adapter's
11710c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         * {@link ThemedSpinnerAdapter#setDropDownViewTheme(Theme)}
11810c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         *
11910c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         * @param theme the theme passed in to
12010c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         *              {@link ThemedSpinnerAdapter#setDropDownViewTheme(Theme)}
12110c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         */
12210c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        public void setDropDownViewTheme(@Nullable Resources.Theme theme) {
12310c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes            if (theme == null) {
12410c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes                mDropDownInflater = null;
12510c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes            } else if (theme == mContext.getTheme()) {
12610c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes                mDropDownInflater = mInflater;
12710c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes            } else {
12810c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes                final Context context = new ContextThemeWrapper(mContext, theme);
12910c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes                mDropDownInflater = LayoutInflater.from(context);
13010c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes            }
13110c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        }
13210c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes
13310c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        /**
13410c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         * Should be called from your adapter's {@link ThemedSpinnerAdapter#getDropDownViewTheme()},
13510c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         * returning the value returned from this method.
13610c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         */
13710c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        @Nullable
13810c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        public Resources.Theme getDropDownViewTheme() {
13910c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes            return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme();
14010c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        }
14110c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes
14210c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        /**
14310c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         * Returns the {@link LayoutInflater} which should be used when inflating any layouts
14410c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         * from your {@link SpinnerAdapter#getDropDownView(int, View, ViewGroup)}.
14510c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         *
14610c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         * <p>The instance returned will have a correct theme, meaning that any inflated views
14710c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         * will be created with the same theme.</p>
14810c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes         */
14910c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        @NonNull
15010c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        public LayoutInflater getDropDownViewInflater() {
15110c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes            return mDropDownInflater != null ? mDropDownInflater : mInflater;
15210c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes        }
15310c8130fe03a6fb77857ac8ec91c0a21b81fcc5eChris Banes    }
154f933672168f6deff5e3636584bec735416b5a93bChris Banes}
155