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 158 public void onCreate(Bundle savedInstanceState) { 159 super.onCreate(savedInstanceState); 160 setHasOptionsMenu(true); 161 } 162 163 @Override 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 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 */ 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 */ 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 */ 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