SearchOrbView.java revision 558f7b70035c50045908efca5f4f3d65df685cac
1/* 2 * Copyright (C) 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 17package android.support.v17.leanback.widget; 18 19import android.animation.ArgbEvaluator; 20import android.animation.ValueAnimator; 21import android.content.Context; 22import android.content.res.Resources; 23import android.content.res.TypedArray; 24import android.graphics.Color; 25import android.graphics.Rect; 26import android.graphics.drawable.Drawable; 27import android.graphics.drawable.GradientDrawable; 28import android.support.v17.leanback.R; 29import android.util.AttributeSet; 30import android.view.LayoutInflater; 31import android.view.View; 32import android.widget.FrameLayout; 33import android.widget.ImageView; 34 35/** 36 * <p>A widget that draws a search affordance, represented by a round background and an icon.</p> 37 * 38 * Background color and icon can be customized 39 */ 40public class SearchOrbView extends FrameLayout implements View.OnClickListener { 41 private OnClickListener mListener; 42 private View mRootView; 43 private View mSearchOrbView; 44 private ImageView mIcon; 45 private Drawable mIconDrawable; 46 private Colors mColors; 47 private final float mFocusedZoom; 48 private final int mPulseDurationMs; 49 private final int mScaleDownDurationMs; 50 private ValueAnimator mColorAnimator; 51 52 /** 53 * A set of colors used to display the search orb. 54 */ 55 public static class Colors { 56 private static final float sBrightnessAlpha = 0.15f; 57 58 /** 59 * Constructs a color set using the given color for the search orb. 60 * Other colors are provided by the framework. 61 * 62 * @param color The main search orb color. 63 */ 64 public Colors(int color) { 65 this(color, color); 66 } 67 68 /** 69 * Constructs a color set using the given colors for the search orb. 70 * Other colors are provided by the framework. 71 * 72 * @param color The main search orb color. 73 * @param brightColor A brighter version of the search orb used for animation. 74 */ 75 public Colors(int color, int brightColor) { 76 this(color, brightColor, Color.TRANSPARENT); 77 } 78 79 /** 80 * Constructs a color set using the given colors. 81 * 82 * @param color The main search orb color. 83 * @param brightColor A brighter version of the search orb used for animation. 84 * @param iconColor A color used to tint the search orb icon. 85 */ 86 public Colors(int color, int brightColor, int iconColor) { 87 this.color = color; 88 this.brightColor = brightColor == color ? getBrightColor(color) : brightColor; 89 this.iconColor = iconColor; 90 } 91 92 /** 93 * The main color of the search orb. 94 */ 95 public int color; 96 97 /** 98 * A brighter version of the search orb used for animation. 99 */ 100 public int brightColor; 101 102 /** 103 * A color used to tint the search orb icon. 104 */ 105 public int iconColor; 106 107 /** 108 * Computes a default brighter version of the given color. 109 */ 110 public static int getBrightColor(int color) { 111 final float brightnessValue = 0xff * sBrightnessAlpha; 112 int red = (int)(Color.red(color) * (1 - sBrightnessAlpha) + brightnessValue); 113 int green = (int)(Color.green(color) * (1 - sBrightnessAlpha) + brightnessValue); 114 int blue = (int)(Color.blue(color) * (1 - sBrightnessAlpha) + brightnessValue); 115 int alpha = (int)(Color.alpha(color) * (1 - sBrightnessAlpha) + brightnessValue); 116 return Color.argb(alpha, red, green, blue); 117 } 118 } 119 120 private final ArgbEvaluator mColorEvaluator = new ArgbEvaluator(); 121 122 private final ValueAnimator.AnimatorUpdateListener mUpdateListener = 123 new ValueAnimator.AnimatorUpdateListener() { 124 @Override 125 public void onAnimationUpdate(ValueAnimator animator) { 126 Integer color = (Integer) animator.getAnimatedValue(); 127 setOrbViewColor(color.intValue()); 128 } 129 }; 130 131 private ValueAnimator mShadowFocusAnimator; 132 133 private final ValueAnimator.AnimatorUpdateListener mFocusUpdateListener = 134 new ValueAnimator.AnimatorUpdateListener() { 135 @Override 136 public void onAnimationUpdate(ValueAnimator animation) { 137 ShadowHelper.getInstance().setZ(mSearchOrbView, animation.getAnimatedFraction()); 138 } 139 }; 140 141 public SearchOrbView(Context context) { 142 this(context, null); 143 } 144 145 public SearchOrbView(Context context, AttributeSet attrs) { 146 this(context, attrs, R.attr.searchOrbViewStyle); 147 } 148 149 public SearchOrbView(Context context, AttributeSet attrs, int defStyleAttr) { 150 super(context, attrs, defStyleAttr); 151 152 final Resources res = context.getResources(); 153 154 LayoutInflater inflater = (LayoutInflater) context 155 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 156 mRootView = inflater.inflate(R.layout.lb_search_orb, this, true); 157 mSearchOrbView = mRootView.findViewById(R.id.search_orb); 158 mIcon = (ImageView) mRootView.findViewById(R.id.icon); 159 160 mFocusedZoom = context.getResources().getFraction( 161 R.fraction.lb_search_orb_focused_zoom, 1, 1); 162 mPulseDurationMs = context.getResources().getInteger( 163 R.integer.lb_search_orb_pulse_duration_ms); 164 mScaleDownDurationMs = context.getResources().getInteger( 165 R.integer.lb_search_orb_scale_down_duration_ms); 166 167 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbSearchOrbView, 168 defStyleAttr, 0); 169 170 Drawable img = a.getDrawable(R.styleable.lbSearchOrbView_searchOrbIcon); 171 if (img == null) { 172 img = res.getDrawable(R.drawable.lb_ic_in_app_search); 173 } 174 setOrbIcon(img); 175 176 int defColor = res.getColor(R.color.lb_default_search_color); 177 int color = a.getColor(R.styleable.lbSearchOrbView_searchOrbColor, defColor); 178 int brightColor = a.getColor( 179 R.styleable.lbSearchOrbView_searchOrbBrightColor, color); 180 int iconColor = a.getColor(R.styleable.lbSearchOrbView_searchOrbIconColor, Color.TRANSPARENT); 181 setOrbColors(new Colors(color, brightColor, iconColor)); 182 a.recycle(); 183 184 setFocusable(true); 185 setClipChildren(false); 186 setOnClickListener(this); 187 188 ShadowHelper.getInstance().setZ(mSearchOrbView, 0f); 189 // Icon has no background, but must be on top of the search orb view 190 ShadowHelper.getInstance().setZ(mIcon, 1f); 191 } 192 193 @Override 194 public void onClick(View view) { 195 if (null != mListener) { 196 mListener.onClick(view); 197 } 198 } 199 200 private void startShadowFocusAnimation(boolean gainFocus, int duration) { 201 if (mShadowFocusAnimator == null) { 202 mShadowFocusAnimator = ValueAnimator.ofFloat(0f, 1f); 203 mShadowFocusAnimator.addUpdateListener(mFocusUpdateListener); 204 } 205 if (gainFocus) { 206 mShadowFocusAnimator.start(); 207 } else { 208 mShadowFocusAnimator.reverse(); 209 } 210 mShadowFocusAnimator.setDuration(duration); 211 } 212 213 @Override 214 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { 215 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); 216 final float zoom = gainFocus ? mFocusedZoom : 1f; 217 final int duration = gainFocus ? mPulseDurationMs : mScaleDownDurationMs; 218 mRootView.animate().scaleX(zoom).scaleY(zoom).setDuration(duration).start(); 219 startShadowFocusAnimation(gainFocus, duration); 220 enableOrbColorAnimation(gainFocus); 221 } 222 223 /** 224 * Set the orb icon 225 * @param icon the drawable to be used as the icon 226 */ 227 public void setOrbIcon(Drawable icon) { 228 mIconDrawable = icon; 229 mIcon.setImageDrawable(mIconDrawable); 230 } 231 232 /** 233 * Returns the orb icon 234 * @return the drawable used as the icon 235 */ 236 public Drawable getOrbIcon() { 237 return mIconDrawable; 238 } 239 240 /** 241 * Set the on click listener for the orb 242 * @param listener The listener. 243 */ 244 public void setOnOrbClickedListener(OnClickListener listener) { 245 mListener = listener; 246 if (null != listener) { 247 setVisibility(View.VISIBLE); 248 } else { 249 setVisibility(View.INVISIBLE); 250 } 251 } 252 253 /** 254 * Sets the background color of the search orb. 255 * Other colors will be provided by the framework. 256 * 257 * @param color the RGBA color 258 */ 259 public void setOrbColor(int color) { 260 setOrbColors(new Colors(color, color, Color.TRANSPARENT)); 261 } 262 263 /** 264 * Sets the search orb colors. 265 * Other colors are provided by the framework. 266 * @deprecated Use {@link #setOrbColors(Colors)} instead. 267 */ 268 @Deprecated 269 public void setOrbColor(int color, int brightColor) { 270 setOrbColors(new Colors(color, brightColor, Color.TRANSPARENT)); 271 } 272 273 /** 274 * Returns the orb color 275 * @return the RGBA color 276 */ 277 public int getOrbColor() { 278 return mColors.color; 279 } 280 281 /** 282 * Set the {@link Colors} used to display the search orb. 283 */ 284 public void setOrbColors(Colors colors) { 285 mColors = colors; 286 mIcon.setColorFilter(mColors.iconColor); 287 288 if (mColorAnimator == null) { 289 setOrbViewColor(mColors.color); 290 } else { 291 enableOrbColorAnimation(true); 292 } 293 } 294 295 /** 296 * Returns the {@link Colors} used to display the search orb. 297 */ 298 public Colors getOrbColors() { 299 return mColors; 300 } 301 302 private void enableOrbColorAnimation(boolean enable) { 303 if (mColorAnimator != null) { 304 mColorAnimator.end(); 305 mColorAnimator = null; 306 } 307 if (enable) { 308 // TODO: set interpolator (material if available) 309 mColorAnimator = ValueAnimator.ofObject(mColorEvaluator, 310 mColors.color, mColors.brightColor, mColors.color); 311 mColorAnimator.setRepeatCount(ValueAnimator.INFINITE); 312 mColorAnimator.setDuration(mPulseDurationMs * 2); 313 mColorAnimator.addUpdateListener(mUpdateListener); 314 mColorAnimator.start(); 315 } 316 } 317 318 private void setOrbViewColor(int color) { 319 if (mSearchOrbView.getBackground() instanceof GradientDrawable) { 320 ((GradientDrawable) mSearchOrbView.getBackground()).setColor(color); 321 } 322 } 323 324} 325