FocusHighlightHelper.java revision c16a0e4e812e393f67b0d55a4df5667a967a8e57
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14package android.support.v17.leanback.widget; 15 16import android.graphics.drawable.TransitionDrawable; 17import android.support.v17.leanback.R; 18import android.view.View; 19import android.view.ViewGroup; 20import android.view.animation.AccelerateDecelerateInterpolator; 21import android.view.animation.Interpolator; 22import android.animation.TimeAnimator; 23import android.content.res.Resources; 24 25import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_NONE; 26import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_SMALL; 27import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_MEDIUM; 28import static android.support.v17.leanback.widget.FocusHighlight.ZOOM_FACTOR_LARGE; 29 30 31/** 32 * Setup the behavior how to highlight when a item gains focus. 33 */ 34public class FocusHighlightHelper { 35 36 static class FocusAnimator implements TimeAnimator.TimeListener { 37 private final View mView; 38 private final int mDuration; 39 private final ShadowOverlayContainer mWrapper; 40 private final float mScaleDiff; 41 private float mFocusLevel = 0f; 42 private float mFocusLevelStart; 43 private float mFocusLevelDelta; 44 private final TimeAnimator mAnimator = new TimeAnimator(); 45 private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator(); 46 47 void animateFocus(boolean select, boolean immediate) { 48 endAnimation(); 49 final float end = select ? 1 : 0; 50 if (immediate) { 51 setFocusLevel(end); 52 } else if (mFocusLevel != end) { 53 mFocusLevelStart = mFocusLevel; 54 mFocusLevelDelta = end - mFocusLevelStart; 55 mAnimator.start(); 56 } 57 } 58 59 FocusAnimator(View view, float scale, int duration) { 60 mView = view; 61 mDuration = duration; 62 mScaleDiff = scale - 1f; 63 if (view instanceof ShadowOverlayContainer) { 64 mWrapper = (ShadowOverlayContainer) view; 65 } else { 66 mWrapper = null; 67 } 68 mAnimator.setTimeListener(this); 69 } 70 71 void setFocusLevel(float level) { 72 mFocusLevel = level; 73 float scale = 1f + mScaleDiff * level; 74 mView.setScaleX(scale); 75 mView.setScaleY(scale); 76 if (mWrapper != null) { 77 mWrapper.setShadowFocusLevel(level); 78 } 79 } 80 81 float getFocusLevel() { 82 return mFocusLevel; 83 } 84 85 void endAnimation() { 86 mAnimator.end(); 87 } 88 89 @Override 90 public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { 91 float fraction; 92 if (totalTime >= mDuration) { 93 fraction = 1; 94 mAnimator.end(); 95 } else { 96 fraction = (float) (totalTime / (double) mDuration); 97 } 98 if (mInterpolator != null) { 99 fraction = mInterpolator.getInterpolation(fraction); 100 } 101 setFocusLevel(mFocusLevelStart + fraction * mFocusLevelDelta); 102 } 103 } 104 105 static class BrowseItemFocusHighlight implements FocusHighlight { 106 private static final int DURATION_MS = 150; 107 108 private static float[] sScaleFactor = new float[4]; 109 110 private int mScaleIndex; 111 112 BrowseItemFocusHighlight(int zoomIndex) { 113 mScaleIndex = (zoomIndex >= 0 && zoomIndex < sScaleFactor.length) ? 114 zoomIndex : ZOOM_FACTOR_MEDIUM; 115 } 116 117 private static void lazyInit(Resources resources) { 118 if (sScaleFactor[ZOOM_FACTOR_NONE] == 0f) { 119 sScaleFactor[ZOOM_FACTOR_NONE] = 1f; 120 sScaleFactor[ZOOM_FACTOR_SMALL] = 121 resources.getFraction(R.fraction.lb_focus_zoom_factor_small, 1, 1); 122 sScaleFactor[ZOOM_FACTOR_MEDIUM] = 123 resources.getFraction(R.fraction.lb_focus_zoom_factor_medium, 1, 1); 124 sScaleFactor[ZOOM_FACTOR_LARGE] = 125 resources.getFraction(R.fraction.lb_focus_zoom_factor_large, 1, 1); 126 } 127 } 128 129 private float getScale(View view) { 130 lazyInit(view.getResources()); 131 return sScaleFactor[mScaleIndex]; 132 } 133 134 private void viewFocused(View view, boolean hasFocus) { 135 view.setSelected(hasFocus); 136 FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator); 137 if (animator == null) { 138 animator = new FocusAnimator(view, getScale(view), DURATION_MS); 139 view.setTag(R.id.lb_focus_animator, animator); 140 } 141 animator.animateFocus(hasFocus, false); 142 } 143 144 @Override 145 public void onItemFocused(View view, boolean hasFocus) { 146 viewFocused(view, hasFocus); 147 } 148 } 149 150 private static ActionItemFocusHighlight sActionItemFocusHighlight = 151 new ActionItemFocusHighlight(); 152 153 /** 154 * Setup the focus highlight behavior of a focused item in browse list row. 155 * @param adapter adapter of the list row. 156 */ 157 public static void setupBrowseItemFocusHighlight(ItemBridgeAdapter adapter, int zoomIndex) { 158 adapter.setFocusHighlight(new BrowseItemFocusHighlight(zoomIndex)); 159 } 160 161 /** 162 * Setup the focus highlight behavior of a focused item in header list. 163 * @param gridView the header list. 164 */ 165 public static void setupHeaderItemFocusHighlight(BaseGridView gridView) { 166 if (gridView.getAdapter() instanceof ItemBridgeAdapter) { 167 ((ItemBridgeAdapter) gridView.getAdapter()) 168 .setFocusHighlight(new HeaderItemFocusHighlight(gridView)); 169 } 170 } 171 172 /** 173 * Setup the focus highlight behavior of a focused item in an action list. 174 * @param adapter adapter of the action list. 175 */ 176 public static void setupActionItemFocusHighlight(ItemBridgeAdapter adapter) { 177 adapter.setFocusHighlight(sActionItemFocusHighlight); 178 } 179 180 static class HeaderItemFocusHighlight implements FocusHighlight { 181 private static boolean sInitialized; 182 private static float sSelectScale; 183 private static int sDuration; 184 private BaseGridView mGridView; 185 186 HeaderItemFocusHighlight(BaseGridView gridView) { 187 mGridView = gridView; 188 lazyInit(gridView.getContext().getResources()); 189 } 190 191 private static void lazyInit(Resources res) { 192 if (!sInitialized) { 193 sSelectScale = 194 Float.parseFloat(res.getString(R.dimen.lb_browse_header_select_scale)); 195 sDuration = 196 Integer.parseInt(res.getString(R.dimen.lb_browse_header_select_duration)); 197 sInitialized = true; 198 } 199 } 200 201 class HeaderFocusAnimator extends FocusAnimator { 202 203 ItemBridgeAdapter.ViewHolder mViewHolder; 204 HeaderFocusAnimator(View view, float scale, int duration) { 205 super(view, scale, duration); 206 mViewHolder = (ItemBridgeAdapter.ViewHolder) mGridView.getChildViewHolder(view); 207 } 208 209 @Override 210 void setFocusLevel(float level) { 211 Presenter presenter = mViewHolder.getPresenter(); 212 if (presenter instanceof RowHeaderPresenter) { 213 ((RowHeaderPresenter) presenter).setSelectLevel( 214 ((RowHeaderPresenter.ViewHolder) mViewHolder.getViewHolder()), level); 215 } 216 super.setFocusLevel(level); 217 } 218 219 } 220 221 private void viewFocused(View view, boolean hasFocus) { 222 view.setSelected(hasFocus); 223 FocusAnimator animator = (FocusAnimator) view.getTag(R.id.lb_focus_animator); 224 if (animator == null) { 225 animator = new HeaderFocusAnimator(view, sSelectScale, sDuration); 226 view.setTag(R.id.lb_focus_animator, animator); 227 } 228 animator.animateFocus(hasFocus, false); 229 } 230 231 @Override 232 public void onItemFocused(View view, boolean hasFocus) { 233 viewFocused(view, hasFocus); 234 } 235 } 236 237 private static class ActionItemFocusHighlight implements FocusHighlight { 238 private boolean mInitialized; 239 private int mDuration; 240 241 private void initializeDimensions(Resources res) { 242 if (!mInitialized) { 243 mDuration = Integer.parseInt(res.getString(R.dimen.lb_details_overview_action_select_duration)); 244 } 245 } 246 247 @Override 248 public void onItemFocused(View view, boolean hasFocus) { 249 initializeDimensions(view.getResources()); 250 TransitionDrawable td = (TransitionDrawable) view.getBackground(); 251 if (hasFocus) { 252 td.startTransition(mDuration); 253 } else { 254 td.reverseTransition(mDuration); 255 } 256 } 257 } 258} 259