New message list formatting per pixel perfects

* Need icon assets (attachment, meeting, star on/off, and
  checkbox on/off)

Change-Id: I6780fb354584ce5ea398b555a8485cf600617586
This commit is contained in:
Marc Blank 2010-12-03 23:12:48 -08:00
parent f946ff0019
commit 06a2f4a3b1
5 changed files with 117 additions and 63 deletions

View File

@ -23,7 +23,7 @@
<!-- XL activity dimensions -->
<!-- width of mailbox list -->
<dimen name="mailbox_list_width">312dip</dimen>
<dimen name="mailbox_list_width">304dip</dimen>
<!-- width of the message list, on the message list + message view mode. -->
<dimen name="message_list_width">466dip</dimen>
</resources>

View File

@ -17,14 +17,19 @@
<resources>
<dimen name="button_minWidth">100sp</dimen>
<dimen name="message_list_item_text_size">16dip</dimen>
<dimen name="message_list_item_checkbox_hit_width">60dip</dimen>
<dimen name="message_list_item_favorite_hit_width">50dip</dimen>
<dimen name="message_list_item_checkbox_hit_width">64dip</dimen>
<dimen name="message_list_item_favorite_hit_width">64dip</dimen>
<dimen name="message_list_item_favorite_padding_right">16dip</dimen>
<dimen name="message_list_item_date_icon_width_wide">144dip</dimen>
<dimen name="message_list_item_date_icon_width_narrow">112dip</dimen>
<dimen name="message_list_item_sender_padding_top_narrow">10dip</dimen>
<dimen name="message_list_item_sender_width">200dip</dimen>
<dimen name="message_list_item_padding_large">32dip</dimen>
<dimen name="message_list_item_padding_medium">6dip</dimen>
<dimen name="message_list_item_padding_small">4dip</dimen>
<dimen name="message_list_item_padding_very_small">2dip</dimen>
<dimen name="message_list_item_minimum_date_width">64dip</dimen>
<dimen name="message_list_item_height_wide">64dip</dimen>
<dimen name="message_list_item_height_narrow">72dip</dimen>
<dimen name="message_list_item_height_narrow">80dip</dimen>
<dimen name="message_list_item_minimum_width_wide_mode">720dip</dimen>
<dimen name="message_list_item_color_tip_width">35dip</dimen>
<dimen name="message_list_item_color_tip_height">8dip</dimen>

View File

@ -443,9 +443,9 @@ save attachment.</string>
<!-- The label of the previous button on the message view screen. -->
<string name="message_view_move_to_older">Older</string>
<!-- The message snippet, of the form subject + separator + start of text -->
<string name="message_list_snippet"><xliff:g id="subject" example="Re: Foo">
%1$s</xliff:g> - <xliff:g id="text" example="Hi, John. Blah...">%2$s</xliff:g></string>
<!-- A simple divider between subject and message snippet in the message list view
[CHAR LIMIT=4]-->
<string name="message_list_subject_snippet_divider">\u0020\u2014\u0020</string>
<!-- Title of screen when setting up new email account [CHAR LIMIT=45] -->
<string name="account_setup_basics_title">Account setup</string>

View File

