Ellipsize texts in contacts list correctly
Measure correct width during onMeasure() so that each TextView
can decide if text should be ellipsized.
Also use View's padding mechanism instead of managing our own
padding values.
Bug: 5265348
Change-Id: I2696a6099ab176dd27117a802997d0cceb2ed153
diff --git a/res/layout/contact_tile_frequent_phone.xml b/res/layout/contact_tile_frequent_phone.xml
index d3f64e4..1cee5c6 100644
--- a/res/layout/contact_tile_frequent_phone.xml
+++ b/res/layout/contact_tile_frequent_phone.xml
@@ -67,7 +67,7 @@
android:id="@+id/contact_tile_phone_number"
android:layout_width="0dip"
android:layout_height="wrap_content"
- android:layout_weight="5"
+ android:layout_weight="?attr/list_item_data_width_weight"
android:textSize="14sp"
android:textColor="@color/dialtacts_secondary_text_color"
android:layout_marginLeft="8dip"
@@ -78,7 +78,7 @@
android:id="@+id/contact_tile_phone_type"
android:layout_width="0dip"
android:layout_height="wrap_content"
- android:layout_weight="3"
+ android:layout_weight="?attr/list_item_label_width_weight"
android:textSize="12sp"
android:ellipsize="end"
android:singleLine="true"
diff --git a/res/values-sw580dp/styles.xml b/res/values-sw580dp/styles.xml
index ee65159..3551378 100644
--- a/res/values-sw580dp/styles.xml
+++ b/res/values-sw580dp/styles.xml
@@ -41,8 +41,10 @@
<item name="list_item_header_text_size">14sp</item>
<item name="list_item_header_text_color">@color/people_app_theme_color</item>
<item name="list_item_header_height">26dip</item>
- <item name="list_item_header_underline_color">@color/people_app_theme_color</item>
<item name="list_item_header_underline_height">1dip</item>
+ <item name="list_item_header_underline_color">@color/people_app_theme_color</item>
+ <item name="list_item_data_width_weight">5</item>
+ <item name="list_item_label_width_weight">3</item>
<item name="list_item_contacts_count_text_color">@color/contact_count_text_color</item>
<item name="list_item_contacts_count_text_size">12sp</item>
<item name="contact_browser_list_padding_left">0dip</item>
@@ -72,8 +74,10 @@
<item name="list_item_header_text_size">14sp</item>
<item name="list_item_header_text_color">@color/people_app_theme_color</item>
<item name="list_item_header_height">24dip</item>
- <item name="list_item_header_underline_color">@color/people_app_theme_color</item>
<item name="list_item_header_underline_height">1dip</item>
+ <item name="list_item_header_underline_color">@color/people_app_theme_color</item>
+ <item name="list_item_data_width_weight">5</item>
+ <item name="list_item_label_width_weight">3</item>
<item name="list_item_contacts_count_text_color">@color/contact_count_text_color</item>
<item name="list_item_contacts_count_text_size">12sp</item>
<item name="contact_browser_list_padding_left">16dip</item>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index d79d373..a95de0f 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -100,6 +100,8 @@
<attr name="list_item_contacts_count_text_color" format="color" />
<attr name="list_item_text_indent" format="dimension" />
<attr name="list_item_contacts_count_text_size" format="dimension" />
+ <attr name="list_item_data_width_weight" format="integer" />
+ <attr name="list_item_label_width_weight" format="integer" />
</declare-styleable>
<declare-styleable name="CallLog">
diff --git a/res/values/styles.xml b/res/values/styles.xml
index dd62594..d324d66 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -44,6 +44,8 @@
<item name="list_item_header_height">24dip</item>
<item name="list_item_header_underline_height">1dip</item>
<item name="list_item_header_underline_color">@color/people_app_theme_color</item>
+ <item name="list_item_data_width_weight">5</item>
+ <item name="list_item_label_width_weight">3</item>
<item name="contact_browser_list_padding_left">16dip</item>
<item name="contact_browser_list_padding_right">0dip</item>
<item name="contact_browser_background">@android:color/transparent</item>
@@ -146,6 +148,8 @@
<item name="list_item_header_height">26dip</item>
<item name="list_item_header_underline_height">1dip</item>
<item name="list_item_header_underline_color">@color/people_app_theme_color</item>
+ <item name="list_item_data_width_weight">5</item>
+ <item name="list_item_label_width_weight">3</item>
<item name="list_item_contacts_count_text_color">@color/contact_count_text_color</item>
<item name="list_item_header_text_indent">8dip</item>
<item name="contact_browser_list_padding_left">16dip</item>
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
index 49957dc..18c4d29 100644
--- a/src/com/android/contacts/list/ContactListItemView.java
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -89,11 +89,14 @@
private final int mTextIndent;
private Drawable mActivatedBackgroundDrawable;
- // In the future we may need to merge these local padding to View's mPaddingXXX
- private final int mExtraPaddingTop;
- private final int mExtraPaddingBottom;
- private int mExtraPaddingLeft;
- private int mExtraPaddingRight;
+ /**
+ * Used with {@link #mLabelView}, specifying the width ratio between label and data.
+ */
+ private final int mLabelViewWidthWeight;
+ /**
+ * Used with {@link #mDataView}, specifying the width ratio between label and data.
+ */
+ private final int mDataViewWidthWeight;
// Will be used with adjustListItemSelectionBounds().
private int mSelectionBoundsMarginLeft;
@@ -230,14 +233,7 @@
R.styleable.ContactListItemView_list_item_divider);
mVerticalDividerMargin = a.getDimensionPixelOffset(
R.styleable.ContactListItemView_list_item_vertical_divider_margin, 0);
- mExtraPaddingTop = a.getDimensionPixelOffset(
- R.styleable.ContactListItemView_list_item_padding_top, 0);
- mExtraPaddingBottom = a.getDimensionPixelOffset(
- R.styleable.ContactListItemView_list_item_padding_bottom, 0);
- mExtraPaddingLeft = a.getDimensionPixelOffset(
- R.styleable.ContactListItemView_list_item_padding_left, 0);
- mExtraPaddingRight = a.getDimensionPixelOffset(
- R.styleable.ContactListItemView_list_item_padding_right, 0);
+
mGapBetweenImageAndText = a.getDimensionPixelOffset(
R.styleable.ContactListItemView_list_item_gap_between_image_and_text, 0);
mGapBetweenLabelAndData = a.getDimensionPixelOffset(
@@ -268,6 +264,20 @@
R.styleable.ContactListItemView_list_item_contacts_count_text_size, 12);
mContactsCountTextColor = a.getColor(
R.styleable.ContactListItemView_list_item_contacts_count_text_color, Color.BLACK);
+ mDataViewWidthWeight = a.getInteger(
+ R.styleable.ContactListItemView_list_item_data_width_weight, 5);
+ mLabelViewWidthWeight = a.getInteger(
+ R.styleable.ContactListItemView_list_item_label_width_weight, 3);
+
+ setPadding(
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_padding_left, 0),
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_padding_top, 0),
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_padding_right, 0),
+ a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_padding_bottom, 0));
mPrefixHighligher = new PrefixHighlighter(
a.getColor(R.styleable.ContactListItemView_list_item_prefix_highlight_color,
@@ -306,9 +316,13 @@
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// We will match parent's width and wrap content vertically, but make sure
// height is no less than listPreferredItemHeight.
- int width = resolveSize(0, widthMeasureSpec);
- int height = 0;
- int preferredHeight = mPreferredHeight;
+ final int specWidth = resolveSize(0, widthMeasureSpec);
+ final int preferredHeight;
+ if (mHorizontalDividerVisible) {
+ preferredHeight = mPreferredHeight + mHorizontalDividerHeight;
+ } else {
+ preferredHeight = mPreferredHeight;
+ }
mNameTextViewHeight = 0;
mPhoneticNameTextViewHeight = 0;
@@ -318,51 +332,76 @@
mSnippetTextViewHeight = 0;
mStatusTextViewHeight = 0;
- // TODO: measure(0, 0) is *wrong*. At least, we should use correct width for each TextView.
- //
- // Reason: TextView applies ellipsis effect in this phase, while measure(0, 0) have those
- // views prepare the effect based on "unlimited width", which makes ellipsis setting
- // meaningless. We should pass a widthMeasureSpec with appropriate width setting.
- // See issue 5439903.
-
ensurePhotoViewSize();
- // Go over all visible text views and add their heights to get the total height
+ // Width each TextView is able to use.
+ final int effectiveWidth;
+ // All the other Views will honor the photo, so available width for them may be shrunk.
+ if (mPhotoViewWidth > 0 || mKeepHorizontalPaddingForPhotoView) {
+ effectiveWidth = specWidth - getPaddingLeft() - getPaddingRight()
+ - (mPhotoViewWidth + mGapBetweenImageAndText);
+ } else {
+ effectiveWidth = specWidth - getPaddingLeft() - getPaddingRight();
+ }
+
+ // Go over all visible text views and measure actual width of each of them.
+ // Also calculate their heights to get the total height for this entire view.
+
if (isVisible(mNameTextView)) {
- mNameTextView.measure(0, 0);
+ mNameTextView.measure(
+ MeasureSpec.makeMeasureSpec(effectiveWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
mNameTextViewHeight = mNameTextView.getMeasuredHeight();
}
if (isVisible(mPhoneticNameTextView)) {
- mPhoneticNameTextView.measure(0, 0);
+ mPhoneticNameTextView.measure(
+ MeasureSpec.makeMeasureSpec(effectiveWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
mPhoneticNameTextViewHeight = mPhoneticNameTextView.getMeasuredHeight();
}
+ // If both data (phone number/email address) and label (type like "MOBILE") are quite long,
+ // we should ellipsize both using appropriate ratio.
+ final int dataWidth;
+ final int labelWidth;
if (isVisible(mDataView)) {
- mDataView.measure(0, 0);
+ if (isVisible(mLabelView)) {
+ final int totalWidth = effectiveWidth - mGapBetweenLabelAndData;
+ dataWidth = ((totalWidth * mDataViewWidthWeight)
+ / (mDataViewWidthWeight + mLabelViewWidthWeight));
+ labelWidth = ((totalWidth * mLabelViewWidthWeight) /
+ (mDataViewWidthWeight + mLabelViewWidthWeight));
+ } else {
+ dataWidth = effectiveWidth;
+ labelWidth = 0;
+ }
+ } else {
+ dataWidth = 0;
+ if (isVisible(mLabelView)) {
+ labelWidth = effectiveWidth;
+ } else {
+ labelWidth = 0;
+ }
+ }
+
+ if (isVisible(mDataView)) {
+ mDataView.measure(MeasureSpec.makeMeasureSpec(dataWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
mDataViewHeight = mDataView.getMeasuredHeight();
}
if (isVisible(mLabelView)) {
- if (mPhotoPosition == PhotoPosition.LEFT) {
- // Manually calculate the width now and see if ellipsis becomes effective or not.
- // See also issue 5438757 and 5439903.
- final int labelViewWidth = width - mExtraPaddingLeft - mExtraPaddingRight
- - (mPhotoViewWidth + mGapBetweenImageAndText)
- - mDataView.getMeasuredWidth()
- - mGapBetweenLabelAndData;
- final int labelViewWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
- labelViewWidth, MeasureSpec.AT_MOST);
- mLabelView.measure(labelViewWidthMeasureSpec, 0);
- } else {
- mLabelView.measure(0, 0);
- }
+ mLabelView.measure(MeasureSpec.makeMeasureSpec(labelWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
mLabelTextViewHeight = mLabelView.getMeasuredHeight();
}
mLabelAndDataViewMaxHeight = Math.max(mLabelTextViewHeight, mDataViewHeight);
if (isVisible(mSnippetView)) {
- mSnippetView.measure(0, 0);
+ mSnippetView.measure(
+ MeasureSpec.makeMeasureSpec(effectiveWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
mSnippetTextViewHeight = mSnippetView.getMeasuredHeight();
}
@@ -373,27 +412,36 @@
}
if (isVisible(mStatusView)) {
- mStatusView.measure(0, 0);
- mStatusTextViewHeight = Math.max(mStatusTextViewHeight,
- mStatusView.getMeasuredHeight());
+ // Presence and status are in a same row, so status will be affected by icon size.
+ final int statusWidth;
+ if (isVisible(mPresenceIcon)) {
+ statusWidth = (effectiveWidth - mPresenceIcon.getMeasuredWidth()
+ - mPresenceIconMargin);
+ } else {
+ statusWidth = effectiveWidth;
+ }
+ mStatusView.measure(MeasureSpec.makeMeasureSpec(statusWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mStatusTextViewHeight =
+ Math.max(mStatusTextViewHeight, mStatusView.getMeasuredHeight());
}
- // Calculate height including padding
- height += mNameTextViewHeight + mPhoneticNameTextViewHeight + mLabelAndDataViewMaxHeight +
- mSnippetTextViewHeight + mStatusTextViewHeight +
- mExtraPaddingTop + mExtraPaddingBottom;
+ // Calculate height including padding.
+ int height = (mNameTextViewHeight + mPhoneticNameTextViewHeight +
+ mLabelAndDataViewMaxHeight +
+ mSnippetTextViewHeight + mStatusTextViewHeight);
if (isVisible(mCallButton)) {
- mCallButton.measure(0, 0);
+ mCallButton.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
}
// Make sure the height is at least as high as the photo
- height = Math.max(height, mPhotoViewHeight + mExtraPaddingBottom + mExtraPaddingTop);
+ height = Math.max(height, mPhotoViewHeight + getPaddingBottom() + getPaddingTop());
// Add horizontal divider height
if (mHorizontalDividerVisible) {
height += mHorizontalDividerHeight;
- preferredHeight += mHorizontalDividerHeight;
}
// Make sure height is at least the preferred height
@@ -402,11 +450,11 @@
// Add the height of the header if visible
if (mHeaderVisible) {
mHeaderTextView.measure(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(specWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mHeaderBackgroundHeight, MeasureSpec.EXACTLY));
if (mCountView != null) {
mCountView.measure(
- MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(specWidth, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(mHeaderBackgroundHeight, MeasureSpec.EXACTLY));
}
mHeaderBackgroundHeight = Math.max(mHeaderBackgroundHeight,
@@ -414,7 +462,7 @@
height += (mHeaderBackgroundHeight + mHeaderUnderlineHeight);
}
- setMeasuredDimension(width, height);
+ setMeasuredDimension(specWidth, height);
}
@Override
@@ -425,8 +473,8 @@
// Determine the vertical bounds by laying out the header first.
int topBound = 0;
int bottomBound = height;
- int leftBound = mExtraPaddingLeft;
- int rightBound = width - mExtraPaddingRight;
+ int leftBound = getPaddingLeft();
+ int rightBound = width - getPaddingRight();
// Put the header in the top of the contact view (Text + underline view)
if (mHeaderVisible) {
@@ -435,7 +483,7 @@
rightBound,
mHeaderBackgroundHeight);
if (mCountView != null) {
- mCountView.layout(width - mExtraPaddingRight - mCountView.getMeasuredWidth(),
+ mCountView.layout(rightBound - mCountView.getMeasuredWidth(),
0,
rightBound,
mHeaderBackgroundHeight);
@@ -463,10 +511,6 @@
mActivatedBackgroundDrawable.setBounds(mBoundsWithoutHeader);
}
- // Set the top/bottom padding
- topBound += mExtraPaddingTop;
- bottomBound -= mExtraPaddingBottom;
-
final View photoView = mQuickContact != null ? mQuickContact : mPhotoView;
if (mPhotoPosition == PhotoPosition.LEFT) {
// Photo is the left most view. All the other Views should on the right of the photo.
@@ -947,7 +991,6 @@
mLabelView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
if (mPhotoPosition == PhotoPosition.LEFT) {
mLabelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mCountViewTextSize);
- mLabelView.setEllipsize(TruncateAt.MIDDLE);
mLabelView.setAllCaps(true);
} else {
mLabelView.setTypeface(mLabelView.getTypeface(), Typeface.BOLD);
@@ -1103,7 +1146,9 @@
}
private TruncateAt getTextEllipsis() {
- return mActivatedStateSupported ? TruncateAt.START : TruncateAt.MARQUEE;
+ // Note: If we want to choose MARQUEE here, we may need to manually trigger TextView's
+ // startStopMarquee(), which is unfortunately *private*. See also issue 5465510.
+ return TruncateAt.MIDDLE;
}
public void showDisplayName(Cursor cursor, int nameColumnIndex, int alternativeNameColumnIndex,
@@ -1262,29 +1307,6 @@
}
/**
- * Sets custom padding inside this object. Do not use this method without any strong reason.
- *
- * Detail: we cannot simply override {@link #setPadding(int, int, int, int)}. {@link View}
- * does *not* know this view's local padding but has completely different ones.
- * See View#mPaddingLeft and View#mPaddingRight. View also has View#mUserPaddingLeft, and
- * View#mUserPaddingRight in addition to View#mPaddingLeft and View#mPaddingRight, to handle
- * {@link View#setPadding(int, int, int, int)} correctly. If setPadding() is overridden to
- * reset our {@link #mExtraPaddingLeft} and {@link #mExtraPaddingRight} carelessly, the whole
- * View layout gets confused.
- *
- * To simplify our implementation, this method just modify the local two padding without
- * confusing its parent.
- *
- * If we want to fix this multiple padding issue correctly, we should merge local padding
- * in this class into View's ones. Also we should remove "list_item_padding_left" and
- * "list_item_padding_right" attributes, using "android:paddingLeft" and "android:paddingRight".
- */
- public void setExtraPadding(int left, int right) {
- mExtraPaddingLeft = left;
- mExtraPaddingRight = right;
- }
-
- /**
* Specifies left and right margin for selection bounds. See also
* {@link #adjustListItemSelectionBounds(Rect)}.
*/
diff --git a/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java b/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java
index 25a158b..f817b4c 100644
--- a/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java
+++ b/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java
@@ -163,9 +163,8 @@
final int localPosition = position - contactTileAdapterCount - 1;
final ContactListItemView itemView = (ContactListItemView)
mContactEntryListAdapter.getView(localPosition, convertView, null);
- // We cannot simply use setPadding() because of ContactListItemView's restriction.
- // See comments for setExtraPaddingPadding().
- itemView.setExtraPadding(mItemPaddingLeft, mItemPaddingRight);
+ itemView.setPadding(mItemPaddingLeft, itemView.getPaddingTop(),
+ mItemPaddingRight, itemView.getPaddingBottom());
itemView.setSelectionBoundsHorizontalMargin(mItemPaddingLeft, mItemPaddingRight);
return itemView;
}