Home | History | Annotate | Download | only in widget
      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  */
     14 package androidx.leanback.widget;
     15 
     16 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
     17 
     18 import android.graphics.Paint;
     19 import android.text.TextUtils;
     20 import android.view.LayoutInflater;
     21 import android.view.View;
     22 import android.view.ViewGroup;
     23 import android.widget.TextView;
     24 
     25 import androidx.annotation.RestrictTo;
     26 import androidx.leanback.R;
     27 
     28 /**
     29  * RowHeaderPresenter provides a default presentation for {@link HeaderItem} using a
     30  * {@link RowHeaderView} and optionally a TextView for description. If a subclass creates its own
     31  * view, the subclass must also override {@link #onCreateViewHolder(ViewGroup)},
     32  * {@link #onSelectLevelChanged(ViewHolder)}.
     33  */
     34 public class RowHeaderPresenter extends Presenter {
     35 
     36     private final int mLayoutResourceId;
     37     private final Paint mFontMeasurePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     38     private boolean mNullItemVisibilityGone;
     39     private final boolean mAnimateSelect;
     40 
     41     /**
     42      * Creates default RowHeaderPresenter using a title view and a description view.
     43      * @see ViewHolder#ViewHolder(View)
     44      */
     45     public RowHeaderPresenter() {
     46         this(R.layout.lb_row_header);
     47     }
     48 
     49     /**
     50      * @hide
     51      */
     52     @RestrictTo(LIBRARY_GROUP)
     53     public RowHeaderPresenter(int layoutResourceId) {
     54         this(layoutResourceId, true);
     55     }
     56 
     57     /**
     58      * @hide
     59      */
     60     @RestrictTo(LIBRARY_GROUP)
     61     public RowHeaderPresenter(int layoutResourceId, boolean animateSelect) {
     62         mLayoutResourceId = layoutResourceId;
     63         mAnimateSelect = animateSelect;
     64     }
     65 
     66     /**
     67      * Optionally sets the view visibility to {@link View#GONE} when bound to null.
     68      */
     69     public void setNullItemVisibilityGone(boolean nullItemVisibilityGone) {
     70         mNullItemVisibilityGone = nullItemVisibilityGone;
     71     }
     72 
     73     /**
     74      * Returns true if the view visibility is set to {@link View#GONE} when bound to null.
     75      */
     76     public boolean isNullItemVisibilityGone() {
     77         return mNullItemVisibilityGone;
     78     }
     79 
     80     /**
     81      * A ViewHolder for the RowHeaderPresenter.
     82      */
     83     public static class ViewHolder extends Presenter.ViewHolder {
     84         float mSelectLevel;
     85         int mOriginalTextColor;
     86         float mUnselectAlpha;
     87         RowHeaderView mTitleView;
     88         TextView mDescriptionView;
     89 
     90         /**
     91          * Creating a new ViewHolder that supports title and description.
     92          * @param view Root of Views.
     93          */
     94         public ViewHolder(View view) {
     95             super(view);
     96             mTitleView = (RowHeaderView)view.findViewById(R.id.row_header);
     97             mDescriptionView = (TextView)view.findViewById(R.id.row_header_description);
     98             initColors();
     99         }
    100 
    101         /**
    102          * Uses a single {@link RowHeaderView} for creating a new ViewHolder.
    103          * @param view The single RowHeaderView.
    104          * @hide
    105          */
    106         @RestrictTo(LIBRARY_GROUP)
    107         public ViewHolder(RowHeaderView view) {
    108             super(view);
    109             mTitleView = view;
    110             initColors();
    111         }
    112 
    113         void initColors() {
    114             if (mTitleView != null) {
    115                 mOriginalTextColor = mTitleView.getCurrentTextColor();
    116             }
    117 
    118             mUnselectAlpha = view.getResources().getFraction(
    119                     R.fraction.lb_browse_header_unselect_alpha, 1, 1);
    120         }
    121 
    122         public final float getSelectLevel() {
    123             return mSelectLevel;
    124         }
    125     }
    126 
    127     @Override
    128     public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
    129         View root = LayoutInflater.from(parent.getContext())
    130                 .inflate(mLayoutResourceId, parent, false);
    131 
    132         ViewHolder viewHolder = new ViewHolder(root);
    133         if (mAnimateSelect) {
    134             setSelectLevel(viewHolder, 0);
    135         }
    136         return viewHolder;
    137     }
    138 
    139     @Override
    140     public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) {
    141         HeaderItem headerItem = item == null ? null : ((Row) item).getHeaderItem();
    142         RowHeaderPresenter.ViewHolder vh = (RowHeaderPresenter.ViewHolder)viewHolder;
    143         if (headerItem == null) {
    144             if (vh.mTitleView != null) {
    145                 vh.mTitleView.setText(null);
    146             }
    147             if (vh.mDescriptionView != null) {
    148                 vh.mDescriptionView.setText(null);
    149             }
    150 
    151             viewHolder.view.setContentDescription(null);
    152             if (mNullItemVisibilityGone) {
    153                 viewHolder.view.setVisibility(View.GONE);
    154             }
    155         } else {
    156             if (vh.mTitleView != null) {
    157                 vh.mTitleView.setText(headerItem.getName());
    158             }
    159             if (vh.mDescriptionView != null) {
    160                 if (TextUtils.isEmpty(headerItem.getDescription())) {
    161                     vh.mDescriptionView.setVisibility(View.GONE);
    162                 } else {
    163                     vh.mDescriptionView.setVisibility(View.VISIBLE);
    164                 }
    165                 vh.mDescriptionView.setText(headerItem.getDescription());
    166             }
    167             viewHolder.view.setContentDescription(headerItem.getContentDescription());
    168             viewHolder.view.setVisibility(View.VISIBLE);
    169         }
    170     }
    171 
    172     @Override
    173     public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {
    174         RowHeaderPresenter.ViewHolder vh = (ViewHolder)viewHolder;
    175         if (vh.mTitleView != null) {
    176             vh.mTitleView.setText(null);
    177         }
    178         if (vh.mDescriptionView != null) {
    179             vh.mDescriptionView.setText(null);
    180         }
    181 
    182         if (mAnimateSelect) {
    183             setSelectLevel((ViewHolder) viewHolder, 0);
    184         }
    185     }
    186 
    187     /**
    188      * Sets the select level.
    189      */
    190     public final void setSelectLevel(ViewHolder holder, float selectLevel) {
    191         holder.mSelectLevel = selectLevel;
    192         onSelectLevelChanged(holder);
    193     }
    194 
    195     /**
    196      * Called when the select level changes.  The default implementation sets the alpha on the view.
    197      */
    198     protected void onSelectLevelChanged(ViewHolder holder) {
    199         if (mAnimateSelect) {
    200             holder.view.setAlpha(holder.mUnselectAlpha + holder.mSelectLevel
    201                     * (1f - holder.mUnselectAlpha));
    202         }
    203     }
    204 
    205     /**
    206      * Returns the space (distance in pixels) below the baseline of the
    207      * text view, if one exists; otherwise, returns 0.
    208      */
    209     public int getSpaceUnderBaseline(ViewHolder holder) {
    210         int space = holder.view.getPaddingBottom();
    211         if (holder.view instanceof TextView) {
    212             space += (int) getFontDescent((TextView) holder.view, mFontMeasurePaint);
    213         }
    214         return space;
    215     }
    216 
    217     @SuppressWarnings("ReferenceEquality")
    218     protected static float getFontDescent(TextView textView, Paint fontMeasurePaint) {
    219         if (fontMeasurePaint.getTextSize() != textView.getTextSize()) {
    220             fontMeasurePaint.setTextSize(textView.getTextSize());
    221         }
    222         if (fontMeasurePaint.getTypeface() != textView.getTypeface()) {
    223             fontMeasurePaint.setTypeface(textView.getTypeface());
    224         }
    225         return fontMeasurePaint.descent();
    226     }
    227 }
    228