@ -28,11 +28,15 @@ import android.graphics.Paint.Align;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Typeface;
import android.text.Layout.Alignment;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
import android.text.format.DateUtils;
import android.text.style.StyleSpan;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@ -88,12 +92,15 @@ public class MessageListItem extends View {
private static Bitmap sInviteIcon;
private static Bitmap sFavoriteIconOff;
private static Bitmap sFavoriteIconOn;
private static int sFavoriteIconLeft;
private static int sFavoriteIconWidth;
private static Bitmap sSelectedIconOn;
private static Bitmap sSelectedIconOff;
private static String sSubjectSnippetDivider;
public String mSender;
public CharSequence mText;
public String mSnippet;
public String mSubject;
public boolean mRead;
public long mTimestamp;
public boolean mHasAttachment = false;
@ -111,8 +118,13 @@ public class MessageListItem extends View {
private int mDateFaveWidth;
private static int sCheckboxHitWidth;
private static int sMinimumDateWidth;
private static int sDateIconWidthWide;
private static int sDateIconWidthNarrow;
private static int sFavoriteHitWidth;
private static int sFavoritePaddingRight;
private static int sSenderPaddingTopNarrow;
private static int sSenderWidth;
private static int sPaddingLarge;
private static int sPaddingVerySmall;
private static int sPaddingSmall;
private static int sPaddingMedium;
@ -133,13 +145,23 @@ public class MessageListItem extends View {
private void init(Context context) {
if (!sInit) {
Resources r = context.getResources();
sSubjectSnippetDivider = r.getString(R.string.message_list_subject_snippet_divider);
sCheckboxHitWidth =
r.getDimensionPixelSize(R.dimen.message_list_item_checkbox_hit_width);
sFavoriteHitWidth =
r.getDimensionPixelSize(R.dimen.message_list_item_favorite_hit_width);
sMinimumDateWidth =
r.getDimensionPixelSize(R.dimen.message_list_item_minimum_date_width);
sFavoritePaddingRight =
r.getDimensionPixelSize(R.dimen.message_list_item_favorite_padding_right);
sSenderPaddingTopNarrow =
r.getDimensionPixelSize(R.dimen.message_list_item_sender_padding_top_narrow);
sDateIconWidthWide =
r.getDimensionPixelSize(R.dimen.message_list_item_date_icon_width_wide);
sDateIconWidthNarrow =
r.getDimensionPixelSize(R.dimen.message_list_item_date_icon_width_narrow);
sSenderWidth =
r.getDimensionPixelSize(R.dimen.message_list_item_sender_width);
sPaddingLarge =
r.getDimensionPixelSize(R.dimen.message_list_item_padding_large);
sPaddingMedium =
r.getDimensionPixelSize(R.dimen.message_list_item_padding_medium);
sPaddingSmall =
@ -184,8 +206,7 @@ public class MessageListItem extends View {
sSelectedIconOn =
BitmapFactory.decodeResource(r, R.drawable.btn_check_on_normal_holo_light);
sFavoriteIconLeft =
sFavoriteHitWidth - ((sFavoriteHitWidth - sFavoriteIconOff.getWidth()) / 2);
sFavoriteIconWidth = sFavoriteIconOff.getWidth();
sInit = true;
}
}
@ -205,27 +226,38 @@ public class MessageListItem extends View {
}
private void calculateDrawingData() {
SpannableStringBuilder ssb = new SpannableStringBuilder();
boolean hasSubject = false;
if (!TextUtils.isEmpty(mSubject)) {
SpannableString ss = new SpannableString(mSubject);
ss.setSpan(new StyleSpan(mRead ? Typeface.NORMAL : Typeface.BOLD), 0, ss.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(ss);
hasSubject = true;
}
if (!TextUtils.isEmpty(mSnippet)) {
if (hasSubject) {
ssb.append(sSubjectSnippetDivider);
}
ssb.append(mSnippet);
}
mText = ssb;
if (mMode == MODE_WIDE) {
mDateFaveWidth = sFavoriteHitWidth + sMinimumDateWidth;
mDateFaveWidth = sFavoriteHitWidth + sDateIconWidthWide;
} else {
mDateFaveWidth = sMinimumDateWidth;
mDateFaveWidth = sDateIconWidthNarrow;
}
mSenderSnippetWidth = mViewWidth - mDateFaveWidth - sCheckboxHitWidth;
// In wide mode, we use 3/4 for snippet and 1/4 for sender
mSnippetWidth = mSenderSnippetWidth;
if (mMode == MODE_WIDE) {
mSnippetWidth = mSenderSnippetWidth * 3 / 4;
}
if (mHasAttachment) {
mSnippetWidth -= (sAttachmentIcon.getWidth() + sPaddingSmall);
}
if (mHasInvite) {
mSnippetWidth -= (sInviteIcon.getWidth() + sPaddingSmall);
mSnippetWidth = mSenderSnippetWidth - sSenderWidth - sPaddingLarge;
}
// First, we create a StaticLayout with our snippet to get the line breaks
StaticLayout layout = new StaticLayout(mSnippet, 0, mSnippet.length(), sDefaultPaint,
// Create a StaticLayout with our snippet to get the line breaks
StaticLayout layout = new StaticLayout(mText, 0, mText.length(), sDefaultPaint,
mSnippetWidth, Alignment.ALIGN_NORMAL, 1, 0, true);
// Get the number of lines needed to render the whole snippet
mSnippetLineCount = layout.getLineCount();
@ -234,26 +266,26 @@ public class MessageListItem extends View {
for (int i = 0; i < MAX_SUBJECT_SNIPPET_LINES; i++) {
int start = layout.getLineStart(i);
if (i == MAX_SUBJECT_SNIPPET_LINES - 1) {
int end = mText.length() - 1;
if (start > end) continue;
// For the final line, ellipsize the text to our width
mSnippetLines[i] = TextUtils.ellipsize(mSnippet.substring(start), sDefaultPaint,
mSnippetLines[i] = TextUtils.ellipsize(mText.subSequence(start, end), sDefaultPaint,
mSnippetWidth, TruncateAt.END);
} else {
// Just extract from start to end
mSnippetLines[i] = mSnippet.substring(start, layout.getLineEnd(i));
mSnippetLines[i] = mText.subSequence(start, layout.getLineEnd(i));
}
}
// Now, format the sender for its width
TextPaint senderPaint = mRead ? sDefaultPaint : sBoldPaint;
// In wide mode, we use 1/4 of the width, otherwise, the whole width
int senderWidth = (mMode == MODE_WIDE) ? mSenderSnippetWidth / 4 : mSenderSnippetWidth;
int senderWidth = (mMode == MODE_WIDE) ? sSenderWidth : mSenderSnippetWidth;
// And get the ellipsized string for the calculated width
mFormattedSender = TextUtils.ellipsize(mSender, senderPaint, senderWidth - sPaddingMedium,
TruncateAt.END);
mFormattedSender = TextUtils.ellipsize(mSender, senderPaint, senderWidth, TruncateAt.END);
// Get a nicely formatted date string (relative to today)
String date = DateUtils.getRelativeTimeSpanString(getContext(), mTimestamp).toString();
// And make it fit to our size
mFormattedDate = TextUtils.ellipsize(date, sDatePaint, sMinimumDateWidth, TruncateAt.END);
mFormattedDate = TextUtils.ellipsize(date, sDatePaint, sDateIconWidthWide, TruncateAt.END);
}
@Override
@ -317,13 +349,13 @@ public class MessageListItem extends View {
int senderY;
if (mMode == MODE_WIDE) {
// In wide mode, we'll use 1/4 for sender and 3/4 for snippet
snippetX += mSenderSnippetWidth / 4;
// Get the right starting point for the snippet
snippetX += sSenderWidth + sPaddingLarge;
// And center the sender and snippet
senderY = (mViewHeight - descent - ascent) / 2;
snippetY = ((mViewHeight - (2 * lineHeight)) / 2) - ascent;
} else {
senderY = 20; // TODO Remove magic number
senderY = -ascent + sSenderPaddingTopNarrow;
snippetY = senderY + lineHeight + sPaddingVerySmall;
}
@ -346,19 +378,49 @@ public class MessageListItem extends View {
mRead ? sDefaultPaint : sBoldPaint);
// Draw each of the snippet lines
int subjectEnd = (mSubject == null) ? 0 : mSubject.length();
int lineStart = 0;
TextPaint subjectPaint = mRead ? sDefaultPaint : sBoldPaint;
for (int i = 0; i < MAX_SUBJECT_SNIPPET_LINES; i++) {
CharSequence line = mSnippetLines[i];
int drawX = snippetX;
if (line != null) {
canvas.drawText(line, 0, line.length(), snippetX, snippetY, sDefaultPaint);
int defaultPaintStart = 0;
if (lineStart <= subjectEnd) {
int boldPaintEnd = subjectEnd - lineStart;
if (boldPaintEnd > line.length()) {
boldPaintEnd = line.length();
}
// From 0 to end, do in bold or default depending on the read flag
canvas.drawText(line, 0, boldPaintEnd, drawX, snippetY, subjectPaint);
defaultPaintStart = boldPaintEnd;
drawX += subjectPaint.measureText(line, 0, boldPaintEnd);
}
canvas.drawText(line, defaultPaintStart, line.length(), drawX, snippetY,
sDefaultPaint);
snippetY += lineHeight;
lineStart += line.length();
}
}
// Draw the attachment and invite icons, if necessary
int left = mSenderSnippetWidth + sCheckboxHitWidth;
int datePaddingRight;
if (mMode == MODE_WIDE) {
datePaddingRight = sFavoriteHitWidth;
} else {
datePaddingRight = sPaddingLarge;
}
int left = mViewWidth - datePaddingRight - (int)sDefaultPaint.measureText(mFormattedDate,
0, mFormattedDate.length()) - sPaddingMedium;
if (mHasAttachment) {
left -= sAttachmentIcon.getWidth() + sPaddingSmall;
int iconTop = (mViewHeight - sAttachmentIcon.getHeight()) / 2;
int iconTop;
if (mMode == MODE_WIDE) {
iconTop = (mViewHeight - sAttachmentIcon.getHeight()) / 2;
} else {
iconTop = senderY - sAttachmentIcon.getHeight();
}
canvas.drawBitmap(sAttachmentIcon, left, iconTop, sDefaultPaint);
}
if (mHasInvite) {
@ -368,17 +430,19 @@ public class MessageListItem extends View {
}
// Draw the date
int dateRight = mViewWidth - sPaddingMedium;
if (mMode == MODE_WIDE) {
dateRight -= sFavoriteHitWidth;
}
canvas.drawText(mFormattedDate, 0, mFormattedDate.length(), dateRight, senderY, sDatePaint);
canvas.drawText(mFormattedDate, 0, mFormattedDate.length(), mViewWidth - datePaddingRight,
senderY, sDatePaint);
// Draw the favorite icon
int faveLeft = mViewWidth - sFavoriteIconLeft;
int faveLeft = mViewWidth - sFavoriteIconWidth;
if (mMode == MODE_WIDE) {
faveLeft -= sFavoritePaddingRight;
} else {
faveLeft -= sPaddingLarge;
}
int faveTop = (mViewHeight - sFavoriteIconOff.getHeight()) / 2;
if (mMode == MODE_NARROW) {
faveTop += sPaddingMedium;
faveTop += sSenderPaddingTopNarrow;
}
canvas.drawBitmap(mIsFavorite ? sFavoriteIconOn : sFavoriteIconOff, faveLeft, faveTop,
sDefaultPaint);

View File

@ -17,7 +17,6 @@
package com.android.email.activity;
import com.android.email.Email;
import com.android.email.R;
import com.android.email.ResourceHelper;
import com.android.email.Utility;
import com.android.email.data.ThrottlingCursorLoader;
@ -29,7 +28,6 @@ import android.content.Context;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@ -146,23 +144,10 @@ import java.util.Set;
itemView.mTimestamp = cursor.getLong(COLUMN_DATE);
itemView.mSender = cursor.getString(COLUMN_DISPLAY_NAME);
itemView.mSnippet = cursor.getString(COLUMN_SNIPPET);
itemView.mSubject = cursor.getString(COLUMN_SUBJECT);
itemView.mSnippetLineCount = MessageListItem.NEEDS_LAYOUT;
itemView.mColorChipPaint =
mShowColorChips ? mResourceHelper.getAccountColorPaint(accountId) : null;
String text = cursor.getString(COLUMN_SUBJECT);
String snippet = cursor.getString(COLUMN_SNIPPET);
if (!TextUtils.isEmpty(snippet)) {
if (TextUtils.isEmpty(text)) {
text = snippet;
} else {
text = context.getString(R.string.message_list_snippet, text, snippet);
}
}
if (text == null) {
text = "";
}
itemView.mSnippet = text;
mShowColorChips ? mResourceHelper.getAccountColorPaint(accountId) : null;
}
@Override