Gradle dependencies
compile group: 'androidx.car', name: 'car', version: '1.0.0-alpha7'
- groupId: androidx.car
- artifactId: car
- version: 1.0.0-alpha7
Artifact androidx.car:car:1.0.0-alpha7 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.car:car com.android.support:car
Overview
Class to build a list item that has up to two actions.
An item visually composes of 3 parts; each part may contain multiple views.
- Primary Action: represented by an icon of following types.
- Primary Icon - icon size could be large or small.
- No Icon - no icon is shown.
- Empty Icon - Text offsets start space as if there was an icon.
- Text: supports any combination of the following text views.
- Supplemental Action: Up to two actions.
ActionListItem binds data to ActionListItem.ViewHolder based on components selected.
When conflicting setter methods are called (e.g. setting primary action to both primary icon
and no icon), the last called method wins.
Summary
Methods |
---|
public static ActionListItem.ViewHolder | createViewHolder(View itemView)
Creates a ActionListItem.ViewHolder. |
protected Context | getContext()
|
public int | getViewType()
Used by ListItemAdapter to choose layout to inflate for view holder. |
public void | onBind(ActionListItem.ViewHolder viewHolder)
Resets all views in ActionListItem.ViewHolder then applies ViewBinders to
adjust view layout params. |
protected void | resolveDirtyState()
Calculates the layout params for views in ActionListItem.ViewHolder. |
public void | setAction(java.lang.String text, boolean showDivider, View.OnClickListener listener)
Sets the primary action of this ListItem. |
public void | setActionBorderless(boolean isActionBorderless)
Sets whether or not the actions should be styled as borderless. |
public void | setActions(java.lang.String primaryActionText, boolean showPrimaryActionDivider, View.OnClickListener primaryActionOnClickListener, java.lang.String secondaryActionText, boolean showSecondaryActionDivider, View.OnClickListener secondaryActionOnClickListener)
Sets the primary and secondary actions for this ListItem. |
public void | setBody(java.lang.CharSequence body)
Sets the body text of item. |
public abstract void | setEnabled(boolean enabled)
Sets the enabled state of the bound ListItem.ViewHolder. |
public void | setOnClickListener(View.OnClickListener listener)
Sets of ActionListItem. |
public void | setPrimaryAction(java.lang.String primaryActionText, boolean showPrimaryActionDivider, View.OnClickListener primaryActionOnClickListener)
Sets the primary action of this ListItem. |
public void | setPrimaryActionEmptyIcon()
Sets Primary Action to be empty icon. |
public void | setPrimaryActionIcon(Drawable drawable, int size)
Sets Primary Action to be represented by an icon. |
public void | setPrimaryActionIcon(int iconResId, int size)
Sets Primary Action to be represented by an icon. |
public void | setPrimaryActionNoIcon()
Sets Primary Action to have no icon. |
public void | setSecondaryAction(java.lang.String secondaryActionText, boolean showSecondaryActionDivider, View.OnClickListener secondaryActionOnClickListener)
Sets the secondary action of this ListItem. |
public void | setTitle(java.lang.CharSequence title)
Sets the title of item. |
from ListItem<VH> | addViewBinder, addViewBinder, getShowDivider, isDirty, markClean, markDirty, onBind, removeViewBinder, setShowDivider |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Fields
public static final int
PRIMARY_ACTION_ICON_SIZE_SMALLSmall sized icon is the mostly commonly used size. It's the same as supplemental action icon.
public static final int
PRIMARY_ACTION_ICON_SIZE_MEDIUMMedium sized icon is slightly bigger than SMALL ones. It is intended for profile
pictures (avatar), in which case caller is responsible for passing in a circular image.
public static final int
PRIMARY_ACTION_ICON_SIZE_LARGELarge sized icon is as tall as a list item with only title text. It is intended for
album art.
Constructors
public
ActionListItem(Context context)
Methods
Creates a ActionListItem.ViewHolder.
Used by ListItemAdapter to choose layout to inflate for view holder.
Resets all views in ActionListItem.ViewHolder then applies ViewBinders to
adjust view layout params.
public abstract void
setEnabled(boolean enabled)
Sets the enabled state of the bound ListItem.ViewHolder.
All visible children views of ViewHolder should be set to enabled. Caller
is responsible for notifying ListItemAdapter about data change.
Disabled items are usually styled at 50% opacity. Consider similar styling for
consistency.
protected void
resolveDirtyState()
Calculates the layout params for views in ActionListItem.ViewHolder.
protected Context
getContext()
public void
setOnClickListener(View.OnClickListener listener)
Sets of ActionListItem.
public void
setPrimaryActionIcon(int iconResId, int size)
Sets Primary Action to be represented by an icon.
Parameters:
iconResId: the resource identifier of the drawable.
size: The size of the icon. Must be one of ActionListItem.PRIMARY_ACTION_ICON_SIZE_SMALL,
ActionListItem.PRIMARY_ACTION_ICON_SIZE_MEDIUM, or
ActionListItem.PRIMARY_ACTION_ICON_SIZE_LARGE.
public void
setPrimaryActionIcon(Drawable drawable, int size)
Sets Primary Action to be represented by an icon.
Parameters:
drawable: the Drawable to set, or null to clear the content.
size: The size of the icon. Must be one of ActionListItem.PRIMARY_ACTION_ICON_SIZE_SMALL,
ActionListItem.PRIMARY_ACTION_ICON_SIZE_MEDIUM, or
ActionListItem.PRIMARY_ACTION_ICON_SIZE_LARGE.
public void
setPrimaryActionEmptyIcon()
Sets Primary Action to be empty icon.
Text would have a start margin as if Primary Action were set to primary
icon.
public void
setPrimaryActionNoIcon()
Sets Primary Action to have no icon. Text would align to the start of item.
public void
setTitle(java.lang.CharSequence title)
Sets the title of item.
Title text is limited to one line, and ellipsizes at the end.
Parameters:
title: text to display as title.
public void
setBody(java.lang.CharSequence body)
Sets the body text of item.
Text beyond length required by regulation will be truncated. Defaults Title
text as the primary.
Parameters:
body: text to be displayed.
public void
setAction(java.lang.String text, boolean showDivider, View.OnClickListener listener)
Deprecated: Use ActionListItem.setPrimaryAction(String, boolean, View.OnClickListener) or
ActionListItem.setSecondaryAction(String, boolean, View.OnClickListener) instead to individually
set the actions.
Sets the primary action of this ListItem.
Parameters:
text: button text to display.
showDivider: whether to display a vertical bar that separates Text and
Action Button.
listener: the callback that will run when action button is clicked.
public void
setActions(java.lang.String primaryActionText, boolean showPrimaryActionDivider, View.OnClickListener primaryActionOnClickListener, java.lang.String secondaryActionText, boolean showSecondaryActionDivider, View.OnClickListener secondaryActionOnClickListener)
Deprecated: Use ActionListItem.setPrimaryAction(String, boolean, View.OnClickListener) and
ActionListItem.setSecondaryAction(String, boolean, View.OnClickListener) to set both actions.
Sets the primary and secondary actions for this ListItem.
Parameters:
primaryActionText: The primary action text.
showPrimaryActionDivider: Whether or not to show a divider before the primary action.
primaryActionOnClickListener: The listener to be invoked when the primary action is
triggered.
secondaryActionText: The secondary action text.
showSecondaryActionDivider: Whether or not to show a divider before the secondary
action.
secondaryActionOnClickListener: The listener to be invoked when the secondary action is
triggered.
public void
setPrimaryAction(java.lang.String primaryActionText, boolean showPrimaryActionDivider, View.OnClickListener primaryActionOnClickListener)
Sets the primary action of this ListItem.
Parameters:
primaryActionText: Action text to display.
showPrimaryActionDivider: Whether or not to display a vertical bar before the primary
action.
primaryActionOnClickListener: The callback that will run when the action is clicked.
public void
setSecondaryAction(java.lang.String secondaryActionText, boolean showSecondaryActionDivider, View.OnClickListener secondaryActionOnClickListener)
Sets the secondary action of this ListItem.
The secondary action will appear before the primary action if both are set.
Parameters:
secondaryActionText: Action text to display.
showSecondaryActionDivider: Whether or not to display a vertical bar before the
secondary action.
secondaryActionOnClickListener: The callback that will run when the action is clicked.
public void
setActionBorderless(boolean isActionBorderless)
Sets whether or not the actions should be styled as borderless.
By default, this value is true.
Parameters:
isActionBorderless: true if the actions should be borderless. false
otherwise.
Source
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.car.widget;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.DimenRes;
import androidx.annotation.Dimension;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.car.R;
import androidx.car.util.CarUxRestrictionsUtils;
import androidx.car.uxrestrictions.CarUxRestrictions;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Guideline;
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.List;
/**
* Class to build a list item that has up to two actions.
*
* <p>An item visually composes of 3 parts; each part may contain multiple views.
* <ul>
* <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 - 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 following text views.
* <ul>
* <li>Title
* <li>Body
* </ul>
* <li>{@code Supplemental Action}: Up to two actions.
* </ul>
*
* <p>{@code ActionListItem} 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 final class ActionListItem extends ListItem<ActionListItem.ViewHolder> {
@Retention(SOURCE)
@IntDef({PRIMARY_ACTION_ICON_SIZE_SMALL, PRIMARY_ACTION_ICON_SIZE_MEDIUM,
PRIMARY_ACTION_ICON_SIZE_LARGE})
private @interface PrimaryActionIconSize {}
/**
* Small sized icon is the mostly commonly used size. It's the same as supplemental action icon.
*/
public static final int PRIMARY_ACTION_ICON_SIZE_SMALL = 0;
/**
* Medium sized icon is slightly bigger than {@code SMALL} ones. It is intended for profile
* pictures (avatar), in which case caller is responsible for passing in a circular image.
*/
public static final int PRIMARY_ACTION_ICON_SIZE_MEDIUM = 1;
/**
* Large sized icon is as tall as a list item with only {@code title} text. It is intended for
* album art.
*/
public static final int PRIMARY_ACTION_ICON_SIZE_LARGE = 2;
@Retention(SOURCE)
@IntDef({
PRIMARY_ACTION_TYPE_NO_ICON, PRIMARY_ACTION_TYPE_EMPTY_ICON,
PRIMARY_ACTION_TYPE_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_ICON = 2;
@PrimaryActionType private int mPrimaryActionType = PRIMARY_ACTION_TYPE_NO_ICON;
private Drawable mPrimaryActionIconDrawable;
@PrimaryActionIconSize private int mPrimaryActionIconSize = PRIMARY_ACTION_ICON_SIZE_SMALL;
private final Context mContext;
private boolean mIsEnabled = true;
private final List<ViewBinder<ViewHolder>> mBinders = new ArrayList<>();
private CharSequence mTitle;
private CharSequence mBody;
@Dimension
private final int mSupplementalGuidelineBegin;
private boolean mIsActionBorderless = true;
private String mPrimaryActionText;
private View.OnClickListener mPrimaryActionOnClickListener;
private boolean mShowPrimaryActionDivider;
private String mSecondaryActionText;
private View.OnClickListener mSecondaryActionOnClickListener;
private boolean mShowSecondaryActionDivider;
private View.OnClickListener mOnClickListener;
/**
* Creates a {@link ActionListItem.ViewHolder}.
*/
@NonNull
public static ViewHolder createViewHolder(View itemView) {
return new ViewHolder(itemView);
}
public ActionListItem(@NonNull Context context) {
mContext = context;
mSupplementalGuidelineBegin = mContext.getResources().getDimensionPixelSize(
R.dimen.car_list_item_supplemental_guideline_top);
markDirty();
}
/**
* Used by {@link ListItemAdapter} to choose layout to inflate for view holder.
*/
@Override
public int getViewType() {
return ListItemAdapter.LIST_ITEM_TYPE_ACTION;
}
/**
* Resets all views in {@link ActionListItem.ViewHolder} then applies ViewBinders to
* adjust view layout params.
*/
@Override
public void onBind(ActionListItem.ViewHolder viewHolder) {
for (View v : viewHolder.getWidgetViews()) {
v.setEnabled(mIsEnabled);
v.setVisibility(View.GONE);
}
// ActionListItem supports clicking on the item so we also update the entire itemView.
viewHolder.itemView.setEnabled(mIsEnabled);
for (ViewBinder<ViewHolder> binder : mBinders) {
binder.bind(viewHolder);
}
}
@Override
public void setEnabled(boolean enabled) {
mIsEnabled = enabled;
}
/**
* Calculates the layout params for views in {@link ViewHolder}.
*/
@Override
protected void resolveDirtyState() {
mBinders.clear();
// Create binders that adjust layout params of each view.
setPrimaryAction();
setText();
setSupplementalActions();
setOnClickListener();
}
@NonNull
protected Context getContext() {
return mContext;
}
private void setPrimaryAction() {
setPrimaryIconContent();
setPrimaryIconLayout();
}
private void setText() {
setTextContent();
setTextVerticalMargin();
setTextStartMargin();
}
private void setOnClickListener() {
mBinders.add(vh -> {
vh.itemView.setOnClickListener(mOnClickListener);
vh.itemView.setClickable(mOnClickListener != null);
});
}
private void setPrimaryIconContent() {
switch (mPrimaryActionType) {
case PRIMARY_ACTION_TYPE_ICON:
mBinders.add(vh -> {
vh.getPrimaryIcon().setVisibility(View.VISIBLE);
vh.getPrimaryIcon().setImageDrawable(mPrimaryActionIconDrawable);
});
break;
case PRIMARY_ACTION_TYPE_EMPTY_ICON:
case PRIMARY_ACTION_TYPE_NO_ICON:
// Do nothing.
break;
default:
throw new IllegalStateException("Unknown primary action type.");
}
}
/**
* Sets the size, start margin, and vertical position of primary icon.
*
* <p>Large icon will have no start margin, and always align center vertically.
*
* <p>Small/medium icon will have start margin, and uses a top margin such that it is "pinned"
* at the same position in list item regardless of item height.
*/
private void setPrimaryIconLayout() {
if (mPrimaryActionType == PRIMARY_ACTION_TYPE_EMPTY_ICON
|| mPrimaryActionType == PRIMARY_ACTION_TYPE_NO_ICON) {
return;
}
// Size of icon.
@DimenRes int sizeResId;
switch (mPrimaryActionIconSize) {
case PRIMARY_ACTION_ICON_SIZE_SMALL:
sizeResId = R.dimen.car_primary_icon_size;
break;
case PRIMARY_ACTION_ICON_SIZE_MEDIUM:
sizeResId = R.dimen.car_avatar_icon_size;
break;
case PRIMARY_ACTION_ICON_SIZE_LARGE:
sizeResId = R.dimen.car_single_line_list_item_height;
break;
default:
throw new IllegalStateException("Unknown primary action icon size.");
}
int iconSize = mContext.getResources().getDimensionPixelSize(sizeResId);
// Start margin of icon.
int startMargin;
switch (mPrimaryActionIconSize) {
case PRIMARY_ACTION_ICON_SIZE_SMALL:
case PRIMARY_ACTION_ICON_SIZE_MEDIUM:
startMargin = mContext.getResources().getDimensionPixelSize(R.dimen.car_keyline_1);
break;
case PRIMARY_ACTION_ICON_SIZE_LARGE:
startMargin = 0;
break;
default:
throw new IllegalStateException("Unknown primary action icon size.");
}
mBinders.add(vh -> {
ConstraintLayout.LayoutParams layoutParams =
(ConstraintLayout.LayoutParams) vh.getPrimaryIcon().getLayoutParams();
layoutParams.height = layoutParams.width = iconSize;
layoutParams.setMarginStart(startMargin);
if (mPrimaryActionIconSize == PRIMARY_ACTION_ICON_SIZE_LARGE) {
// A large icon is always vertically centered.
layoutParams.verticalBias = 0.5f;
layoutParams.topMargin = 0;
} else {
// Align the icon to the top of the parent. This allows the topMargin to shift it
// down relative to the top.
layoutParams.verticalBias = 0f;
// For all other icon sizes, the icon should be centered within the height of
// car_double_line_list_item_height. Note: the actual height of the item can be
// larger than this.
int itemHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.car_double_line_list_item_height);
layoutParams.topMargin = (itemHeight - iconSize) / 2;
}
vh.getPrimaryIcon().requestLayout();
});
}
private void setTextContent() {
boolean hasTitle = !TextUtils.isEmpty(mTitle);
boolean hasBody = !TextUtils.isEmpty(mBody);
if (!hasTitle && !hasBody) {
return;
}
mBinders.add(vh -> {
if (hasTitle) {
vh.getTitle().setVisibility(View.VISIBLE);
vh.getTitle().setText(mTitle);
}
if (hasBody) {
vh.getBody().setVisibility(View.VISIBLE);
vh.getBody().setText(mBody);
}
if (hasTitle && !hasBody) {
// If only title, then center the supplemental actions.
vh.getSupplementalGuideline().setGuidelineBegin(
ConstraintLayout.LayoutParams.UNSET);
vh.getSupplementalGuideline().setGuidelinePercent(0.5f);
} else {
// Otherwise, position it a fixed distance from the top.
vh.getSupplementalGuideline().setGuidelinePercent(
ConstraintLayout.LayoutParams.UNSET);
vh.getSupplementalGuideline().setGuidelineBegin(
mSupplementalGuidelineBegin);
}
});
}
/**
* Sets start margin of text view depending on icon type.
*/
private void setTextStartMargin() {
@DimenRes 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_ICON:
startMarginResId = mPrimaryActionIconSize == PRIMARY_ACTION_ICON_SIZE_LARGE
? R.dimen.car_keyline_4
: R.dimen.car_keyline_3; // Small and medium sized icon.
break;
default:
throw new IllegalStateException("Unknown primary action type.");
}
int startMargin = mContext.getResources().getDimensionPixelSize(startMarginResId);
mBinders.add(vh -> {
ViewGroup.MarginLayoutParams titleLayoutParams =
(ViewGroup.MarginLayoutParams) vh.getTitle().getLayoutParams();
titleLayoutParams.setMarginStart(startMargin);
vh.getTitle().requestLayout();
ViewGroup.MarginLayoutParams bodyLayoutParams =
(ViewGroup.MarginLayoutParams) 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 -> {
ViewGroup.MarginLayoutParams layoutParams =
(ViewGroup.MarginLayoutParams) vh.getTitle().getLayoutParams();
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);
ViewGroup.MarginLayoutParams layoutParams =
(ViewGroup.MarginLayoutParams) vh.getBody().getLayoutParams();
layoutParams.topMargin = margin;
layoutParams.bottomMargin = margin;
vh.getBody().requestLayout();
});
} else {
mBinders.add(vh -> {
Resources resources = mContext.getResources();
int padding2 = resources.getDimensionPixelSize(R.dimen.car_padding_2);
// Title has a top margin
ViewGroup.MarginLayoutParams titleLayoutParams =
(ViewGroup.MarginLayoutParams) vh.getTitle().getLayoutParams();
titleLayoutParams.topMargin = padding2;
vh.getTitle().requestLayout();
// Body is below title with no margin and has bottom margin.
ViewGroup.MarginLayoutParams bodyLayoutParams =
(ViewGroup.MarginLayoutParams) vh.getBody().getLayoutParams();
bodyLayoutParams.topMargin = 0;
bodyLayoutParams.bottomMargin = padding2;
vh.getBody().requestLayout();
});
}
}
/**
* Sets up view(s) for supplemental action.
*/
private void setSupplementalActions() {
boolean hasPrimaryAction = !TextUtils.isEmpty(mPrimaryActionText);
boolean hasSecondaryAction = !TextUtils.isEmpty(mSecondaryActionText);
if (!hasPrimaryAction && !hasSecondaryAction) {
return;
}
mBinders.add(vh -> {
vh.setActionBorderless(mIsActionBorderless);
if (hasSecondaryAction) {
Button secondaryAction = vh.getSecondaryAction();
secondaryAction.setVisibility(View.VISIBLE);
if (mShowSecondaryActionDivider) {
vh.getSecondaryActionDivider().setVisibility(View.VISIBLE);
}
secondaryAction.setText(mSecondaryActionText);
secondaryAction.setOnClickListener(mSecondaryActionOnClickListener);
// Add spacing between the buttons if there is a primary action.
int endMargin = hasPrimaryAction
? mContext.getResources().getDimensionPixelSize(R.dimen.car_padding_4)
: 0;
ViewGroup.MarginLayoutParams layoutParams =
(ViewGroup.MarginLayoutParams) secondaryAction.getLayoutParams();
layoutParams.setMarginEnd(endMargin);
secondaryAction.requestLayout();
}
if (hasPrimaryAction) {
Button primaryAction = vh.getPrimaryAction();
primaryAction.setVisibility(View.VISIBLE);
if (mShowPrimaryActionDivider) {
vh.getPrimaryActionDivider().setVisibility(View.VISIBLE);
}
primaryAction.setText(mPrimaryActionText);
primaryAction.setOnClickListener(mPrimaryActionOnClickListener);
}
});
}
/**
* Sets {@link View.OnClickListener} of {@code ActionListItem}.
*/
public void setOnClickListener(View.OnClickListener listener) {
mOnClickListener = listener;
markDirty();
}
/**
* Sets {@code Primary Action} to be represented by an icon.
*
* @param iconResId the resource identifier of the drawable.
* @param size The size of the icon. Must be one of {@link #PRIMARY_ACTION_ICON_SIZE_SMALL},
* {@link #PRIMARY_ACTION_ICON_SIZE_MEDIUM}, or
* {@link #PRIMARY_ACTION_ICON_SIZE_LARGE}.
*/
public void setPrimaryActionIcon(@DrawableRes int iconResId, @PrimaryActionIconSize int size) {
setPrimaryActionIcon(mContext.getDrawable(iconResId), size);
}
/**
* Sets {@code Primary Action} to be represented by an icon.
*
* @param drawable the Drawable to set, or null to clear the content.
* @param size The size of the icon. Must be one of {@link #PRIMARY_ACTION_ICON_SIZE_SMALL},
* {@link #PRIMARY_ACTION_ICON_SIZE_MEDIUM}, or
* {@link #PRIMARY_ACTION_ICON_SIZE_LARGE}.
*/
public void setPrimaryActionIcon(@Nullable Drawable drawable, @PrimaryActionIconSize int size) {
mPrimaryActionType = PRIMARY_ACTION_TYPE_ICON;
mPrimaryActionIconDrawable = drawable;
mPrimaryActionIconSize = size;
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>{@code Title} text is limited to one line, and ellipsizes at the end.
*
* @param title text to display as title.
*/
public void setTitle(@NonNull CharSequence 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(@NonNull CharSequence body) {
mBody = body;
markDirty();
}
/**
* Sets the primary action of this {@code ListItem}.
*
* @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.
* @deprecated Use {@link #setPrimaryAction(String, boolean, View.OnClickListener)} or
* {@link #setSecondaryAction(String, boolean, View.OnClickListener)} instead to individually
* set the actions.
*/
@Deprecated
public void setAction(@NonNull String text, boolean showDivider,
@NonNull View.OnClickListener listener) {
setPrimaryAction(text, showDivider, listener);
}
/**
* Sets the primary and secondary actions for this {@code ListItem}.
*
* @param primaryActionText The primary action text.
* @param showPrimaryActionDivider Whether or not to show a divider before the primary action.
* @param primaryActionOnClickListener The listener to be invoked when the primary action is
* triggered.
* @param secondaryActionText The secondary action text.
* @param showSecondaryActionDivider Whether or not to show a divider before the secondary
* action.
* @param secondaryActionOnClickListener The listener to be invoked when the secondary action is
* triggered.
* @deprecated Use {@link #setPrimaryAction(String, boolean, View.OnClickListener)} and
* {@link #setSecondaryAction(String, boolean, View.OnClickListener)} to set both actions.
*/
@Deprecated
public void setActions(@NonNull String primaryActionText, boolean showPrimaryActionDivider,
@NonNull View.OnClickListener primaryActionOnClickListener,
@NonNull String secondaryActionText, boolean showSecondaryActionDivider,
@NonNull View.OnClickListener secondaryActionOnClickListener) {
setPrimaryAction(primaryActionText, showPrimaryActionDivider, primaryActionOnClickListener);
setSecondaryAction(secondaryActionText, showSecondaryActionDivider,
secondaryActionOnClickListener);
}
/**
* Sets the primary action of this {@code ListItem}.
*
* @param primaryActionText Action text to display.
* @param showPrimaryActionDivider Whether or not to display a vertical bar before the primary
* action.
* @param primaryActionOnClickListener The callback that will run when the action is clicked.
*
* @throws IllegalArgumentException If {@code primaryActionText} is {@code null} or empty.
* @throws IllegalArgumentException If {@code primaryActionOnClickListener} is {@code null}.
*/
public void setPrimaryAction(@NonNull String primaryActionText,
boolean showPrimaryActionDivider,
@NonNull View.OnClickListener primaryActionOnClickListener) {
if (TextUtils.isEmpty(primaryActionText)) {
throw new IllegalArgumentException("Action text cannot be empty.");
}
if (primaryActionOnClickListener == null) {
throw new IllegalArgumentException("Action OnClickListener cannot be null.");
}
mPrimaryActionText = primaryActionText;
mPrimaryActionOnClickListener = primaryActionOnClickListener;
mShowPrimaryActionDivider = showPrimaryActionDivider;
markDirty();
}
/**
* Sets the secondary action of this {@code ListItem}.
*
* <p>The secondary action will appear before the primary action if both are set.
*
* @param secondaryActionText Action text to display.
* @param showSecondaryActionDivider Whether or not to display a vertical bar before the
* secondary action.
* @param secondaryActionOnClickListener The callback that will run when the action is clicked.
*
* @throws IllegalArgumentException If {@code secondaryActionText} is {@code null} or empty.
* @throws IllegalArgumentException If {@code secondaryActionOnClickListener} is {@code null}.
*/
public void setSecondaryAction(@NonNull String secondaryActionText,
boolean showSecondaryActionDivider,
@NonNull View.OnClickListener secondaryActionOnClickListener) {
if (TextUtils.isEmpty(secondaryActionText)) {
throw new IllegalArgumentException("Action text cannot be empty.");
}
if (secondaryActionOnClickListener == null) {
throw new IllegalArgumentException("Action OnClickListener cannot be null.");
}
mSecondaryActionText = secondaryActionText;
mSecondaryActionOnClickListener = secondaryActionOnClickListener;
mShowSecondaryActionDivider = showSecondaryActionDivider;
markDirty();
}
/**
* Sets whether or not the actions should be styled as borderless.
*
* <p>By default, this value is {@code true}.
*
* @param isActionBorderless {@code true} if the actions should be borderless. {@code false}
* otherwise.
*/
public void setActionBorderless(boolean isActionBorderless) {
mIsActionBorderless = isActionBorderless;
}
/**
* Holds the children views of {@link ActionListItem}.
*/
public static final class ViewHolder extends ListItem.ViewHolder {
private final View[] mWidgetViews;
private ImageView mPrimaryIcon;
private TextView mTitle;
private TextView mBody;
private boolean mIsActionBorderless = true;
private Guideline mSupplementalGuideline;
private Button mPrimaryActionBorderless;
private Button mPrimaryAction;
private View mPrimaryActionDivider;
private Button mSecondaryActionBorderless;
private Button mSecondaryAction;
private View mSecondaryActionDivider;
private View mClickInterceptor;
public ViewHolder(@NonNull View itemView) {
super(itemView);
mPrimaryIcon = itemView.findViewById(R.id.primary_icon);
mTitle = itemView.findViewById(R.id.title);
mBody = itemView.findViewById(R.id.body);
mSupplementalGuideline = itemView.findViewById(R.id.actions_guideline);
mPrimaryAction = itemView.findViewById(R.id.primary_action);
mPrimaryActionBorderless = itemView.findViewById(R.id.primary_action_borderless);
mPrimaryActionDivider = itemView.findViewById(R.id.primary_action_divider);
mSecondaryAction = itemView.findViewById(R.id.secondary_action);
mSecondaryActionBorderless = itemView.findViewById(R.id.secondary_action_borderless);
mSecondaryActionDivider = itemView.findViewById(R.id.secondary_action_divider);
mClickInterceptor = itemView.findViewById(R.id.click_interceptor);
// Each line groups relevant child views in an effort to help keep this view array
// updated with actual child views in the ViewHolder.
mWidgetViews = new View[] {
// Primary action.
mPrimaryIcon,
// Text.
mTitle, mBody,
// Supplemental actions
mPrimaryAction,
mPrimaryActionBorderless,
mPrimaryActionDivider,
mSecondaryAction,
mSecondaryActionBorderless,
mSecondaryActionDivider
};
}
@Override
public void onUxRestrictionsChanged(@NonNull CarUxRestrictions restrictions) {
CarUxRestrictionsUtils.apply(itemView.getContext(), restrictions, getBody());
}
/**
* Sets if the action returned is styled as borderless or non-borderless.
*
* <p>By default, this value is {@code true}.
*
* @param isBorderless Whether or not the action is borderless.
*/
public void setActionBorderless(boolean isBorderless) {
mIsActionBorderless = isBorderless;
}
@NonNull
public ImageView getPrimaryIcon() {
return mPrimaryIcon;
}
@NonNull
public TextView getTitle() {
return mTitle;
}
@NonNull
public TextView getBody() {
return mBody;
}
@NonNull
public Button getPrimaryAction() {
return mIsActionBorderless ? mPrimaryActionBorderless : mPrimaryAction;
}
@NonNull
@VisibleForTesting
Button getBorderlessPrimaryAction() {
return mPrimaryActionBorderless;
}
@NonNull
@VisibleForTesting
Button getBorderedPrimaryAction() {
return mPrimaryAction;
}
@NonNull
public View getPrimaryActionDivider() {
return mPrimaryActionDivider;
}
@NonNull
public Button getSecondaryAction() {
return mIsActionBorderless ? mSecondaryActionBorderless : mSecondaryAction;
}
@NonNull
@VisibleForTesting
Button getBorderlessSecondaryAction() {
return mSecondaryActionBorderless;
}
@NonNull
@VisibleForTesting
Button getBorderedSecondaryAction() {
return mSecondaryAction;
}
@NonNull
public View getSecondaryActionDivider() {
return mSecondaryActionDivider;
}
@NonNull
View[] getWidgetViews() {
return mWidgetViews;
}
/** Returns the Guideline that the actions should be centered upon. */
@NonNull
Guideline getSupplementalGuideline() {
return mSupplementalGuideline;
}
/**
* Returns the view that will intercept clicks beneath the supplemental icon and action
* views.
*/
@NonNull
View getClickInterceptView() {
return mClickInterceptor;
}
}
}