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 */ RowHeaderPresenter()45 public RowHeaderPresenter() { 46 this(R.layout.lb_row_header); 47 } 48 49 /** 50 * @hide 51 */ 52 @RestrictTo(LIBRARY_GROUP) RowHeaderPresenter(int layoutResourceId)53 public RowHeaderPresenter(int layoutResourceId) { 54 this(layoutResourceId, true); 55 } 56 57 /** 58 * @hide 59 */ 60 @RestrictTo(LIBRARY_GROUP) RowHeaderPresenter(int layoutResourceId, boolean animateSelect)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 */ setNullItemVisibilityGone(boolean nullItemVisibilityGone)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 */ isNullItemVisibilityGone()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 */ ViewHolder(View view)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) ViewHolder(RowHeaderView view)107 public ViewHolder(RowHeaderView view) { 108 super(view); 109 mTitleView = view; 110 initColors(); 111 } 112 initColors()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 getSelectLevel()122 public final float getSelectLevel() { 123 return mSelectLevel; 124 } 125 } 126 127 @Override onCreateViewHolder(ViewGroup parent)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 onBindViewHolder(Presenter.ViewHolder viewHolder, Object item)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 onUnbindViewHolder(Presenter.ViewHolder viewHolder)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 */ setSelectLevel(ViewHolder holder, float selectLevel)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 */ onSelectLevelChanged(ViewHolder holder)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 */ getSpaceUnderBaseline(ViewHolder holder)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") getFontDescent(TextView textView, Paint fontMeasurePaint)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