/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.drawabletinting;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
import com.example.android.common.logger.Log;
/**
* Sample that shows tinting of Drawables programmatically and of Drawable resources in XML.
* Tinting is set on a nine-patch drawable through the "tint" and "tintMode" parameters.
* A color state list is referenced as the tint color, which defines colors for different
* states of a View (for example disabled/enabled, focused, pressed or selected).
* Programmatically, tinting is applied to a Drawable through its "setColorFilter" method, with
* a reference to a color and a PorterDuff blend mode. The color and blend mode can be
* changed from the UI.
*
* @see android.graphics.drawable.Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)
* @see android.graphics.drawable.Drawable#setTint(android.content.res.ColorStateList, android.graphics.PorterDuff.Mode)
*/
public class DrawableTintingFragment extends Fragment {
/**
* String that identifies logging output from this Fragment.
*/
private static final String TAG = "DrawableTintingFragment";
/**
* Image that tinting is applied to programmatically.
*/
private ImageView mImage;
/**
* Seekbar for alpha component of tinting color.
*/
private SeekBar mAlphaBar;
/**
* Seekbar for red component of tinting color.
*/
private SeekBar mRedBar;
/**
* Seekbar for green bar of tinting color.
*/
private SeekBar mGreenBar;
/**
* Seekbar for blue bar of tinting color.
*/
private SeekBar mBlueBar;
/**
* Text label for alpha component seekbar.
*/
private TextView mAlphaText;
/**
* Text label for red component seekbar.
*/
private TextView mRedText;
/**
* Text label for green component seekbar.
*/
private TextView mGreenText;
/**
* Text label for blue component seekbar.
*/
private TextView mBlueText;
/**
* Selector for blend type for color tinting.
*/
private Spinner mBlendSpinner;
/**
* Computed color for tinting of drawable.
*/
private int mHintColor;
/**
* Selected color tinting mode.
*/
private PorterDuff.Mode mMode;
/**
* Identifier for state of blend mod spinner in state bundle.
*/
private static final String STATE_BLEND = "DRAWABLETINTING_BLEND";
/**
* Identifier for state of alpha seek bar in state bundle.
*/
private static final String STATE_ALPHA = "DRAWABLETINTING_ALPHA";
/**
* Identifier for state of red seek bar in state bundle.
*/
private static final String STATE_RED = "DRAWABLETINTING_RED";
/**
* Identifier for state of green seek bar in state bundle.
*/
private static final String STATE_GREEN = "DRAWABLETINTING_GREEN";
/**
* Identifier for state of blue seek bar in state bundle.
*/
private static final String STATE_BLUE = "DRAWABLETINTING_BLUE";
/**
* Available tinting modes. Note that this array must be kept in sync with the
* <code>blend_modes</code> string array that provides labels for these modes.
*/
private static final PorterDuff.Mode[] MODES = new PorterDuff.Mode[]{
PorterDuff.Mode.ADD,
PorterDuff.Mode.CLEAR,
PorterDuff.Mode.DARKEN,
PorterDuff.Mode.DST,
PorterDuff.Mode.DST_ATOP,
PorterDuff.Mode.DST_IN,
PorterDuff.Mode.DST_OUT,
PorterDuff.Mode.DST_OVER,
PorterDuff.Mode.LIGHTEN,
PorterDuff.Mode.MULTIPLY,
PorterDuff.Mode.OVERLAY,
PorterDuff.Mode.SCREEN,
PorterDuff.Mode.SRC,
PorterDuff.Mode.SRC_ATOP,
PorterDuff.Mode.SRC_IN,
PorterDuff.Mode.SRC_OUT,
PorterDuff.Mode.SRC_OVER,
PorterDuff.Mode.XOR
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.tinting_fragment, null);
// Set a drawable as the image to display
mImage = (ImageView) v.findViewById(R.id.image);
mImage.setImageResource(R.drawable.btn_default_normal_holo);
// Get text labels and seekbars for the four color components: ARGB
mAlphaBar = (SeekBar) v.findViewById(R.id.alphaSeek);
mAlphaText = (TextView) v.findViewById(R.id.alphaText);
mGreenBar = (SeekBar) v.findViewById(R.id.greenSeek);
mGreenText = (TextView) v.findViewById(R.id.greenText);
mRedBar = (SeekBar) v.findViewById(R.id.redSeek);
mRedText = (TextView) v.findViewById(R.id.redText);
mBlueText = (TextView) v.findViewById(R.id.blueText);
mBlueBar = (SeekBar) v.findViewById(R.id.blueSeek);
// Set a listener to update tinted image when selections have changed
mAlphaBar.setOnSeekBarChangeListener(mSeekBarListener);
mRedBar.setOnSeekBarChangeListener(mSeekBarListener);
mGreenBar.setOnSeekBarChangeListener(mSeekBarListener);
mBlueBar.setOnSeekBarChangeListener(mSeekBarListener);
// Set up the spinner for blend mode selection from a string array resource
mBlendSpinner = (Spinner) v.findViewById(R.id.blendSpinner);
SpinnerAdapter sa = ArrayAdapter.createFromResource(getActivity(),
R.array.blend_modes, android.R.layout.simple_spinner_dropdown_item);
mBlendSpinner.setAdapter(sa);
// Set a listener to update the tinted image when a blend mode is selected
mBlendSpinner.setOnItemSelectedListener(mBlendListener);
// Select the first item
mBlendSpinner.setSelection(0);
mMode = MODES[0];
if (savedInstanceState != null) {
// Restore the previous state if this fragment has been restored
mBlendSpinner.setSelection(savedInstanceState.getInt(STATE_BLEND));
mAlphaBar.setProgress(savedInstanceState.getInt(STATE_ALPHA));
mRedBar.setProgress(savedInstanceState.getInt(STATE_RED));
mGreenBar.setProgress(savedInstanceState.getInt(STATE_GREEN));
mBlueBar.setProgress(savedInstanceState.getInt(STATE_BLUE));
}
// Apply the default blend mode and color
updateTint(getColor(), getTintMode());
return v;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.d(TAG, "state saved.");
outState.putInt(STATE_BLEND, mBlendSpinner.getSelectedItemPosition());
outState.putInt(STATE_ALPHA, mAlphaBar.getProgress());
outState.putInt(STATE_RED, mRedBar.getProgress());
outState.putInt(STATE_GREEN, mGreenBar.getProgress());
outState.putInt(STATE_BLUE, mBlueBar.getProgress());
}
/**
* Computes the {@link Color} value from selection on ARGB sliders.
*
* @return color computed from selected ARGB values
*/
public int getColor() {
final int alpha = mAlphaBar.getProgress();
final int red = mRedBar.getProgress();
final int green = mGreenBar.getProgress();
final int blue = mBlueBar.getProgress();
return Color.argb(alpha, red, green, blue);
}
/**
* Returns the {@link android.graphics.PorterDuff.Mode} for the selected tint mode option.
*
* @return selected tint mode
*/
public PorterDuff.Mode getTintMode() {
return MODES[mBlendSpinner.getSelectedItemPosition()];
}
/**
* Update the tint of the image with the color set in the seekbars and selected blend mode.
* The seekbars are set to a maximum of 255, with one for each of the four components of the
* ARGB color. (Alpha, Red, Green, Blue.) Once a color has been computed using
* {@link Color#argb(int, int, int, int)}, it is set togethe with the blend mode on the background
* image using
* {@link android.widget.ImageView#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
*/
public void updateTint(int color, PorterDuff.Mode mode) {
// Set the color hint of the image: ARGB
mHintColor = color;
// Set the color tint mode based on the selection of the Spinner
mMode = mode;
// Log selection
Log.d(TAG, String.format("Updating tint with color [ARGB: %d,%d,%d,%d] and mode [%s]",
Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color),
mode.toString()));
// Apply the color tint for the selected tint mode
mImage.setColorFilter(mHintColor, mMode);
// Update the text for each label with the value of each channel
mAlphaText.setText(getString(R.string.value_alpha, Color.alpha(color)));
mRedText.setText(getString(R.string.value_red, Color.red(color)));
mGreenText.setText(getString(R.string.value_green, Color.green(color)));
mBlueText.setText(getString(R.string.value_blue, Color.blue(color)));
}
/**
* Listener that updates the tint when a blend mode is selected.
*/
private AdapterView.OnItemSelectedListener mBlendListener =
new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
// Selected a blend mode and update the tint of image
updateTint(getColor(), getTintMode());
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
};
/**
* Seekbar listener that updates the tinted color when the progress bar has changed.
*/
private SeekBar.OnSeekBarChangeListener mSeekBarListener =
new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
// Update the tinted color from all selections in the UI
updateTint(getColor(), getTintMode());
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
};
}