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