[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);
         }