AbstractDetailsDescriptionPresenter.java revision 433391b15bd5ff711ada5894427e90f52186ea76
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 android.graphics.Paint; 17import android.graphics.Paint.FontMetricsInt; 18import android.support.v17.leanback.R; 19import android.text.TextUtils; 20import android.view.LayoutInflater; 21import android.view.View; 22import android.view.ViewGroup; 23import android.view.ViewTreeObserver; 24import android.widget.TextView; 25 26/** 27 * An abstract {@link Presenter} for rendering a detailed description of an 28 * item. Typically this Presenter will be used in a DetailsOveriewRowPresenter. 29 * 30 * <p>Subclasses will override {@link #onBindDescription} to implement the data 31 * binding for this Presenter. 32 */ 33public abstract class AbstractDetailsDescriptionPresenter extends Presenter { 34 35 public static class ViewHolder extends Presenter.ViewHolder { 36 private final TextView mTitle; 37 private final TextView mSubtitle; 38 private final TextView mBody; 39 private final int mTitleMargin; 40 private final int mUnderTitleBaselineMargin; 41 private final int mUnderSubtitleBaselineMargin; 42 private final int mTitleLineSpacing; 43 private final int mBodyLineSpacing; 44 private final int mBodyMaxLines; 45 private final int mBodyMinLines; 46 private final FontMetricsInt mTitleFontMetricsInt; 47 private final FontMetricsInt mSubtitleFontMetricsInt; 48 private final FontMetricsInt mBodyFontMetricsInt; 49 private ViewTreeObserver.OnPreDrawListener mPreDrawListener; 50 51 public ViewHolder(final View view) { 52 super(view); 53 mTitle = (TextView) view.findViewById(R.id.lb_details_description_title); 54 mSubtitle = (TextView) view.findViewById(R.id.lb_details_description_subtitle); 55 mBody = (TextView) view.findViewById(R.id.lb_details_description_body); 56 57 FontMetricsInt titleFontMetricsInt = getFontMetricsInt(mTitle); 58 final int titleAscent = view.getResources().getDimensionPixelSize( 59 R.dimen.lb_details_description_title_baseline); 60 // Ascent is negative 61 mTitleMargin = titleAscent + titleFontMetricsInt.ascent; 62 63 mUnderTitleBaselineMargin = view.getResources().getDimensionPixelSize( 64 R.dimen.lb_details_description_under_title_baseline_margin); 65 mUnderSubtitleBaselineMargin = view.getResources().getDimensionPixelSize( 66 R.dimen.lb_details_description_under_subtitle_baseline_margin); 67 68 mTitleLineSpacing = view.getResources().getDimensionPixelSize( 69 R.dimen.lb_details_description_title_line_spacing); 70 mBodyLineSpacing = view.getResources().getDimensionPixelSize( 71 R.dimen.lb_details_description_body_line_spacing); 72 73 mBodyMaxLines = view.getResources().getInteger( 74 R.integer.lb_details_description_body_max_lines); 75 mBodyMinLines = view.getResources().getInteger( 76 R.integer.lb_details_description_body_min_lines); 77 78 mTitleFontMetricsInt = getFontMetricsInt(mTitle); 79 mSubtitleFontMetricsInt = getFontMetricsInt(mSubtitle); 80 mBodyFontMetricsInt = getFontMetricsInt(mBody); 81 82 mTitle.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 83 @Override 84 public void onLayoutChange(View v, int left, int top, int right, int bottom, 85 int oldLeft, int oldTop, int oldRight, int oldBottom) { 86 addPreDrawListener(); 87 } 88 }); 89 } 90 91 void addPreDrawListener() { 92 if (mPreDrawListener != null) { 93 return; 94 } 95 mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { 96 @Override 97 public boolean onPreDraw() { 98 final int titleLines = mTitle.getLineCount(); 99 final int maxLines = titleLines > 1 ? mBodyMinLines : mBodyMaxLines; 100 if (mBody.getMaxLines() != maxLines) { 101 mBody.setMaxLines(maxLines); 102 return false; 103 } else { 104 removePreDrawListener(); 105 return true; 106 } 107 } 108 }; 109 view.getViewTreeObserver().addOnPreDrawListener(mPreDrawListener); 110 } 111 112 void removePreDrawListener() { 113 if (mPreDrawListener != null) { 114 view.getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener); 115 mPreDrawListener = null; 116 } 117 } 118 119 public TextView getTitle() { 120 return mTitle; 121 } 122 123 public TextView getSubtitle() { 124 return mSubtitle; 125 } 126 127 public TextView getBody() { 128 return mBody; 129 } 130 131 private FontMetricsInt getFontMetricsInt(TextView textView) { 132 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 133 paint.setTextSize(textView.getTextSize()); 134 paint.setTypeface(textView.getTypeface()); 135 return paint.getFontMetricsInt(); 136 } 137 } 138 139 @Override 140 public final ViewHolder onCreateViewHolder(ViewGroup parent) { 141 View v = LayoutInflater.from(parent.getContext()) 142 .inflate(R.layout.lb_details_description, parent, false); 143 return new ViewHolder(v); 144 } 145 146 @Override 147 public final void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { 148 ViewHolder vh = (ViewHolder) viewHolder; 149 onBindDescription(vh, item); 150 151 boolean hasTitle = true; 152 if (TextUtils.isEmpty(vh.mTitle.getText())) { 153 vh.mTitle.setVisibility(View.GONE); 154 hasTitle = false; 155 } else { 156 vh.mTitle.setVisibility(View.VISIBLE); 157 vh.mTitle.setLineSpacing(vh.mTitleLineSpacing - vh.mTitle.getLineHeight() + 158 vh.mTitle.getLineSpacingExtra(), vh.mTitle.getLineSpacingMultiplier()); 159 } 160 setTopMargin(vh.mTitle, vh.mTitleMargin); 161 162 boolean hasSubtitle = true; 163 if (TextUtils.isEmpty(vh.mSubtitle.getText())) { 164 vh.mSubtitle.setVisibility(View.GONE); 165 hasSubtitle = false; 166 } else { 167 vh.mSubtitle.setVisibility(View.VISIBLE); 168 if (hasTitle) { 169 setTopMargin(vh.mSubtitle, vh.mUnderTitleBaselineMargin + 170 vh.mSubtitleFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent); 171 } else { 172 setTopMargin(vh.mSubtitle, 0); 173 } 174 } 175 176 if (TextUtils.isEmpty(vh.mBody.getText())) { 177 vh.mBody.setVisibility(View.GONE); 178 } else { 179 vh.mBody.setVisibility(View.VISIBLE); 180 vh.mBody.setLineSpacing(vh.mBodyLineSpacing - vh.mBody.getLineHeight() + 181 vh.mBody.getLineSpacingExtra(), vh.mBody.getLineSpacingMultiplier()); 182 183 if (hasSubtitle) { 184 setTopMargin(vh.mBody, vh.mUnderSubtitleBaselineMargin + 185 vh.mBodyFontMetricsInt.ascent - vh.mSubtitleFontMetricsInt.descent); 186 } else if (hasTitle) { 187 setTopMargin(vh.mBody, vh.mUnderTitleBaselineMargin + 188 vh.mBodyFontMetricsInt.ascent - vh.mTitleFontMetricsInt.descent); 189 } else { 190 setTopMargin(vh.mBody, 0); 191 } 192 } 193 } 194 195 /** 196 * Binds the data from the item referenced in the DetailsOverviewRow to the 197 * ViewHolder. 198 * 199 * @param vh The ViewHolder for this details description view. 200 * @param item The item from the DetailsOverviewRow being presented. 201 */ 202 protected abstract void onBindDescription(ViewHolder vh, Object item); 203 204 @Override 205 public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) {} 206 207 @Override 208 public void onViewAttachedToWindow(Presenter.ViewHolder holder) { 209 // In case predraw listener was removed in detach, make sure 210 // we have the proper layout. 211 ViewHolder vh = (ViewHolder) holder; 212 vh.addPreDrawListener(); 213 super.onViewAttachedToWindow(holder); 214 } 215 216 @Override 217 public void onViewDetachedFromWindow(Presenter.ViewHolder holder) { 218 ViewHolder vh = (ViewHolder) holder; 219 vh.removePreDrawListener(); 220 super.onViewDetachedFromWindow(holder); 221 } 222 223 private void setTopMargin(TextView textView, int topMargin) { 224 ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) textView.getLayoutParams(); 225 lp.topMargin = topMargin; 226 textView.setLayoutParams(lp); 227 } 228} 229