Index: chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java |
index 3dda3a85710c18eadac7ff6c75a289836eac3e18..fe8dda549e2f102fee52e35d7d8bdebd2d58b1ca 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarLayout.java |
@@ -5,8 +5,13 @@ |
package org.chromium.chrome.browser.infobar; |
import android.content.Context; |
+import android.content.res.Resources; |
+import android.content.res.TypedArray; |
+import android.graphics.Color; |
+import android.graphics.drawable.Drawable; |
import android.text.TextUtils; |
import android.text.method.LinkMovementMethod; |
+import android.view.Gravity; |
import android.view.LayoutInflater; |
import android.view.View; |
import android.view.ViewGroup; |
@@ -15,732 +20,525 @@ import android.widget.ImageButton; |
import android.widget.ImageView; |
import android.widget.TextView; |
+import org.chromium.base.ApiCompatibilityUtils; |
import org.chromium.chrome.R; |
-import org.chromium.ui.base.LocalizationUtils; |
- |
-import java.util.ArrayList; |
/** |
- * Layout that can be used to arrange an InfoBar's View. |
- * All InfoBars consist of at least: |
- * - An icon representing the InfoBar's purpose on the left side. |
+ * Layout that arranges an InfoBar's views. An InfoBarLayout consists of: |
* - A message describing the action that the user can take. |
* - A close button on the right side. |
+ * - (optional) An icon representing the infobar's purpose on the left side. |
+ * - (optional) Additional "custom" views (e.g. a checkbox and text, or a pair of spinners) |
+ * - (optional) One or two buttons with text at the bottom. |
* |
- * Views should never be added with anything but a call to addGroup() to ensure that groups are not |
- * broken apart. |
- * |
- * Widths and heights defined in the LayoutParams will be overwritten due to the nature of the |
- * layout algorithm. However, setting a minimum width in another way, like TextView.getMinWidth(), |
- * should still be obeyed. |
+ * When adding custom views, widths and heights defined in the LayoutParams will be ignored. |
+ * However, setting a minimum width in another way, like TextView.getMinWidth(), should still be |
+ * obeyed. |
* |
* Logic for what happens when things are clicked should be implemented by the InfoBarView. |
*/ |
public class InfoBarLayout extends ViewGroup implements View.OnClickListener { |
- private static final String TAG = "InfoBarLayout"; |
/** |
* Parameters used for laying out children. |
*/ |
- public static class LayoutParams extends ViewGroup.LayoutParams { |
- /** Alignment parameters that determine where in the main row an item will float. */ |
- public static final int ALIGN_START = 0; |
- public static final int ALIGN_END = 1; |
+ private static class LayoutParams extends ViewGroup.LayoutParams { |
- /** Whether the View is meant for the main row. */ |
- public boolean isInMainRow; |
+ public int startMargin; |
+ public int endMargin; |
+ public int topMargin; |
+ public int bottomMargin; |
- /** Views grouped together are laid out together immediately adjacent to each other. */ |
- public boolean isGroupedWithNextView; |
+ // Where this view will be laid out. These values are assigned in onMeasure() and used in |
+ // onLayout(). |
+ public int start; |
+ public int top; |
- /** When on the main row, indicates whether the control floats on the left or the right. */ |
- public int align; |
+ LayoutParams(int startMargin, int topMargin, int endMargin, int bottomMargin) { |
+ super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); |
+ this.startMargin = startMargin; |
+ this.topMargin = topMargin; |
+ this.endMargin = endMargin; |
+ this.bottomMargin = bottomMargin; |
+ } |
+ } |
- /** If the control is a button, ID of the resource that was last used as its background. */ |
- public int background; |
+ private static class Group { |
+ public View[] views; |
+ |
+ /** |
+ * The gravity of each view in Group. Must be either Gravity.START, Gravity.END, or |
+ * Gravity.FILL_HORIZONTAL. |
+ */ |
+ public int gravity = Gravity.START; |
+ |
+ /** Whether the views are vertically stacked. */ |
+ public boolean isStacked; |
+ |
+ void setHorizontalMode(int horizontalSpacing, int startMargin, int endMargin) { |
+ isStacked = false; |
+ for (int i = 0; i < views.length; i++) { |
+ LayoutParams lp = (LayoutParams) views[i].getLayoutParams(); |
+ lp.startMargin = i == 0 ? startMargin : horizontalSpacing; |
+ lp.topMargin = 0; |
+ lp.endMargin = i == views.length - 1 ? endMargin : 0; |
+ lp.bottomMargin = 0; |
+ } |
- public LayoutParams() { |
- super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); |
- align = ALIGN_END; |
- isInMainRow = true; |
} |
- public LayoutParams(LayoutParams other) { |
- super(other); |
- isGroupedWithNextView = other.isGroupedWithNextView; |
- align = other.align; |
- isInMainRow = other.isInMainRow; |
+ void setVerticalMode(int verticalSpacing, int bottomMargin) { |
+ isStacked = true; |
+ for (int i = 0; i < views.length; i++) { |
+ LayoutParams lp = (LayoutParams) views[i].getLayoutParams(); |
+ lp.startMargin = 0; |
+ lp.topMargin = i == 0 ? 0 : verticalSpacing; |
+ lp.endMargin = 0; |
+ lp.bottomMargin = i == views.length - 1 ? bottomMargin : 0; |
+ } |
} |
} |
- private static class GroupInfo { |
- public int numViews; |
- public int width; |
- public int greatestMemberWidth; |
- public int endIndex; |
- public boolean hasButton; |
- }; |
- |
- private final int mDimensionMinSize; |
- private final int mDimensionMargin; |
- private final int mDimensionIconSize; |
- private final boolean mLayoutRTL; |
- private final InfoBarView mInfoBarView; |
+ private static final int ROW_MAIN = 1; |
+ private static final int ROW_OTHER = 2; |
- private final ImageView mIconView; |
+ private final int mMargin; |
+ private final int mIconSize; |
+ private final int mMinWidth; |
+ private final int mAccentColor; |
+ |
+ private final InfoBarView mInfoBarView; |
private final TextView mMessageView; |
private final ImageButton mCloseButton; |
+ private ImageView mIconView; |
- /** Background resource IDs to use for the buttons. */ |
- private final int mBackgroundFloating; |
- private final int mBackgroundFullLeft; |
- private final int mBackgroundFullRight; |
+ private Group mMainGroup; |
+ private Group mCustomGroup; |
+ private Group mButtonGroup; |
/** |
- * Indices of child Views that start new layout rows. |
- * The last entry is the number of child Views, allowing calculation of the size of each row by |
- * taking the difference between subsequent indices. |
+ * These values are used during onMeasure() to track where the next view will be placed. |
+ * |
+ * mWidth is the infobar width. |
+ * [mStart, mEnd) is the range of unoccupied space on the current row. |
+ * mTop and mBottom are the top and bottom of the current row. |
+ * |
+ * These values, along with a view's gravity, are used to position the next view. |
+ * These values are updated after placing a view and after starting a new row. |
*/ |
- private final ArrayList<Integer> mIndicesOfRows; |
+ private int mWidth; |
+ private int mStart; |
+ private int mEnd; |
+ private int mTop; |
+ private int mBottom; |
/** |
- * Constructs the layout for the specified InfoBar. |
+ * Constructs a layout for the specified InfoBar. After calling this, be sure to set the |
+ * message, the buttons, and/or the custom content using setMessage(), setButtons(), and |
+ * setCustomContent(). |
+ * |
* @param context The context used to render. |
* @param infoBarView InfoBarView that listens to events. |
* @param iconResourceId ID of the icon to use for the InfoBar. |
+ * @param message The message to show in the infobar. |
*/ |
- public InfoBarLayout(Context context, InfoBarView infoBarView, int iconResourceId) { |
+ public InfoBarLayout(Context context, InfoBarView infoBarView, int iconResourceId, |
+ CharSequence message) { |
super(context); |
- mIndicesOfRows = new ArrayList<Integer>(); |
- mLayoutRTL = LocalizationUtils.isLayoutRtl(); |
mInfoBarView = infoBarView; |
- // Determine what backgrounds we'll be needing for the buttons. |
- mBackgroundFloating = R.drawable.infobar_button_normal_floating; |
- mBackgroundFullLeft = R.drawable.infobar_button_normal_full_left; |
- mBackgroundFullRight = R.drawable.infobar_button_normal_full_right; |
- |
// Grab the dimensions. |
- mDimensionMinSize = |
- context.getResources().getDimensionPixelSize(R.dimen.infobar_min_size); |
- mDimensionMargin = |
- context.getResources().getDimensionPixelSize(R.dimen.infobar_margin); |
- mDimensionIconSize = |
- context.getResources().getDimensionPixelSize(R.dimen.infobar_icon_size); |
- |
- // Create the main controls. |
- mCloseButton = new ImageButton(context); |
- mIconView = new ImageView(context); |
- mMessageView = (TextView) LayoutInflater.from(context).inflate(R.layout.infobar_text, null); |
- addGroup(mCloseButton, mIconView, mMessageView); |
+ Resources res = getResources(); |
+ mMargin = res.getDimensionPixelOffset(R.dimen.infobar_margin); |
+ mIconSize = res.getDimensionPixelSize(R.dimen.infobar_icon_size); |
+ mMinWidth = res.getDimensionPixelSize(R.dimen.infobar_min_width); |
+ mAccentColor = res.getColor(R.color.infobar_accent_blue); |
- // Set up the close button. |
+ // Set up the close button. Apply padding so it has a big touch target. |
+ mCloseButton = new ImageButton(context); |
mCloseButton.setId(R.id.infobar_close_button); |
mCloseButton.setImageResource(R.drawable.infobar_close_button); |
- mCloseButton.setBackgroundResource(R.drawable.infobar_close_bg); |
+ TypedArray a = getContext().obtainStyledAttributes( |
+ new int [] {android.R.attr.selectableItemBackground}); |
+ Drawable closeButtonBackground = a.getDrawable(0); |
+ a.recycle(); |
+ ApiCompatibilityUtils.setBackgroundForView(mCloseButton, closeButtonBackground); |
+ mCloseButton.setPadding(mMargin, mMargin, mMargin, mMargin); |
mCloseButton.setOnClickListener(this); |
- |
- mCloseButton.setContentDescription(getResources().getString(R.string.infobar_close)); |
+ mCloseButton.setContentDescription(res.getString(R.string.infobar_close)); |
+ mCloseButton.setLayoutParams(new LayoutParams(0, -mMargin, -mMargin, -mMargin)); |
+ addView(mCloseButton); |
// Set up the icon. |
- mIconView.setFocusable(false); |
if (iconResourceId != 0) { |
+ mIconView = new ImageView(context); |
mIconView.setImageResource(iconResourceId); |
- } else { |
- mIconView.setVisibility(View.INVISIBLE); |
+ mIconView.setFocusable(false); |
+ mIconView.setLayoutParams(new LayoutParams(0, 0, mMargin / 2, 0)); |
+ mIconView.getLayoutParams().width = mIconSize; |
+ mIconView.getLayoutParams().height = mIconSize; |
} |
- // Set up the TextView. |
+ // Set up the message view. |
+ mMessageView = (TextView) LayoutInflater.from(context).inflate(R.layout.infobar_text, null); |
+ mMessageView.setText(message, TextView.BufferType.SPANNABLE); |
mMessageView.setMovementMethod(LinkMovementMethod.getInstance()); |
- mMessageView.setText(infoBarView.getMessageText(context), TextView.BufferType.SPANNABLE); |
- |
- // Only the close button floats to the right; the icon and the message both float left. |
- ((LayoutParams) mIconView.getLayoutParams()).align = LayoutParams.ALIGN_START; |
- ((LayoutParams) mMessageView.getLayoutParams()).align = LayoutParams.ALIGN_START; |
- |
- // Vertically center the icon and close buttons of an unstretched InfoBar. If the InfoBar |
- // is stretched, they both stay in place. |
- mIconView.getLayoutParams().width = mDimensionIconSize; |
- mIconView.getLayoutParams().height = mDimensionIconSize; |
- |
- // We apply padding to the close button so that it has a big touch target. |
- int closeButtonHeight = mCloseButton.getDrawable().getIntrinsicHeight(); |
- int closePadding = (mDimensionMinSize - closeButtonHeight) / 2; |
- if (closePadding >= 0) { |
- mCloseButton.setPadding(closePadding, closePadding, closePadding, closePadding); |
+ mMessageView.setLinkTextColor(mAccentColor); |
+ mMessageView.setLayoutParams(new LayoutParams(0, mMargin / 4, 0, 0)); |
+ |
+ if (mIconView != null) { |
+ mMainGroup = addGroup(mIconView, mMessageView); |
} else { |
- assert closePadding >= 0 : "Assets are too large for this layout."; |
+ mMainGroup = addGroup(mMessageView); |
} |
- |
- // Add all of the other InfoBar specific controls. |
- infoBarView.createContent(this); |
- } |
- |
- @Override |
- protected LayoutParams generateDefaultLayoutParams() { |
- return new LayoutParams(); |
} |
/** |
- * Add a view to the Layout. |
- * This function must never be called with an index that isn't -1 to ensure that groups aren't |
- * broken apart. |
+ * Sets the message to show on the infobar. |
*/ |
- @Override |
- public void addView(View child, int index, ViewGroup.LayoutParams params) { |
- if (index == -1) { |
- super.addView(child, index, params); |
- } else { |
- assert false : "Adding children at random places can break group structure."; |
- super.addView(child, -1, params); |
- } |
+ public void setMessage(CharSequence message) { |
+ mMessageView.setText(message, TextView.BufferType.SPANNABLE); |
} |
/** |
- * Add a group of Views that are measured and laid out together. |
+ * Sets the custom content of the infobar. These views will be displayed in addition to the |
+ * standard infobar controls (icon, text, buttons). Depending on the available space, view1 and |
+ * view2 will be laid out: |
+ * - Side by side on the main row, |
+ * - Side by side on a separate row, each taking up half the width of the infobar, |
+ * - Stacked above each other on two separate rows, taking up the full width of the infobar. |
*/ |
- public void addGroup(View... group) { |
- for (int i = 0; i < group.length; i++) { |
- final View member = group[i]; |
- addView(member); |
- |
- LayoutParams params = (LayoutParams) member.getLayoutParams(); |
- params.isGroupedWithNextView = (i != group.length - 1); |
- } |
+ public void setCustomContent(View view1, View view2) { |
+ mCustomGroup = addGroup(view1, view2); |
} |
/** |
- * Add up to two buttons to the layout. |
- * |
- * Buttons with null text are hidden from view. The secondary button may only exist if the |
- * primary button does. |
- * |
- * @param primaryText Text for the primary button. |
- * @param secondaryText Text for the secondary button. |
+ * Sets the custom content of the infobar to a single view. This view will be displayed in |
+ * addition to the standard infobar controls. Depending on the available space, the view will be |
+ * displayed: |
+ * - On the main row, start-aligned or end-aligned depending on whether there are also |
+ * buttons on the main row, OR |
+ * - On a separate row, start-aligned |
*/ |
- public void addButtons(String primaryText, String secondaryText) { |
- Button primaryButton = null; |
- Button secondaryButton = null; |
- |
- if (!TextUtils.isEmpty(secondaryText)) { |
- secondaryButton = (Button) LayoutInflater.from(getContext()).inflate( |
- R.layout.infobar_button, null); |
- secondaryButton.setId(R.id.button_secondary); |
- secondaryButton.setOnClickListener(this); |
- secondaryButton.setText(secondaryText); |
- } |
- |
- if (!TextUtils.isEmpty(primaryText)) { |
- primaryButton = (Button) LayoutInflater.from(getContext()).inflate( |
- R.layout.infobar_button, null); |
- primaryButton.setId(R.id.button_primary); |
- primaryButton.setOnClickListener(this); |
- primaryButton.setText(primaryText); |
- } |
- |
- // Group the buttons together so that they are laid out next to each other. |
- if (primaryButton == null && secondaryButton != null) { |
- assert false : "When using only one button, make it the primary button."; |
- } else if (primaryButton != null && secondaryButton != null) { |
- addGroup(secondaryButton, primaryButton); |
- } else if (primaryButton != null) { |
- addGroup(primaryButton); |
- } |
+ public void setCustomContent(View view) { |
+ mCustomGroup = addGroup(view); |
} |
- @Override |
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) { |
- final int rowWidth = right - left; |
- int rowTop = layoutMainRow(rowWidth); |
- for (int row = 1; row < mIndicesOfRows.size() - 1; row++) { |
- rowTop = layoutRow(row, rowTop, rowWidth); |
- } |
+ /** |
+ * Calls setButtons(primaryText, secondaryText, null). |
+ */ |
+ public void setButtons(String primaryText, String secondaryText) { |
+ setButtons(primaryText, secondaryText, null); |
} |
/** |
- * Lays out the controls in the main row. |
- * |
- * This method is complicated mainly because of the arbitrariness for when a control can |
- * float either left or right, and whether we're doing an RTL layout. |
+ * Adds one, two, or three buttons to the layout. |
* |
- * Layout proceeds in three phases: |
- * - Laying out of the icon and close button are done separately from the rest of the controls |
- * because they are locked into their respective corners. These two controls bound the rest |
- * of the controls in the main row. |
- * |
- * - Items floating to the left are then laid out, traversing the children array in a forwards |
- * manner. This includes the InfoBar message. |
- * |
- * - A final pass lays out items aligned to the end of the bar, traversing the children array |
- * backwards so that the correct ordering of the children is preserved. Going forwards would |
- * cause buttons to flip (e.g.). |
- * |
- * @param width Maximum width of the row. |
- * @return How tall the main row is. |
+ * @param primaryText Text for the primary button. |
+ * @param secondaryText Text for the secondary button, or null if there isn't a second button. |
+ * @param tertiaryText Text for the tertiary button, or null if there isn't a third button. |
*/ |
- private int layoutMainRow(int width) { |
- final int rowStart = mIndicesOfRows.get(0); |
- final int rowEnd = mIndicesOfRows.get(1); |
- final int rowHeight = computeMainRowHeight(rowStart, rowEnd); |
- |
- // Lay out the icon and the close button. |
- int closeLeft; |
- int iconPadding = (mDimensionMinSize - mDimensionIconSize) / 2; |
- int iconLeft = iconPadding; |
- if (mLayoutRTL) { |
- iconLeft += width - mDimensionMinSize; |
- closeLeft = 0; |
- } else { |
- closeLeft = width - mCloseButton.getMeasuredWidth(); |
+ public void setButtons(String primaryText, String secondaryText, String tertiaryText) { |
+ if (TextUtils.isEmpty(primaryText)) return; |
+ |
+ LayoutInflater inflater = LayoutInflater.from(getContext()); |
+ Button primaryButton = (Button) inflater.inflate(R.layout.infobar_button, null); |
+ primaryButton.setId(R.id.button_primary); |
+ primaryButton.setOnClickListener(this); |
+ primaryButton.setText(primaryText); |
+ primaryButton.setBackgroundResource(R.drawable.btn_infobar_blue); |
+ primaryButton.setTextColor(Color.WHITE); |
+ |
+ if (TextUtils.isEmpty(secondaryText)) { |
+ mButtonGroup = addGroup(primaryButton); |
+ return; |
} |
- mIconView.layout(iconLeft, iconPadding, iconLeft + mDimensionIconSize, |
- iconPadding + mDimensionIconSize); |
- mCloseButton.layout(closeLeft, 0, closeLeft + mDimensionMinSize, mDimensionMinSize); |
- |
- // Go from left to right to catch all items aligned with the start of the InfoBar. |
- int rowLeft = mDimensionMinSize; |
- int rowRight = width - mDimensionMinSize; |
- for (int i = rowStart; i < rowEnd; i++) { |
- final View child = getChildAt(i); |
- LayoutParams params = (LayoutParams) child.getLayoutParams(); |
- if (params.align != LayoutParams.ALIGN_START || child.getVisibility() == View.GONE |
- || child == mCloseButton || child == mIconView) { |
- continue; |
- } |
- // Everything is vertically centered. |
- int childTop = (rowHeight - child.getMeasuredHeight()) / 2; |
- int childLeft; |
- |
- if (mLayoutRTL) { |
- if (!isMainControl(child)) rowRight -= mDimensionMargin; |
- childLeft = rowRight - child.getMeasuredWidth(); |
- rowRight -= child.getMeasuredWidth(); |
- } else { |
- if (!isMainControl(child)) rowLeft += mDimensionMargin; |
- childLeft = rowLeft; |
- rowLeft += child.getMeasuredWidth(); |
- } |
+ Button secondaryButton = (Button) inflater.inflate(R.layout.infobar_button, null); |
+ secondaryButton.setId(R.id.button_secondary); |
+ secondaryButton.setOnClickListener(this); |
+ secondaryButton.setText(secondaryText); |
+ secondaryButton.setTextColor(mAccentColor); |
- child.layout(childLeft, childTop, childLeft + child.getMeasuredWidth(), |
- childTop + child.getMeasuredHeight()); |
+ if (TextUtils.isEmpty(tertiaryText)) { |
+ mButtonGroup = addGroup(secondaryButton, primaryButton); |
+ return; |
} |
- // Go from right to left to catch all items aligned with the end of the InfoBar. |
- for (int i = rowEnd - 1; i >= rowStart; i--) { |
- final View child = getChildAt(i); |
- LayoutParams params = (LayoutParams) child.getLayoutParams(); |
- if (params.align != LayoutParams.ALIGN_END || child.getVisibility() == View.GONE |
- || child == mCloseButton || child == mIconView) { |
- continue; |
- } |
- |
- // Everything is vertically centered. |
- int childTop = (rowHeight - child.getMeasuredHeight()) / 2; |
- int childLeft; |
- |
- if (!mLayoutRTL) { |
- childLeft = rowRight - child.getMeasuredWidth(); |
- rowRight -= child.getMeasuredWidth(); |
- if (!isMainControl(child)) rowRight -= mDimensionMargin; |
- } else { |
- childLeft = rowLeft; |
- rowLeft += child.getMeasuredWidth(); |
- if (!isMainControl(child)) rowLeft += mDimensionMargin; |
- } |
- |
- child.layout(childLeft, childTop, childLeft + child.getMeasuredWidth(), |
- childTop + child.getMeasuredHeight()); |
- } |
+ Button tertiaryButton = (Button) inflater.inflate(R.layout.infobar_button, null); |
+ tertiaryButton.setId(R.id.button_tertiary); |
+ tertiaryButton.setOnClickListener(this); |
+ tertiaryButton.setText(tertiaryText); |
+ tertiaryButton.setPadding(mMargin / 2, tertiaryButton.getPaddingTop(), mMargin / 2, |
+ tertiaryButton.getPaddingBottom()); |
+ tertiaryButton.setTextColor( |
+ getContext().getResources().getColor(R.color.infobar_tertiary_button_text)); |
- return rowHeight; |
+ mButtonGroup = addGroup(tertiaryButton, secondaryButton, primaryButton); |
} |
/** |
- * Lays out the controls in the row other than the main one. |
- * |
- * This case is much simpler than the main row since the items are all equally sized and simply |
- * entails moving through the children and laying them down from the start of the InfoBar to the |
- * end. |
- * |
- * @param row Index of the row |
- * @param rowTop Y-coordinate of the layout the controls should be aligned to. |
- * @param width Maximum width of the row. |
- * @return How tall the row is. |
+ * Adds a group of Views that are measured and laid out together. |
*/ |
- private int layoutRow(int row, int rowTop, int width) { |
- final int rowStart = mIndicesOfRows.get(row); |
- final int rowEnd = mIndicesOfRows.get(row + 1); |
- final boolean hasButton = isButton(getChildAt(rowStart)); |
- |
- int rowLeft = hasButton ? 0 : mDimensionMargin; |
- int rowRight = width - (hasButton ? 0 : mDimensionMargin); |
- |
- for (int i = rowStart; i < rowEnd; i++) { |
- final View child = getChildAt(i); |
- if (child.getVisibility() == View.GONE) continue; |
- |
- int childLeft; |
- if (mLayoutRTL) { |
- childLeft = rowRight - child.getMeasuredWidth(); |
- rowRight -= child.getMeasuredWidth() + (hasButton ? 0 : mDimensionMargin); |
- } else { |
- childLeft = rowLeft; |
- rowLeft += child.getMeasuredWidth() + (hasButton ? 0 : mDimensionMargin); |
- } |
+ private Group addGroup(View... views) { |
+ Group group = new Group(); |
+ group.views = views; |
- child.layout(childLeft, rowTop, childLeft + child.getMeasuredWidth(), |
- rowTop + child.getMeasuredHeight()); |
+ for (View v : views) { |
+ addView(v); |
} |
- |
- return rowTop + computeRowHeight(rowStart, rowEnd); |
+ return group; |
} |
- /** |
- * Checks if the child is one of the main InfoBar controls. |
- * @param child View to check. |
- * @return True if the child is one of the main controls. |
- */ |
- private boolean isMainControl(View child) { |
- return child == mIconView || child == mMessageView || child == mCloseButton; |
+ @Override |
+ protected LayoutParams generateDefaultLayoutParams() { |
+ return new LayoutParams(0, 0, 0, 0); |
} |
- /** |
- * Marks that the given index is the start of its own row. |
- * @param rowStartIndex Index of the child view at the start of the next row. |
- */ |
- private void addRowStartIndex(int rowStartIndex) { |
- if (mIndicesOfRows.size() == 0 |
- || rowStartIndex != mIndicesOfRows.get(mIndicesOfRows.size() - 1)) { |
- mIndicesOfRows.add(rowStartIndex); |
- } |
- } |
+ @Override |
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) { |
+ // Place all the views in the positions already determined during onMeasure(). |
+ int width = right - left; |
+ boolean isRtl = ApiCompatibilityUtils.isLayoutRtl(this); |
- /** |
- * Computes properties of the next group of Views to assign to rows. |
- * @param startIndex Index of the first child in the group. |
- * @return GroupInfo containing information about the current group. |
- */ |
- private GroupInfo getNextGroup(int startIndex) { |
- GroupInfo groupInfo = new GroupInfo(); |
- groupInfo.endIndex = startIndex; |
- |
- final int childCount = getChildCount(); |
- int currentChildIndex = startIndex; |
- while (groupInfo.endIndex < childCount) { |
- final View groupChild = getChildAt(groupInfo.endIndex); |
- if (groupChild.getVisibility() != View.GONE) { |
- groupInfo.hasButton |= isButton(groupChild); |
- groupInfo.width += groupChild.getMeasuredWidth(); |
- groupInfo.greatestMemberWidth = |
- Math.max(groupInfo.greatestMemberWidth, groupChild.getMeasuredWidth()); |
- groupInfo.numViews++; |
+ for (int i = 0; i < getChildCount(); i++) { |
+ View child = getChildAt(i); |
+ LayoutParams lp = (LayoutParams) child.getLayoutParams(); |
+ int childLeft = lp.start; |
+ int childRight = lp.start + child.getMeasuredWidth(); |
+ |
+ if (isRtl) { |
+ int tmp = width - childRight; |
+ childRight = width - childLeft; |
+ childLeft = tmp; |
} |
- groupInfo.endIndex++; |
- LayoutParams params = (LayoutParams) groupChild.getLayoutParams(); |
- if (!params.isGroupedWithNextView) break; |
+ child.layout(childLeft, lp.top, childRight, lp.top + child.getMeasuredHeight()); |
} |
- |
- return groupInfo; |
- } |
- |
- @Override |
- protected void measureChild(View child, int widthSpec, int heightSpec) { |
- // If a control is on the main row, then it should be only as large as it wants to be. |
- // Otherwise, it must occupy the same amount of space as everything else on its row. |
- LayoutParams params = (LayoutParams) child.getLayoutParams(); |
- params.width = params.isInMainRow ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT; |
- super.measureChild(child, widthSpec, heightSpec); |
} |
+ /** |
+ * Measures *and* assigns positions to all of the views in the infobar. These positions are |
+ * saved in each view's LayoutParams (lp.start and lp.top) and used during onLayout(). All of |
+ * the interesting logic happens inside onMeasure(); onLayout() just assigns the already- |
+ * determined positions and mirrors everything for RTL, if needed. |
+ */ |
@Override |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
assert getLayoutParams().height == LayoutParams.WRAP_CONTENT |
: "InfoBar heights cannot be constrained."; |
- final int maxWidth = MeasureSpec.getSize(widthMeasureSpec); |
- mIndicesOfRows.clear(); |
- |
- // Measure all children with the assumption that they may take up the full size of the |
- // parent. This determines how big each child wants to be. |
- final int childCount = getChildCount(); |
- for (int numChild = 0; numChild < childCount; numChild++) { |
- final View child = getChildAt(numChild); |
- if (child.getVisibility() == View.GONE) continue; |
- ((LayoutParams) child.getLayoutParams()).isInMainRow = true; |
- measureChild(child, widthMeasureSpec, heightMeasureSpec); |
+ // Measure all children without imposing any size constraints on them. This determines how |
+ // big each child wants to be. |
+ int unspecifiedSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); |
+ for (int i = 0; i < getChildCount(); i++) { |
+ measureChild(getChildAt(i), unspecifiedSpec, unspecifiedSpec); |
} |
- // Allocate as many Views as possible to the main row, then place everything else on the |
- // following rows. |
- int currentChildIndex = measureMainRow(maxWidth); |
- measureRemainingRows(maxWidth, currentChildIndex); |
- |
- // Buttons must have their backgrounds manually changed to give the illusion of having a |
- // single pixel boundary between them. |
- updateBackgroundsForButtons(); |
+ // Avoid overlapping views, division by zero, infinite heights, and other fun problems that |
+ // could arise with extremely narrow infobars. |
+ mWidth = Math.max(MeasureSpec.getSize(widthMeasureSpec), mMinWidth); |
+ mTop = mBottom = 0; |
+ placeGroups(); |
- // Determine how tall the container should be by measuring all the children in their rows. |
- int layoutHeight = computeHeight(); |
- setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), |
- resolveSize(layoutHeight, heightMeasureSpec)); |
+ setMeasuredDimension(mWidth, resolveSize(mBottom, heightMeasureSpec)); |
} |
/** |
- * Assign as many Views as can fit onto the main row. |
- * |
- * The main row consists of at least the icon, the close button, and the message. Groups of |
- * controls are added to the main row as long as they can fit within the width of the InfoBar. |
- * |
- * @param maxWidth The maximum width of the main row. |
- * @return The index of the last child that couldn't fit on the main row. |
+ * Assigns positions to all of the views in the infobar. The icon, text, and close button are |
+ * placed on the main row. The custom content and finally the buttons are placed on the main row |
+ * if they fit. Otherwise, they go on their own rows. |
*/ |
- private int measureMainRow(int maxWidth) { |
- final int childCount = getChildCount(); |
- |
- // The main row has the icon and the close button taking the upper left and upper right |
- // corners of the InfoBar, each of which occupies a square of |
- // mDimensionMinSize x mDimensionMinSize pixels. |
- GroupInfo mainControlInfo = getNextGroup(0); |
- int remainingWidth = maxWidth - (mDimensionMinSize * 2) - mMessageView.getMeasuredWidth(); |
- addRowStartIndex(0); |
- |
- // Go through the rest of the Views and keep adding them until they can't fit. |
- int currentChildIndex = mainControlInfo.endIndex; |
- while (currentChildIndex < childCount && remainingWidth > 0) { |
- GroupInfo groupInfo = getNextGroup(currentChildIndex); |
- int widthWithMargins = groupInfo.width + mDimensionMargin * groupInfo.numViews; |
- |
- if (widthWithMargins <= remainingWidth) { |
- // If the group fits on the main row, add it. |
- currentChildIndex = groupInfo.endIndex; |
- remainingWidth -= widthWithMargins; |
+ private void placeGroups() { |
+ startRow(); |
+ placeChild(mCloseButton, Gravity.END); |
+ placeGroup(mMainGroup); |
+ |
+ int customGroupWidth = 0; |
+ if (mCustomGroup != null) { |
+ updateCustomGroupForRow(ROW_MAIN); |
+ customGroupWidth = getWidthWithMargins(mCustomGroup); |
+ } |
+ |
+ int buttonGroupWidth = 0; |
+ if (mButtonGroup != null) { |
+ updateButtonGroupForRow(ROW_MAIN); |
+ buttonGroupWidth = getWidthWithMargins(mButtonGroup); |
+ } |
+ |
+ boolean customGroupOnMainRow = customGroupWidth <= availableWidth(); |
+ boolean buttonGroupOnMainRow = customGroupWidth + buttonGroupWidth <= availableWidth(); |
+ |
+ if (mCustomGroup != null) { |
+ if (customGroupOnMainRow) { |
+ mCustomGroup.gravity = (mButtonGroup != null && buttonGroupOnMainRow) |
+ ? Gravity.START : Gravity.END; |
} else { |
- // We can't fit the current group on the main row. |
- break; |
+ startRow(); |
+ updateCustomGroupForRow(ROW_OTHER); |
} |
+ placeGroup(mCustomGroup); |
} |
- addRowStartIndex(currentChildIndex); |
- |
- // The icon and the close button are set to be squares occupying the upper left and |
- // upper right corners of the InfoBar. |
- int specWidth = MeasureSpec.makeMeasureSpec(mDimensionMinSize, MeasureSpec.EXACTLY); |
- int specHeight = MeasureSpec.makeMeasureSpec(mDimensionMinSize, MeasureSpec.EXACTLY); |
- measureChild(mIconView, specWidth, specHeight); |
- measureChild(mCloseButton, specWidth, specHeight); |
- |
- // Measure out everything else except the message. |
- remainingWidth = maxWidth - (mDimensionMinSize * 2); |
- for (int i = 0; i < currentChildIndex; i++) { |
- final View child = getChildAt(i); |
- if (child.getVisibility() == View.GONE || isMainControl(child)) continue; |
- |
- specWidth = MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST); |
- specHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); |
- measureChild(child, specWidth, specHeight); |
- remainingWidth -= child.getMeasuredWidth() + mDimensionMargin; |
+ |
+ if (mButtonGroup != null) { |
+ if (!buttonGroupOnMainRow) { |
+ startRow(); |
+ updateButtonGroupForRow(ROW_OTHER); |
+ |
+ // If the infobar consists of just a main row and a buttons row, the buttons must be |
+ // at least 32dp below the bottom of the message text. |
+ if (mCustomGroup == null) { |
+ LayoutParams lp = (LayoutParams) mMessageView.getLayoutParams(); |
+ int messageBottom = lp.top + mMessageView.getMeasuredHeight(); |
+ mTop = Math.max(mTop, messageBottom + 2 * mMargin); |
+ } |
+ } |
+ placeGroup(mButtonGroup); |
} |
- // The message sucks up the remaining width on the line after all other controls |
- // have gotten all the space they requested. |
- specWidth = MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST); |
- specHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); |
- measureChild(mMessageView, specWidth, specHeight); |
+ startRow(); |
- return currentChildIndex; |
+ // If everything fits on a single row, center everything vertically. |
+ if (buttonGroupOnMainRow) { |
+ int layoutHeight = mBottom; |
+ for (int i = 0; i < getChildCount(); i++) { |
+ View child = getChildAt(i); |
+ int extraSpace = layoutHeight - child.getMeasuredHeight(); |
+ LayoutParams lp = (LayoutParams) child.getLayoutParams(); |
+ lp.top = extraSpace / 2; |
+ } |
+ } |
} |
/** |
- * Assign children to rows in the layout. |
- * |
- * We first try to assign children in the same group to the same row, but only if they fit when |
- * they are of equal width. Otherwise, we split the group onto multiple rows. |
- * |
- * @param maxWidth Maximum width that the row can take. |
- * @param currentChildIndex Start index of the current group. |
+ * Places a group of views on the current row, or stacks them over multiple rows if |
+ * group.isStacked is true. mStart, mEnd, and mBottom are updated to reflect the space taken by |
+ * the group. |
*/ |
- private void measureRemainingRows(int maxWidth, int currentChildIndex) { |
- final int childCount = getChildCount(); |
- final int specHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); |
- |
- while (currentChildIndex < childCount) { |
- GroupInfo groupInfo = getNextGroup(currentChildIndex); |
- |
- int availableWidth; |
- int boundaryMargins; |
- if (groupInfo.hasButton) { |
- // Buttons take up the full width of the InfoBar. |
- availableWidth = maxWidth; |
- boundaryMargins = 0; |
- } else { |
- // Other controls obey the side boundaries, and have boundaries between them. |
- availableWidth = maxWidth - mDimensionMargin * 2; |
- boundaryMargins = (groupInfo.numViews - 1) * mDimensionMargin; |
+ private void placeGroup(Group group) { |
+ if (group.gravity == Gravity.END) { |
+ for (int i = group.views.length - 1; i >= 0; i--) { |
+ placeChild(group.views[i], group.gravity); |
+ if (group.isStacked && i != 0) startRow(); |
} |
- |
- // Determine how wide each item would be on the same row, including boundaries. |
- int evenWidth = (availableWidth - boundaryMargins) / groupInfo.numViews; |
- |
- if (groupInfo.greatestMemberWidth <= evenWidth) { |
- // Fit everything on the same row. |
- int specWidth = MeasureSpec.makeMeasureSpec(evenWidth, MeasureSpec.EXACTLY); |
- for (int i = currentChildIndex; i < groupInfo.endIndex; i++) { |
- final View child = getChildAt(i); |
- if (child.getVisibility() == View.GONE) continue; |
- ((LayoutParams) child.getLayoutParams()).isInMainRow = false; |
- measureChild(child, specWidth, specHeight); |
- } |
- addRowStartIndex(currentChildIndex); |
- } else { |
- // Add each member of the group to its own row. |
- int specWidth = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.EXACTLY); |
- for (int i = currentChildIndex; i < groupInfo.endIndex; i++) { |
- final View child = getChildAt(i); |
- if (child.getVisibility() == View.GONE) continue; |
- ((LayoutParams) child.getLayoutParams()).isInMainRow = false; |
- measureChild(child, specWidth, specHeight); |
- addRowStartIndex(i); |
- } |
+ } else { // group.gravity is Gravity.START or Gravity.FILL_HORIZONTAL |
+ for (int i = 0; i < group.views.length; i++) { |
+ placeChild(group.views[i], group.gravity); |
+ if (group.isStacked && i != group.views.length - 1) startRow(); |
} |
- |
- currentChildIndex = groupInfo.endIndex; |
} |
- |
- addRowStartIndex(childCount); |
} |
/** |
- * Calculate how tall the layout is, accounting for margins and children. |
- * @return How big the layout should be. |
+ * Places a single view on the current row, and updates the view's layout parameters to remember |
+ * its position. mStart, mEnd, and mBottom are updated to reflect the space taken by the view. |
*/ |
- private int computeHeight() { |
- int cumulativeHeight = 0; |
+ private void placeChild(View child, int gravity) { |
+ LayoutParams lp = (LayoutParams) child.getLayoutParams(); |
- // Calculate how big each row is. |
- final int numRows = mIndicesOfRows.size() - 1; |
- for (int row = 0; row < numRows; row++) { |
- final int rowStart = mIndicesOfRows.get(row); |
- final int rowEnd = mIndicesOfRows.get(row + 1); |
+ int availableWidth = Math.max(0, mEnd - mStart - lp.startMargin - lp.endMargin); |
+ if (child.getMeasuredWidth() > availableWidth || gravity == Gravity.FILL_HORIZONTAL) { |
+ measureChildWithFixedWidth(child, availableWidth); |
+ } |
- if (row == 0) { |
- cumulativeHeight += computeMainRowHeight(rowStart, rowEnd); |
- } else { |
- cumulativeHeight += computeRowHeight(rowStart, rowEnd); |
- } |
+ if (gravity == Gravity.START || gravity == Gravity.FILL_HORIZONTAL) { |
+ lp.start = mStart + lp.startMargin; |
+ mStart = lp.start + child.getMeasuredWidth() + lp.endMargin; |
+ } else { // gravity == Gravity.END |
+ lp.start = mEnd - lp.endMargin - child.getMeasuredWidth(); |
+ mEnd = lp.start - lp.startMargin; |
} |
- return cumulativeHeight; |
+ lp.top = mTop + lp.topMargin; |
+ mBottom = Math.max(mBottom, lp.top + child.getMeasuredHeight() + lp.bottomMargin); |
} |
/** |
- * Computes how tall the main row is. |
- * @param rowStart Index of the first child. |
- * @param rowEnd One past the index of the last child. |
+ * Advances the current position to the next row and adds margins on the left, right, and top |
+ * of the new row. |
*/ |
- private int computeMainRowHeight(int rowStart, int rowEnd) { |
- // The icon and close button already have their margins baked into their padding values, |
- // but the other Views have a margin above and below. |
- final int verticalMargins = mDimensionMargin * 2; |
- int rowHeight = mDimensionMinSize; |
- for (int i = rowStart; i < rowEnd; i++) { |
- View child = getChildAt(i); |
- if (child == mCloseButton || child == mIconView || child.getVisibility() == View.GONE) { |
- continue; |
- } |
- rowHeight = Math.max(rowHeight, child.getMeasuredHeight() + verticalMargins); |
- } |
- return rowHeight; |
+ private void startRow() { |
+ mStart = mMargin; |
+ mEnd = mWidth - mMargin; |
+ mTop = mBottom + mMargin; |
+ mBottom = mTop; |
+ } |
+ |
+ private int availableWidth() { |
+ return mEnd - mStart; |
} |
/** |
- * Computes how tall a row below the main row is. |
- * |
- * Margins are only applied downward since the rows above are handling the margin on their side. |
- * Buttons ignore margins since they have to be right against the boundary. |
- * |
- * @param rowStart Index of the first child. |
- * @param rowEnd One past the index of the last child. |
+ * @return The width of the group, including the items' margins. |
*/ |
- private int computeRowHeight(int rowStart, int rowEnd) { |
- boolean isButtonRow = isButton(getChildAt(rowStart)); |
- final int verticalMargins = isButtonRow ? 0 : mDimensionMargin; |
- int rowHeight = 0; |
- for (int i = rowStart; i < rowEnd; i++) { |
- final View child = getChildAt(i); |
- if (child.getVisibility() == View.GONE) continue; |
- rowHeight = Math.max(rowHeight, child.getMeasuredHeight() + verticalMargins); |
+ private int getWidthWithMargins(Group group) { |
+ if (group.isStacked) return getWidthWithMargins(group.views[0]); |
+ |
+ int width = 0; |
+ for (View v : group.views) { |
+ width += getWidthWithMargins(v); |
} |
- return rowHeight; |
+ return width; |
+ } |
+ |
+ private int getWidthWithMargins(View child) { |
+ LayoutParams lp = (LayoutParams) child.getLayoutParams(); |
+ return child.getMeasuredWidth() + lp.startMargin + lp.endMargin; |
+ } |
+ |
+ private void measureChildWithFixedWidth(View child, int width) { |
+ int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); |
+ int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); |
+ child.measure(widthSpec, heightSpec); |
} |
/** |
- * Determines if the given View is either the primary or secondary button. |
- * @param child View to check. |
- * @return Whether the child is the primary or secondary button. |
+ * The button group has different layout properties (margins, gravity, etc) when placed on the |
+ * main row as opposed to on a separate row. This updates the layout properties of the button |
+ * group to prepare for placing it on either the main row or a separate row. |
+ * |
+ * @param row One of ROW_MAIN or ROW_OTHER. |
*/ |
- private boolean isButton(View child) { |
- return child.getId() == R.id.button_secondary || child.getId() == R.id.button_primary; |
+ private void updateButtonGroupForRow(int row) { |
+ int startEndMargin = row == ROW_MAIN ? mMargin : 0; |
+ mButtonGroup.setHorizontalMode(mMargin / 2, startEndMargin, startEndMargin); |
+ mButtonGroup.gravity = Gravity.END; |
+ |
+ if (row == ROW_OTHER && mButtonGroup.views.length >= 2) { |
+ int extraWidth = availableWidth() - getWidthWithMargins(mButtonGroup); |
+ if (extraWidth < 0) { |
+ // Group is too wide to fit on a single row, so stack the group items vertically. |
+ mButtonGroup.setVerticalMode(mMargin / 2, 0); |
+ mButtonGroup.gravity = Gravity.FILL_HORIZONTAL; |
+ } else if (mButtonGroup.views.length == 3) { |
+ // Align tertiary button at the start and the other two buttons at the end. |
+ ((LayoutParams) mButtonGroup.views[0].getLayoutParams()).endMargin += extraWidth; |
+ } |
+ } |
} |
/** |
- * Update the backgrounds for the buttons to account for their current positioning. |
- * The primary and secondary buttons are special-cased in that their backgrounds change to |
- * create the illusion of a single-stroke boundary between them. |
+ * Analagous to updateButtonGroupForRow(), but for the custom group istead of the button group. |
*/ |
- private void updateBackgroundsForButtons() { |
- boolean bothButtonsExist = findViewById(R.id.button_primary) != null |
- && findViewById(R.id.button_secondary) != null; |
- |
- for (int row = 0; row < mIndicesOfRows.size() - 1; row++) { |
- final int rowStart = mIndicesOfRows.get(row); |
- final int rowEnd = mIndicesOfRows.get(row + 1); |
- final int rowSize = rowEnd - rowStart; |
- |
- for (int i = rowStart; i < rowEnd; i++) { |
- final View child = getChildAt(i); |
- if (child.getVisibility() == View.GONE || !isButton(child)) continue; |
- |
- // Determine which background we need to show. |
- int background; |
- if (row == 0) { |
- // Button will be floating. |
- background = mBackgroundFloating; |
- } else if (rowSize == 1 || !bothButtonsExist) { |
- // Button takes up the full width of the screen. |
- background = mBackgroundFullRight; |
- } else if (mLayoutRTL) { |
- // Primary button will be to the left of the secondary. |
- background = child.getId() == R.id.button_primary |
- ? mBackgroundFullLeft : mBackgroundFullRight; |
- } else { |
- // Primary button will be to the right of the secondary. |
- background = child.getId() == R.id.button_primary |
- ? mBackgroundFullRight : mBackgroundFullLeft; |
- } |
- |
- // Update the background. |
- LayoutParams params = (LayoutParams) child.getLayoutParams(); |
- if (params.background != background) { |
- params.background = background; |
- |
- // Save the padding; Android decides to overwrite it on some builds. |
- int paddingLeft = child.getPaddingLeft(); |
- int paddingTop = child.getPaddingTop(); |
- int paddingRight = child.getPaddingRight(); |
- int paddingBottom = child.getPaddingBottom(); |
- int buttonWidth = child.getMeasuredWidth(); |
- int buttonHeight = child.getMeasuredHeight(); |
- |
- // Set the background, then restore the padding. |
- child.setBackgroundResource(background); |
- child.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); |
- |
- // Re-measuring is necessary to correct the text gravity. |
- int specWidth = MeasureSpec.makeMeasureSpec(buttonWidth, MeasureSpec.EXACTLY); |
- int specHeight = MeasureSpec.makeMeasureSpec(buttonHeight, MeasureSpec.EXACTLY); |
- measureChild(child, specWidth, specHeight); |
- } |
+ private void updateCustomGroupForRow(int row) { |
+ int startEndMargin = row == ROW_MAIN ? mMargin : 0; |
+ mCustomGroup.setHorizontalMode(mMargin, startEndMargin, startEndMargin); |
+ mCustomGroup.gravity = Gravity.START; |
+ |
+ if (row == ROW_OTHER && mCustomGroup.views.length == 2) { |
+ int extraWidth = availableWidth() - getWidthWithMargins(mCustomGroup); |
+ if (extraWidth < 0) { |
+ // Group is too wide to fit on a single row, so stack the group items vertically. |
+ mCustomGroup.setVerticalMode(0, mMargin); |
+ mCustomGroup.gravity = Gravity.FILL_HORIZONTAL; |
+ } else { |
+ // Expand the children to take up the entire row. |
+ View view0 = mCustomGroup.views[0]; |
+ View view1 = mCustomGroup.views[1]; |
+ int extraWidth0 = extraWidth / 2; |
+ int extraWidth1 = extraWidth - extraWidth0; |
+ measureChildWithFixedWidth(view0, view0.getMeasuredWidth() + extraWidth0); |
+ measureChildWithFixedWidth(view1, view1.getMeasuredWidth() + extraWidth1); |
} |
} |
} |
@@ -759,7 +557,8 @@ public class InfoBarLayout extends ViewGroup implements View.OnClickListener { |
mInfoBarView.onButtonClicked(true); |
} else if (view.getId() == R.id.button_secondary) { |
mInfoBarView.onButtonClicked(false); |
+ } else if (view.getId() == R.id.button_tertiary) { |
+ mInfoBarView.onLinkClicked(); |
} |
} |
- |
} |