[automerger] Update ListItem API from Builder to setters. am: 1724255122
Change-Id: Ia9825f2f967625590922d9f1922fa019b2b2377e
diff --git a/car/src/main/java/androidx/car/widget/ListItem.java b/car/src/main/java/androidx/car/widget/ListItem.java
index ec210b7..74ff2dd 100644
--- a/car/src/main/java/androidx/car/widget/ListItem.java
+++ b/car/src/main/java/androidx/car/widget/ListItem.java
@@ -11,6 +11,13 @@
*/
public abstract class ListItem<VH extends RecyclerView.ViewHolder> {
+ // Whether the item should calculate view layout params. This usually happens when the item is
+ // updated after bind() is called. Calling bind() resets to false.
+ private boolean mDirty;
+
+ // Tag for indicating whether to hide the divider.
+ private boolean mHideDivider;
+
/**
* Classes that extends {@code ListItem} should register its view type in
* {@link ListItemAdapter#registerListItemViewType(int, int, Function)}.
@@ -25,11 +32,47 @@
public abstract void bind(VH viewHolder);
/**
- * @return whether the divider that comes after this ListItem should be hidden. Defaults to
- * false.
+ * Marks this item so that sub-views in ViewHolder will need layout params re-calculated
+ * in next bind().
+ *
+ * This method should be called in each setter.
+ */
+ protected void markDirty() {
+ mDirty = true;
+ }
+
+ /**
+ * Marks this item as not dirty - no need to calculate sub-view layout params in bind().
+ */
+ protected void markClean() {
+ mDirty = false;
+ }
+
+ /**
+ * @return {@code true} if this item needs to calculate sub-view layout params.
+ */
+ protected boolean isDirty() {
+ return mDirty;
+ }
+
+ /**
+ * Whether hide the item divider coming after this {@code ListItem}.
+ *
+ * <p>Note: For this to work, one must invoke
+ * {@code PagedListView.setDividerVisibilityManager(adapter} for {@link ListItemAdapter} and
+ * have dividers enabled on {@link PagedListView}.
+ */
+ public void setHideDivider(boolean hideDivider) {
+ mHideDivider = hideDivider;
+ markDirty();
+ }
+
+ /**
+ * @return {@code true} if the divider that comes after this ListItem should be hidden.
+ * Defaults to false.
*/
public boolean shouldHideDivider() {
- return false;
+ return mHideDivider;
};
/**
diff --git a/car/src/main/java/androidx/car/widget/SeekbarListItem.java b/car/src/main/java/androidx/car/widget/SeekbarListItem.java
index 56e87dc..54c37ef 100644
--- a/car/src/main/java/androidx/car/widget/SeekbarListItem.java
+++ b/car/src/main/java/androidx/car/widget/SeekbarListItem.java
@@ -27,6 +27,7 @@
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
@@ -40,7 +41,7 @@
import androidx.car.R;
/**
- * Class to build a list item with Seekbar.
+ * Class to build a list item with {@link SeekBar}.
*
* <p>An item supports primary action and supplemental action(s).
*
@@ -53,18 +54,63 @@
* <li>Empty Icon - {@code Seekbar} offsets start space as if there was an icon.
* </ul>
* <li>{@code Seekbar}: with optional {@code Text}.
- * <li>{@code Supplemental Action}: presented by an icon of following types.
+ * <li>{@code Supplemental Action}: presented by an icon of following types; aligned to
+ * the end of item.
* <ul>
* <li>Supplemental Icon.
* <li>Supplemental Empty Icon - {@code Seekbar} offsets end space as if there was an icon.
* </ul>
* </ul>
*
- * {@link SeekbarListItem} can be built through its {@link SeekbarListItem.Builder}. It binds data
- * to {@link ViewHolder} based on components selected.
+ * {@code SeekbarListItem} binds data to {@link ViewHolder} based on components selected.
+ *
+ * <p>When conflicting methods are called (e.g. setting primary action to both primary icon and
+ * no icon), the last called method wins.
*/
public class SeekbarListItem extends ListItem<SeekbarListItem.ViewHolder> {
+ @Retention(SOURCE)
+ @IntDef({
+ PRIMARY_ACTION_TYPE_NO_ICON, PRIMARY_ACTION_TYPE_EMPTY_ICON,
+ PRIMARY_ACTION_TYPE_SMALL_ICON})
+ private @interface PrimaryActionType {}
+
+ private static final int PRIMARY_ACTION_TYPE_NO_ICON = 0;
+ private static final int PRIMARY_ACTION_TYPE_EMPTY_ICON = 1;
+ private static final int PRIMARY_ACTION_TYPE_SMALL_ICON = 2;
+
+ @Retention(SOURCE)
+ @IntDef({SUPPLEMENTAL_ACTION_NO_ACTION, SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON,
+ SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON,
+ SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER})
+ private @interface SupplementalActionType {}
+
+ private static final int SUPPLEMENTAL_ACTION_NO_ACTION = 0;
+ private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON = 1;
+ private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON = 2;
+ private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER = 3;
+
+ private final Context mContext;
+
+ private final List<ViewBinder<ViewHolder>> mBinders = new ArrayList<>();
+ // Store custom binders separately so they will bind after binders created by setters.
+ private final List<ViewBinder<ViewHolder>> mCustomBinders = new ArrayList<>();
+
+ @PrimaryActionType private int mPrimaryActionType = PRIMARY_ACTION_TYPE_NO_ICON;
+ private int mPrimaryActionIconResId;
+ private Drawable mPrimaryActionIconDrawable;
+
+ private String mText;
+
+ private int mProgress;
+ private int mMax;
+ private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener;
+
+ @SupplementalActionType private int mSupplementalActionType = SUPPLEMENTAL_ACTION_NO_ACTION;
+ private int mSupplementalIconResId;
+ private View.OnClickListener mSupplementalIconOnClickListener;
+ private boolean mShowSupplementalIconDivider;
+
/**
* Creates a {@link SeekbarListItem.ViewHolder}.
*/
@@ -72,10 +118,33 @@
return new ViewHolder(itemView);
}
- private Builder mBuilder;
+ /**
+ * Creates a SeekbarListItem.
+ *
+ * @param context context
+ * @param max the upper range of the SeekBar.
+ * @param progress the current progress of the specified value.
+ * @param listener listener to receive notification of changes to progress level.
+ * @param text displays a text on top of the SeekBar. Text beyond length required by
+ * regulation will be truncated. null value hides the text field.
+ */
+ public SeekbarListItem(Context context, int max, int progress,
+ SeekBar.OnSeekBarChangeListener listener, String text) {
+ mContext = context;
- SeekbarListItem(Builder builder) {
- mBuilder = builder;
+ mMax = max;
+ mProgress = progress;
+ mOnSeekBarChangeListener = listener;
+
+ int limit = mContext.getResources().getInteger(
+ R.integer.car_list_item_text_length_limit);
+ if (TextUtils.isEmpty(text) || text.length() < limit) {
+ mText = text;
+ } else {
+ mText = text.substring(0, limit) + mContext.getString(R.string.ellipsis);
+ }
+
+ markDirty();
}
/**
@@ -91,18 +160,28 @@
*/
@Override
public void bind(ViewHolder viewHolder) {
- setSubViewsGone(viewHolder);
+ if (isDirty()) {
+ mBinders.clear();
- for (ViewBinder binder : mBuilder.mBinders) {
+ // Create binders that adjust layout params of each view.
+ setItemLayoutHeight();
+ setPrimaryAction();
+ setSeekBarAndText();
+ setSupplementalAction();
+
+ // Custom view binders are always applied after the one created by this class.
+ mBinders.addAll(mCustomBinders);
+
+ markClean();
+ }
+
+ // Hide all subviews then apply view binders to adjust subviews.
+ setSubViewsGone(viewHolder);
+ for (ViewBinder binder : mBinders) {
binder.bind(viewHolder);
}
}
- @Override
- public boolean shouldHideDivider() {
- return mBuilder.mHideDivider;
- }
-
private void setSubViewsGone(ViewHolder vh) {
View[] subviews = new View[] {
vh.getPrimaryIcon(),
@@ -115,446 +194,330 @@
}
}
- /**
- * Builds a {@link SeekbarListItem}.
- *
- * <p>When conflicting methods are called, e.g. setting primary action to both primary icon and
- * no icon, the last called method wins.
- */
- public static class Builder {
-
- @Retention(SOURCE)
- @IntDef({
- PRIMARY_ACTION_TYPE_NO_ICON, PRIMARY_ACTION_TYPE_EMPTY_ICON,
- PRIMARY_ACTION_TYPE_SMALL_ICON})
- private @interface PrimaryActionType {}
-
- private static final int PRIMARY_ACTION_TYPE_NO_ICON = 0;
- private static final int PRIMARY_ACTION_TYPE_EMPTY_ICON = 1;
- private static final int PRIMARY_ACTION_TYPE_SMALL_ICON = 2;
-
- @Retention(SOURCE)
- @IntDef({SUPPLEMENTAL_ACTION_NO_ACTION, SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON,
- SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON,
- SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER})
- private @interface SupplementalActionType {}
-
- private static final int SUPPLEMENTAL_ACTION_NO_ACTION = 0;
- private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON = 1;
- private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON = 2;
- private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER = 3;
-
- private final Context mContext;
-
- // tag for indicating whether to hide the divider
- private boolean mHideDivider;
-
- private final List<ViewBinder<ViewHolder>> mBinders = new ArrayList<>();
- // Store custom view binders separately so they can be applied after build().
- private final List<ViewBinder<ViewHolder>> mCustomBinders =
- new ArrayList<>();
-
- @PrimaryActionType private int mPrimaryActionType = PRIMARY_ACTION_TYPE_NO_ICON;
- private int mPrimaryActionIconResId;
- private Drawable mPrimaryActionIconDrawable;
-
- private String mText;
-
- private int mProgress;
- private int mMax;
- private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener;
-
- @SupplementalActionType private int mSupplementalActionType = SUPPLEMENTAL_ACTION_NO_ACTION;
- private int mSupplementalIconResId;
- private View.OnClickListener mSupplementalIconOnClickListener;
- private boolean mShowSupplementalIconDivider;
-
- /**
- * Creates a Builder for SeekbarListItem.
- * @param context Context
- * @param max the upper range of the SeekBar.
- * @param progress the current progress of the specified value.
- * @param listener listener to receive notification of changes to progress level.
- * @param text Displays a text on top of the SeekBar. Text beyond length required by
- * regulation will be truncated. null value hides the text field.
- */
- public Builder(Context context, int max, int progress,
- SeekBar.OnSeekBarChangeListener listener, String text) {
- mContext = context;
-
- mMax = max;
- mProgress = progress;
- mOnSeekBarChangeListener = listener;
-
- // TODO(yaoyx): consolidate length limit in TextListItem to util function.
- int limit = mContext.getResources().getInteger(
- R.integer.car_list_item_text_length_limit);
- if (TextUtils.isEmpty(text) || text.length() < limit) {
- mText = text;
- } else {
- mText = text.substring(0, limit) + mContext.getString(R.string.ellipsis);
- }
- }
-
- /**
- * Builds a {@link TextListItem}.
- */
- public SeekbarListItem build() {
- setItemLayoutHeight();
- setPrimaryAction();
- setSeekBarAndText();
- setSupplementalAction();
-
- mBinders.addAll(mCustomBinders);
-
- return new SeekbarListItem(this);
- }
-
- private void setItemLayoutHeight() {
- int minHeight = mContext.getResources().getDimensionPixelSize(
+ private void setItemLayoutHeight() {
+ int minHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.car_single_line_list_item_height);
- mBinders.add(vh -> {
- vh.itemView.setMinimumHeight(minHeight);
- vh.getContainerLayout().setMinimumHeight(minHeight);
+ mBinders.add(vh -> {
+ vh.itemView.setMinimumHeight(minHeight);
+ vh.getContainerLayout().setMinimumHeight(minHeight);
- RecyclerView.LayoutParams layoutParams =
- (RecyclerView.LayoutParams) vh.itemView.getLayoutParams();
- layoutParams.height = RecyclerView.LayoutParams.WRAP_CONTENT;
- vh.itemView.setLayoutParams(layoutParams);
- });
+ ViewGroup.LayoutParams layoutParams = vh.itemView.getLayoutParams();
+ layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ vh.itemView.requestLayout();
+ });
+ }
+
+ private void setPrimaryAction() {
+ setPrimaryActionLayout();
+ setPrimaryActionContent();
+ }
+
+ private void setSeekBarAndText() {
+ setSeekBarAndTextContent();
+ setSeekBarAndTextLayout();
+ }
+
+ private void setSupplementalAction() {
+ setSupplementalActionLayout();
+ setSupplementalActionContent();
+ }
+
+ private void setPrimaryActionLayout() {
+ switch (mPrimaryActionType) {
+ case PRIMARY_ACTION_TYPE_NO_ICON:
+ case PRIMARY_ACTION_TYPE_EMPTY_ICON:
+ // Do nothing.
+ break;
+ case PRIMARY_ACTION_TYPE_SMALL_ICON:
+ int startMargin = mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_keyline_1);
+ int iconSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_primary_icon_size);
+ mBinders.add(vh -> {
+ RelativeLayout.LayoutParams layoutParams =
+ (RelativeLayout.LayoutParams) vh.getPrimaryIcon().getLayoutParams();
+ // Icon size.
+ layoutParams.height = layoutParams.width = iconSize;
+ // Start margin.
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START);
+ layoutParams.setMarginStart(startMargin);
+ layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
+
+ vh.getPrimaryIcon().requestLayout();
+ });
+ break;
+ default:
+ throw new IllegalStateException("Unknown primary action type.");
}
+ }
- private void setPrimaryAction() {
- setPrimaryActionLayout();
- setPrimaryActionContent();
+ private void setPrimaryActionContent() {
+ switch (mPrimaryActionType) {
+ case PRIMARY_ACTION_TYPE_NO_ICON:
+ case PRIMARY_ACTION_TYPE_EMPTY_ICON:
+ // Do nothing.
+ break;
+ case PRIMARY_ACTION_TYPE_SMALL_ICON:
+ mBinders.add(vh -> {
+ vh.getPrimaryIcon().setVisibility(View.VISIBLE);
+
+ if (mPrimaryActionIconDrawable != null) {
+ vh.getPrimaryIcon().setImageDrawable(mPrimaryActionIconDrawable);
+ } else if (mPrimaryActionIconResId != 0) {
+ vh.getPrimaryIcon().setImageResource(mPrimaryActionIconResId);
+ }
+ });
+ break;
+ default:
+ throw new IllegalStateException("Unknown primary action type.");
}
+ }
- private void setSeekBarAndText() {
- setSeekBarAndTextContent();
- setSeekBarAndTextLayout();
- }
+ private void setSeekBarAndTextContent() {
+ mBinders.add(vh -> {
+ vh.getSeekBar().setMax(mMax);
+ vh.getSeekBar().setProgress(mProgress);
+ vh.getSeekBar().setOnSeekBarChangeListener(mOnSeekBarChangeListener);
- private void setSupplementalAction() {
- setSupplementalActionLayout();
- setSupplementalActionContent();
- }
-
- private void setPrimaryActionLayout() {
- switch (mPrimaryActionType) {
- case PRIMARY_ACTION_TYPE_NO_ICON:
- case PRIMARY_ACTION_TYPE_EMPTY_ICON:
- // Do nothing.
- break;
- case PRIMARY_ACTION_TYPE_SMALL_ICON:
- int startMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.car_keyline_1);
- int iconSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.car_primary_icon_size);
- mBinders.add(vh -> {
- RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams) vh.getPrimaryIcon().getLayoutParams();
- // Icon size.
- layoutParams.height = layoutParams.width = iconSize;
- // Start margin.
- layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START);
- layoutParams.setMarginStart(startMargin);
- layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
-
- vh.getPrimaryIcon().setLayoutParams(layoutParams);
- });
- break;
- default:
- throw new IllegalStateException("Unknown primary action type.");
+ if (!TextUtils.isEmpty(mText)) {
+ vh.getText().setVisibility(View.VISIBLE);
+ vh.getText().setText(mText);
+ vh.getText().setTextAppearance(R.style.CarBody1);
}
+ });
+ }
+
+ private void setSeekBarAndTextLayout() {
+ mBinders.add(vh -> {
+ // SeekBar is below text with a gap.
+ ViewGroup.MarginLayoutParams seekBarLayoutParams =
+ (ViewGroup.MarginLayoutParams) vh.getSeekBar().getLayoutParams();
+ seekBarLayoutParams.topMargin = TextUtils.isEmpty(mText)
+ ? 0
+ : mContext.getResources().getDimensionPixelSize(R.dimen.car_padding_1);
+ vh.getSeekBar().requestLayout();
+
+ // Set start and end margin of text and seek bar.
+ setViewStartMargin(vh.getSeekBarContainer());
+ setViewEndMargin(vh.getSeekBarContainer());
+
+ RelativeLayout.LayoutParams containerLayoutParams =
+ (RelativeLayout.LayoutParams) vh.getSeekBarContainer().getLayoutParams();
+ containerLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
+ });
+ }
+
+ // Helper method to set start margin of seekbar/text.
+ private void setViewStartMargin(View v) {
+ int startMarginResId;
+ switch (mPrimaryActionType) {
+ case PRIMARY_ACTION_TYPE_NO_ICON:
+ startMarginResId = R.dimen.car_keyline_1;
+ break;
+ case PRIMARY_ACTION_TYPE_EMPTY_ICON:
+ case PRIMARY_ACTION_TYPE_SMALL_ICON:
+ startMarginResId = R.dimen.car_keyline_3;
+ break;
+ default:
+ throw new IllegalStateException("Unknown primary action type.");
}
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) v.getLayoutParams();
+ layoutParams.setMarginStart(
+ mContext.getResources().getDimensionPixelSize(startMarginResId));
+ v.requestLayout();
+ }
- private void setPrimaryActionContent() {
- switch (mPrimaryActionType) {
- case PRIMARY_ACTION_TYPE_NO_ICON:
- case PRIMARY_ACTION_TYPE_EMPTY_ICON:
- // Do nothing.
- break;
- case PRIMARY_ACTION_TYPE_SMALL_ICON:
- mBinders.add(vh -> {
- vh.getPrimaryIcon().setVisibility(View.VISIBLE);
+ // Helper method to set end margin of seekbar/text.
+ private void setViewEndMargin(View v) {
+ RelativeLayout.LayoutParams layoutParams =
+ (RelativeLayout.LayoutParams) v.getLayoutParams();
+ int endMargin = 0;
+ switch (mSupplementalActionType) {
+ case SUPPLEMENTAL_ACTION_NO_ACTION:
+ // Aligned to parent end with margin.
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
+ layoutParams.removeRule(RelativeLayout.START_OF);
+ layoutParams.setMarginEnd(mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_keyline_1));
+ break;
+ case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON:
+ // Align to start of divider with padding.
+ layoutParams.addRule(RelativeLayout.START_OF, R.id.supplemental_icon_divider);
+ layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
+ layoutParams.setMarginEnd(mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_padding_4));
+ break;
+ case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER:
+ // Align to parent end with a margin as if the icon and an optional divider were
+ // present. We do this by setting
- if (mPrimaryActionIconDrawable != null) {
- vh.getPrimaryIcon().setImageDrawable(mPrimaryActionIconDrawable);
- } else if (mPrimaryActionIconResId != 0) {
- vh.getPrimaryIcon().setImageResource(mPrimaryActionIconResId);
- }
- });
- break;
- default:
- throw new IllegalStateException("Unknown primary action type.");
- }
+ // Add divider padding to icon, and width of divider.
+ endMargin += mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_padding_4);
+ endMargin += mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_vertical_line_divider_width);
+ // Fall through.
+ case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON:
+ // Add view padding, width of icon, and icon end margin.
+ endMargin += mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_padding_4);
+ endMargin += mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_primary_icon_size);
+ endMargin += mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_keyline_1);
+
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
+ layoutParams.removeRule(RelativeLayout.START_OF);
+ layoutParams.setMarginEnd(endMargin);
+ break;
+ default:
+ throw new IllegalStateException("Unknown supplemental action type.");
}
+ v.requestLayout();
+ }
- private void setSeekBarAndTextContent() {
- mBinders.add(vh -> {
- vh.getSeekBar().setMax(mMax);
- vh.getSeekBar().setProgress(mProgress);
- vh.getSeekBar().setOnSeekBarChangeListener(mOnSeekBarChangeListener);
+ private void setSupplementalActionLayout() {
+ int keyline1 = mContext.getResources().getDimensionPixelSize(R.dimen.car_keyline_1);
+ int padding4 = mContext.getResources().getDimensionPixelSize(R.dimen.car_padding_4);
+ mBinders.add(vh -> {
+ RelativeLayout.LayoutParams iconLayoutParams =
+ (RelativeLayout.LayoutParams) vh.getSupplementalIcon().getLayoutParams();
+ // Align to parent end with margin.
+ iconLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
+ iconLayoutParams.setMarginEnd(keyline1);
+ iconLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
- if (!TextUtils.isEmpty(mText)) {
- vh.getText().setVisibility(View.VISIBLE);
- vh.getText().setText(mText);
- vh.getText().setTextAppearance(R.style.CarBody1);
- }
- });
+ vh.getSupplementalIcon().requestLayout();
+
+ // Divider aligns to the start of supplemental icon with margin.
+ RelativeLayout.LayoutParams dividerLayoutParams =
+ (RelativeLayout.LayoutParams) vh.getSupplementalIconDivider()
+ .getLayoutParams();
+ dividerLayoutParams.addRule(RelativeLayout.START_OF, R.id.supplemental_icon);
+ dividerLayoutParams.setMarginEnd(padding4);
+ dividerLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
+
+ vh.getSupplementalIconDivider().requestLayout();
+ });
+ }
+
+ private void setSupplementalActionContent() {
+ switch (mSupplementalActionType) {
+ case SUPPLEMENTAL_ACTION_NO_ACTION:
+ case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER:
+ case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON:
+ break;
+ case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON:
+ mBinders.add(vh -> {
+ vh.getSupplementalIcon().setVisibility(View.VISIBLE);
+ if (mShowSupplementalIconDivider) {
+ vh.getSupplementalIconDivider().setVisibility(View.VISIBLE);
+ }
+
+ vh.getSupplementalIcon().setImageResource(mSupplementalIconResId);
+ vh.getSupplementalIcon().setOnClickListener(
+ mSupplementalIconOnClickListener);
+ vh.getSupplementalIcon().setClickable(
+ mSupplementalIconOnClickListener != null);
+ });
+ break;
+ default:
+ throw new IllegalStateException("Unknown supplemental action type.");
}
+ }
- private void setSeekBarAndTextLayout() {
- mBinders.add(vh -> {
- // SeekBar is below text with a gap.
- LinearLayout.LayoutParams seekBarLayoutParams =
- (LinearLayout.LayoutParams) vh.getSeekBar().getLayoutParams();
- seekBarLayoutParams.topMargin = TextUtils.isEmpty(mText)
- ? 0
- : mContext.getResources().getDimensionPixelSize(R.dimen.car_padding_1);
- vh.getSeekBar().setLayoutParams(seekBarLayoutParams);
+ /**
+ * Sets {@code Primary Action} to be represented by an icon.
+ *
+ * @param iconResId the resource identifier of the drawable.
+ */
+ public void setPrimaryActionIcon(@DrawableRes int iconResId) {
+ setPrimaryActionIcon(null, iconResId);
+ }
- // Set start and end margin of text and seek bar.
- setViewStartMargin(vh.getSeekBarContainer());
- setViewEndMargin(vh.getSeekBarContainer());
+ /**
+ * Sets {@code Primary Action} to be represented by an icon.
+ *
+ * @param drawable the Drawable to set, or null to clear the content.
+ */
+ public void setPrimaryActionIcon(Drawable drawable) {
+ setPrimaryActionIcon(drawable, 0);
+ }
- RelativeLayout.LayoutParams containerLayoutParams =
- (RelativeLayout.LayoutParams) vh.getSeekBarContainer().getLayoutParams();
- containerLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
- });
- }
+ private void setPrimaryActionIcon(Drawable drawable, @DrawableRes int iconResId) {
+ mPrimaryActionType = PRIMARY_ACTION_TYPE_SMALL_ICON;
- // Helper method to set start margin of seekbar/text.
- private void setViewStartMargin(View v) {
- int startMarginResId;
- switch (mPrimaryActionType) {
- case PRIMARY_ACTION_TYPE_NO_ICON:
- startMarginResId = R.dimen.car_keyline_1;
- break;
- case PRIMARY_ACTION_TYPE_EMPTY_ICON:
- case PRIMARY_ACTION_TYPE_SMALL_ICON:
- startMarginResId = R.dimen.car_keyline_3;
- break;
- default:
- throw new IllegalStateException("Unknown primary action type.");
- }
- RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams) v.getLayoutParams();
- layoutParams.setMarginStart(
- mContext.getResources().getDimensionPixelSize(startMarginResId));
- v.setLayoutParams(layoutParams);
- }
+ mPrimaryActionIconDrawable = drawable;
+ mPrimaryActionIconResId = iconResId;
- // Helper method to set end margin of seekbar/text.
- private void setViewEndMargin(View v) {
- RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams) v.getLayoutParams();
- int endMargin = 0;
- switch (mSupplementalActionType) {
- case SUPPLEMENTAL_ACTION_NO_ACTION:
- // Aligned to parent end with margin.
- layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
- layoutParams.removeRule(RelativeLayout.START_OF);
- layoutParams.setMarginEnd(mContext.getResources().getDimensionPixelSize(
- R.dimen.car_keyline_1));
- break;
- case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON:
- // Align to start of divider with padding.
- layoutParams.addRule(RelativeLayout.START_OF, R.id.supplemental_icon_divider);
- layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
- layoutParams.setMarginEnd(mContext.getResources().getDimensionPixelSize(
- R.dimen.car_padding_4));
- break;
- case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER:
- // Align to parent end with a margin as if the icon and an optional divider were
- // present. We do this by setting
+ markDirty();
+ }
- // Add divider padding to icon, and width of divider.
- endMargin += mContext.getResources().getDimensionPixelSize(
- R.dimen.car_padding_4);
- endMargin += mContext.getResources().getDimensionPixelSize(
- R.dimen.car_vertical_line_divider_width);
- // Fall through.
- case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON:
- // Add view padding, width of icon, and icon end margin.
- endMargin += mContext.getResources().getDimensionPixelSize(
- R.dimen.car_padding_4);
- endMargin += mContext.getResources().getDimensionPixelSize(
- R.dimen.car_primary_icon_size);
- endMargin += mContext.getResources().getDimensionPixelSize(
- R.dimen.car_keyline_1);
+ /**
+ * Sets {@code Primary Action} to be empty icon.
+ *
+ * {@code Seekbar} would have a start margin as if {@code Primary Action} were set as icon.
+ */
+ public void setPrimaryActionEmptyIcon() {
+ mPrimaryActionType = PRIMARY_ACTION_TYPE_EMPTY_ICON;
- layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
- layoutParams.removeRule(RelativeLayout.START_OF);
- layoutParams.setMarginEnd(endMargin);
- break;
- default:
- throw new IllegalStateException("Unknown supplemental action type.");
- }
- v.setLayoutParams(layoutParams);
- }
+ markDirty();
+ }
- private void setSupplementalActionLayout() {
- int keyline1 = mContext.getResources().getDimensionPixelSize(R.dimen.car_keyline_1);
- int padding4 = mContext.getResources().getDimensionPixelSize(R.dimen.car_padding_4);
- mBinders.add(vh -> {
- RelativeLayout.LayoutParams iconLayoutParams =
- (RelativeLayout.LayoutParams) vh.getSupplementalIcon().getLayoutParams();
- // Align to parent end with margin.
- iconLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
- iconLayoutParams.setMarginEnd(keyline1);
- iconLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
+ /**
+ * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
+ */
+ public void setSupplementalIcon(int iconResId, boolean showSupplementalIconDivider) {
+ setSupplementalIcon(iconResId, showSupplementalIconDivider, null);
+ }
- vh.getSupplementalIcon().setLayoutParams(iconLayoutParams);
+ /**
+ * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
+ */
+ public void setSupplementalIcon(@IdRes int iconResId,
+ boolean showSupplementalIconDivider, @Nullable View.OnClickListener listener) {
+ mSupplementalActionType = SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON;
- // Divider aligns to the start of supplemental icon with margin.
- RelativeLayout.LayoutParams dividerLayoutParams =
- (RelativeLayout.LayoutParams) vh.getSupplementalIconDivider()
- .getLayoutParams();
- dividerLayoutParams.addRule(RelativeLayout.START_OF, R.id.supplemental_icon);
- dividerLayoutParams.setMarginEnd(padding4);
- dividerLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
+ mSupplementalIconResId = iconResId;
+ mShowSupplementalIconDivider = showSupplementalIconDivider;
+ mSupplementalIconOnClickListener = listener;
- vh.getSupplementalIconDivider().setLayoutParams(dividerLayoutParams);
- });
- }
+ markDirty();
+ }
- private void setSupplementalActionContent() {
- switch (mSupplementalActionType) {
- case SUPPLEMENTAL_ACTION_NO_ACTION:
- case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER:
- case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON:
- break;
- case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON:
- mBinders.add(vh -> {
- vh.getSupplementalIcon().setVisibility(View.VISIBLE);
- if (mShowSupplementalIconDivider) {
- vh.getSupplementalIconDivider().setVisibility(View.VISIBLE);
- }
+ /**
+ * Sets {@code Supplemental Action} to be empty icon.
+ *
+ * {@code Seekbar} would have an end margin as if {@code Supplemental Action} were set.
+ */
+ public void setSupplementalEmptyIcon(boolean seekbarOffsetDividerWidth) {
+ mSupplementalActionType = seekbarOffsetDividerWidth
+ ? SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER
+ : SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON;
+ markDirty();
+ }
- vh.getSupplementalIcon().setImageResource(mSupplementalIconResId);
- vh.getSupplementalIcon().setOnClickListener(
- mSupplementalIconOnClickListener);
- vh.getSupplementalIcon().setClickable(
- mSupplementalIconOnClickListener != null);
- });
- break;
- default:
- throw new IllegalStateException("Unknown supplemental action type.");
- }
- }
-
- /**
- * Sets {@code Primary Action} to be represented by an icon.
- *
- * @param iconResId the resource identifier of the drawable.
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withPrimaryActionIcon(@DrawableRes int iconResId) {
- return withPrimaryActionIcon(null, iconResId);
- }
-
- /**
- * Sets {@code Primary Action} to be represented by an icon.
- *
- * @param drawable the Drawable to set, or null to clear the content.
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withPrimaryActionIcon(Drawable drawable) {
- return withPrimaryActionIcon(drawable, 0);
- }
-
- private Builder withPrimaryActionIcon(Drawable drawable, @DrawableRes int iconResId) {
- mPrimaryActionType = PRIMARY_ACTION_TYPE_SMALL_ICON;
-
- mPrimaryActionIconDrawable = drawable;
- mPrimaryActionIconResId = iconResId;
- return this;
- }
-
- /**
- * Sets {@code Primary Action} to be empty icon.
- *
- * {@code Seekbar} would have a start margin as if {@code Primary Action} were set.
- *
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withPrimaryActionEmptyIcon() {
- mPrimaryActionType = PRIMARY_ACTION_TYPE_EMPTY_ICON;
- return this;
- }
-
- /**
- * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
- *
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withSupplementalIcon(int iconResId, boolean showSupplementalIconDivider) {
- return withSupplementalIcon(iconResId, showSupplementalIconDivider, null);
- }
-
- /**
- * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
- *
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withSupplementalIcon(@IdRes int iconResId,
- boolean showSupplementalIconDivider, @Nullable View.OnClickListener listener) {
- mSupplementalActionType = SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON;
-
- mSupplementalIconResId = iconResId;
- mShowSupplementalIconDivider = showSupplementalIconDivider;
- mSupplementalIconOnClickListener = listener;
- return this;
- }
-
- /**
- * Sets {@code Supplemental Action} to be empty icon.
- *
- * {@code Seekbar} would have an end margin as if {@code Supplemental Action} were set.
- *
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withSupplementalEmptyIcon(boolean seekbarOffsetDividerWidth) {
- mSupplementalActionType = seekbarOffsetDividerWidth
- ? SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER
- : SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON;
- return this;
- }
-
- /**
- * Instructs the Builder to always hide the item divider coming after this ListItem.
- *
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withDividerHidden() {
- mHideDivider = true;
- return this;
- }
-
-
- /**
- * Adds {@code ViewBinder} to interact with sub-views in {@link ViewHolder}. These view
- * binders will always be applied after other {@link Builder} methods are called.
- *
- * <p>Make sure to call with...() method on the intended sub-view first.
- *
- * <p>Example:
- * <pre>
- * {@code
- * new Builder()
- * .withPrimaryActionIcon(R.drawable.icon)
- * .withViewBinder((viewHolder) -> {
- * viewHolder.getPrimaryIcon().doMoreStuff();
- * })
- * .build();
- * }
- * </pre>
- */
- public Builder withViewBinder(ViewBinder<ViewHolder> viewBinder) {
- mCustomBinders.add(viewBinder);
- return this;
- }
+ /**
+ * Adds {@code ViewBinder} to interact with sub-views in {@link ViewHolder}. These ViewBinders
+ * will always be applied after other {@code setFoobar} methods have bound.
+ *
+ * <p>Make sure to call setFoobar() method on the intended sub-view first.
+ *
+ * <p>Example:
+ * <pre>
+ * {@code
+ * SeekbarListItem item = new SeebarListItem(context);
+ * item.setPrimaryActionIcon(R.drawable.icon);
+ * item.addViewBinder((viewHolder) -> {
+ * viewHolder.getPrimaryIcon().doMoreStuff();
+ * });
+ * }
+ * </pre>
+ */
+ public void addViewBinder(ViewBinder<ViewHolder> viewBinder) {
+ mCustomBinders.add(viewBinder);
+ markDirty();
}
/**
diff --git a/car/src/main/java/androidx/car/widget/TextListItem.java b/car/src/main/java/androidx/car/widget/TextListItem.java
index a79f582..71b6c96 100644
--- a/car/src/main/java/androidx/car/widget/TextListItem.java
+++ b/car/src/main/java/androidx/car/widget/TextListItem.java
@@ -50,29 +50,86 @@
* <li>{@code Primary Action}: represented by an icon of following types.
* <ul>
* <li>Primary Icon - icon size could be large or small.
- * <li>No Icon
- * <li>Empty Icon - different from No Icon by how much margin {@code Text} offsets
+ * <li>No Icon - no icon is shown.
+ * <li>Empty Icon - {@code Text} offsets start space as if there was an icon.
* </ul>
- * <li>{@code Text}: supports any combination of the follow text views.
+ * <li>{@code Text}: supports any combination of the following text views.
* <ul>
* <li>Title
* <li>Body
* </ul>
- * <li>{@code Supplemental Action(s)}: represented by one of the following types; aligned toward
+ * <li>{@code Supplemental Action}: represented by one of the following types; aligned toward
* the end of item.
* <ul>
* <li>Supplemental Icon
* <li>One Action Button
* <li>Two Action Buttons
- * <li>Switch</li>
+ * <li>Switch
* </ul>
* </ul>
*
- * {@link TextListItem} can be built through its {@link TextListItem.Builder}. It binds data
- * to {@link ViewHolder} based on components selected.
+ * <p>{@code TextListItem} binds data to {@link ViewHolder} based on components selected.
+ *
+ * <p>When conflicting setter methods are called (e.g. setting primary action to both primary icon
+ * and no icon), the last called method wins.
*/
public class TextListItem extends ListItem<TextListItem.ViewHolder> {
+ @Retention(SOURCE)
+ @IntDef({
+ PRIMARY_ACTION_TYPE_NO_ICON, PRIMARY_ACTION_TYPE_EMPTY_ICON,
+ PRIMARY_ACTION_TYPE_LARGE_ICON, PRIMARY_ACTION_TYPE_SMALL_ICON})
+ private @interface PrimaryActionType {}
+
+ private static final int PRIMARY_ACTION_TYPE_NO_ICON = 0;
+ private static final int PRIMARY_ACTION_TYPE_EMPTY_ICON = 1;
+ private static final int PRIMARY_ACTION_TYPE_LARGE_ICON = 2;
+ private static final int PRIMARY_ACTION_TYPE_SMALL_ICON = 3;
+
+ @Retention(SOURCE)
+ @IntDef({SUPPLEMENTAL_ACTION_NO_ACTION, SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON,
+ SUPPLEMENTAL_ACTION_ONE_ACTION, SUPPLEMENTAL_ACTION_TWO_ACTIONS,
+ SUPPLEMENTAL_ACTION_SWITCH})
+ private @interface SupplementalActionType {}
+
+ private static final int SUPPLEMENTAL_ACTION_NO_ACTION = 0;
+ private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON = 1;
+ private static final int SUPPLEMENTAL_ACTION_ONE_ACTION = 2;
+ private static final int SUPPLEMENTAL_ACTION_TWO_ACTIONS = 3;
+ private static final int SUPPLEMENTAL_ACTION_SWITCH = 4;
+
+ private final Context mContext;
+
+ private final List<ViewBinder<ViewHolder>> mBinders = new ArrayList<>();
+ // Store custom binders separately so they will bind after binders created by setters.
+ private final List<ViewBinder<ViewHolder>> mCustomBinders = new ArrayList<>();
+
+ private View.OnClickListener mOnClickListener;
+
+ @PrimaryActionType private int mPrimaryActionType = PRIMARY_ACTION_TYPE_NO_ICON;
+ private int mPrimaryActionIconResId;
+ private Drawable mPrimaryActionIconDrawable;
+
+ private String mTitle;
+ private String mBody;
+ private boolean mIsBodyPrimary;
+
+ @SupplementalActionType private int mSupplementalActionType = SUPPLEMENTAL_ACTION_NO_ACTION;
+ private int mSupplementalIconResId;
+ private View.OnClickListener mSupplementalIconOnClickListener;
+ private boolean mShowSupplementalIconDivider;
+
+ private boolean mSwitchChecked;
+ private boolean mShowSwitchDivider;
+ private CompoundButton.OnCheckedChangeListener mSwitchOnCheckedChangeListener;
+
+ private String mAction1Text;
+ private View.OnClickListener mAction1OnClickListener;
+ private boolean mShowAction1Divider;
+ private String mAction2Text;
+ private View.OnClickListener mAction2OnClickListener;
+ private boolean mShowAction2Divider;
+
/**
* Creates a {@link TextListItem.ViewHolder}.
*/
@@ -80,10 +137,9 @@
return new ViewHolder(itemView);
}
- private Builder mBuilder;
-
- private TextListItem(Builder builder) {
- mBuilder = builder;
+ public TextListItem(Context context) {
+ mContext = context;
+ markDirty();
}
/**
@@ -95,12 +151,29 @@
}
/**
- * Applies all {@link ViewBinder} to {@code viewHolder}.
+ * Applies all {@link ViewBinder} to {@code ViewHolder}.
*/
@Override
public void bind(ViewHolder viewHolder) {
+ if (isDirty()) {
+ mBinders.clear();
+
+ // Create binders that adjust layout params of each view.
+ setItemLayoutHeight();
+ setPrimaryAction();
+ setText();
+ setSupplementalActions();
+ setOnClickListener();
+
+ // Custom view binders are always applied after the one created by this class.
+ mBinders.addAll(mCustomBinders);
+
+ markClean();
+ }
+
+ // Hide all subviews then apply view binders to adjust subviews.
setAllSubViewsGone(viewHolder);
- for (ViewBinder binder : mBuilder.mBinders) {
+ for (ViewBinder binder : mBinders) {
binder.bind(viewHolder);
}
}
@@ -118,660 +191,543 @@
}
/**
- * Returns whether the divider that comes after the ListItem should be hidden or not.
- *
- * <p>Note: For this to work, one must invoke
- * {@code PagedListView.setDividerVisibilityManager(adapter} for {@link ListItemAdapter} and
- * have dividers enabled on {@link PagedListView}.
- *
- * @return {@code true} if divider coming after the item should be hidden, {@code false}
- * otherwise.
+ * Sets the height of item depending on which text field is set.
*/
- @Override
- public boolean shouldHideDivider() {
- return mBuilder.mHideDivider;
+ private void setItemLayoutHeight() {
+ if (TextUtils.isEmpty(mBody)) {
+ // If the item only has title or no text, it uses fixed-height as single line.
+ int height = mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_single_line_list_item_height);
+ mBinders.add(vh -> {
+ ViewGroup.LayoutParams layoutParams = vh.itemView.getLayoutParams();
+ layoutParams.height = height;
+ vh.itemView.requestLayout();
+ });
+ } else {
+ // If body is present, the item should be at least as tall as min height, and wraps
+ // content.
+ int minHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_double_line_list_item_height);
+ mBinders.add(vh -> {
+ vh.itemView.setMinimumHeight(minHeight);
+ vh.getContainerLayout().setMinimumHeight(minHeight);
+
+ ViewGroup.LayoutParams layoutParams = vh.itemView.getLayoutParams();
+ layoutParams.height = RecyclerView.LayoutParams.WRAP_CONTENT;
+ vh.itemView.requestLayout();
+ });
+ }
+ }
+
+ private void setPrimaryAction() {
+ setPrimaryIconContent();
+ setPrimaryIconLayout();
+ }
+
+ private void setText() {
+ setTextContent();
+ setTextVerticalMargin();
+ // Only set start margin because text end is relative to the start of supplemental actions.
+ setTextStartMargin();
+ }
+
+ private void setOnClickListener() {
+ if (mOnClickListener != null) {
+ mBinders.add(vh -> vh.itemView.setOnClickListener(mOnClickListener));
+ }
+ }
+
+ private void setPrimaryIconContent() {
+ switch (mPrimaryActionType) {
+ case PRIMARY_ACTION_TYPE_SMALL_ICON:
+ case PRIMARY_ACTION_TYPE_LARGE_ICON:
+ mBinders.add(vh -> {
+ vh.getPrimaryIcon().setVisibility(View.VISIBLE);
+
+ if (mPrimaryActionIconDrawable != null) {
+ vh.getPrimaryIcon().setImageDrawable(mPrimaryActionIconDrawable);
+ } else if (mPrimaryActionIconResId != 0) {
+ vh.getPrimaryIcon().setImageResource(mPrimaryActionIconResId);
+ }
+ });
+ break;
+ case PRIMARY_ACTION_TYPE_EMPTY_ICON:
+ case PRIMARY_ACTION_TYPE_NO_ICON:
+ // Do nothing.
+ break;
+ default:
+ throw new IllegalStateException("Unrecognizable primary action type.");
+ }
}
/**
- * Builds a {@link TextListItem}.
+ * Sets layout params of primary icon.
*
- * <p>When conflicting methods are called, e.g. setting primary action to both primary icon and
- * no icon, the last called method wins.
+ * <p>Large icon will have no start margin, and always align center vertically.
+ *
+ * <p>Small icon will have start margin. When body text is present small icon uses a top
+ * margin otherwise align center vertically.
*/
- public static class Builder {
-
- @Retention(SOURCE)
- @IntDef({
- PRIMARY_ACTION_TYPE_NO_ICON, PRIMARY_ACTION_TYPE_EMPTY_ICON,
- PRIMARY_ACTION_TYPE_LARGE_ICON, PRIMARY_ACTION_TYPE_SMALL_ICON})
- private @interface PrimaryActionType {}
-
- private static final int PRIMARY_ACTION_TYPE_NO_ICON = 0;
- private static final int PRIMARY_ACTION_TYPE_EMPTY_ICON = 1;
- private static final int PRIMARY_ACTION_TYPE_LARGE_ICON = 2;
- private static final int PRIMARY_ACTION_TYPE_SMALL_ICON = 3;
-
- @Retention(SOURCE)
- @IntDef({SUPPLEMENTAL_ACTION_NO_ACTION, SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON,
- SUPPLEMENTAL_ACTION_ONE_ACTION, SUPPLEMENTAL_ACTION_TWO_ACTIONS,
- SUPPLEMENTAL_ACTION_SWITCH})
- private @interface SupplementalActionType {}
-
- private static final int SUPPLEMENTAL_ACTION_NO_ACTION = 0;
- private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON = 1;
- private static final int SUPPLEMENTAL_ACTION_ONE_ACTION = 2;
- private static final int SUPPLEMENTAL_ACTION_TWO_ACTIONS = 3;
- private static final int SUPPLEMENTAL_ACTION_SWITCH = 4;
-
- private final Context mContext;
-
- // tag for indicating whether to hide the divider
- private boolean mHideDivider;
-
- private final List<ViewBinder<ViewHolder>> mBinders = new ArrayList<>();
- // Store custom binders separately so they will bind after binders are created in build().
- private final List<ViewBinder<ViewHolder>> mCustomBinders =
- new ArrayList<>();
-
- private View.OnClickListener mOnClickListener;
-
- @PrimaryActionType private int mPrimaryActionType = PRIMARY_ACTION_TYPE_NO_ICON;
- private int mPrimaryActionIconResId;
- private Drawable mPrimaryActionIconDrawable;
-
- private String mTitle;
- private String mBody;
- private boolean mIsBodyPrimary;
-
- @SupplementalActionType private int mSupplementalActionType = SUPPLEMENTAL_ACTION_NO_ACTION;
- private int mSupplementalIconResId;
- private View.OnClickListener mSupplementalIconOnClickListener;
- private boolean mShowSupplementalIconDivider;
-
- private boolean mSwitchChecked;
- private boolean mShowSwitchDivider;
- private CompoundButton.OnCheckedChangeListener mSwitchOnCheckedChangeListener;
-
- private String mAction1Text;
- private View.OnClickListener mAction1OnClickListener;
- private boolean mShowAction1Divider;
- private String mAction2Text;
- private View.OnClickListener mAction2OnClickListener;
- private boolean mShowAction2Divider;
-
- public Builder(Context context) {
- mContext = context;
- }
-
- /**
- * Builds a {@link TextListItem}.
- */
- public TextListItem build() {
- setItemLayoutHeight();
- setPrimaryAction();
- setText();
- setSupplementalActions();
- setOnClickListener();
-
- mBinders.addAll(mCustomBinders);
-
- return new TextListItem(this);
- }
-
- /**
- * Sets the height of item depending on which text field is set.
- */
- private void setItemLayoutHeight() {
- if (TextUtils.isEmpty(mBody)) {
- // If the item only has title or no text, it uses fixed-height as single line.
- int height = mContext.getResources().getDimensionPixelSize(
- R.dimen.car_single_line_list_item_height);
+ private void setPrimaryIconLayout() {
+ // Set all relevant fields in layout params to avoid carried over params when the item
+ // gets bound to a recycled view holder.
+ switch (mPrimaryActionType) {
+ case PRIMARY_ACTION_TYPE_SMALL_ICON:
mBinders.add(vh -> {
- ViewGroup.LayoutParams layoutParams = vh.itemView.getLayoutParams();
- layoutParams.height = height;
- vh.itemView.setLayoutParams(layoutParams);
- });
- } else {
- // If body is present, the item should be at least as tall as min height, and wraps
- // content.
- int minHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.car_double_line_list_item_height);
- mBinders.add(vh -> {
- vh.itemView.setMinimumHeight(minHeight);
- vh.getContainerLayout().setMinimumHeight(minHeight);
+ int iconSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_primary_icon_size);
+ // Icon size.
+ RelativeLayout.LayoutParams layoutParams =
+ (RelativeLayout.LayoutParams) vh.getPrimaryIcon().getLayoutParams();
+ layoutParams.height = layoutParams.width = iconSize;
- RecyclerView.LayoutParams layoutParams =
- (RecyclerView.LayoutParams) vh.itemView.getLayoutParams();
- layoutParams.height = RecyclerView.LayoutParams.WRAP_CONTENT;
- vh.itemView.setLayoutParams(layoutParams);
- });
- }
- }
+ // Start margin.
+ layoutParams.setMarginStart(mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_keyline_1));
- private void setPrimaryAction() {
- setPrimaryIconContent();
- setPrimaryIconLayout();
- }
-
- private void setText() {
- setTextContent();
- setTextVerticalMargin();
- // Only setting start margin because text end is relative to the start of supplemental
- // actions.
- setTextStartMargin();
- }
-
- private void setOnClickListener() {
- if (mOnClickListener != null) {
- mBinders.add(vh -> vh.itemView.setOnClickListener(mOnClickListener));
- }
- }
-
- private void setPrimaryIconContent() {
- switch (mPrimaryActionType) {
- case PRIMARY_ACTION_TYPE_SMALL_ICON:
- case PRIMARY_ACTION_TYPE_LARGE_ICON:
- mBinders.add(vh -> {
- vh.getPrimaryIcon().setVisibility(View.VISIBLE);
-
- if (mPrimaryActionIconDrawable != null) {
- vh.getPrimaryIcon().setImageDrawable(mPrimaryActionIconDrawable);
- } else if (mPrimaryActionIconResId != 0) {
- vh.getPrimaryIcon().setImageResource(mPrimaryActionIconResId);
- }
- });
- break;
- case PRIMARY_ACTION_TYPE_EMPTY_ICON:
- case PRIMARY_ACTION_TYPE_NO_ICON:
- // Do nothing.
- break;
- default:
- throw new IllegalStateException("Unrecognizable primary action type.");
- }
- }
-
- /**
- * Sets layout params of primary icon.
- *
- * <p>Large icon will have no start margin, and always align center vertically.
- *
- * <p>Small icon will have start margin. When body text is present small icon uses a top
- * margin otherwise align center vertically.
- */
- private void setPrimaryIconLayout() {
- // Set all relevant fields in layout params to avoid carried over params when the item
- // gets bound to a recycled view holder.
- switch (mPrimaryActionType) {
- case PRIMARY_ACTION_TYPE_SMALL_ICON:
- mBinders.add(vh -> {
- int iconSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.car_primary_icon_size);
- // Icon size.
- RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams) vh.getPrimaryIcon().getLayoutParams();
- layoutParams.height = layoutParams.width = iconSize;
-
- // Start margin.
- layoutParams.setMarginStart(mContext.getResources().getDimensionPixelSize(
- R.dimen.car_keyline_1));
-
- if (!TextUtils.isEmpty(mBody)) {
- // Set icon top margin so that the icon remains in the same position it
- // would've been in for non-long-text item, namely so that the center
- // line of icon matches that of line item.
- layoutParams.removeRule(RelativeLayout.CENTER_VERTICAL);
- int itemHeight = mContext.getResources().getDimensionPixelSize(
- R.dimen.car_double_line_list_item_height);
- layoutParams.topMargin = (itemHeight - iconSize) / 2;
- } else {
- // If the icon can be centered vertically, leave the work for framework.
- layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
- layoutParams.topMargin = 0;
- }
- vh.getPrimaryIcon().setLayoutParams(layoutParams);
- });
- break;
- case PRIMARY_ACTION_TYPE_LARGE_ICON:
- mBinders.add(vh -> {
- int iconSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.car_single_line_list_item_height);
- // Icon size.
- RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams) vh.getPrimaryIcon().getLayoutParams();
- layoutParams.height = layoutParams.width = iconSize;
-
- // No start margin.
- layoutParams.setMarginStart(0);
-
- // Always centered vertically.
+ if (!TextUtils.isEmpty(mBody)) {
+ // Set icon top margin so that the icon remains in the same position it
+ // would've been in for non-long-text item, namely so that the center
+ // line of icon matches that of line item.
+ layoutParams.removeRule(RelativeLayout.CENTER_VERTICAL);
+ int itemHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_double_line_list_item_height);
+ layoutParams.topMargin = (itemHeight - iconSize) / 2;
+ } else {
+ // If the icon can be centered vertically, leave the work for framework.
layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
layoutParams.topMargin = 0;
+ }
+ vh.getPrimaryIcon().requestLayout();
+ });
+ break;
+ case PRIMARY_ACTION_TYPE_LARGE_ICON:
+ mBinders.add(vh -> {
+ int iconSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_single_line_list_item_height);
+ // Icon size.
+ RelativeLayout.LayoutParams layoutParams =
+ (RelativeLayout.LayoutParams) vh.getPrimaryIcon().getLayoutParams();
+ layoutParams.height = layoutParams.width = iconSize;
- vh.getPrimaryIcon().setLayoutParams(layoutParams);
- });
- break;
- case PRIMARY_ACTION_TYPE_EMPTY_ICON:
- case PRIMARY_ACTION_TYPE_NO_ICON:
- // Do nothing.
- break;
- default:
- throw new IllegalStateException("Unrecognizable primary action type.");
- }
+ // No start margin.
+ layoutParams.setMarginStart(0);
+
+ // Always centered vertically.
+ layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
+ layoutParams.topMargin = 0;
+
+ vh.getPrimaryIcon().requestLayout();
+ });
+ break;
+ case PRIMARY_ACTION_TYPE_EMPTY_ICON:
+ case PRIMARY_ACTION_TYPE_NO_ICON:
+ // Do nothing.
+ break;
+ default:
+ throw new IllegalStateException("Unrecognizable primary action type.");
}
+ }
- private void setTextContent() {
- if (!TextUtils.isEmpty(mTitle)) {
- mBinders.add(vh -> {
- vh.getTitle().setVisibility(View.VISIBLE);
- vh.getTitle().setText(mTitle);
- });
- }
- if (!TextUtils.isEmpty(mBody)) {
- mBinders.add(vh -> {
- vh.getBody().setVisibility(View.VISIBLE);
- vh.getBody().setText(mBody);
- });
- }
-
- if (mIsBodyPrimary) {
- mBinders.add(vh -> {
- vh.getTitle().setTextAppearance(R.style.CarBody2);
- vh.getBody().setTextAppearance(R.style.CarBody1);
- });
- } else {
- mBinders.add(vh -> {
- vh.getTitle().setTextAppearance(R.style.CarBody1);
- vh.getBody().setTextAppearance(R.style.CarBody2);
- });
- }
- }
-
- /**
- * Sets start margin of text view depending on icon type.
- */
- private void setTextStartMargin() {
- final int startMarginResId;
- switch (mPrimaryActionType) {
- case PRIMARY_ACTION_TYPE_NO_ICON:
- startMarginResId = R.dimen.car_keyline_1;
- break;
- case PRIMARY_ACTION_TYPE_EMPTY_ICON:
- startMarginResId = R.dimen.car_keyline_3;
- break;
- case PRIMARY_ACTION_TYPE_SMALL_ICON:
- startMarginResId = R.dimen.car_keyline_3;
- break;
- case PRIMARY_ACTION_TYPE_LARGE_ICON:
- startMarginResId = R.dimen.car_keyline_4;
- break;
- default:
- throw new IllegalStateException("Unrecognizable primary action type.");
- }
- int startMargin = mContext.getResources().getDimensionPixelSize(startMarginResId);
+ private void setTextContent() {
+ if (!TextUtils.isEmpty(mTitle)) {
mBinders.add(vh -> {
- RelativeLayout.LayoutParams titleLayoutParams =
- (RelativeLayout.LayoutParams) vh.getTitle().getLayoutParams();
- titleLayoutParams.setMarginStart(startMargin);
- vh.getTitle().setLayoutParams(titleLayoutParams);
-
- RelativeLayout.LayoutParams bodyLayoutParams =
- (RelativeLayout.LayoutParams) vh.getBody().getLayoutParams();
- bodyLayoutParams.setMarginStart(startMargin);
- vh.getBody().setLayoutParams(bodyLayoutParams);
+ vh.getTitle().setVisibility(View.VISIBLE);
+ vh.getTitle().setText(mTitle);
+ });
+ }
+ if (!TextUtils.isEmpty(mBody)) {
+ mBinders.add(vh -> {
+ vh.getBody().setVisibility(View.VISIBLE);
+ vh.getBody().setText(mBody);
});
}
- /**
- * Sets top/bottom margins of {@code Title} and {@code Body}.
- */
- private void setTextVerticalMargin() {
- // Set all relevant fields in layout params to avoid carried over params when the item
- // gets bound to a recycled view holder.
- if (!TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mBody)) {
- // Title only - view is aligned center vertically by itself.
+ if (mIsBodyPrimary) {
+ mBinders.add(vh -> {
+ vh.getTitle().setTextAppearance(R.style.CarBody2);
+ vh.getBody().setTextAppearance(R.style.CarBody1);
+ });
+ } else {
+ mBinders.add(vh -> {
+ vh.getTitle().setTextAppearance(R.style.CarBody1);
+ vh.getBody().setTextAppearance(R.style.CarBody2);
+ });
+ }
+ }
+
+ /**
+ * Sets start margin of text view depending on icon type.
+ */
+ private void setTextStartMargin() {
+ final int startMarginResId;
+ switch (mPrimaryActionType) {
+ case PRIMARY_ACTION_TYPE_NO_ICON:
+ startMarginResId = R.dimen.car_keyline_1;
+ break;
+ case PRIMARY_ACTION_TYPE_EMPTY_ICON:
+ startMarginResId = R.dimen.car_keyline_3;
+ break;
+ case PRIMARY_ACTION_TYPE_SMALL_ICON:
+ startMarginResId = R.dimen.car_keyline_3;
+ break;
+ case PRIMARY_ACTION_TYPE_LARGE_ICON:
+ startMarginResId = R.dimen.car_keyline_4;
+ break;
+ default:
+ throw new IllegalStateException("Unrecognizable primary action type.");
+ }
+ int startMargin = mContext.getResources().getDimensionPixelSize(startMarginResId);
+ mBinders.add(vh -> {
+ RelativeLayout.LayoutParams titleLayoutParams =
+ (RelativeLayout.LayoutParams) vh.getTitle().getLayoutParams();
+ titleLayoutParams.setMarginStart(startMargin);
+ vh.getTitle().requestLayout();
+
+ RelativeLayout.LayoutParams bodyLayoutParams =
+ (RelativeLayout.LayoutParams) vh.getBody().getLayoutParams();
+ bodyLayoutParams.setMarginStart(startMargin);
+ vh.getBody().requestLayout();
+ });
+ }
+
+ /**
+ * Sets top/bottom margins of {@code Title} and {@code Body}.
+ */
+ private void setTextVerticalMargin() {
+ // Set all relevant fields in layout params to avoid carried over params when the item
+ // gets bound to a recycled view holder.
+ if (!TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mBody)) {
+ // Title only - view is aligned center vertically by itself.
+ mBinders.add(vh -> {
+ RelativeLayout.LayoutParams layoutParams =
+ (RelativeLayout.LayoutParams) vh.getTitle().getLayoutParams();
+ layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
+ layoutParams.topMargin = 0;
+ vh.getTitle().requestLayout();
+ });
+ } else if (TextUtils.isEmpty(mTitle) && !TextUtils.isEmpty(mBody)) {
+ mBinders.add(vh -> {
+ // Body uses top and bottom margin.
+ int margin = mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_padding_3);
+ RelativeLayout.LayoutParams layoutParams =
+ (RelativeLayout.LayoutParams) vh.getBody().getLayoutParams();
+ layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
+ layoutParams.removeRule(RelativeLayout.BELOW);
+ layoutParams.topMargin = margin;
+ layoutParams.bottomMargin = margin;
+ vh.getBody().requestLayout();
+ });
+ } else {
+ mBinders.add(vh -> {
+ // Title has a top margin
+ Resources resources = mContext.getResources();
+ int padding1 = resources.getDimensionPixelSize(R.dimen.car_padding_1);
+ int padding3 = resources.getDimensionPixelSize(R.dimen.car_padding_3);
+
+ RelativeLayout.LayoutParams titleLayoutParams =
+ (RelativeLayout.LayoutParams) vh.getTitle().getLayoutParams();
+ titleLayoutParams.removeRule(RelativeLayout.CENTER_VERTICAL);
+ titleLayoutParams.topMargin = padding3;
+ vh.getTitle().requestLayout();
+ // Body is below title with a margin, and has bottom margin.
+ RelativeLayout.LayoutParams bodyLayoutParams =
+ (RelativeLayout.LayoutParams) vh.getBody().getLayoutParams();
+ bodyLayoutParams.removeRule(RelativeLayout.CENTER_VERTICAL);
+ bodyLayoutParams.addRule(RelativeLayout.BELOW, R.id.title);
+ bodyLayoutParams.topMargin = padding1;
+ bodyLayoutParams.bottomMargin = padding3;
+ vh.getBody().requestLayout();
+ });
+ }
+ }
+
+ /**
+ * Sets up view(s) for supplemental action.
+ */
+ private void setSupplementalActions() {
+ switch (mSupplementalActionType) {
+ case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON:
mBinders.add(vh -> {
- RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams) vh.getTitle().getLayoutParams();
- layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
- layoutParams.topMargin = 0;
- vh.getTitle().setLayoutParams(layoutParams);
+ vh.getSupplementalIcon().setVisibility(View.VISIBLE);
+ if (mShowSupplementalIconDivider) {
+ vh.getSupplementalIconDivider().setVisibility(View.VISIBLE);
+ }
+
+ vh.getSupplementalIcon().setImageResource(mSupplementalIconResId);
+ vh.getSupplementalIcon().setOnClickListener(
+ mSupplementalIconOnClickListener);
+ vh.getSupplementalIcon().setClickable(
+ mSupplementalIconOnClickListener != null);
});
- } else if (TextUtils.isEmpty(mTitle) && !TextUtils.isEmpty(mBody)) {
+ break;
+ case SUPPLEMENTAL_ACTION_TWO_ACTIONS:
mBinders.add(vh -> {
- // Body uses top and bottom margin.
- int margin = mContext.getResources().getDimensionPixelSize(
- R.dimen.car_padding_3);
- RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams) vh.getBody().getLayoutParams();
- layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
- layoutParams.removeRule(RelativeLayout.BELOW);
- layoutParams.topMargin = margin;
- layoutParams.bottomMargin = margin;
- vh.getBody().setLayoutParams(layoutParams);
+ vh.getAction2().setVisibility(View.VISIBLE);
+ if (mShowAction2Divider) {
+ vh.getAction2Divider().setVisibility(View.VISIBLE);
+ }
+
+ vh.getAction2().setText(mAction2Text);
+ vh.getAction2().setOnClickListener(mAction2OnClickListener);
});
- } else {
+ // Fall through
+ case SUPPLEMENTAL_ACTION_ONE_ACTION:
mBinders.add(vh -> {
- // Title has a top margin
- Resources resources = mContext.getResources();
- int padding1 = resources.getDimensionPixelSize(R.dimen.car_padding_1);
- int padding3 = resources.getDimensionPixelSize(R.dimen.car_padding_3);
+ vh.getAction1().setVisibility(View.VISIBLE);
+ if (mShowAction1Divider) {
+ vh.getAction1Divider().setVisibility(View.VISIBLE);
+ }
- RelativeLayout.LayoutParams titleLayoutParams =
- (RelativeLayout.LayoutParams) vh.getTitle().getLayoutParams();
- titleLayoutParams.removeRule(RelativeLayout.CENTER_VERTICAL);
- titleLayoutParams.topMargin = padding3;
- vh.getTitle().setLayoutParams(titleLayoutParams);
- // Body is below title with a margin, and has bottom margin.
- RelativeLayout.LayoutParams bodyLayoutParams =
- (RelativeLayout.LayoutParams) vh.getBody().getLayoutParams();
- bodyLayoutParams.removeRule(RelativeLayout.CENTER_VERTICAL);
- bodyLayoutParams.addRule(RelativeLayout.BELOW, R.id.title);
- bodyLayoutParams.topMargin = padding1;
- bodyLayoutParams.bottomMargin = padding3;
- vh.getBody().setLayoutParams(bodyLayoutParams);
+ vh.getAction1().setText(mAction1Text);
+ vh.getAction1().setOnClickListener(mAction1OnClickListener);
});
- }
+ break;
+ case SUPPLEMENTAL_ACTION_NO_ACTION:
+ // Do nothing
+ break;
+ case SUPPLEMENTAL_ACTION_SWITCH:
+ mBinders.add(vh -> {
+ vh.getSwitch().setVisibility(View.VISIBLE);
+ vh.getSwitch().setChecked(mSwitchChecked);
+ vh.getSwitch().setOnCheckedChangeListener(mSwitchOnCheckedChangeListener);
+ if (mShowSwitchDivider) {
+ vh.getSwitchDivider().setVisibility(View.VISIBLE);
+ }
+ });
+ break;
+ default:
+ throw new IllegalArgumentException("Unrecognized supplemental action type.");
}
+ }
- /**
- * Sets up view(s) for supplemental action.
- */
- private void setSupplementalActions() {
- switch (mSupplementalActionType) {
- case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON:
- mBinders.add(vh -> {
- vh.getSupplementalIcon().setVisibility(View.VISIBLE);
- if (mShowSupplementalIconDivider) {
- vh.getSupplementalIconDivider().setVisibility(View.VISIBLE);
- }
+ /**
+ * Sets {@link View.OnClickListener} of {@code TextListItem}.
+ */
+ public void setOnClickListener(View.OnClickListener listener) {
+ mOnClickListener = listener;
+ markDirty();
+ }
- vh.getSupplementalIcon().setImageResource(mSupplementalIconResId);
- vh.getSupplementalIcon().setOnClickListener(
- mSupplementalIconOnClickListener);
- vh.getSupplementalIcon().setClickable(
- mSupplementalIconOnClickListener != null);
- });
- break;
- case SUPPLEMENTAL_ACTION_TWO_ACTIONS:
- mBinders.add(vh -> {
- vh.getAction2().setVisibility(View.VISIBLE);
- if (mShowAction2Divider) {
- vh.getAction2Divider().setVisibility(View.VISIBLE);
- }
+ /**
+ * Sets {@code Primary Action} to be represented by an icon.
+ *
+ * @param iconResId the resource identifier of the drawable.
+ * @param useLargeIcon the size of primary icon. Large Icon is a square as tall as an item.
+ */
+ public void setPrimaryActionIcon(@DrawableRes int iconResId, boolean useLargeIcon) {
+ setPrimaryActionIcon(null, iconResId, useLargeIcon);
+ }
- vh.getAction2().setText(mAction2Text);
- vh.getAction2().setOnClickListener(mAction2OnClickListener);
- });
- // Fall through
- case SUPPLEMENTAL_ACTION_ONE_ACTION:
- mBinders.add(vh -> {
- vh.getAction1().setVisibility(View.VISIBLE);
- if (mShowAction1Divider) {
- vh.getAction1Divider().setVisibility(View.VISIBLE);
- }
+ /**
+ * Sets {@code Primary Action} to be represented by an icon.
+ *
+ * @param drawable the Drawable to set, or null to clear the content.
+ * @param useLargeIcon the size of primary icon. Large Icon is a square as tall as an item.
+ */
+ public void setPrimaryActionIcon(Drawable drawable, boolean useLargeIcon) {
+ setPrimaryActionIcon(drawable, 0, useLargeIcon);
+ }
- vh.getAction1().setText(mAction1Text);
- vh.getAction1().setOnClickListener(mAction1OnClickListener);
- });
- break;
- case SUPPLEMENTAL_ACTION_NO_ACTION:
- // Do nothing
- break;
- case SUPPLEMENTAL_ACTION_SWITCH:
- mBinders.add(vh -> {
- vh.getSwitch().setVisibility(View.VISIBLE);
- vh.getSwitch().setChecked(mSwitchChecked);
- vh.getSwitch().setOnCheckedChangeListener(mSwitchOnCheckedChangeListener);
- if (mShowSwitchDivider) {
- vh.getSwitchDivider().setVisibility(View.VISIBLE);
- }
- });
- break;
- default:
- throw new IllegalArgumentException("Unrecognized supplemental action type.");
- }
+ private void setPrimaryActionIcon(Drawable drawable, @DrawableRes int iconResId,
+ boolean useLargeIcon) {
+ mPrimaryActionType = useLargeIcon
+ ? PRIMARY_ACTION_TYPE_LARGE_ICON
+ : PRIMARY_ACTION_TYPE_SMALL_ICON;
+ mPrimaryActionIconResId = iconResId;
+ mPrimaryActionIconDrawable = drawable;
+
+ markDirty();
+ }
+
+ /**
+ * Sets {@code Primary Action} to be empty icon.
+ *
+ * <p>{@code Text} would have a start margin as if {@code Primary Action} were set to primary
+ * icon.
+ */
+ public void setPrimaryActionEmptyIcon() {
+ mPrimaryActionType = PRIMARY_ACTION_TYPE_EMPTY_ICON;
+ markDirty();
+ }
+
+ /**
+ * Sets {@code Primary Action} to have no icon. Text would align to the start of item.
+ */
+ public void setPrimaryActionNoIcon() {
+ mPrimaryActionType = PRIMARY_ACTION_TYPE_NO_ICON;
+ markDirty();
+ }
+
+ /**
+ * Sets the title of item.
+ *
+ * <p>Primary text is {@code Title} by default. It can be set by
+ * {@link #setBody(String, boolean)}
+ *
+ * <p>{@code Title} text is limited to one line, and ellipses at the end.
+ *
+ * @param title text to display as title.
+ */
+ public void setTitle(String title) {
+ mTitle = title;
+ markDirty();
+ }
+
+ /**
+ * Sets the body text of item.
+ *
+ * <p>Text beyond length required by regulation will be truncated. Defaults {@code Title}
+ * text as the primary.
+ * @param body text to be displayed.
+ */
+ public void setBody(String body) {
+ setBody(body, false);
+ }
+
+ /**
+ * Sets the body text of item.
+ *
+ * <p>Text beyond length required by regulation will be truncated.
+ *
+ * @param body text to be displayed.
+ * @param asPrimary sets {@code Body Text} as primary text of item.
+ */
+ public void setBody(String body, boolean asPrimary) {
+ int limit = mContext.getResources().getInteger(
+ R.integer.car_list_item_text_length_limit);
+ if (body.length() < limit) {
+ mBody = body;
+ } else {
+ mBody = body.substring(0, limit) + mContext.getString(R.string.ellipsis);
}
+ mIsBodyPrimary = asPrimary;
- /**
- * Instructs the Builder to always hide the item divider coming after this ListItem.
- *
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withDividerHidden() {
- mHideDivider = true;
- return this;
+ markDirty();
+ }
+
+ /**
+ * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
+ *
+ * @param iconResId drawable resource id.
+ * @param showDivider whether to display a vertical bar that separates {@code text} and
+ * {@code Supplemental Icon}.
+ */
+ public void setSupplementalIcon(int iconResId, boolean showDivider) {
+ setSupplementalIcon(iconResId, showDivider, null);
+ }
+
+ /**
+ * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
+ *
+ * @param iconResId drawable resource id.
+ * @param showDivider whether to display a vertical bar that separates {@code text} and
+ * {@code Supplemental Icon}.
+ * @param listener the callback that will run when icon is clicked.
+ */
+ public void setSupplementalIcon(int iconResId, boolean showDivider,
+ View.OnClickListener listener) {
+ mSupplementalActionType = SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON;
+
+ mSupplementalIconResId = iconResId;
+ mSupplementalIconOnClickListener = listener;
+ mShowSupplementalIconDivider = showDivider;
+ markDirty();
+ }
+
+ /**
+ * Sets {@code Supplemental Action} to be represented by an {@code Action Button}.
+ *
+ * @param text button text to display.
+ * @param showDivider whether to display a vertical bar that separates {@code Text} and
+ * {@code Action Button}.
+ * @param listener the callback that will run when action button is clicked.
+ */
+ public void setAction(String text, boolean showDivider, View.OnClickListener listener) {
+ if (TextUtils.isEmpty(text)) {
+ throw new IllegalArgumentException("Action text cannot be empty.");
}
-
- /**
- * Sets {@link View.OnClickListener} of {@code TextListItem}.
- *
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withOnClickListener(View.OnClickListener listener) {
- mOnClickListener = listener;
- return this;
+ if (listener == null) {
+ throw new IllegalArgumentException("Action OnClickListener cannot be null.");
}
+ mSupplementalActionType = SUPPLEMENTAL_ACTION_ONE_ACTION;
- /**
- * Sets {@code Primary Action} to be represented by an icon.
- *
- * @param iconResId the resource identifier of the drawable.
- * @param useLargeIcon the size of primary icon. Large Icon is a square as tall as an item
- * with only title set; useful for album cover art.
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withPrimaryActionIcon(@DrawableRes int iconResId, boolean useLargeIcon) {
- return withPrimaryActionIcon(null, iconResId, useLargeIcon);
+ mAction1Text = text;
+ mAction1OnClickListener = listener;
+ mShowAction1Divider = showDivider;
+
+ markDirty();
+ }
+
+ /**
+ * Sets {@code Supplemental Action} to be represented by two {@code Action Button}s.
+ *
+ * <p>These two action buttons will be aligned towards item end.
+ *
+ * @param action1Text button text to display - this button will be closer to item end.
+ * @param action2Text button text to display.
+ */
+ public void setActions(String action1Text, boolean showAction1Divider,
+ View.OnClickListener action1OnClickListener,
+ String action2Text, boolean showAction2Divider,
+ View.OnClickListener action2OnClickListener) {
+ if (TextUtils.isEmpty(action1Text) || TextUtils.isEmpty(action2Text)) {
+ throw new IllegalArgumentException("Action text cannot be empty.");
}
-
- /**
- * Sets {@code Primary Action} to be represented by an icon.
- *
- * @param drawable the Drawable to set, or null to clear the content.
- * @param useLargeIcon the size of primary icon. Large Icon is a square as tall as an item
- * with only title set; useful for album cover art.
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withPrimaryActionIcon(Drawable drawable, boolean useLargeIcon) {
- return withPrimaryActionIcon(drawable, 0, useLargeIcon);
+ if (action1OnClickListener == null || action2OnClickListener == null) {
+ throw new IllegalArgumentException("Action OnClickListener cannot be null.");
}
+ mSupplementalActionType = SUPPLEMENTAL_ACTION_TWO_ACTIONS;
- private Builder withPrimaryActionIcon(Drawable drawable, @DrawableRes int iconResId,
- boolean useLargeIcon) {
- mPrimaryActionType = useLargeIcon
- ? PRIMARY_ACTION_TYPE_LARGE_ICON
- : PRIMARY_ACTION_TYPE_SMALL_ICON;
- mPrimaryActionIconResId = iconResId;
- mPrimaryActionIconDrawable = drawable;
- return this;
- }
+ mAction1Text = action1Text;
+ mAction1OnClickListener = action1OnClickListener;
+ mShowAction1Divider = showAction1Divider;
+ mAction2Text = action2Text;
+ mAction2OnClickListener = action2OnClickListener;
+ mShowAction2Divider = showAction2Divider;
- /**
- * Sets {@code Primary Action} to be empty icon.
- *
- * {@code Text} would have a start margin as if {@code Primary Action} were set to
- * primary icon.
- *
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withPrimaryActionEmptyIcon() {
- mPrimaryActionType = PRIMARY_ACTION_TYPE_EMPTY_ICON;
- return this;
- }
+ markDirty();
+ }
- /**
- * Sets {@code Primary Action} to have no icon. Text would align to the start of item.
- *
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withPrimaryActionNoIcon() {
- mPrimaryActionType = PRIMARY_ACTION_TYPE_NO_ICON;
- return this;
- }
+ /**
+ * Sets {@code Supplemental Action} to be represented by a {@link android.widget.Switch}.
+ *
+ * @param checked initial value for switched.
+ * @param showDivider whether to display a vertical bar between switch and text.
+ * @param listener callback to be invoked when the checked state is markDirty.
+ */
+ public void setSwitch(boolean checked, boolean showDivider,
+ CompoundButton.OnCheckedChangeListener listener) {
+ mSupplementalActionType = SUPPLEMENTAL_ACTION_SWITCH;
- /**
- * Sets the title of item.
- *
- * <p>Primary text is {@code title} by default. It can be set by
- * {@link #withBody(String, boolean)}
- *
- * @param title text to display as title.
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withTitle(String title) {
- mTitle = title;
- return this;
- }
+ mSwitchChecked = checked;
+ mShowSwitchDivider = showDivider;
+ mSwitchOnCheckedChangeListener = listener;
- /**
- * Sets the body text of item.
- *
- * <p>Text beyond length required by regulation will be truncated. Defaults {@code Title}
- * text as the primary.
- *
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withBody(String body) {
- return withBody(body, false);
- }
+ markDirty();
+ }
- /**
- * Sets the body text of item.
- *
- * <p>Text beyond length required by regulation will be truncated.
- *
- * @param asPrimary sets {@code Body Text} as primary text of item.
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withBody(String body, boolean asPrimary) {
- int limit = mContext.getResources().getInteger(
- R.integer.car_list_item_text_length_limit);
- if (body.length() < limit) {
- mBody = body;
- } else {
- mBody = body.substring(0, limit) + mContext.getString(R.string.ellipsis);
- }
- mIsBodyPrimary = asPrimary;
- return this;
- }
-
- /**
- * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
- *
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withSupplementalIcon(int iconResId, boolean showDivider) {
- return withSupplementalIcon(iconResId, showDivider, null);
- }
-
- /**
- * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
- *
- * @param iconResId drawable resource id.
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withSupplementalIcon(int iconResId, boolean showDivider,
- View.OnClickListener listener) {
- mSupplementalActionType = SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON;
-
- mSupplementalIconResId = iconResId;
- mSupplementalIconOnClickListener = listener;
- mShowSupplementalIconDivider = showDivider;
- return this;
- }
-
- /**
- * Sets {@code Supplemental Action} to be represented by an {@code Action Button}.
- *
- * @param text button text to display.
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withAction(String text, boolean showDivider, View.OnClickListener listener) {
- if (TextUtils.isEmpty(text)) {
- throw new IllegalArgumentException("Action text cannot be empty.");
- }
- if (listener == null) {
- throw new IllegalArgumentException("Action OnClickListener cannot be null.");
- }
- mSupplementalActionType = SUPPLEMENTAL_ACTION_ONE_ACTION;
-
- mAction1Text = text;
- mAction1OnClickListener = listener;
- mShowAction1Divider = showDivider;
- return this;
- }
-
- /**
- * Sets {@code Supplemental Action} to be represented by two {@code Action Button}s.
- *
- * <p>These two action buttons will be aligned towards item end.
- *
- * @param action1Text button text to display - this button will be closer to item end.
- * @param action2Text button text to display.
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withActions(String action1Text, boolean showAction1Divider,
- View.OnClickListener action1OnClickListener,
- String action2Text, boolean showAction2Divider,
- View.OnClickListener action2OnClickListener) {
- if (TextUtils.isEmpty(action1Text) || TextUtils.isEmpty(action2Text)) {
- throw new IllegalArgumentException("Action text cannot be empty.");
- }
- if (action1OnClickListener == null || action2OnClickListener == null) {
- throw new IllegalArgumentException("Action OnClickListener cannot be null.");
- }
- mSupplementalActionType = SUPPLEMENTAL_ACTION_TWO_ACTIONS;
-
- mAction1Text = action1Text;
- mAction1OnClickListener = action1OnClickListener;
- mShowAction1Divider = showAction1Divider;
- mAction2Text = action2Text;
- mAction2OnClickListener = action2OnClickListener;
- mShowAction2Divider = showAction2Divider;
- return this;
- }
-
- /**
- * Sets {@code Supplemental Action} to be represented by a {@link android.widget.Switch}.
- *
- * @param checked initial value for switched.
- * @param showDivider whether to display a vertical bar between switch and text.
- * @param listener callback to be invoked when the checked state is changed.
- * @return This Builder object to allow for chaining calls to set methods.
- */
- public Builder withSwitch(boolean checked, boolean showDivider,
- CompoundButton.OnCheckedChangeListener listener) {
- mSupplementalActionType = SUPPLEMENTAL_ACTION_SWITCH;
-
- mSwitchChecked = checked;
- mShowSwitchDivider = showDivider;
- mSwitchOnCheckedChangeListener = listener;
- return this;
- }
-
- /**
- * Adds {@link ViewBinder} to interact with sub-views in
- * {@link ViewHolder}. These ViewBinders will always bind after
- * other {@link Builder} methods have bond.
- *
- * <p>Make sure to call with...() method on the intended sub-view first.
- *
- * <p>Example:
- * <pre>
- * {@code
- * new Builder()
- * .withTitle("title")
- * .withViewBinder((viewHolder) -> {
- * viewHolder.getTitle().doMoreStuff();
- * })
- * .build();
- * }
- * </pre>
- */
- public Builder withViewBinder(ViewBinder<ViewHolder> binder) {
- mCustomBinders.add(binder);
- return this;
- }
+ /**
+ * Adds {@link ViewBinder} to interact with sub-views in {@link ViewHolder}. These ViewBinders
+ * will always bind after other {@code setFoobar} methods have bound.
+ *
+ * <p>Make sure to call setFoobar() method on the intended sub-view first.
+ *
+ * <p>Example:
+ * <pre>
+ * {@code
+ * TextListItem item = new TextListItem(context);
+ * item.setTitle("title");
+ * item.addViewBinder((viewHolder) -> {
+ * viewHolder.getTitle().doMoreStuff();
+ * });
+ * }
+ * </pre>
+ */
+ public void addViewBinder(ViewBinder<ViewHolder> binder) {
+ mCustomBinders.add(binder);
+ markDirty();
}
/**
diff --git a/car/tests/src/androidx/car/widget/DividerVisibilityManagerTest.java b/car/tests/src/androidx/car/widget/DividerVisibilityManagerTest.java
index 64b310b..84f6464 100644
--- a/car/tests/src/androidx/car/widget/DividerVisibilityManagerTest.java
+++ b/car/tests/src/androidx/car/widget/DividerVisibilityManagerTest.java
@@ -17,7 +17,9 @@
package androidx.car.widget;
import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
@@ -144,19 +146,21 @@
@Test
public void testListItemAdapterAsVisibilityManager() {
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setHideDivider(true);
+
+ TextListItem item1 = new TextListItem(mActivity);
+ item1.setHideDivider(false);
+
+ TextListItem item2 = new TextListItem(mActivity);
+ item2.setHideDivider(true);
+
+ TextListItem item3 = new TextListItem(mActivity);
+ item3.setHideDivider(true);
+
// Create and populate ListItemAdapter.
ListItemProvider provider = new ListItemProvider.ListProvider(Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withDividerHidden()
- .build(),
- new TextListItem.Builder(mActivity)
- .build(),
- new TextListItem.Builder(mActivity)
- .withDividerHidden()
- .build(),
- new TextListItem.Builder(mActivity)
- .withDividerHidden()
- .build()));
+ item0, item1, item2, item3));
ListItemAdapter itemAdapter = new ListItemAdapter(mActivity, provider);
assertTrue(itemAdapter.shouldHideDivider(0));
@@ -165,6 +169,43 @@
assertTrue(itemAdapter.shouldHideDivider(3));
}
+ @Test
+ public void testSettingItemDividersHidden() throws Throwable {
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setHideDivider(true);
+
+ TextListItem item1 = new TextListItem(mActivity);
+
+ ListItemProvider provider = new ListItemProvider.ListProvider(Arrays.asList(item0, item1));
+ mActivityRule.runOnUiThread(() -> {
+ mPagedListView.setAdapter(new ListItemAdapter(mActivity, provider));
+ });
+
+ assertThat(item0.shouldHideDivider(), is(true));
+ assertThat(item1.shouldHideDivider(), is(false));
+
+ // First verify hiding divider works.
+ PagedListView.DividerVisibilityManager dvm = (PagedListView.DividerVisibilityManager)
+ mPagedListView.getAdapter();
+ assertThat(dvm, is(notNullValue()));
+ assertThat(dvm.shouldHideDivider(0), is(true));
+ assertThat(dvm.shouldHideDivider(1), is(false));
+
+ // Then verify we can show divider by checking the space between items reserved by
+ // divider decorator.
+ item0.setHideDivider(false);
+ mActivityRule.runOnUiThread(() -> {
+ mPagedListView.getAdapter().notifyDataSetChanged();
+ });
+
+ assertThat(dvm.shouldHideDivider(0), is(false));
+ int upper = mPagedListView.getRecyclerView().getLayoutManager()
+ .findViewByPosition(0).getBottom();
+ int lower = mPagedListView.getRecyclerView().getLayoutManager()
+ .findViewByPosition(1).getTop();
+ assertThat(lower - upper, is(greaterThan(0)));
+ }
+
private class TestDividerVisibilityManager implements PagedListView.DividerVisibilityManager {
@Override
public boolean shouldHideDivider(int position) {
diff --git a/car/tests/src/androidx/car/widget/SeekbarListItemTest.java b/car/tests/src/androidx/car/widget/SeekbarListItemTest.java
index 18cb97c..7645c6d 100644
--- a/car/tests/src/androidx/car/widget/SeekbarListItemTest.java
+++ b/car/tests/src/androidx/car/widget/SeekbarListItemTest.java
@@ -110,22 +110,22 @@
@Test
public void testOnlySliderIsVisibleInEmptyItem() {
- setupPagedListView(Arrays.asList(
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .build()));
+ SeekbarListItem item = new SeekbarListItem(mActivity, 0, 0, null, null);
+
+ setupPagedListView(Arrays.asList(item));
verifyViewDefaultVisibility(mPagedListView.getRecyclerView().getLayoutManager()
.getChildAt(0));
}
@Test
public void testPrimaryActionVisible() {
- List<ListItem> items = Arrays.asList(
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon)
- .build(),
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withPrimaryActionIcon(new ColorDrawable(Color.BLACK))
- .build());
+ SeekbarListItem item0 = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon);
+
+ SeekbarListItem item1 = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item1.setPrimaryActionIcon(new ColorDrawable(Color.BLACK));
+
+ List<ListItem> items = Arrays.asList(item0, item1);
setupPagedListView(items);
assertThat(getViewHolderAtPosition(0).getPrimaryIcon().getVisibility(),
@@ -136,21 +136,20 @@
@Test
public void testSliderTextVisible() {
- List<ListItem> items = Arrays.asList(
- new SeekbarListItem.Builder(mActivity, 0, 0, null, "Text")
- .build());
+ SeekbarListItem item = new SeekbarListItem(mActivity, 0, 0, null, "Text");
+
+ List<ListItem> items = Arrays.asList(item);
setupPagedListView(items);
assertThat(getViewHolderAtPosition(0).getText().getVisibility(), is(equalTo(View.VISIBLE)));
-
}
@Test
public void testSupplementalIconVisible() {
- List<ListItem> items = Arrays.asList(
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, false)
- .build());
+ SeekbarListItem item = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false);
+
+ List<ListItem> items = Arrays.asList(item);
setupPagedListView(items);
assertThat(getViewHolderAtPosition(0).getSupplementalIcon().getVisibility(),
@@ -161,10 +160,10 @@
@Test
public void testSupplementalIconDividerVisible() {
- List<ListItem> items = Arrays.asList(
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true)
- .build());
+ SeekbarListItem item = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true);
+
+ List<ListItem> items = Arrays.asList(item);
setupPagedListView(items);
assertThat(getViewHolderAtPosition(0).getSupplementalIcon().getVisibility(),
@@ -175,13 +174,13 @@
@Test
public void testPrimaryIconIsNotClickable() {
- List<ListItem> items = Arrays.asList(
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon)
- .build(),
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withPrimaryActionIcon(new ColorDrawable(Color.BLACK))
- .build());
+ SeekbarListItem item0 = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon);
+
+ SeekbarListItem item1 = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item1.setPrimaryActionIcon(new ColorDrawable(Color.BLACK));
+
+ List<ListItem> items = Arrays.asList(item0, item1);
setupPagedListView(items);
assertFalse(getViewHolderAtPosition(0).getPrimaryIcon().isClickable());
@@ -190,10 +189,10 @@
@Test
public void testSupplementalIconNotClickableWithoutListener() {
- List<ListItem> items = Arrays.asList(
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, false)
- .build());
+ SeekbarListItem item = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false);
+
+ List<ListItem> items = Arrays.asList(item);
setupPagedListView(items);
assertFalse(getViewHolderAtPosition(0).getSupplementalIcon().isClickable());
@@ -202,11 +201,11 @@
@Test
public void testClickingSupplementalIcon() {
boolean[] clicked = {false};
- List<ListItem> items = Arrays.asList(
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, false,
- v -> clicked[0] = true)
- .build());
+ SeekbarListItem item = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false,
+ v -> clicked[0] = true);
+
+ List<ListItem> items = Arrays.asList(item);
setupPagedListView(items);
onView(withId(R.id.recycler_view)).perform(
@@ -217,16 +216,16 @@
@Test
public void testPrimaryActionEmptyIconSpacing() {
- List<ListItem> items = Arrays.asList(
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon)
- .build(),
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withPrimaryActionIcon(new ColorDrawable(Color.BLACK))
- .build(),
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withPrimaryActionEmptyIcon()
- .build());
+ SeekbarListItem item0 = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon);
+
+ SeekbarListItem item1 = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item1.setPrimaryActionIcon(new ColorDrawable(Color.BLACK));
+
+ SeekbarListItem item2 = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item2.setPrimaryActionEmptyIcon();
+
+ List<ListItem> items = Arrays.asList(item0, item1, item2);
setupPagedListView(items);
for (int i = 1; i < items.size(); i++) {
@@ -238,13 +237,13 @@
@Test
public void testSupplementalIconSpacingWithoutDivider() {
final boolean showDivider = false;
- List<ListItem> items = Arrays.asList(
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, showDivider)
- .build(),
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withSupplementalEmptyIcon(showDivider)
- .build());
+ SeekbarListItem item0 = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, showDivider);
+
+ SeekbarListItem item1 = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item1.setSupplementalEmptyIcon(showDivider);
+
+ List<ListItem> items = Arrays.asList(item0, item1);
setupPagedListView(items);
for (int i = 1; i < items.size(); i++) {
@@ -256,13 +255,13 @@
@Test
public void testSupplementalIconSpacingWithDivider() {
final boolean showDivider = true;
- List<ListItem> items = Arrays.asList(
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, showDivider)
- .build(),
- new SeekbarListItem.Builder(mActivity, 0, 0, null, null)
- .withSupplementalEmptyIcon(showDivider)
- .build());
+ SeekbarListItem item0 = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, showDivider);
+
+ SeekbarListItem item1 = new SeekbarListItem(mActivity, 0, 0, null, null);
+ item1.setSupplementalEmptyIcon(showDivider);
+
+ List<ListItem> items = Arrays.asList(item0, item1);
setupPagedListView(items);
for (int i = 1; i < items.size(); i++) {
@@ -276,10 +275,9 @@
String longText = mActivity.getString(R.string.over_120_chars);
int lengthLimit = mActivity.getResources().getInteger(
R.integer.car_list_item_text_length_limit);
- List<ListItem> items = Arrays.asList(
- new SeekbarListItem.Builder(mActivity, 0, 0, null, longText)
- .build());
+ SeekbarListItem item0 = new SeekbarListItem(mActivity, 0, 0, null, longText);
+ List<ListItem> items = Arrays.asList(item0);
setupPagedListView(items);
assertThat(getViewHolderAtPosition(0).getText().getText().length(),
diff --git a/car/tests/src/androidx/car/widget/TextListItemTest.java b/car/tests/src/androidx/car/widget/TextListItemTest.java
index a8999c3..529a9f0 100644
--- a/car/tests/src/androidx/car/widget/TextListItemTest.java
+++ b/car/tests/src/androidx/car/widget/TextListItemTest.java
@@ -23,7 +23,6 @@
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
-import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.number.IsCloseTo.closeTo;
@@ -110,20 +109,20 @@
@Test
public void testEmptyItemHidesAllViews() {
- TextListItem item = new TextListItem.Builder(mActivity).build();
+ TextListItem item = new TextListItem(mActivity);
setupPagedListView(Arrays.asList(item));
verifyViewIsHidden(mPagedListView.getRecyclerView().getLayoutManager().getChildAt(0));
}
@Test
public void testPrimaryActionVisible() {
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
- .build(),
- new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .build());
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
+
+ TextListItem item1 = new TextListItem(mActivity);
+ item1.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+
+ List<TextListItem> items = Arrays.asList(item0, item1);
setupPagedListView(items);
assertThat(getViewHolderAtPosition(0).getPrimaryIcon().getVisibility(),
@@ -134,13 +133,13 @@
@Test
public void testTextVisible() {
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withTitle("title")
- .build(),
- new TextListItem.Builder(mActivity)
- .withBody("body")
- .build());
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setTitle("title");
+
+ TextListItem item1 = new TextListItem(mActivity);
+ item1.setBody("body");
+
+ List<TextListItem> items = Arrays.asList(item0, item1);
setupPagedListView(items);
assertThat(getViewHolderAtPosition(0).getTitle().getVisibility(),
@@ -151,17 +150,18 @@
@Test
public void testSupplementalActionVisible() {
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true)
- .build(),
- new TextListItem.Builder(mActivity)
- .withAction("text", true, v -> { /* Do nothing. */ })
- .build(),
- new TextListItem.Builder(mActivity)
- .withActions("text", true, v -> { /* Do nothing. */ },
- "text", true, v -> { /* Do nothing. */ })
- .build());
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true);
+
+ TextListItem item1 = new TextListItem(mActivity);
+ item1.setAction("text", true, v -> { /* Do nothing. */ });
+
+
+ TextListItem item2 = new TextListItem(mActivity);
+ item2.setActions("text", true, v -> { /* Do nothing. */ },
+ "text", true, v -> { /* Do nothing. */ });
+
+ List<TextListItem> items = Arrays.asList(item0, item1, item2);
setupPagedListView(items);
TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
@@ -182,13 +182,13 @@
@Test
public void testSwitchVisibleAndCheckedState() {
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withSwitch(true, true, null)
- .build(),
- new TextListItem.Builder(mActivity)
- .withSwitch(false, true, null)
- .build());
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setSwitch(true, true, null);
+
+ TextListItem item1 = new TextListItem(mActivity);
+ item1.setSwitch(false, true, null);
+
+ List<TextListItem> items = Arrays.asList(item0, item1);
setupPagedListView(items);
TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
@@ -204,20 +204,20 @@
@Test
public void testDividersAreOptional() {
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, false)
- .build(),
- new TextListItem.Builder(mActivity)
- .withAction("text", false, v -> { /* Do nothing. */ })
- .build(),
- new TextListItem.Builder(mActivity)
- .withActions("text", false, v -> { /* Do nothing. */ },
- "text", false, v -> { /* Do nothing. */ })
- .build(),
- new TextListItem.Builder(mActivity)
- .withSwitch(true, false, null)
- .build());
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false);
+
+ TextListItem item1 = new TextListItem(mActivity);
+ item1.setAction("text", false, v -> { /* Do nothing. */ });
+
+ TextListItem item2 = new TextListItem(mActivity);
+ item2.setActions("text", false, v -> { /* Do nothing. */ },
+ "text", false, v -> { /* Do nothing. */ });
+
+ TextListItem item3 = new TextListItem(mActivity);
+ item3.setSwitch(true, false, null);
+
+ List<TextListItem> items = Arrays.asList(item0, item1, item2, item3);
setupPagedListView(items);
TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
@@ -241,40 +241,20 @@
}
@Test
- public void testCanHideItemDividers() {
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withDividerHidden()
- .build(),
- new TextListItem.Builder(mActivity)
- .build());
- setupPagedListView(items);
-
- assertThat(items.get(0).shouldHideDivider(), is(true));
- assertThat(items.get(1).shouldHideDivider(), is(false));
-
- PagedListView.DividerVisibilityManager dvm = (PagedListView.DividerVisibilityManager)
- mPagedListView.getAdapter();
- assertThat(dvm, is(notNullValue()));
- assertThat(dvm.shouldHideDivider(0), is(true));
- assertThat(dvm.shouldHideDivider(1), is(false));
- }
-
- @Test
public void testTextStartMarginMatchesPrimaryActionType() {
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
- .build(),
- new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .build(),
- new TextListItem.Builder(mActivity)
- .withPrimaryActionEmptyIcon()
- .build(),
- new TextListItem.Builder(mActivity)
- .withPrimaryActionNoIcon()
- .build());
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
+
+ TextListItem item1 = new TextListItem(mActivity);
+ item1.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+
+ TextListItem item2 = new TextListItem(mActivity);
+ item2.setPrimaryActionEmptyIcon();
+
+ TextListItem item3 = new TextListItem(mActivity);
+ item3.setPrimaryActionNoIcon();
+
+ List<TextListItem> items = Arrays.asList(item0, item1, item2, item3);
List<Integer> expectedStartMargin = Arrays.asList(R.dimen.car_keyline_4,
R.dimen.car_keyline_3, R.dimen.car_keyline_3, R.dimen.car_keyline_1);
setupPagedListView(items);
@@ -293,23 +273,23 @@
@Test
public void testItemWithOnlyTitleIsSingleLine() {
- List<TextListItem> items = Arrays.asList(
- // Only space
- new TextListItem.Builder(mActivity)
- .withTitle(" ")
- .build(),
- // Underscore
- new TextListItem.Builder(mActivity)
- .withTitle("______")
- .build(),
- new TextListItem.Builder(mActivity)
- .withTitle("ALL UPPER CASE")
- .build(),
- // String wouldn't fit in one line
- new TextListItem.Builder(mActivity)
- .withTitle(InstrumentationRegistry.getContext().getResources().getString(
- R.string.over_120_chars))
- .build());
+ // Only space.
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setTitle(" ");
+
+ // Underscore.
+ TextListItem item1 = new TextListItem(mActivity);
+ item1.setTitle("______");
+
+ TextListItem item2 = new TextListItem(mActivity);
+ item2.setTitle("ALL UPPER CASE");
+
+ // String wouldn't fit in one line.
+ TextListItem item3 = new TextListItem(mActivity);
+ item3.setTitle(InstrumentationRegistry.getContext().getResources().getString(
+ R.string.over_120_chars));
+
+ List<TextListItem> items = Arrays.asList(item0, item1, item2, item3);
setupPagedListView(items);
double singleLineHeight = InstrumentationRegistry.getContext().getResources().getDimension(
@@ -325,20 +305,20 @@
@Test
public void testItemWithBodyTextIsAtLeastDoubleLine() {
- List<TextListItem> items = Arrays.asList(
- // Only space
- new TextListItem.Builder(mActivity)
- .withBody(" ")
- .build(),
- // Underscore
- new TextListItem.Builder(mActivity)
- .withBody("____")
- .build(),
- // String wouldn't fit in one line
- new TextListItem.Builder(mActivity)
- .withBody(InstrumentationRegistry.getContext().getResources().getString(
- R.string.over_120_chars))
- .build());
+ // Only space.
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setBody(" ");
+
+ // Underscore.
+ TextListItem item1 = new TextListItem(mActivity);
+ item1.setBody("____");
+
+ // String wouldn't fit in one line.
+ TextListItem item2 = new TextListItem(mActivity);
+ item2.setBody(InstrumentationRegistry.getContext().getResources().getString(
+ R.string.over_120_chars));
+
+ List<TextListItem> items = Arrays.asList(item0, item1, item2);
setupPagedListView(items);
final int doubleLineHeight =
@@ -359,10 +339,11 @@
R.string.over_120_chars);
final int limit = InstrumentationRegistry.getContext().getResources().getInteger(
R.integer.car_list_item_text_length_limit);
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withBody(longText)
- .build());
+
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setBody(longText);
+
+ List<TextListItem> items = Arrays.asList(item0);
setupPagedListView(items);
// + 1 for appended ellipsis.
@@ -374,10 +355,11 @@
public void testPrimaryIconDrawable() {
Drawable drawable = InstrumentationRegistry.getContext().getResources().getDrawable(
android.R.drawable.sym_def_app_icon, null);
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(drawable, true)
- .build());
+
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setPrimaryActionIcon(drawable, true);
+
+ List<TextListItem> items = Arrays.asList(item0);
setupPagedListView(items);
assertTrue(getViewHolderAtPosition(0).getPrimaryIcon().getDrawable().getConstantState()
@@ -386,10 +368,10 @@
@Test
public void testLargePrimaryIconHasNoStartMargin() {
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
- .build());
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
+
+ List<TextListItem> items = Arrays.asList(item0);
setupPagedListView(items);
TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
@@ -399,10 +381,10 @@
@Test
public void testSmallPrimaryIconStartMargin() {
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .build());
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+
+ List<TextListItem> items = Arrays.asList(item0);
setupPagedListView(items);
int expected = InstrumentationRegistry.getContext().getResources().getDimensionPixelSize(
@@ -417,34 +399,35 @@
public void testSmallPrimaryIconTopMarginRemainsTheSameRegardlessOfTextLength() {
final String longText = InstrumentationRegistry.getContext().getResources().getString(
R.string.over_120_chars);
- List<TextListItem> items = Arrays.asList(
- // Single line item.
- new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .withTitle("one line text")
- .build(),
- // Double line item with one line text.
- new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .withTitle("one line text")
- .withBody("one line text")
- .build(),
- // Double line item with long text.
- new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .withTitle("one line text")
- .withBody(longText)
- .build(),
- // Body text only - long text.
- new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .withBody(longText)
- .build(),
- // Body text only - one line text.
- new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .withBody("one line text")
- .build());
+
+ // Single line item.
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ item0.setTitle("one line text");
+
+ // Double line item with one line text.
+ TextListItem item1 = new TextListItem(mActivity);
+ item1.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ item1.setTitle("one line text");
+ item1.setBody("one line text");
+
+ // Double line item with long text.
+ TextListItem item2 = new TextListItem(mActivity);
+ item2.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ item2.setTitle("one line text");
+ item2.setBody(longText);
+
+ // Body text only - long text.
+ TextListItem item3 = new TextListItem(mActivity);
+ item3.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ item3.setBody(longText);
+
+ // Body text only - one line text.
+ TextListItem item4 = new TextListItem(mActivity);
+ item4.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ item4.setBody("one line text");
+
+ List<TextListItem> items = Arrays.asList(item0, item1, item2, item3, item4);
setupPagedListView(items);
for (int i = 1; i < items.size(); i++) {
@@ -459,12 +442,13 @@
@Test
public void testClickingPrimaryActionIsSeparateFromSupplementalAction() {
final boolean[] clicked = {false, false};
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withOnClickListener(v -> clicked[0] = true)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true,
- v -> clicked[1] = true)
- .build());
+
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setOnClickListener(v -> clicked[0] = true);
+ item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true,
+ v -> clicked[1] = true);
+
+ List<TextListItem> items = Arrays.asList(item0);
setupPagedListView(items);
onView(withId(R.id.recycler_view)).perform(actionOnItemAtPosition(0, click()));
@@ -479,11 +463,12 @@
@Test
public void testClickingSupplementalIcon() {
final boolean[] clicked = {false};
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true,
- v -> clicked[0] = true)
- .build());
+
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true,
+ v -> clicked[0] = true);
+
+ List<TextListItem> items = Arrays.asList(item0);
setupPagedListView(items);
onView(withId(R.id.recycler_view)).perform(
@@ -493,10 +478,10 @@
@Test
public void testSupplementalIconWithoutClickListenerIsNotClickable() {
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true)
- .build());
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true);
+
+ List<TextListItem> items = Arrays.asList(item0);
setupPagedListView(items);
TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
@@ -506,21 +491,22 @@
@Test
public void testCheckingSwitch() {
final boolean[] clicked = {false, false};
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withSwitch(false, false, (button, isChecked) -> {
- // Initial value is false.
- assertTrue(isChecked);
- clicked[0] = true;
- })
- .build(),
- new TextListItem.Builder(mActivity)
- .withSwitch(true, false, (button, isChecked) -> {
- // Initial value is true.
- assertFalse(isChecked);
- clicked[1] = true;
- })
- .build());
+
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setSwitch(false, false, (button, isChecked) -> {
+ // Initial value is false.
+ assertTrue(isChecked);
+ clicked[0] = true;
+ });
+
+ TextListItem item1 = new TextListItem(mActivity);
+ item1.setSwitch(true, false, (button, isChecked) -> {
+ // Initial value is true.
+ assertFalse(isChecked);
+ clicked[1] = true;
+ });
+
+ List<TextListItem> items = Arrays.asList(item0, item1);
setupPagedListView(items);
onView(withId(R.id.recycler_view)).perform(
@@ -535,10 +521,11 @@
@Test
public void testClickingSupplementalAction() {
final boolean[] clicked = {false};
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withAction("action", true, v -> clicked[0] = true)
- .build());
+
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setAction("action", true, v -> clicked[0] = true);
+
+ List<TextListItem> items = Arrays.asList(item0);
setupPagedListView(items);
onView(withId(R.id.recycler_view)).perform(
@@ -549,11 +536,12 @@
@Test
public void testClickingBothSupplementalActions() {
final boolean[] clicked = {false, false};
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withActions("action 1", true, v -> clicked[0] = true,
- "action 2", true, v -> clicked[1] = true)
- .build());
+
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setActions("action 1", true, v -> clicked[0] = true,
+ "action 2", true, v -> clicked[1] = true);
+
+ List<TextListItem> items = Arrays.asList(item0);
setupPagedListView(items);
onView(withId(R.id.recycler_view)).perform(
@@ -567,13 +555,14 @@
}
@Test
- public void testCustomViewBinderAreCalledLast() {
+ public void testCustomViewBinderBindsLast() {
final String updatedTitle = "updated title";
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withTitle("original title")
- .withViewBinder((viewHolder) -> viewHolder.getTitle().setText(updatedTitle))
- .build());
+
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setTitle("original title");
+ item0.addViewBinder((viewHolder) -> viewHolder.getTitle().setText(updatedTitle));
+
+ List<TextListItem> items = Arrays.asList(item0);
setupPagedListView(items);
TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
@@ -582,10 +571,10 @@
@Test
public void testCustomViewBinderOnUnusedViewsHasNoEffect() {
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withViewBinder((viewHolder) -> viewHolder.getBody().setText("text"))
- .build());
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.addViewBinder((viewHolder) -> viewHolder.getBody().setText("text"));
+
+ List<TextListItem> items = Arrays.asList(item0);
setupPagedListView(items);
TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
@@ -601,15 +590,15 @@
public void testSettingTitleOrBodyAsPrimaryText() {
// Create 2 items, one with Title as primary (default) and one with Body.
// The primary text, regardless of view, should have consistent look (as primary).
- List<TextListItem> items = Arrays.asList(
- new TextListItem.Builder(mActivity)
- .withTitle("title")
- .withBody("body")
- .build(),
- new TextListItem.Builder(mActivity)
- .withTitle("title")
- .withBody("body", true)
- .build());
+ TextListItem item0 = new TextListItem(mActivity);
+ item0.setTitle("title");
+ item0.setBody("body");
+
+ TextListItem item1 = new TextListItem(mActivity);
+ item1.setTitle("title");
+ item1.setBody("body", true);
+
+ List<TextListItem> items = Arrays.asList(item0, item1);
setupPagedListView(items);
TextListItem.ViewHolder titlePrimary = getViewHolderAtPosition(0);
@@ -622,12 +611,15 @@
@Test
public void testNoCarriedOverLayoutParamsForTextView() throws Throwable {
- TextListItem singleLine = new TextListItem.Builder(mActivity).withTitle("t").build();
+ TextListItem singleLine = new TextListItem(mActivity);
+ singleLine.setTitle("title");
+
setupPagedListView(Arrays.asList(singleLine));
// Manually rebind the view holder of a single line item to a double line item.
- TextListItem doubleLine = new TextListItem.Builder(mActivity).withTitle("t").withBody("b")
- .build();
+ TextListItem doubleLine = new TextListItem(mActivity);
+ doubleLine.setTitle("title");
+ doubleLine.setBody("body");
TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
mActivityRule.runOnUiThread(() -> doubleLine.bind(viewHolder));
@@ -641,16 +633,14 @@
@Test
public void testNoCarriedOverLayoutParamsForPrimaryIcon() throws Throwable {
- TextListItem smallIcon = new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .withBody("body") // Small icon of items with body text should use top margin.
- .build();
+ TextListItem smallIcon = new TextListItem(mActivity);
+ smallIcon.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ smallIcon.setBody("body"); // Small icon of items with body text should use top margin.
setupPagedListView(Arrays.asList(smallIcon));
// Manually rebind the view holder.
- TextListItem largeIcon = new TextListItem.Builder(mActivity)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
- .build();
+ TextListItem largeIcon = new TextListItem(mActivity);
+ largeIcon.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
mActivityRule.runOnUiThread(() -> largeIcon.bind(viewHolder));
@@ -661,6 +651,22 @@
assertThat(iconLayoutParams.topMargin, is(equalTo(0)));
}
+ @Test
+ public void testUpdateItem() throws Throwable {
+ TextListItem item = new TextListItem(mActivity);
+ setupPagedListView(Arrays.asList(item));
+
+ String title = "updated title";
+ item.setTitle(title);
+ mActivityRule.runOnUiThread(() -> mPagedListView.getAdapter().notifyItemChanged(0));
+
+ // Wait for paged list view to layout by using espresso to scroll to a position.
+ onView(withId(R.id.recycler_view)).perform(scrollToPosition(0));
+
+ TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
+ assertThat(viewHolder.getTitle().getText(), is(equalTo(title)));
+ }
+
private static ViewAction clickChildViewWithId(final int id) {
return new ViewAction() {
@Override
diff --git a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SeekbarListItemActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SeekbarListItemActivity.java
index a1134cb..198f092 100644
--- a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SeekbarListItemActivity.java
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SeekbarListItemActivity.java
@@ -87,94 +87,109 @@
String longText = mContext.getString(R.string.long_text);
+ TextListItem textListItem;
+ SeekbarListItem item;
+
// Slider only.
- mItems.add(new TextListItem.Builder(mContext).withTitle("Slider Only").build());
+ textListItem = new TextListItem(mContext);
+ textListItem.setTitle("Slider Only");
+ mItems.add(textListItem);
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, null)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, null);
+ mItems.add(item);
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, "one line text")
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, "one line text");
+ mItems.add(item);
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, longText)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, longText);
+ mItems.add(item);
// Start icon.
- mItems.add(new TextListItem.Builder(mContext).withTitle("With Primary Action").build());
+ textListItem = new TextListItem(mContext);
+ textListItem.setTitle("With Primary Action");
+ mItems.add(textListItem);
// Only slider. No text.
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, null)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, null);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon);
+ mItems.add(item);
// One line text.
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, "one line text")
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, "one line text");
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon);
+ mItems.add(item);
// Long text.
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, longText)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, longText);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon);
+ mItems.add(item);
// End icon with divider.
- mItems.add(new TextListItem.Builder(mContext).withTitle(
- "With Supplemental Action").build());
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, null)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true)
- .build());
+ textListItem = new TextListItem(mContext);
+ textListItem.setTitle(
+ "With Supplemental Action");
+ mItems.add(textListItem);
+ item = new SeekbarListItem(mContext, 100, 0, mListener, null);
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true);
+ mItems.add(item);
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, "one line text")
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, "one line text");
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true);
+ mItems.add(item);
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, longText)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, longText);
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true);
+ mItems.add(item);
// Empty end icon with divider.
- mItems.add(new TextListItem.Builder(mContext).withTitle("With Empty Icon").build());
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, null)
- .withSupplementalEmptyIcon(true)
- .build());
+ textListItem = new TextListItem(mContext);
+ textListItem.setTitle("With Empty Icon");
+ mItems.add(textListItem);
+ item = new SeekbarListItem(mContext, 100, 0, mListener, null);
+ item.setSupplementalEmptyIcon(true);
+ mItems.add(item);
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, "one line text")
- .withSupplementalEmptyIcon(true)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, "one line text");
+ item.setSupplementalEmptyIcon(true);
+ mItems.add(item);
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, longText)
- .withSupplementalEmptyIcon(true)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, longText);
+ item.setSupplementalEmptyIcon(true);
+ mItems.add(item);
// End icon without divider.
- mItems.add(new TextListItem.Builder(mContext).withTitle(
- "Without Supplemental Action Divider").build());
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, null)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, false)
- .build());
+ textListItem = new TextListItem(mContext);
+ textListItem.setTitle(
+ "Without Supplemental Action Divider");
+ mItems.add(textListItem);
+ item = new SeekbarListItem(mContext, 100, 0, mListener, null);
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false);
+ mItems.add(item);
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, "one line text")
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, false)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, "one line text");
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false);
+ mItems.add(item);
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, longText)
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, false)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, longText);
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false);
+ mItems.add(item);
// Empty end icon without divider.
- mItems.add(new TextListItem.Builder(mContext).withTitle(
- "With Empty Icon No Divider").build());
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, null)
- .withSupplementalEmptyIcon(false)
- .build());
+ textListItem = new TextListItem(mContext);
+ textListItem.setTitle(
+ "With Empty Icon No Divider");
+ mItems.add(textListItem);
+ item = new SeekbarListItem(mContext, 100, 0, mListener, null);
+ item.setSupplementalEmptyIcon(false);
+ mItems.add(item);
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, "one line text")
- .withSupplementalEmptyIcon(false)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, "one line text");
+ item.setSupplementalEmptyIcon(false);
+ mItems.add(item);
- mItems.add(new SeekbarListItem.Builder(mContext, 100, 0, mListener, longText)
- .withSupplementalEmptyIcon(false)
- .build());
+ item = new SeekbarListItem(mContext, 100, 0, mListener, longText);
+ item.setSupplementalEmptyIcon(false);
+ mItems.add(item);
mListProvider = new ListItemProvider.ListProvider(mItems);
}
diff --git a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/TextListItemActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/TextListItemActivity.java
index e7ec34a..b5d71a7 100644
--- a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/TextListItemActivity.java
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/TextListItemActivity.java
@@ -54,8 +54,39 @@
mPagedListView = findViewById(R.id.paged_list_view);
- ListItemAdapter adapter = new ListItemAdapter(this,
- new SampleProvider(this), ListItemAdapter.BackgroundStyle.PANEL);
+ SampleProvider provider = new SampleProvider(this);
+ ListItemAdapter adapter = new ListItemAdapter(this, provider,
+ ListItemAdapter.BackgroundStyle.PANEL);
+
+ final boolean[] hideDivider = {true};
+ // Demonstrate how to update list item post construction.
+ TextListItem toBeUpdated = new TextListItem(this);
+ toBeUpdated.setPrimaryActionEmptyIcon();
+ toBeUpdated.setTitle("tap next item to update my icon");
+ toBeUpdated.setHideDivider(hideDivider[0]);
+ provider.mItems.add(0, toBeUpdated);
+
+ boolean[] useEmptyIcon = new boolean[]{false};
+ TextListItem update = new TextListItem(this);
+ update.setTitle("tap me to update the icon of item above");
+ update.setOnClickListener(v -> {
+ // Change icon.
+ if (useEmptyIcon[0]) {
+ toBeUpdated.setPrimaryActionEmptyIcon();
+ } else {
+ toBeUpdated.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ }
+ useEmptyIcon[0] = !useEmptyIcon[0];
+
+ // Show/hide item divider.
+ toBeUpdated.setHideDivider(hideDivider[0]);
+ hideDivider[0] = !hideDivider[0];
+
+ // Make sure to notify adapter about the change.
+ adapter.notifyItemChanged(0);
+ });
+ provider.mItems.add(1, update);
+
mPagedListView.setAdapter(adapter);
mPagedListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
mPagedListView.setDividerVisibilityManager(adapter);
@@ -63,12 +94,12 @@
private static class SampleProvider extends ListItemProvider {
private Context mContext;
- private List<ListItem> mItems;
+ List<ListItem> mItems;
private View.OnClickListener mOnClickListener = v ->
Toast.makeText(mContext, "Clicked!", Toast.LENGTH_SHORT).show();
- private View.OnClickListener mGetParentHeight = (v) -> {
+ private View.OnClickListener mGetParentHeight = v -> {
int parentHeight = ((FrameLayout) v.getParent().getParent().getParent()).getHeight();
Toast.makeText(v.getContext(),
"card height is " + pixelToDip(mContext, parentHeight) + " dp",
@@ -81,158 +112,156 @@
mContext = context;
mItems = new ArrayList<>();
- mItems.add(new TextListItem.Builder(mContext)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
- .withTitle("clickable single line with full icon and one action")
- .withAction("card height", true, mGetParentHeight)
- .build());
+ TextListItem item;
- mItems.add(new TextListItem.Builder(mContext)
- .withTitle("primary action set by drawable")
- .withPrimaryActionIcon(mContext.getDrawable(R.drawable.pressed_icon), true)
- .withViewBinder(vh -> vh.getPrimaryIcon().setClickable(true))
- .build());
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
+ item.setTitle("clickable single line with full icon and one action");
+ item.setAction("card height", true, mGetParentHeight);
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .withTitle("clickable single line with small icon and clickable end icon")
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true,
- mGetParentHeight)
- .build());
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(mContext.getDrawable(R.drawable.pressed_icon), true);
+ item.setTitle("primary action set by drawable");
+ item.addViewBinder(vh -> vh.getPrimaryIcon().setClickable(true));
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .withTitle("single line without a list divider")
- .withDividerHidden()
- .build());
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ item.setTitle("clickable single line with small icon and clickable end icon");
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true, mGetParentHeight);
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withOnClickListener(mOnClickListener)
- .withPrimaryActionEmptyIcon()
- .withTitle("clickable single line with empty icon and end icon no divider")
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, false)
- .build());
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ item.setTitle("single line without a list divider");
+ item.setHideDivider(true);
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withTitle("title is single line and ellipsizes. "
- + mContext.getString(R.string.long_text))
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true)
- .build());
+ item = new TextListItem(mContext);
+ item.setOnClickListener(mOnClickListener);
+ item.setPrimaryActionEmptyIcon();
+ item.setTitle("clickable single line with empty icon and end icon no divider");
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false);
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withTitle("Subtitle-like line without a list divider")
- .withDividerHidden()
- .withViewBinder(viewHolder ->
- viewHolder.getTitle().setTextAppearance(R.style.CarListSubtitle))
- .build());
+ item = new TextListItem(mContext);
+ item.setTitle("title is single line and ellipsizes. "
+ + mContext.getString(R.string.long_text));
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true);
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withPrimaryActionNoIcon()
- .withTitle("single line with two actions and no divider")
- .withActions("action 1", false,
- (v) -> Toast.makeText(
- v.getContext(), "action 1", Toast.LENGTH_SHORT).show(),
- "action 2", false,
- (v) -> Toast.makeText(
- v.getContext(), "action 2", Toast.LENGTH_SHORT).show())
- .build());
+ item = new TextListItem(mContext);
+ item.setTitle("Subtitle-like line without a list divider");
+ item.setHideDivider(true);
+ item.addViewBinder(viewHolder ->
+ viewHolder.getTitle().setTextAppearance(R.style.CarListSubtitle));
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withPrimaryActionNoIcon()
- .withTitle("single line with two actions and action 2 divider")
- .withActions("action 1", false,
- (v) -> Toast.makeText(
- v.getContext(), "action 1", Toast.LENGTH_SHORT).show(),
- "action 2", true,
- (v) -> Toast.makeText(
- v.getContext(), "action 2", Toast.LENGTH_SHORT).show())
- .build());
+ item = new TextListItem(mContext);
+ item.setPrimaryActionNoIcon();
+ item.setTitle("single line with two actions and no divider");
+ item.setActions("action 1", false,
+ v -> Toast.makeText(
+ v.getContext(), "action 1", Toast.LENGTH_SHORT).show(),
+ "action 2", false,
+ v -> Toast.makeText(
+ v.getContext(), "action 2", Toast.LENGTH_SHORT).show());
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withPrimaryActionNoIcon()
- .withTitle("single line with divider between actions. "
- + mContext.getString(R.string.long_text))
- .withActions("action 1", true,
- (v) -> Toast.makeText(
- v.getContext(), "action 1", Toast.LENGTH_SHORT).show(),
- "action 2", false,
- (v) -> Toast.makeText(
- v.getContext(), "action 2", Toast.LENGTH_SHORT).show())
- .build());
+ item = new TextListItem(mContext);
+ item.setPrimaryActionNoIcon();
+ item.setTitle("single line with two actions and action 2 divider");
+ item.setActions("action 1", false,
+ v -> Toast.makeText(
+ v.getContext(), "action 1", Toast.LENGTH_SHORT).show(),
+ "action 2", true,
+ v -> Toast.makeText(
+ v.getContext(), "action 2", Toast.LENGTH_SHORT).show());
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
- .withTitle("double line with full icon and no end icon divider")
- .withBody("one line text")
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, false,
- mGetParentHeight)
- .build());
+ item = new TextListItem(mContext);
+ item.setPrimaryActionNoIcon();
+ item.setTitle("single line with divider between actions. "
+ + mContext.getString(R.string.long_text));
+ item.setActions("action 1", true,
+ v -> Toast.makeText(
+ v.getContext(), "action 1", Toast.LENGTH_SHORT).show(),
+ "action 2", false,
+ v -> Toast.makeText(
+ v.getContext(), "action 2", Toast.LENGTH_SHORT).show());
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .withTitle("double line with small icon and one action")
- .withBody("one line text")
- .withAction("card height", true, mGetParentHeight)
- .build());
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
+ item.setTitle("double line with full icon and no end icon divider");
+ item.setBody("one line text");
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false, mGetParentHeight);
+ mItems.add(item);
+
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ item.setTitle("double line with small icon and one action");
+ item.setBody("one line text");
+ item.setAction("card height", true, mGetParentHeight);
+ mItems.add(item);
String tenChars = "Ten Chars.";
- mItems.add(new TextListItem.Builder(mContext)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .withTitle("Card with small icon and text longer than limit")
- .withBody("some chars")
- .withBody(TextUtils.join("", Collections.nCopies(20, tenChars)))
- .withSupplementalIcon(android.R.drawable.sym_def_app_icon, true,
- mGetParentHeight)
- .build());
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ item.setTitle("Card with small icon and text longer than limit");
+ item.setBody(TextUtils.join("", Collections.nCopies(20, tenChars)));
+ item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true, mGetParentHeight);
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withPrimaryActionEmptyIcon()
- .withTitle("double line with empty primary icon."
- + mContext.getString(R.string.long_text))
- .withBody("one line text as primary", true)
- .withActions("screen size", false, (v) -> {
- Context c = v.getContext();
- Point size = new Point();
- c.getSystemService(WindowManager.class).getDefaultDisplay().getSize(size);
+ item = new TextListItem(mContext);
+ item.setPrimaryActionEmptyIcon();
+ item.setTitle("double line with empty primary icon."
+ + mContext.getString(R.string.long_text));
+ item.setBody("one line text as primary", true);
+ item.setActions("screen size", false, v -> {
+ Context c = v.getContext();
+ Point size = new Point();
+ c.getSystemService(WindowManager.class).getDefaultDisplay().getSize(size);
- Toast.makeText(v.getContext(),
- String.format("%s x %s dp", pixelToDip(c, size.x),
- pixelToDip(c, size.y)), Toast.LENGTH_SHORT).show();
- }, "card height", true, mGetParentHeight)
- .build());
+ Toast.makeText(v.getContext(), String.format("%s x %s dp", pixelToDip(c, size.x),
+ pixelToDip(c, size.y)),
+ Toast.LENGTH_SHORT).show();
+ }, "card height", true, mGetParentHeight);
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withTitle("double line with no primary action and one divider")
- .withBody("one line text as primary", true)
- .withActions("screen size", false, (v) -> {
- Context c = v.getContext();
- Point size = new Point();
- c.getSystemService(WindowManager.class).getDefaultDisplay().getSize(size);
+ item = new TextListItem(mContext);
+ item.setTitle("double line with no primary action and one divider");
+ item.setBody("one line text as primary", true);
+ item.setActions("screen size", false, v -> {
+ Context c = v.getContext();
+ Point size = new Point();
+ c.getSystemService(WindowManager.class).getDefaultDisplay().getSize(size);
- Toast.makeText(v.getContext(),
- String.format("%s x %s dp", pixelToDip(c, size.x),
- pixelToDip(c, size.y)), Toast.LENGTH_SHORT).show();
- }, "card height", true, mGetParentHeight)
- .build());
+ Toast.makeText(v.getContext(),
+ String.format("%s x %s dp", pixelToDip(c, size.x),
+ pixelToDip(c, size.y)), Toast.LENGTH_SHORT).show();
+ }, "card height", true, mGetParentHeight);
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true)
- .withBody("Only body - no title is set")
- .withAction("card height", true, mGetParentHeight)
- .build());
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
+ item.setBody("Only body - no title is set");
+ item.setAction("card height", true, mGetParentHeight);
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false)
- .withBody("Only body - no title. " + mContext.getString(R.string.long_text))
- .build());
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ item.setBody("Only body - no title. " + mContext.getString(R.string.long_text));
+ mItems.add(item);
- mItems.add(new TextListItem.Builder(mContext)
- .withTitle("Switch - initially unchecked")
- .withSwitch(false, true, (button, isChecked) -> {
- Toast.makeText(mContext,
- isChecked ? "checked" : "unchecked", Toast.LENGTH_SHORT).show();
- })
- .build());
+ item = new TextListItem(mContext);
+ item.setTitle("Switch - initially unchecked");
+ item.setSwitch(false, true, (button, isChecked) -> {
+ Toast.makeText(mContext,
+ isChecked ? "checked" : "unchecked", Toast.LENGTH_SHORT).show();
+ });
+ mItems.add(item);
mListProvider = new ListItemProvider.ListProvider(mItems);
}