1 /*
2 * Copyright 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 
17 package com.example.android.drawabletinting;
18 
19 import android.graphics.Color;
20 import android.graphics.PorterDuff;
21 import android.os.Bundle;
22 import android.support.annotation.Nullable;
23 import android.support.v4.app.Fragment;
24 import android.view.LayoutInflater;
25 import android.view.View;
26 import android.view.ViewGroup;
27 import android.widget.AdapterView;
28 import android.widget.ArrayAdapter;
29 import android.widget.ImageView;
30 import android.widget.SeekBar;
31 import android.widget.Spinner;
32 import android.widget.SpinnerAdapter;
33 import android.widget.TextView;
34 
35 import com.example.android.common.logger.Log;
36 
37 /**
38  * Sample that shows tinting of Drawables programmatically and of Drawable resources in XML.
39  * Tinting is set on a nine-patch drawable through the "tint" and "tintMode" parameters.
40  * A color state list is referenced as the tint color, which  defines colors for different
41  * states of a View (for example disabled/enabled, focused, pressed or selected).
42  * Programmatically, tinting is applied to a Drawable through its "setColorFilter" method, with
43  * a reference to a color and a PorterDuff blend mode. The color and blend mode can be
44  * changed from the UI.
45  *
46  * @see android.graphics.drawable.Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)
47  * @see android.graphics.drawable.Drawable#setTint(android.content.res.ColorStateList, android.graphics.PorterDuff.Mode)
48  */
49 public class DrawableTintingFragment extends Fragment {
50 
51     /**
52      * String that identifies logging output from this Fragment.
53      */
54     private static final String TAG = "DrawableTintingFragment";
55 
56     /**
57      * Image that tinting is applied to programmatically.
58      */
59     private ImageView mImage;
60 
61     /**
62      * Seekbar for alpha component of tinting color.
63      */
64     private SeekBar mAlphaBar;
65     /**
66      * Seekbar for red component of tinting color.
67      */
68     private SeekBar mRedBar;
69     /**
70      * Seekbar for green bar of tinting color.
71      */
72     private SeekBar mGreenBar;
73     /**
74      * Seekbar for blue bar of tinting color.
75      */
76     private SeekBar mBlueBar;
77 
78     /**
79      * Text label for alpha component seekbar.
80      */
81     private TextView mAlphaText;
82     /**
83      * Text label for red component seekbar.
84      */
85     private TextView mRedText;
86     /**
87      * Text label for green component seekbar.
88      */
89     private TextView mGreenText;
90     /**
91      * Text label for blue component seekbar.
92      */
93     private TextView mBlueText;
94 
95     /**
96      * Selector for blend type for color tinting.
97      */
98     private Spinner mBlendSpinner;
99 
100     /**
101      * Computed color for tinting of drawable.
102      */
103     private int mHintColor;
104 
105     /**
106      * Selected color tinting mode.
107      */
108     private PorterDuff.Mode mMode;
109 
110     /**
111      * Identifier for state of blend mod spinner in state bundle.
112      */
113     private static final String STATE_BLEND = "DRAWABLETINTING_BLEND";
114     /**
115      * Identifier for state of alpha seek bar in state bundle.
116      */
117     private static final String STATE_ALPHA = "DRAWABLETINTING_ALPHA";
118     /**
119      * Identifier for state of red seek bar in state bundle.
120      */
121     private static final String STATE_RED = "DRAWABLETINTING_RED";
122     /**
123      * Identifier for state of green seek bar in state bundle.
124      */
125     private static final String STATE_GREEN = "DRAWABLETINTING_GREEN";
126     /**
127      * Identifier for state of blue seek bar in state bundle.
128      */
129     private static final String STATE_BLUE = "DRAWABLETINTING_BLUE";
130 
131     /**
132      * Available tinting modes. Note that this array must be kept in sync with the
133      * <code>blend_modes</code> string array that provides labels for these modes.
134      */
135     private static final PorterDuff.Mode[] MODES = new PorterDuff.Mode[]{
136             PorterDuff.Mode.ADD,
137             PorterDuff.Mode.CLEAR,
138             PorterDuff.Mode.DARKEN,
139             PorterDuff.Mode.DST,
140             PorterDuff.Mode.DST_ATOP,
141             PorterDuff.Mode.DST_IN,
142             PorterDuff.Mode.DST_OUT,
143             PorterDuff.Mode.DST_OVER,
144             PorterDuff.Mode.LIGHTEN,
145             PorterDuff.Mode.MULTIPLY,
146             PorterDuff.Mode.OVERLAY,
147             PorterDuff.Mode.SCREEN,
148             PorterDuff.Mode.SRC,
149             PorterDuff.Mode.SRC_ATOP,
150             PorterDuff.Mode.SRC_IN,
151             PorterDuff.Mode.SRC_OUT,
152             PorterDuff.Mode.SRC_OVER,
153             PorterDuff.Mode.XOR
154     };
155 
156 
157     @Override
onCreate(Bundle savedInstanceState)158     public void onCreate(Bundle savedInstanceState) {
159         super.onCreate(savedInstanceState);
160         setHasOptionsMenu(true);
161     }
162 
163     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)164     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
165         View v = inflater.inflate(R.layout.tinting_fragment, null);
166 
167         // Set a drawable as the image to display
168         mImage = (ImageView) v.findViewById(R.id.image);
169         mImage.setImageResource(R.drawable.btn_default_normal_holo);
170 
171         // Get text labels and seekbars for the four color components: ARGB
172         mAlphaBar = (SeekBar) v.findViewById(R.id.alphaSeek);
173         mAlphaText = (TextView) v.findViewById(R.id.alphaText);
174         mGreenBar = (SeekBar) v.findViewById(R.id.greenSeek);
175         mGreenText = (TextView) v.findViewById(R.id.greenText);
176         mRedBar = (SeekBar) v.findViewById(R.id.redSeek);
177         mRedText = (TextView) v.findViewById(R.id.redText);
178         mBlueText = (TextView) v.findViewById(R.id.blueText);
179         mBlueBar = (SeekBar) v.findViewById(R.id.blueSeek);
180 
181         // Set a listener to update tinted image when selections have changed
182         mAlphaBar.setOnSeekBarChangeListener(mSeekBarListener);
183         mRedBar.setOnSeekBarChangeListener(mSeekBarListener);
184         mGreenBar.setOnSeekBarChangeListener(mSeekBarListener);
185         mBlueBar.setOnSeekBarChangeListener(mSeekBarListener);
186 
187 
188         // Set up the spinner for blend mode selection from a string array resource
189         mBlendSpinner = (Spinner) v.findViewById(R.id.blendSpinner);
190         SpinnerAdapter sa = ArrayAdapter.createFromResource(getActivity(),
191                 R.array.blend_modes, android.R.layout.simple_spinner_dropdown_item);
192         mBlendSpinner.setAdapter(sa);
193         // Set a listener to update the tinted image when a blend mode is selected
194         mBlendSpinner.setOnItemSelectedListener(mBlendListener);
195         // Select the first item
196         mBlendSpinner.setSelection(0);
197         mMode = MODES[0];
198 
199         if (savedInstanceState != null) {
200             // Restore the previous state if this fragment has been restored
201             mBlendSpinner.setSelection(savedInstanceState.getInt(STATE_BLEND));
202             mAlphaBar.setProgress(savedInstanceState.getInt(STATE_ALPHA));
203             mRedBar.setProgress(savedInstanceState.getInt(STATE_RED));
204             mGreenBar.setProgress(savedInstanceState.getInt(STATE_GREEN));
205             mBlueBar.setProgress(savedInstanceState.getInt(STATE_BLUE));
206         }
207 
208         // Apply the default blend mode and color
209         updateTint(getColor(), getTintMode());
210 
211         return v;
212     }
213 
214     @Override
onSaveInstanceState(Bundle outState)215     public void onSaveInstanceState(Bundle outState) {
216         super.onSaveInstanceState(outState);
217         Log.d(TAG, "state saved.");
218         outState.putInt(STATE_BLEND, mBlendSpinner.getSelectedItemPosition());
219         outState.putInt(STATE_ALPHA, mAlphaBar.getProgress());
220         outState.putInt(STATE_RED, mRedBar.getProgress());
221         outState.putInt(STATE_GREEN, mGreenBar.getProgress());
222         outState.putInt(STATE_BLUE, mBlueBar.getProgress());
223     }
224 
225     /**
226      * Computes the {@link Color} value from selection on ARGB sliders.
227      *
228      * @return color computed from selected ARGB values
229      */
getColor()230     public int getColor() {
231         final int alpha = mAlphaBar.getProgress();
232         final int red = mRedBar.getProgress();
233         final int green = mGreenBar.getProgress();
234         final int blue = mBlueBar.getProgress();
235 
236         return Color.argb(alpha, red, green, blue);
237     }
238 
239     /**
240      * Returns the {@link android.graphics.PorterDuff.Mode} for the selected tint mode option.
241      *
242      * @return selected tint mode
243      */
getTintMode()244     public PorterDuff.Mode getTintMode() {
245         return MODES[mBlendSpinner.getSelectedItemPosition()];
246     }
247 
248     /**
249      * Update the tint of the image with the color set in the seekbars and selected blend mode.
250      * The seekbars are set to a maximum of 255, with one for each of the four components of the
251      * ARGB color. (Alpha, Red, Green, Blue.) Once a color has been computed using
252      * {@link Color#argb(int, int, int, int)}, it is set togethe with the blend mode on the background
253      * image using
254      * {@link android.widget.ImageView#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
255      */
updateTint(int color, PorterDuff.Mode mode)256     public void updateTint(int color, PorterDuff.Mode mode) {
257         // Set the color hint of the image: ARGB
258         mHintColor = color;
259 
260         // Set the color tint mode based on the selection of the Spinner
261         mMode = mode;
262 
263         // Log selection
264         Log.d(TAG, String.format("Updating tint with color [ARGB: %d,%d,%d,%d] and mode [%s]",
265                 Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color),
266                 mode.toString()));
267 
268         // Apply the color tint for the selected tint mode
269         mImage.setColorFilter(mHintColor, mMode);
270 
271         // Update the text for each label with the value of each channel
272         mAlphaText.setText(getString(R.string.value_alpha, Color.alpha(color)));
273         mRedText.setText(getString(R.string.value_red, Color.red(color)));
274         mGreenText.setText(getString(R.string.value_green, Color.green(color)));
275         mBlueText.setText(getString(R.string.value_blue, Color.blue(color)));
276     }
277 
278     /**
279      * Listener that updates the tint when a blend mode is selected.
280      */
281     private AdapterView.OnItemSelectedListener mBlendListener =
282             new AdapterView.OnItemSelectedListener() {
283 
284                 @Override
285                 public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
286                     // Selected a blend mode and update the tint of image
287                     updateTint(getColor(), getTintMode());
288                 }
289 
290                 @Override
291                 public void onNothingSelected(AdapterView<?> adapterView) {
292 
293                 }
294 
295             };
296 
297     /**
298      * Seekbar listener that updates the tinted color when the progress bar has changed.
299      */
300     private SeekBar.OnSeekBarChangeListener mSeekBarListener =
301             new SeekBar.OnSeekBarChangeListener() {
302                 @Override
303                 public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
304                     // Update the tinted color from all selections in the UI
305                     updateTint(getColor(), getTintMode());
306                 }
307 
308                 @Override
309                 public void onStartTrackingTouch(SeekBar seekBar) {
310                 }
311 
312                 @Override
313                 public void onStopTrackingTouch(SeekBar seekBar) {
314                 }
315             };
316 }
317