GuidanceStylist.java revision db14ff55f779f2a0c6e2c4c8009bb67458beb267
1/* 2 * Copyright (C) 2015 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.animation.Animator; 17import android.animation.AnimatorInflater; 18import android.content.Context; 19import android.content.res.TypedArray; 20import android.graphics.drawable.Drawable; 21import android.support.annotation.NonNull; 22import android.support.v17.leanback.R; 23import android.text.TextUtils; 24import android.util.TypedValue; 25import android.view.LayoutInflater; 26import android.view.View; 27import android.view.ViewGroup; 28import android.widget.ImageView; 29import android.widget.TextView; 30 31import java.util.List; 32 33/** 34 * GuidanceStylist is used within a {@link android.support.v17.leanback.app.GuidedStepFragment} 35 * to display contextual information for the decision(s) required at that step. 36 * <p> 37 * Many aspects of the base GuidanceStylist can be customized through theming; see the theme 38 * attributes below. Note that these attributes are not set on individual elements in layout 39 * XML, but instead would be set in a custom theme. See 40 * <a href="http://developer.android.com/guide/topics/ui/themes.html">Styles and Themes</a> 41 * for more information. 42 * <p> 43 * If these hooks are insufficient, this class may also be subclassed. Subclasses 44 * may wish to override the {@link #onProvideLayoutId} method to change the layout file used to 45 * display the guidance; more complex layouts may be supported by also providing a subclass of 46 * {@link GuidanceStylist.Guidance} with extra fields. 47 * <p> 48 * Note: If an alternate layout is provided, the following view IDs should be used to refer to base 49 * elements: 50 * <ul> 51 * <li>{@link android.support.v17.leanback.R.id#guidance_title}</li> 52 * <li>{@link android.support.v17.leanback.R.id#guidance_description}</li> 53 * <li>{@link android.support.v17.leanback.R.id#guidance_breadcrumb}</li> 54 * <li>{@link android.support.v17.leanback.R.id#guidance_icon}</li> 55 * </ul><p> 56 * View IDs are allowed to be missing, in which case the corresponding views will be null. 57 * 58 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation 59 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation 60 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceContainerStyle 61 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceTitleStyle 62 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceDescriptionStyle 63 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceBreadcrumbStyle 64 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceIconStyle 65 * @see android.support.v17.leanback.app.GuidedStepFragment 66 * @see GuidanceStylist.Guidance 67 */ 68public class GuidanceStylist implements FragmentAnimationProvider { 69 70 /** 71 * A data class representing contextual information for a {@link 72 * android.support.v17.leanback.app.GuidedStepFragment}. Guidance consists of a short title, 73 * a longer description, a breadcrumb to help with global navigation (often indicating where 74 * the back button will lead), and an optional icon. All this information is intended to 75 * provide users with the appropriate context to make the decision(s) required by the current 76 * step. 77 * <p> 78 * Clients may provide a subclass of this if they wish to remember auxiliary data for use in 79 * a customized GuidanceStylist. 80 */ 81 public static class Guidance { 82 private final String mTitle; 83 private final String mDescription; 84 private final String mBreadcrumb; 85 private final Drawable mIconDrawable; 86 87 /** 88 * Constructs a Guidance object with the specified title, description, breadcrumb, and 89 * icon drawable. 90 * @param title The title for the current guided step. 91 * @param description The description for the current guided step. 92 * @param breadcrumb The breadcrumb for the current guided step. 93 * @param icon The icon drawable representing the current guided step. 94 */ 95 public Guidance(String title, String description, String breadcrumb, Drawable icon) { 96 mBreadcrumb = breadcrumb; 97 mTitle = title; 98 mDescription = description; 99 mIconDrawable = icon; 100 } 101 102 /** 103 * Returns the title specified when this Guidance was constructed. 104 * @return The title for this Guidance. 105 */ 106 public String getTitle() { 107 return mTitle; 108 } 109 110 /** 111 * Returns the description specified when this Guidance was constructed. 112 * @return The description for this Guidance. 113 */ 114 public String getDescription() { 115 return mDescription; 116 } 117 118 /** 119 * Returns the breadcrumb specified when this Guidance was constructed. 120 * @return The breadcrumb for this Guidance. 121 */ 122 public String getBreadcrumb() { 123 return mBreadcrumb; 124 } 125 126 /** 127 * Returns the icon drawable specified when this Guidance was constructed. 128 * @return The icon for this Guidance. 129 */ 130 public Drawable getIconDrawable() { 131 return mIconDrawable; 132 } 133 } 134 135 private TextView mTitleView; 136 private TextView mDescriptionView; 137 private TextView mBreadcrumbView; 138 private ImageView mIconView; 139 140 /** 141 * Creates an appropriately configured view for the given Guidance, using the provided 142 * inflater and container. 143 * <p> 144 * <i>Note: Does not actually add the created view to the container; the caller should do 145 * this.</i> 146 * @param inflater The layout inflater to be used when constructing the view. 147 * @param container The view group to be passed in the call to 148 * <code>LayoutInflater.inflate</code>. 149 * @param guidance The guidance data for the view. 150 * @return The view to be added to the caller's view hierarchy. 151 */ 152 public View onCreateView(LayoutInflater inflater, ViewGroup container, Guidance guidance) { 153 View guidanceView = inflater.inflate(onProvideLayoutId(), container, false); 154 mTitleView = (TextView) guidanceView.findViewById(R.id.guidance_title); 155 mBreadcrumbView = (TextView) guidanceView.findViewById(R.id.guidance_breadcrumb); 156 mDescriptionView = (TextView) guidanceView.findViewById(R.id.guidance_description); 157 mIconView = (ImageView) guidanceView.findViewById(R.id.guidance_icon); 158 View guidanceContainer = guidanceView.findViewById(R.id.guidance_container); 159 160 // We allow any of the cached subviews to be null, so that subclasses can choose not to 161 // display a particular piece of information. 162 if (mTitleView != null) { 163 mTitleView.setText(guidance.getTitle()); 164 } 165 if (mBreadcrumbView != null) { 166 mBreadcrumbView.setText(guidance.getBreadcrumb()); 167 } 168 if (mDescriptionView != null) { 169 mDescriptionView.setText(guidance.getDescription()); 170 } 171 if (mIconView != null) { 172 mIconView.setImageDrawable(guidance.getIconDrawable()); 173 } 174 if (guidanceContainer != null) { 175 CharSequence contentDescription = guidanceContainer.getContentDescription(); 176 if (TextUtils.isEmpty(contentDescription)) { 177 guidanceContainer.setContentDescription(new StringBuilder() 178 .append(guidance.getBreadcrumb()).append('\n') 179 .append(guidance.getTitle()).append('\n') 180 .append(guidance.getDescription()) 181 .toString()); 182 } 183 } 184 return guidanceView; 185 } 186 187 /** 188 * Called when destroy the View created by GuidanceStylist. 189 */ 190 public void onDestroyView() { 191 mBreadcrumbView = null; 192 mDescriptionView = null; 193 mIconView = null; 194 mTitleView = null; 195 } 196 197 /** 198 * Provides the resource ID of the layout defining the guidance view. Subclasses may override 199 * to provide their own customized layouts. The base implementation returns 200 * {@link android.support.v17.leanback.R.layout#lb_guidance}. If overridden, the substituted 201 * layout should contain matching IDs for any views that should be managed by the base class; 202 * this can be achieved by starting with a copy of the base layout file. 203 * @return The resource ID of the layout to be inflated to define the guidance view. 204 */ 205 public int onProvideLayoutId() { 206 return R.layout.lb_guidance; 207 } 208 209 /** 210 * Returns the view displaying the title of the guidance. 211 * @return The text view object for the title. 212 */ 213 public TextView getTitleView() { 214 return mTitleView; 215 } 216 217 /** 218 * Returns the view displaying the description of the guidance. 219 * @return The text view object for the description. 220 */ 221 public TextView getDescriptionView() { 222 return mDescriptionView; 223 } 224 225 /** 226 * Returns the view displaying the breadcrumb of the guidance. 227 * @return The text view object for the breadcrumb. 228 */ 229 public TextView getBreadcrumbView() { 230 return mBreadcrumbView; 231 } 232 233 /** 234 * Returns the view displaying the icon of the guidance. 235 * @return The image view object for the icon. 236 */ 237 public ImageView getIconView() { 238 return mIconView; 239 } 240 241 /** 242 * {@inheritDoc} 243 */ 244 @Override 245 public void onImeAppearing(@NonNull List<Animator> animators) { 246 addAnimator(animators, mTitleView, R.attr.guidedStepImeAppearingAnimation); 247 addAnimator(animators, mBreadcrumbView, R.attr.guidedStepImeAppearingAnimation); 248 addAnimator(animators, mDescriptionView, R.attr.guidedStepImeAppearingAnimation); 249 addAnimator(animators, mIconView, R.attr.guidedStepImeAppearingAnimation); 250 } 251 252 /** 253 * {@inheritDoc} 254 */ 255 @Override 256 public void onImeDisappearing(@NonNull List<Animator> animators) { 257 addAnimator(animators, mTitleView, R.attr.guidedStepImeDisappearingAnimation); 258 addAnimator(animators, mBreadcrumbView, R.attr.guidedStepImeDisappearingAnimation); 259 addAnimator(animators, mDescriptionView, R.attr.guidedStepImeDisappearingAnimation); 260 addAnimator(animators, mIconView, R.attr.guidedStepImeDisappearingAnimation); 261 } 262 263 private void addAnimator(List<Animator> animators, View v, int attrId) { 264 if (v != null) { 265 Context ctx = v.getContext(); 266 TypedValue typedValue = new TypedValue(); 267 ctx.getTheme().resolveAttribute(attrId, typedValue, true); 268 Animator animator = AnimatorInflater.loadAnimator(ctx, typedValue.resourceId); 269 animator.setTarget(v); 270 animators.add(animator); 271 } 272 } 273 274} 275