GuidedActionsStylist.java revision 1ed9dc77616514e20c51baa67a04adab42e4135e
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.animation.AnimatorListenerAdapter;
19import android.animation.AnimatorSet;
20import android.animation.ObjectAnimator;
21import android.content.Context;
22import android.content.pm.PackageManager;
23import android.content.res.Resources;
24import android.content.res.TypedArray;
25import android.graphics.Rect;
26import android.graphics.drawable.Drawable;
27import android.net.Uri;
28import android.os.Build.VERSION;
29import android.support.annotation.NonNull;
30import android.support.v17.leanback.R;
31import android.support.v17.leanback.transition.TransitionHelper;
32import android.support.v17.leanback.transition.TransitionListener;
33import android.support.v17.leanback.widget.VerticalGridView;
34import android.support.v4.content.ContextCompat;
35import android.support.v4.view.ViewCompat;
36import android.support.v7.widget.RecyclerView;
37import android.support.v7.widget.RecyclerView.ViewHolder;
38import android.text.TextUtils;
39import android.util.Log;
40import android.util.TypedValue;
41import android.view.animation.DecelerateInterpolator;
42import android.view.inputmethod.EditorInfo;
43import android.view.Gravity;
44import android.view.LayoutInflater;
45import android.view.View;
46import android.view.ViewGroup;
47import android.view.ViewGroup.LayoutParams;
48import android.view.ViewPropertyAnimator;
49import android.view.ViewTreeObserver;
50import android.view.WindowManager;
51import android.widget.Checkable;
52import android.widget.EditText;
53import android.widget.ImageView;
54import android.widget.TextView;
55
56import java.util.Collections;
57import java.util.List;
58
59/**
60 * GuidedActionsStylist is used within a {@link android.support.v17.leanback.app.GuidedStepFragment}
61 * to supply the right-side panel where users can take actions. It consists of a container for the
62 * list of actions, and a stationary selector view that indicates visually the location of focus.
63 * GuidedActionsStylist has two different layouts: default is for normal actions including text,
64 * radio, checkbox etc, the other when {@link #setAsButtonActions()} is called is recommended for
65 * button actions such as "yes", "no".
66 * <p>
67 * Many aspects of the base GuidedActionsStylist can be customized through theming; see the
68 * theme attributes below. Note that these attributes are not set on individual elements in layout
69 * XML, but instead would be set in a custom theme. See
70 * <a href="http://developer.android.com/guide/topics/ui/themes.html">Styles and Themes</a>
71 * for more information.
72 * <p>
73 * If these hooks are insufficient, this class may also be subclassed. Subclasses may wish to
74 * override the {@link #onProvideLayoutId} method to change the layout used to display the
75 * list container and selector, or the {@link #onProvideItemLayoutId} method to change the layout
76 * used to display each action.
77 * <p>
78 * Note: If an alternate list layout is provided, the following view IDs must be supplied:
79 * <ul>
80 * <li>{@link android.support.v17.leanback.R.id#guidedactions_selector}</li>
81 * <li>{@link android.support.v17.leanback.R.id#guidedactions_list}</li>
82 * </ul><p>
83 * These view IDs must be present in order for the stylist to function. The list ID must correspond
84 * to a {@link VerticalGridView} or subclass.
85 * <p>
86 * If an alternate item layout is provided, the following view IDs should be used to refer to base
87 * elements:
88 * <ul>
89 * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_content}</li>
90 * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_title}</li>
91 * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_description}</li>
92 * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_icon}</li>
93 * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_checkmark}</li>
94 * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_chevron}</li>
95 * </ul><p>
96 * These view IDs are allowed to be missing, in which case the corresponding views in {@link
97 * GuidedActionsStylist.ViewHolder} will be null.
98 * <p>
99 * In order to support editable actions, the view associated with guidedactions_item_title should
100 * be a subclass of {@link android.widget.EditText}, and should satisfy the {@link
101 * ImeKeyMonitor} interface.
102 *
103 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation
104 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation
105 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorShowAnimation
106 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorHideAnimation
107 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorStyle
108 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsListStyle
109 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedSubActionsListStyle
110 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedButtonActionsListStyle
111 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemContainerStyle
112 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemCheckmarkStyle
113 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemIconStyle
114 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemContentStyle
115 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemTitleStyle
116 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemDescriptionStyle
117 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemChevronStyle
118 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionPressedAnimation
119 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionUnpressedAnimation
120 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionEnabledChevronAlpha
121 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionDisabledChevronAlpha
122 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionTitleMinLines
123 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionTitleMaxLines
124 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionDescriptionMinLines
125 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionVerticalPadding
126 * @see android.R.styleable#Theme_listChoiceIndicatorSingle
127 * @see android.R.styleable#Theme_listChoiceIndicatorMultiple
128 * @see android.support.v17.leanback.app.GuidedStepFragment
129 * @see GuidedAction
130 */
131public class GuidedActionsStylist implements FragmentAnimationProvider {
132
133    /**
134     * Default viewType that associated with default layout Id for the action item.
135     * @see #getItemViewType(GuidedAction)
136     * @see #onProvideItemLayoutId(int)
137     * @see #onCreateViewHolder(ViewGroup, int)
138     */
139    public static final int VIEW_TYPE_DEFAULT = 0;
140
141    /**
142     * ViewHolder caches information about the action item layouts' subviews. Subclasses of {@link
143     * GuidedActionsStylist} may also wish to subclass this in order to add fields.
144     * @see GuidedAction
145     */
146    public static class ViewHolder extends RecyclerView.ViewHolder {
147
148        private GuidedAction mAction;
149        private View mContentView;
150        private TextView mTitleView;
151        private TextView mDescriptionView;
152        private ImageView mIconView;
153        private ImageView mCheckmarkView;
154        private ImageView mChevronView;
155        private boolean mInEditing;
156        private boolean mInEditingDescription;
157        private final boolean mIsSubAction;
158
159        /**
160         * Constructs an ViewHolder and caches the relevant subviews.
161         */
162        public ViewHolder(View v) {
163            this(v, false);
164        }
165
166        /**
167         * Constructs an ViewHolder for sub action and caches the relevant subviews.
168         */
169        public ViewHolder(View v, boolean isSubAction) {
170            super(v);
171
172            mContentView = v.findViewById(R.id.guidedactions_item_content);
173            mTitleView = (TextView) v.findViewById(R.id.guidedactions_item_title);
174            mDescriptionView = (TextView) v.findViewById(R.id.guidedactions_item_description);
175            mIconView = (ImageView) v.findViewById(R.id.guidedactions_item_icon);
176            mCheckmarkView = (ImageView) v.findViewById(R.id.guidedactions_item_checkmark);
177            mChevronView = (ImageView) v.findViewById(R.id.guidedactions_item_chevron);
178            mIsSubAction = isSubAction;
179        }
180
181        /**
182         * Returns the content view within this view holder's view, where title and description are
183         * shown.
184         */
185        public View getContentView() {
186            return mContentView;
187        }
188
189        /**
190         * Returns the title view within this view holder's view.
191         */
192        public TextView getTitleView() {
193            return mTitleView;
194        }
195
196        /**
197         * Convenience method to return an editable version of the title, if possible,
198         * or null if the title view isn't an EditText.
199         */
200        public EditText getEditableTitleView() {
201            return (mTitleView instanceof EditText) ? (EditText)mTitleView : null;
202        }
203
204        /**
205         * Returns the description view within this view holder's view.
206         */
207        public TextView getDescriptionView() {
208            return mDescriptionView;
209        }
210
211        /**
212         * Convenience method to return an editable version of the description, if possible,
213         * or null if the description view isn't an EditText.
214         */
215        public EditText getEditableDescriptionView() {
216            return (mDescriptionView instanceof EditText) ? (EditText)mDescriptionView : null;
217        }
218
219        /**
220         * Returns the icon view within this view holder's view.
221         */
222        public ImageView getIconView() {
223            return mIconView;
224        }
225
226        /**
227         * Returns the checkmark view within this view holder's view.
228         */
229        public ImageView getCheckmarkView() {
230            return mCheckmarkView;
231        }
232
233        /**
234         * Returns the chevron view within this view holder's view.
235         */
236        public ImageView getChevronView() {
237            return mChevronView;
238        }
239
240        /**
241         * Returns true if the TextView is in editing title or description, false otherwise.
242         */
243        public boolean isInEditing() {
244            return mInEditing;
245        }
246
247        /**
248         * Returns true if the TextView is in editing description, false otherwise.
249         */
250        public boolean isInEditingDescription() {
251            return mInEditingDescription;
252        }
253
254        /**
255         * @return Current editing title view or description view or null if not in editing.
256         */
257        public View getEditingView() {
258            if (mInEditing) {
259                return mInEditingDescription ?  mDescriptionView : mTitleView;
260            } else {
261                return null;
262            }
263        }
264
265        /**
266         * @return True if bound action is inside {@link GuidedAction#getSubActions()}, false
267         * otherwise.
268         */
269        public boolean isSubAction() {
270            return mIsSubAction;
271        }
272
273        /**
274         * @return Currently bound action.
275         */
276        public GuidedAction getAction() {
277            return mAction;
278        }
279    }
280
281    private static String TAG = "GuidedActionsStylist";
282
283    private ViewGroup mMainView;
284    private VerticalGridView mActionsGridView;
285    private VerticalGridView mSubActionsGridView;
286    private View mBgView;
287    private View mSelectorView;
288    private View mContentView;
289    private boolean mButtonActions;
290
291    private Animator mSelectorAnimator;
292
293    // Cached values from resources
294    private float mEnabledTextAlpha;
295    private float mDisabledTextAlpha;
296    private float mEnabledDescriptionAlpha;
297    private float mDisabledDescriptionAlpha;
298    private float mEnabledChevronAlpha;
299    private float mDisabledChevronAlpha;
300    private int mTitleMinLines;
301    private int mTitleMaxLines;
302    private int mDescriptionMinLines;
303    private int mVerticalPadding;
304    private int mDisplayHeight;
305
306    private GuidedAction mExpandedAction = null;
307    private Object mExpandTransition;
308
309    private final RecyclerView.OnScrollListener mOnGridScrollListener =
310            new RecyclerView.OnScrollListener() {
311        @Override
312        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
313            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
314                if (mSelectorView.getAlpha() != 1f) {
315                    updateSelectorView(true);
316                }
317            }
318        }
319    };
320
321    /**
322     * Creates a view appropriate for displaying a list of GuidedActions, using the provided
323     * inflater and container.
324     * <p>
325     * <i>Note: Does not actually add the created view to the container; the caller should do
326     * this.</i>
327     * @param inflater The layout inflater to be used when constructing the view.
328     * @param container The view group to be passed in the call to
329     * <code>LayoutInflater.inflate</code>.
330     * @return The view to be added to the caller's view hierarchy.
331     */
332    public View onCreateView(LayoutInflater inflater, ViewGroup container) {
333        mMainView = (ViewGroup) inflater.inflate(onProvideLayoutId(), container, false);
334        mContentView = mMainView.findViewById(mButtonActions ? R.id.guidedactions_content2 :
335                R.id.guidedactions_content);
336        mSelectorView = mMainView.findViewById(mButtonActions ? R.id.guidedactions_selector2 :
337                R.id.guidedactions_selector);
338        mSelectorView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
339            @Override
340            public void onLayoutChange(View v, int left, int top, int right, int bottom,
341                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
342                updateSelectorView(false);
343            }
344        });
345        mBgView = mMainView.findViewById(mButtonActions ? R.id.guidedactions_list_background2 :
346                R.id.guidedactions_list_background);
347        if (mMainView instanceof VerticalGridView) {
348            mActionsGridView = (VerticalGridView) mMainView;
349        } else {
350            mActionsGridView = (VerticalGridView) mMainView.findViewById(mButtonActions ?
351                    R.id.guidedactions_list2 : R.id.guidedactions_list);
352            if (mActionsGridView == null) {
353                throw new IllegalStateException("No ListView exists.");
354            }
355            mActionsGridView.setWindowAlignmentOffset(0);
356            mActionsGridView.setWindowAlignmentOffsetPercent(50f);
357            mActionsGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
358            if (mSelectorView != null) {
359                mActionsGridView.setOnScrollListener(mOnGridScrollListener);
360            }
361            if (!mButtonActions) {
362                mSubActionsGridView = (VerticalGridView) mMainView.findViewById(
363                        R.id.guidedactions_sub_list);
364                if (mSelectorView != null && mSubActionsGridView != null) {
365                    mSubActionsGridView.setOnScrollListener(mOnGridScrollListener);
366                }
367            }
368        }
369
370        if (mSelectorView != null) {
371            // ALlow focus to move to other views
372            mMainView.getViewTreeObserver().addOnGlobalFocusChangeListener(
373                    mGlobalFocusChangeListener);
374        }
375
376        // Cache widths, chevron alpha values, max and min text lines, etc
377        Context ctx = mMainView.getContext();
378        TypedValue val = new TypedValue();
379        mEnabledChevronAlpha = getFloat(ctx, val, R.attr.guidedActionEnabledChevronAlpha);
380        mDisabledChevronAlpha = getFloat(ctx, val, R.attr.guidedActionDisabledChevronAlpha);
381        mTitleMinLines = getInteger(ctx, val, R.attr.guidedActionTitleMinLines);
382        mTitleMaxLines = getInteger(ctx, val, R.attr.guidedActionTitleMaxLines);
383        mDescriptionMinLines = getInteger(ctx, val, R.attr.guidedActionDescriptionMinLines);
384        mVerticalPadding = getDimension(ctx, val, R.attr.guidedActionVerticalPadding);
385        mDisplayHeight = ((WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE))
386                .getDefaultDisplay().getHeight();
387
388        mEnabledTextAlpha = Float.valueOf(ctx.getResources().getString(R.string
389                .lb_guidedactions_item_unselected_text_alpha));
390        mDisabledTextAlpha = Float.valueOf(ctx.getResources().getString(R.string
391                .lb_guidedactions_item_disabled_text_alpha));
392        mEnabledDescriptionAlpha = Float.valueOf(ctx.getResources().getString(R.string
393                .lb_guidedactions_item_unselected_description_text_alpha));
394        mDisabledDescriptionAlpha = Float.valueOf(ctx.getResources().getString(R.string
395                .lb_guidedactions_item_disabled_description_text_alpha));
396        return mMainView;
397    }
398
399    /**
400     * Choose the layout resource for button actions in {@link #onProvideLayoutId()}.
401     */
402    public void setAsButtonActions() {
403        if (mMainView != null) {
404            throw new IllegalStateException("setAsButtonActions() must be called before creating "
405                    + "views");
406        }
407        mButtonActions = true;
408    }
409
410    /**
411     * Returns true if it is button actions list, false for normal actions list.
412     * @return True if it is button actions list, false for normal actions list.
413     */
414    public boolean isButtonActions() {
415        return mButtonActions;
416    }
417
418    final ViewTreeObserver.OnGlobalFocusChangeListener mGlobalFocusChangeListener =
419            new ViewTreeObserver.OnGlobalFocusChangeListener() {
420
421        @Override
422        public void onGlobalFocusChanged(View oldFocus, View newFocus) {
423            updateSelectorView(false);
424        }
425    };
426
427    /**
428     * Called when destroy the View created by GuidedActionsStylist.
429     */
430    public void onDestroyView() {
431        if (mSelectorView != null) {
432            mMainView.getViewTreeObserver().removeOnGlobalFocusChangeListener(
433                    mGlobalFocusChangeListener);
434        }
435        endSelectorAnimator();
436        mExpandedAction = null;
437        mExpandTransition = null;
438        mActionsGridView = null;
439        mSubActionsGridView = null;
440        mSelectorView = null;
441        mContentView = null;
442        mBgView = null;
443        mMainView = null;
444    }
445
446    /**
447     * Returns the VerticalGridView that displays the list of GuidedActions.
448     * @return The VerticalGridView for this presenter.
449     */
450    public VerticalGridView getActionsGridView() {
451        return mActionsGridView;
452    }
453
454    /**
455     * Returns the VerticalGridView that displays the sub actions list of an expanded action.
456     * @return The VerticalGridView that displays the sub actions list of an expanded action.
457     */
458    public VerticalGridView getSubActionsGridView() {
459        return mSubActionsGridView;
460    }
461
462    /**
463     * Provides the resource ID of the layout defining the host view for the list of guided actions.
464     * Subclasses may override to provide their own customized layouts. The base implementation
465     * returns {@link android.support.v17.leanback.R.layout#lb_guidedactions} or
466     * {@link android.support.v17.leanback.R.layout#lb_guidedbuttonactions} if
467     * {@link #isButtonActions()} is true. If overridden, the substituted layout should contain
468     * matching IDs for any views that should be managed by the base class; this can be achieved by
469     * starting with a copy of the base layout file.
470     *
471     * @return The resource ID of the layout to be inflated to define the host view for the list of
472     *         GuidedActions.
473     */
474    public int onProvideLayoutId() {
475        return mButtonActions ? R.layout.lb_guidedbuttonactions : R.layout.lb_guidedactions;
476    }
477
478    /**
479     * Return view type of action, each different type can have differently associated layout Id.
480     * Default implementation returns {@link #VIEW_TYPE_DEFAULT}.
481     * @param action  The action object.
482     * @return View type that used in {@link #onProvideItemLayoutId(int)}.
483     */
484    public int getItemViewType(GuidedAction action) {
485        return VIEW_TYPE_DEFAULT;
486    }
487
488    /**
489     * Provides the resource ID of the layout defining the view for an individual guided actions.
490     * Subclasses may override to provide their own customized layouts. The base implementation
491     * returns {@link android.support.v17.leanback.R.layout#lb_guidedactions_item}. If overridden,
492     * the substituted layout should contain matching IDs for any views that should be managed by
493     * the base class; this can be achieved by starting with a copy of the base layout file. Note
494     * that in order for the item to support editing, the title view should both subclass {@link
495     * android.widget.EditText} and implement {@link ImeKeyMonitor}; see {@link
496     * GuidedActionEditText}.  To support different types of Layouts, override {@link
497     * #onProvideItemLayoutId(int)}.
498     * @return The resource ID of the layout to be inflated to define the view to display an
499     * individual GuidedAction.
500     */
501    public int onProvideItemLayoutId() {
502        return R.layout.lb_guidedactions_item;
503    }
504
505    /**
506     * Provides the resource ID of the layout defining the view for an individual guided actions.
507     * Subclasses may override to provide their own customized layouts. The base implementation
508     * returns {@link android.support.v17.leanback.R.layout#lb_guidedactions_item}. If overridden,
509     * the substituted layout should contain matching IDs for any views that should be managed by
510     * the base class; this can be achieved by starting with a copy of the base layout file. Note
511     * that in order for the item to support editing, the title view should both subclass {@link
512     * android.widget.EditText} and implement {@link ImeKeyMonitor}; see {@link
513     * GuidedActionEditText}.
514     * @param viewType View type returned by {@link #getItemViewType(GuidedAction)}
515     * @return The resource ID of the layout to be inflated to define the view to display an
516     * individual GuidedAction.
517     */
518    public int onProvideItemLayoutId(int viewType) {
519        if (viewType == VIEW_TYPE_DEFAULT) {
520            return onProvideItemLayoutId();
521        } else {
522            throw new RuntimeException("ViewType " + viewType +
523                    " not supported in GuidedActionsStylist");
524        }
525    }
526
527    /**
528     * Constructs a {@link ViewHolder} capable of representing {@link GuidedAction}s. Subclasses
529     * may choose to return a subclass of ViewHolder.  To support different view types, override
530     * {@link #onCreateViewHolder(ViewGroup, int)}
531     * <p>
532     * <i>Note: Should not actually add the created view to the parent; the caller will do
533     * this.</i>
534     * @param parent The view group to be used as the parent of the new view.
535     * @return The view to be added to the caller's view hierarchy.
536     */
537    public ViewHolder onCreateViewHolder(ViewGroup parent) {
538        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
539        View v = inflater.inflate(onProvideItemLayoutId(), parent, false);
540        return new ViewHolder(v, parent == mSubActionsGridView);
541    }
542
543    /**
544     * Constructs a {@link ViewHolder} capable of representing {@link GuidedAction}s. Subclasses
545     * may choose to return a subclass of ViewHolder.
546     * <p>
547     * <i>Note: Should not actually add the created view to the parent; the caller will do
548     * this.</i>
549     * @param parent The view group to be used as the parent of the new view.
550     * @param viewType The viewType returned by {@link #getItemViewType(GuidedAction)}
551     * @return The view to be added to the caller's view hierarchy.
552     */
553    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
554        if (viewType == VIEW_TYPE_DEFAULT) {
555            return onCreateViewHolder(parent);
556        }
557        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
558        View v = inflater.inflate(onProvideItemLayoutId(viewType), parent, false);
559        return new ViewHolder(v, parent == mSubActionsGridView);
560    }
561
562    /**
563     * Binds a {@link ViewHolder} to a particular {@link GuidedAction}.
564     * @param vh The view holder to be associated with the given action.
565     * @param action The guided action to be displayed by the view holder's view.
566     * @return The view to be added to the caller's view hierarchy.
567     */
568    public void onBindViewHolder(ViewHolder vh, GuidedAction action) {
569
570        vh.mAction = action;
571        if (vh.mTitleView != null) {
572            vh.mTitleView.setText(action.getTitle());
573            vh.mTitleView.setAlpha(action.isEnabled() ? mEnabledTextAlpha : mDisabledTextAlpha);
574            vh.mTitleView.setFocusable(action.isEditable());
575        }
576        if (vh.mDescriptionView != null) {
577            vh.mDescriptionView.setText(action.getDescription());
578            vh.mDescriptionView.setVisibility(TextUtils.isEmpty(action.getDescription()) ?
579                    View.GONE : View.VISIBLE);
580            vh.mDescriptionView.setAlpha(action.isEnabled() ? mEnabledDescriptionAlpha :
581                mDisabledDescriptionAlpha);
582            vh.mDescriptionView.setFocusable(action.isDescriptionEditable());
583        }
584        // Clients might want the check mark view to be gone entirely, in which case, ignore it.
585        if (vh.mCheckmarkView != null) {
586            onBindCheckMarkView(vh, action);
587        }
588
589        if (action.hasMultilineDescription()) {
590            if (vh.mTitleView != null) {
591                vh.mTitleView.setMaxLines(mTitleMaxLines);
592                if (vh.mDescriptionView != null) {
593                    vh.mDescriptionView.setMaxHeight(getDescriptionMaxHeight(
594                            vh.itemView.getContext(), vh.mTitleView));
595                }
596            }
597        } else {
598            if (vh.mTitleView != null) {
599                vh.mTitleView.setMaxLines(mTitleMinLines);
600            }
601            if (vh.mDescriptionView != null) {
602                vh.mDescriptionView.setMaxLines(mDescriptionMinLines);
603            }
604        }
605        setEditingMode(vh, action, false);
606        if (action.isFocusable()) {
607            vh.itemView.setFocusable(true);
608            ((ViewGroup) vh.itemView).setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
609        } else {
610            vh.itemView.setFocusable(false);
611            ((ViewGroup) vh.itemView).setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
612        }
613        setupImeOptions(vh, action);
614
615        updateChevronAndVisibility(vh);
616    }
617
618    /**
619     * Called by {@link #onBindViewHolder(ViewHolder, GuidedAction)} to setup IME options.  Default
620     * implementation assigns {@link EditorInfo#IME_ACTION_DONE}.  Subclass may override.
621     * @param vh The view holder to be associated with the given action.
622     * @param action The guided action to be displayed by the view holder's view.
623     */
624    protected void setupImeOptions(ViewHolder vh, GuidedAction action) {
625        setupNextImeOptions(vh.getEditableTitleView());
626        setupNextImeOptions(vh.getEditableDescriptionView());
627    }
628
629    private void setupNextImeOptions(EditText edit) {
630        if (edit != null) {
631            edit.setImeOptions(EditorInfo.IME_ACTION_NEXT);
632        }
633    }
634
635    public void setEditingMode(ViewHolder vh, GuidedAction action, boolean editing) {
636        if (editing != vh.mInEditing) {
637            vh.mInEditing = editing;
638            onEditingModeChange(vh, action, editing);
639        }
640    }
641
642    protected void onEditingModeChange(ViewHolder vh, GuidedAction action, boolean editing) {
643        action = vh.getAction();
644        TextView titleView = vh.getTitleView();
645        TextView descriptionView = vh.getDescriptionView();
646        if (editing) {
647            CharSequence editTitle = action.getEditTitle();
648            if (titleView != null && editTitle != null) {
649                titleView.setText(editTitle);
650            }
651            CharSequence editDescription = action.getEditDescription();
652            if (descriptionView != null && editDescription != null) {
653                descriptionView.setText(editDescription);
654            }
655            if (action.isDescriptionEditable()) {
656                if (descriptionView != null) {
657                    descriptionView.setVisibility(View.VISIBLE);
658                    descriptionView.setInputType(action.getDescriptionEditInputType());
659                }
660                vh.mInEditingDescription = true;
661            } else {
662                vh.mInEditingDescription = false;
663                if (titleView != null) {
664                    titleView.setInputType(action.getEditInputType());
665                }
666            }
667        } else {
668            if (titleView != null) {
669                titleView.setText(action.getTitle());
670            }
671            if (descriptionView != null) {
672                descriptionView.setText(action.getDescription());
673            }
674            if (vh.mInEditingDescription) {
675                if (descriptionView != null) {
676                    descriptionView.setVisibility(TextUtils.isEmpty(action.getDescription()) ?
677                            View.GONE : View.VISIBLE);
678                    descriptionView.setInputType(action.getDescriptionInputType());
679                }
680                vh.mInEditingDescription = false;
681            } else {
682                if (titleView != null) {
683                    titleView.setInputType(action.getInputType());
684                }
685            }
686        }
687    }
688
689    /**
690     * Animates the view holder's view (or subviews thereof) when the action has had its focus
691     * state changed.
692     * @param vh The view holder associated with the relevant action.
693     * @param focused True if the action has become focused, false if it has lost focus.
694     */
695    public void onAnimateItemFocused(ViewHolder vh, boolean focused) {
696        // No animations for this, currently, because the animation is done on
697        // mSelectorView
698    }
699
700    /**
701     * Animates the view holder's view (or subviews thereof) when the action has had its press
702     * state changed.
703     * @param vh The view holder associated with the relevant action.
704     * @param pressed True if the action has been pressed, false if it has been unpressed.
705     */
706    public void onAnimateItemPressed(ViewHolder vh, boolean pressed) {
707        int attr = pressed ? R.attr.guidedActionPressedAnimation :
708                R.attr.guidedActionUnpressedAnimation;
709        createAnimator(vh.itemView, attr).start();
710    }
711
712    /**
713     * Resets the view holder's view to unpressed state.
714     * @param vh The view holder associated with the relevant action.
715     */
716    public void onAnimateItemPressedCancelled(ViewHolder vh) {
717        createAnimator(vh.itemView, R.attr.guidedActionUnpressedAnimation).end();
718    }
719
720    /**
721     * Animates the view holder's view (or subviews thereof) when the action has had its check state
722     * changed. Default implementation calls setChecked() if {@link ViewHolder#getCheckmarkView()}
723     * is instance of {@link Checkable}.
724     *
725     * @param vh The view holder associated with the relevant action.
726     * @param checked True if the action has become checked, false if it has become unchecked.
727     * @see #onBindCheckMarkView(ViewHolder, GuidedAction)
728     */
729    public void onAnimateItemChecked(ViewHolder vh, boolean checked) {
730        if (vh.mCheckmarkView instanceof Checkable) {
731            ((Checkable) vh.mCheckmarkView).setChecked(checked);
732        }
733    }
734
735    /**
736     * Sets states of check mark view, called by {@link #onBindViewHolder(ViewHolder, GuidedAction)}
737     * when action's checkset Id is other than {@link GuidedAction#NO_CHECK_SET}. Default
738     * implementation assigns drawable loaded from theme attribute
739     * {@link android.R.attr#listChoiceIndicatorMultiple} for checkbox or
740     * {@link android.R.attr#listChoiceIndicatorSingle} for radio button. Subclass rarely needs
741     * override the method, instead app can provide its own drawable that supports transition
742     * animations, change theme attributes {@link android.R.attr#listChoiceIndicatorMultiple} and
743     * {@link android.R.attr#listChoiceIndicatorSingle} in {android.support.v17.leanback.R.
744     * styleable#LeanbackGuidedStepTheme}.
745     *
746     * @param vh The view holder associated with the relevant action.
747     * @param action The GuidedAction object to bind to.
748     * @see #onAnimateItemChecked(ViewHolder, boolean)
749     */
750    public void onBindCheckMarkView(ViewHolder vh, GuidedAction action) {
751        if (action.getCheckSetId() != GuidedAction.NO_CHECK_SET) {
752            vh.mCheckmarkView.setVisibility(View.VISIBLE);
753            int attrId = action.getCheckSetId() == GuidedAction.CHECKBOX_CHECK_SET_ID ?
754                    android.R.attr.listChoiceIndicatorMultiple :
755                    android.R.attr.listChoiceIndicatorSingle;
756            final Context context = vh.mCheckmarkView.getContext();
757            Drawable drawable = null;
758            TypedValue typedValue = new TypedValue();
759            if (context.getTheme().resolveAttribute(attrId, typedValue, true)) {
760                drawable = ContextCompat.getDrawable(context, typedValue.resourceId);
761            }
762            vh.mCheckmarkView.setImageDrawable(drawable);
763            if (vh.mCheckmarkView instanceof Checkable) {
764                ((Checkable) vh.mCheckmarkView).setChecked(action.isChecked());
765            }
766        } else {
767            vh.mCheckmarkView.setVisibility(View.GONE);
768        }
769    }
770
771    /**
772     * Sets states of chevron view, called by {@link #onBindViewHolder(ViewHolder, GuidedAction)}.
773     * Subclass may override.
774     *
775     * @param vh The view holder associated with the relevant action.
776     * @param action The GuidedAction object to bind to.
777     */
778    public void onBindChevronView(ViewHolder vh, GuidedAction action) {
779        final boolean hasNext = action.hasNext();
780        final boolean hasSubActions = action.hasSubActions();
781        if (hasNext || hasSubActions) {
782            vh.mChevronView.setVisibility(View.VISIBLE);
783            vh.mChevronView.setAlpha(action.isEnabled() ? mEnabledChevronAlpha :
784                    mDisabledChevronAlpha);
785            if (hasNext) {
786                vh.mChevronView.setRotation(0f);
787            } else if (action == mExpandedAction) {
788                vh.mChevronView.setRotation(270);
789            } else {
790                vh.mChevronView.setRotation(90);
791            }
792        } else {
793            vh.mChevronView.setVisibility(View.GONE);
794
795        }
796    }
797
798    /**
799     * Expands or collapse the sub actions list view.
800     * @param avh When not null, fill sub actions list of this ViewHolder into sub actions list and
801     * hide the other items in main list.  When null, collapse the sub actions list.
802     */
803    public void setExpandedViewHolder(ViewHolder avh) {
804        if (mSubActionsGridView == null || isInExpandTransition()) {
805            return;
806        }
807        if (isExpandTransitionSupported()) {
808            startExpandedTransition(avh);
809        } else {
810            onUpdateExpandedViewHolder(avh);
811        }
812    }
813
814    /**
815     * Returns true if it is running an expanding or collapsing transition, false otherwise.
816     * @return True if it is running an expanding or collapsing transition, false otherwise.
817     */
818    public boolean isInExpandTransition() {
819        return mExpandTransition != null;
820    }
821
822    /**
823     * Returns if expand/collapse animation is supported.  When this method returns true,
824     * {@link #startExpandedTransition(ViewHolder)} will be used.  When this method returns false,
825     * {@link #onUpdateExpandedViewHolder(ViewHolder)} will be called.
826     * @return True if it is running an expanding or collapsing transition, false otherwise.
827     */
828    public boolean isExpandTransitionSupported() {
829        return VERSION.SDK_INT >= 21;
830    }
831
832    /**
833     * Start transition to expand or collapse GuidedActionStylist.
834     * @param avh When not null, the GuidedActionStylist expands the sub actions of avh.  When null
835     * the GuidedActionStylist will collapse sub actions.
836     */
837    public void startExpandedTransition(ViewHolder avh) {
838        ViewHolder focusAvh = null; // expand / collapse view holder
839        final int count = mActionsGridView.getChildCount();
840        for (int i = 0; i < count; i++) {
841            ViewHolder vh = (ViewHolder) mActionsGridView
842                    .getChildViewHolder(mActionsGridView.getChildAt(i));
843            if (avh == null && vh.itemView.getVisibility() == View.VISIBLE) {
844                // going to collapse this one.
845                focusAvh = vh;
846                break;
847            } else if (avh != null && vh.getAction() == avh.getAction()) {
848                // going to expand this one.
849                focusAvh = vh;
850                break;
851            }
852        }
853        if (focusAvh == null) {
854            // huh?
855            onUpdateExpandedViewHolder(avh);
856            return;
857        }
858        Object set = TransitionHelper.createTransitionSet(false);
859        Object slideAndFade = TransitionHelper.createFadeAndShortSlide(Gravity.TOP | Gravity.BOTTOM,
860                (float) focusAvh.itemView.getHeight());
861        Object changeFocusItemTransform = TransitionHelper.createChangeTransform();
862        Object changeFocusItemBounds = TransitionHelper.createChangeBounds(false);
863        Object fadeGrid = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN |
864                TransitionHelper.FADE_OUT);
865        Object changeGridBounds = TransitionHelper.createChangeBounds(false);
866        if (avh == null) {
867            TransitionHelper.setStartDelay(slideAndFade, 150);
868            TransitionHelper.setStartDelay(changeFocusItemTransform, 100);
869            TransitionHelper.setStartDelay(changeFocusItemBounds, 100);
870        } else {
871            TransitionHelper.setStartDelay(fadeGrid, 100);
872            TransitionHelper.setStartDelay(changeGridBounds, 100);
873            TransitionHelper.setStartDelay(changeFocusItemTransform, 50);
874            TransitionHelper.setStartDelay(changeFocusItemBounds, 50);
875        }
876        for (int i = 0; i < count; i++) {
877            ViewHolder vh = (ViewHolder) mActionsGridView
878                    .getChildViewHolder(mActionsGridView.getChildAt(i));
879            if (vh == focusAvh) {
880                // going to expand/collapse this one.
881                TransitionHelper.include(changeFocusItemTransform, vh.itemView);
882                TransitionHelper.include(changeFocusItemBounds, vh.itemView);
883            } else {
884                // going to slide this item to top / bottom.
885                TransitionHelper.include(slideAndFade, vh.itemView);
886            }
887        }
888        if (mSelectorView != null) {
889            TransitionHelper.include(changeFocusItemTransform, mSelectorView);
890            TransitionHelper.include(changeFocusItemBounds, mSelectorView);
891        }
892        TransitionHelper.include(fadeGrid, mSubActionsGridView);
893        TransitionHelper.include(changeGridBounds, mSubActionsGridView);
894        TransitionHelper.addTransition(set, slideAndFade);
895        TransitionHelper.addTransition(set, changeFocusItemTransform);
896        TransitionHelper.addTransition(set, changeFocusItemBounds);
897        TransitionHelper.addTransition(set, fadeGrid);
898        TransitionHelper.addTransition(set, changeGridBounds);
899        mExpandTransition = set;
900        TransitionHelper.addTransitionListener(mExpandTransition, new TransitionListener() {
901            @Override
902            public void onTransitionEnd(Object transition) {
903                mExpandTransition = null;
904            }
905        });
906        if (avh != null && mSubActionsGridView.getTop() != avh.itemView.getTop()) {
907            // For expanding, set the initial position of subActionsGridView before running
908            // a ChangeBounds on it.
909            final ViewHolder toUpdate = avh;
910            mSubActionsGridView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
911                @Override
912                public void onLayoutChange(View v, int left, int top, int right, int bottom,
913                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
914                    if (mSubActionsGridView == null) {
915                        return;
916                    }
917                    mSubActionsGridView.removeOnLayoutChangeListener(this);
918                    mMainView.post(new Runnable() {
919                        public void run() {
920                            if (mMainView == null) {
921                                return;
922                            }
923                            TransitionHelper.beginDelayedTransition(mMainView, mExpandTransition);
924                            onUpdateExpandedViewHolder(toUpdate);
925                        }
926                    });
927                }
928            });
929            ViewGroup.MarginLayoutParams lp =
930                    (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
931            lp.topMargin = avh.itemView.getTop();
932            lp.height = 0;
933            mSubActionsGridView.setLayoutParams(lp);
934            return;
935        }
936        TransitionHelper.beginDelayedTransition(mMainView, mExpandTransition);
937        onUpdateExpandedViewHolder(avh);
938    }
939
940    /**
941     * @return True if sub actions list is expanded.
942     */
943    public boolean isSubActionsExpanded() {
944        return mExpandedAction != null;
945    }
946
947    /**
948     * @return Current expanded GuidedAction or null if not expanded.
949     */
950    public GuidedAction getExpandedAction() {
951        return mExpandedAction;
952    }
953
954    /**
955     * Expand or collapse GuidedActionStylist.
956     * @param avh When not null, the GuidedActionStylist expands the sub actions of avh.  When null
957     * the GuidedActionStylist will collapse sub actions.
958     */
959    public void onUpdateExpandedViewHolder(ViewHolder avh) {
960        if (avh == null) {
961            mExpandedAction = null;
962        } else if (avh.getAction() != mExpandedAction) {
963            mExpandedAction = avh.getAction();
964        }
965        final int count = mActionsGridView.getChildCount();
966        for (int i = 0; i < count; i++) {
967            ViewHolder vh = (ViewHolder) mActionsGridView
968                    .getChildViewHolder(mActionsGridView.getChildAt(i));
969            updateChevronAndVisibility(vh);
970        }
971        if (mSubActionsGridView != null) {
972            if (avh != null) {
973                ViewGroup.MarginLayoutParams lp =
974                        (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
975                lp.topMargin = avh.itemView.getTop();
976                lp.height = ViewGroup.MarginLayoutParams.MATCH_PARENT;
977                mSubActionsGridView.setLayoutParams(lp);
978                mSubActionsGridView.setVisibility(View.VISIBLE);
979                mSubActionsGridView.requestFocus();
980                mSubActionsGridView.setSelectedPosition(0);
981                ((GuidedActionAdapter) mSubActionsGridView.getAdapter())
982                        .setActions(avh.getAction().getSubActions());
983            } else {
984                mSubActionsGridView.setVisibility(View.INVISIBLE);
985                ViewGroup.MarginLayoutParams lp =
986                        (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
987                lp.height = 0;
988                mSubActionsGridView.setLayoutParams(lp);
989                ((GuidedActionAdapter) mSubActionsGridView.getAdapter())
990                        .setActions(Collections.EMPTY_LIST);
991                mActionsGridView.requestFocus();
992            }
993        }
994    }
995
996    private void updateChevronAndVisibility(ViewHolder vh) {
997        if (!vh.isSubAction()) {
998            if (mExpandedAction == null) {
999                vh.itemView.setVisibility(View.VISIBLE);
1000                vh.itemView.setTranslationY(0);
1001            } else if (vh.getAction() == mExpandedAction) {
1002                vh.itemView.setVisibility(View.VISIBLE);
1003                vh.itemView.setTranslationY(- vh.itemView.getHeight());
1004            } else {
1005                vh.itemView.setVisibility(View.INVISIBLE);
1006                vh.itemView.setTranslationY(0);
1007            }
1008        }
1009        if (vh.mChevronView != null) {
1010            onBindChevronView(vh, vh.getAction());
1011        }
1012    }
1013
1014    /*
1015     * ==========================================
1016     * FragmentAnimationProvider overrides
1017     * ==========================================
1018     */
1019
1020    /**
1021     * {@inheritDoc}
1022     */
1023    @Override
1024    public void onImeAppearing(@NonNull List<Animator> animators) {
1025        animators.add(createAnimator(mContentView, R.attr.guidedStepImeAppearingAnimation));
1026    }
1027
1028    /**
1029     * {@inheritDoc}
1030     */
1031    @Override
1032    public void onImeDisappearing(@NonNull List<Animator> animators) {
1033        animators.add(createAnimator(mContentView, R.attr.guidedStepImeDisappearingAnimation));
1034    }
1035
1036    /*
1037     * ==========================================
1038     * Private methods
1039     * ==========================================
1040     */
1041
1042    private float getFloat(Context ctx, TypedValue typedValue, int attrId) {
1043        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
1044        // Android resources don't have a native float type, so we have to use strings.
1045        return Float.valueOf(ctx.getResources().getString(typedValue.resourceId));
1046    }
1047
1048    private int getInteger(Context ctx, TypedValue typedValue, int attrId) {
1049        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
1050        return ctx.getResources().getInteger(typedValue.resourceId);
1051    }
1052
1053    private int getDimension(Context ctx, TypedValue typedValue, int attrId) {
1054        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
1055        return ctx.getResources().getDimensionPixelSize(typedValue.resourceId);
1056    }
1057
1058    private static Animator createAnimator(View v, int attrId) {
1059        Context ctx = v.getContext();
1060        TypedValue typedValue = new TypedValue();
1061        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
1062        Animator animator = AnimatorInflater.loadAnimator(ctx, typedValue.resourceId);
1063        animator.setTarget(v);
1064        return animator;
1065    }
1066
1067    private boolean setIcon(final ImageView iconView, GuidedAction action) {
1068        Drawable icon = null;
1069        if (iconView != null) {
1070            Context context = iconView.getContext();
1071            icon = action.getIcon();
1072            if (icon != null) {
1073                // setImageDrawable resets the drawable's level unless we set the view level first.
1074                iconView.setImageLevel(icon.getLevel());
1075                iconView.setImageDrawable(icon);
1076                iconView.setVisibility(View.VISIBLE);
1077            } else {
1078                iconView.setVisibility(View.GONE);
1079            }
1080        }
1081        return icon != null;
1082    }
1083
1084    /**
1085     * @return the max height in pixels the description can be such that the
1086     *         action nicely takes up the entire screen.
1087     */
1088    private int getDescriptionMaxHeight(Context context, TextView title) {
1089        // The 2 multiplier on the title height calculation is a
1090        // conservative estimate for font padding which can not be
1091        // calculated at this stage since the view hasn't been rendered yet.
1092        return (int)(mDisplayHeight - 2*mVerticalPadding - 2*mTitleMaxLines*title.getLineHeight());
1093    }
1094
1095    private void endSelectorAnimator() {
1096        if (mSelectorAnimator != null) {
1097            mSelectorAnimator.end();
1098            mSelectorAnimator = null;
1099        }
1100    }
1101
1102    private void updateSelectorView(boolean animate) {
1103        if ((mActionsGridView == null && mSubActionsGridView == null)
1104                || mSelectorView == null || mSelectorView.getHeight() <= 0) {
1105            return;
1106        }
1107        RecyclerView actionsGridView = null;
1108        View focusedChild = mActionsGridView.getFocusedChild();
1109        if (focusedChild == null && mSubActionsGridView != null) {
1110            focusedChild = mSubActionsGridView.getFocusedChild();
1111            if (focusedChild != null) {
1112                actionsGridView = mSubActionsGridView;
1113            }
1114        } else {
1115            actionsGridView = mActionsGridView;
1116        }
1117        endSelectorAnimator();
1118        if (focusedChild == null || !actionsGridView.hasFocus()
1119                || actionsGridView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
1120            if (animate) {
1121                mSelectorAnimator = createAnimator(mSelectorView,
1122                        R.attr.guidedActionsSelectorHideAnimation);
1123                mSelectorAnimator.start();
1124            } else {
1125                mSelectorView.setAlpha(0f);
1126            }
1127        } else {
1128            final float scaleY = (float) focusedChild.getHeight() / mSelectorView.getHeight();
1129            Rect r = new Rect(0, 0, focusedChild.getWidth(), focusedChild.getHeight());
1130            mMainView.offsetDescendantRectToMyCoords(focusedChild, r);
1131            mMainView.offsetRectIntoDescendantCoords(mSelectorView, r);
1132            mSelectorView.setTranslationY(r.exactCenterY() - mSelectorView.getHeight() * 0.5f);
1133            if (animate) {
1134                mSelectorAnimator = createAnimator(mSelectorView,
1135                        R.attr.guidedActionsSelectorShowAnimation);
1136                ((ObjectAnimator) ((AnimatorSet) mSelectorAnimator).getChildAnimations().get(1))
1137                        .setFloatValues(scaleY);
1138                mSelectorAnimator.start();
1139            } else {
1140                mSelectorView.setAlpha(1f);
1141                mSelectorView.setScaleY(scaleY);
1142            }
1143        }
1144    }
1145
1146}
1147