SearchOrbView.java revision 69e74bd8956577d9a3414b81ec661fd5fee42e19
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 mSearchOrbView; 43 private ImageView mIcon; 44 private Drawable mIconDrawable; 45 private int mSearchOrbColor, mSearchOrbColorBright; 46 private final float mFocusedZoom; 47 private final float mBrightnessAlpha; 48 private final int mPulseDurationMs; 49 private final int mScaleDownDurationMs; 50 private ValueAnimator mColorAnimator; 51 52 private final ArgbEvaluator mColorEvaluator = new ArgbEvaluator(); 53 54 private final ValueAnimator.AnimatorUpdateListener mUpdateListener = 55 new ValueAnimator.AnimatorUpdateListener() { 56 @Override 57 public void onAnimationUpdate(ValueAnimator animator) { 58 Integer color = (Integer) animator.getAnimatedValue(); 59 setOrbViewColor(color.intValue()); 60 } 61 }; 62 63 private ValueAnimator mShadowFocusAnimator; 64 65 private final ValueAnimator.AnimatorUpdateListener mFocusUpdateListener = 66 new ValueAnimator.AnimatorUpdateListener() { 67 @Override 68 public void onAnimationUpdate(ValueAnimator animation) { 69 ShadowHelper.getInstance().setZ(mSearchOrbView, animation.getAnimatedFraction()); 70 } 71 }; 72 73 public SearchOrbView(Context context) { 74 this(context, null); 75 } 76 77 public SearchOrbView(Context context, AttributeSet attrs) { 78 this(context, attrs, R.attr.searchOrbViewStyle); 79 } 80 81 public SearchOrbView(Context context, AttributeSet attrs, int defStyleAttr) { 82 super(context, attrs, defStyleAttr); 83 84 final Resources res = context.getResources(); 85 86 LayoutInflater inflater = (LayoutInflater) context 87 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 88 View root = inflater.inflate(R.layout.lb_search_orb, this, true); 89 mSearchOrbView = root.findViewById(R.id.search_orb); 90 mIcon = (ImageView)root.findViewById(R.id.icon); 91 92 mFocusedZoom = context.getResources().getFraction( 93 R.fraction.lb_search_orb_focused_zoom, 1, 1); 94 mBrightnessAlpha = context.getResources().getFraction( 95 R.fraction.lb_search_orb_brightness_alpha, 1, 1); 96 mPulseDurationMs = context.getResources().getInteger( 97 R.integer.lb_search_orb_pulse_duration_ms); 98 mScaleDownDurationMs = context.getResources().getInteger( 99 R.integer.lb_search_orb_scale_down_duration_ms); 100 101 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbSearchOrbView, 102 defStyleAttr, 0); 103 104 Drawable img = a.getDrawable(R.styleable.lbSearchOrbView_searchOrbIcon); 105 if (img == null) { 106 img = res.getDrawable(R.drawable.lb_ic_in_app_search); 107 } 108 setOrbIcon(img); 109 110 int defColor = res.getColor(R.color.lb_default_search_color); 111 int color = a.getColor(R.styleable.lbSearchOrbView_searchOrbColor, defColor); 112 int brightColor = a.getColor( 113 R.styleable.lbSearchOrbView_searchOrbBrightColor, getBrightColor(color)); 114 setOrbColor(color, brightColor); 115 a.recycle(); 116 117 setFocusable(true); 118 setClipChildren(false); 119 setOnClickListener(this); 120 121 ShadowHelper.getInstance().setZ(mSearchOrbView, 0f); 122 // Icon has no background, but must be on top of the search orb view 123 ShadowHelper.getInstance().setZ(mIcon, 1f); 124 } 125 126 @Override 127 public void onClick(View view) { 128 if (null != mListener) { 129 mListener.onClick(view); 130 } 131 } 132 133 private void startShadowFocusAnimation(boolean gainFocus, int duration) { 134 if (mShadowFocusAnimator == null) { 135 mShadowFocusAnimator = ValueAnimator.ofFloat(0f, 1f); 136 mShadowFocusAnimator.addUpdateListener(mFocusUpdateListener); 137 } 138 if (gainFocus) { 139 mShadowFocusAnimator.start(); 140 } else { 141 mShadowFocusAnimator.reverse(); 142 } 143 mShadowFocusAnimator.setDuration(duration); 144 } 145 146 @Override 147 protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { 148 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); 149 final float zoom = gainFocus ? mFocusedZoom : 1f; 150 final int duration = gainFocus ? mPulseDurationMs : mScaleDownDurationMs; 151 mSearchOrbView.animate().scaleX(zoom).scaleY(zoom).setDuration(duration).start(); 152 startShadowFocusAnimation(gainFocus, duration); 153 enableOrbColorAnimation(gainFocus); 154 } 155 156 /** 157 * Set the orb icon 158 * @param icon the drawable to be used as the icon 159 */ 160 public void setOrbIcon(Drawable icon) { 161 mIconDrawable = icon; 162 mIcon.setImageDrawable(mIconDrawable); 163 } 164 165 /** 166 * Returns the orb icon 167 * @return the drawable used as the icon 168 */ 169 public Drawable getOrbIcon() { 170 return mIconDrawable; 171 } 172 173 /** 174 * Set the on click listener for the orb 175 * @param listener The listener. 176 */ 177 public void setOnOrbClickedListener(OnClickListener listener) { 178 mListener = listener; 179 if (null != listener) { 180 setVisibility(View.VISIBLE); 181 } else { 182 setVisibility(View.INVISIBLE); 183 } 184 } 185 186 /** 187 * Set the background color of the search orb. 188 * @param color the RGBA color 189 */ 190 public void setOrbColor(int color) { 191 setOrbColor(color, getBrightColor(color)); 192 } 193 194 public void setOrbColor(int color, int brightColor) { 195 mSearchOrbColor = color; 196 mSearchOrbColorBright = brightColor; 197 198 if (mColorAnimator == null) { 199 setOrbViewColor(color); 200 } else { 201 enableOrbColorAnimation(true); 202 } 203 } 204 205 /** 206 * Returns the orb color 207 * @return the RGBA color 208 */ 209 public int getOrbColor() { 210 return mSearchOrbColor; 211 } 212 213 private int getBrightColor(int color) { 214 final float brightnessValue = 0xff * mBrightnessAlpha; 215 int red = (int)(Color.red(color) * (1 - mBrightnessAlpha) + brightnessValue); 216 int green = (int)(Color.green(color) * (1 - mBrightnessAlpha) + brightnessValue); 217 int blue = (int)(Color.blue(color) * (1 - mBrightnessAlpha) + brightnessValue); 218 int alpha = (int)(Color.alpha(color) * (1 - mBrightnessAlpha) + brightnessValue); 219 return Color.argb(alpha, red, green, blue); 220 } 221 222 private void enableOrbColorAnimation(boolean enable) { 223 if (mColorAnimator != null) { 224 mColorAnimator.end(); 225 mColorAnimator = null; 226 } 227 if (enable) { 228 // TODO: set interpolator (material if available) 229 mColorAnimator = ValueAnimator.ofObject(mColorEvaluator, 230 mSearchOrbColor, mSearchOrbColorBright, mSearchOrbColor); 231 mColorAnimator.setRepeatCount(ValueAnimator.INFINITE); 232 mColorAnimator.setDuration(mPulseDurationMs * 2); 233 mColorAnimator.addUpdateListener(mUpdateListener); 234 mColorAnimator.start(); 235 } 236 } 237 238 private void setOrbViewColor(int color) { 239 if (mSearchOrbView.getBackground() instanceof GradientDrawable) { 240 ((GradientDrawable) mSearchOrbView.getBackground()).setColor(color); 241 } 242 } 243 244} 245