mirror of
https://github.com/material-components/material-components-android.git
synced 2026-01-20 12:01:46 +08:00
Previously [most of] the components were either in `widget` or depended on it, and all theme-related resources (attributes, styles, etc.) were placed there. This structure doesn't really work for our goals, and makes it harder to split components by functional area. In this commit, I move all the theme definitions to `theme` (as well as `dialog` and `bottomsheet` since they also have relevant themes, and splitting them into separate packages is the only way to make the dep tree non-circular). Component packages declare their own theme attributes (e.g. `chip` declares all the theme attributes necessary for chips), as well as styles, colors, dimens, drawables, etc. This move isn't entirely complete in this commit, but it is enough to get things building with this setup. I'll move the rest of the components' resources in follow-up commits. PiperOrigin-RevId: 182611275
1083 lines
31 KiB
Java
1083 lines
31 KiB
Java
/*
|
|
* Copyright 2017 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
|
|
*
|
|
* https://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 android.support.design.chip;
|
|
|
|
import android.annotation.TargetApi;
|
|
import android.content.Context;
|
|
import android.content.res.ColorStateList;
|
|
import android.graphics.Outline;
|
|
import android.graphics.Rect;
|
|
import android.graphics.RectF;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.graphics.drawable.RippleDrawable;
|
|
import android.os.Build;
|
|
import android.os.Build.VERSION;
|
|
import android.os.Build.VERSION_CODES;
|
|
import android.os.Bundle;
|
|
import android.support.annotation.AnimatorRes;
|
|
import android.support.annotation.BoolRes;
|
|
import android.support.annotation.CallSuper;
|
|
import android.support.annotation.ColorRes;
|
|
import android.support.annotation.DimenRes;
|
|
import android.support.annotation.DrawableRes;
|
|
import android.support.annotation.IntDef;
|
|
import android.support.annotation.NonNull;
|
|
import android.support.annotation.Nullable;
|
|
import android.support.annotation.StringRes;
|
|
import android.support.annotation.StyleRes;
|
|
import android.support.design.animation.MotionSpec;
|
|
import android.support.design.chip.ChipDrawable.Delegate;
|
|
import android.support.design.internal.ThemeEnforcement;
|
|
import android.support.design.internal.ViewUtils;
|
|
import android.support.design.resources.TextAppearance;
|
|
import android.support.design.ripple.RippleUtils;
|
|
import android.support.v4.view.ViewCompat;
|
|
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
|
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
|
|
import android.support.v4.widget.ExploreByTouchHelper;
|
|
import android.support.v7.widget.AppCompatCheckBox;
|
|
import android.util.AttributeSet;
|
|
import android.view.KeyEvent;
|
|
import android.view.MotionEvent;
|
|
import android.view.PointerIcon;
|
|
import android.view.SoundEffectConstants;
|
|
import android.view.View;
|
|
import android.view.ViewOutlineProvider;
|
|
import android.view.ViewParent;
|
|
import android.view.accessibility.AccessibilityEvent;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Chips are compact elements that represent an attribute, text, entity, or action. They allow users
|
|
* to enter information, select a choice, filter content, or trigger an action.
|
|
*
|
|
* <p>The Chip widget is a thin view wrapper around the {@link ChipDrawable}, which contains all of
|
|
* the layout and draw logic. The extra logic exists to support touch, mouse, keyboard, and
|
|
* accessibility navigation. The main chip and close icon are considered to be separate logical
|
|
* sub-views, and contain their own navigation behavior and state.
|
|
*
|
|
* <p>All attributes from {@link R.styleable#ChipDrawable} are supported. Do not use the {@code
|
|
* android:background} and {@code android:text} attributes. They will be ignored because Chip
|
|
* manages its own background Drawable and text. The basic attributes you can set are:
|
|
*
|
|
* <ul>
|
|
* <li>{@link android.R.attr#checkable android:checkable} - If true, the chip can be toggled. If
|
|
* false, the chip acts like a button.
|
|
* <li>{@link R.attr#chipText app:chipText} - Sets the text of the chip.
|
|
* <li>{@link R.attr#chipIcon app:chipIcon} and {@link R.attr#chipIconEnabled app:chipIconEnabled}
|
|
* - Sets the icon of the chip. Usually on the left.
|
|
* <li>{@link R.attr#checkedIcon app:checkedIcon} and {@link R.attr#checkedIconEnabled
|
|
* app:checkedIconEnabled} - Sets a custom icon to use when checked. Usually on the left.
|
|
* <li>{@link R.attr#closeIcon app:closeIcon} and {@link R.attr#closeIconEnabled
|
|
* app:closeIconEnabled} - Sets a custom icon that the user can click to close. Usually on the
|
|
* right.
|
|
* </ul>
|
|
*
|
|
* <p>You can register a listener on the main chip with {@link #setOnClickListener(OnClickListener)}
|
|
* or {@link #setOnCheckedChangeListener(OnCheckedChangeListener)}. You can register a listener on
|
|
* the close icon with {@link #setOnCloseIconClickListener(OnClickListener)}.
|
|
*
|
|
* @see ChipDrawable
|
|
*/
|
|
public class Chip extends AppCompatCheckBox implements Delegate {
|
|
|
|
private static final int CLOSE_ICON_VIRTUAL_ID = 0;
|
|
|
|
private static final int[] SELECTED_STATE = new int[] {android.R.attr.state_selected};
|
|
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef({ExploreByTouchHelper.INVALID_ID, ExploreByTouchHelper.HOST_ID, CLOSE_ICON_VIRTUAL_ID})
|
|
private @interface VirtualId {}
|
|
|
|
@Nullable private ChipDrawable chipDrawable;
|
|
|
|
@Nullable private OnClickListener onCloseIconClickListener;
|
|
@Nullable private OnCheckedChangeListener onCheckedChangeListenerInternal;
|
|
private boolean deferredCheckedValue;
|
|
@VirtualId private int focusedVirtualView = ExploreByTouchHelper.INVALID_ID;
|
|
private boolean closeIconPressed;
|
|
private boolean closeIconHovered;
|
|
private boolean closeIconFocused;
|
|
|
|
private final ChipTouchHelper touchHelper;
|
|
private final Rect rect = new Rect();
|
|
private final RectF rectF = new RectF();
|
|
|
|
public Chip(Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
public Chip(Context context, AttributeSet attrs) {
|
|
this(context, attrs, R.attr.chipStyle);
|
|
}
|
|
|
|
public Chip(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
super(context, attrs, defStyleAttr);
|
|
|
|
ThemeEnforcement.checkAppCompatTheme(context);
|
|
|
|
ChipDrawable drawable =
|
|
ChipDrawable.createFromAttributes(
|
|
context, attrs, defStyleAttr, R.style.Widget_MaterialComponents_Chip_Action);
|
|
setChipDrawable(drawable);
|
|
|
|
touchHelper = new ChipTouchHelper(this);
|
|
ViewCompat.setAccessibilityDelegate(this, touchHelper);
|
|
ViewCompat.setImportantForAccessibility(this, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
|
|
|
|
initOutlineProvider();
|
|
setChecked(deferredCheckedValue);
|
|
}
|
|
|
|
private void initOutlineProvider() {
|
|
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
|
setOutlineProvider(
|
|
new ViewOutlineProvider() {
|
|
@Override
|
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
public void getOutline(View view, Outline outline) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.getOutline(outline);
|
|
} else {
|
|
outline.setAlpha(0.0f);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the ChipDrawable backing this chip.
|
|
*/
|
|
public Drawable getChipDrawable() {
|
|
return chipDrawable;
|
|
}
|
|
|
|
/**
|
|
* Sets the ChipDrawable backing this chip.
|
|
*/
|
|
public void setChipDrawable(@NonNull ChipDrawable drawable) {
|
|
if (chipDrawable != drawable) {
|
|
unapplyChipDrawable(chipDrawable);
|
|
chipDrawable = drawable;
|
|
applyChipDrawable(chipDrawable);
|
|
|
|
if (RippleUtils.USE_FRAMEWORK_RIPPLE) {
|
|
//noinspection NewApi
|
|
RippleDrawable ripple =
|
|
new RippleDrawable(
|
|
RippleUtils.convertToRippleDrawableColor(chipDrawable.getRippleColor()),
|
|
chipDrawable,
|
|
null);
|
|
chipDrawable.setUseCompatRipple(false);
|
|
//noinspection NewApi
|
|
ViewCompat.setBackground(this, ripple);
|
|
} else {
|
|
chipDrawable.setUseCompatRipple(true);
|
|
ViewCompat.setBackground(this, chipDrawable);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void unapplyChipDrawable(@Nullable ChipDrawable chipDrawable) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setDelegate(null);
|
|
}
|
|
}
|
|
|
|
private void applyChipDrawable(@NonNull ChipDrawable chipDrawable) {
|
|
chipDrawable.setDelegate(this);
|
|
}
|
|
|
|
@Override
|
|
protected int[] onCreateDrawableState(int extraSpace) {
|
|
final int[] state = super.onCreateDrawableState(extraSpace + 1);
|
|
if (isChecked()) {
|
|
mergeDrawableStates(state, SELECTED_STATE);
|
|
}
|
|
return state;
|
|
}
|
|
|
|
@Override
|
|
public void onChipDrawableSizeChange() {
|
|
requestLayout();
|
|
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
|
invalidateOutline();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setChecked(boolean checked) {
|
|
if (chipDrawable == null) {
|
|
// Defer the setChecked() call until after initialization.
|
|
deferredCheckedValue = checked;
|
|
} else if (chipDrawable.isCheckable()) {
|
|
boolean wasChecked = isChecked();
|
|
super.setChecked(checked);
|
|
|
|
if (wasChecked != checked) {
|
|
if (onCheckedChangeListenerInternal != null) {
|
|
onCheckedChangeListenerInternal.onCheckedChanged(this, checked);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register a callback to be invoked when the checked state of this chip changes. This callback is
|
|
* used for internal purpose only.
|
|
*/
|
|
void setOnCheckedChangeListenerInternal(OnCheckedChangeListener listener) {
|
|
onCheckedChangeListenerInternal = listener;
|
|
}
|
|
|
|
/** Register a callback to be invoked when the close icon is clicked. */
|
|
public void setOnCloseIconClickListener(OnClickListener listener) {
|
|
this.onCloseIconClickListener = listener;
|
|
}
|
|
|
|
/**
|
|
* Call this chip's {@link #onCloseIconClickListener}, if it is defined. Performs all normal
|
|
* actions associated with clicking: reporting accessibility event, playing a sound, etc.
|
|
*
|
|
* @return True there was an assigned {@link #onCloseIconClickListener} that was called, false
|
|
* otherwise is returned.
|
|
*/
|
|
@CallSuper
|
|
public boolean performCloseIconClick() {
|
|
playSoundEffect(SoundEffectConstants.CLICK);
|
|
|
|
boolean result;
|
|
if (onCloseIconClickListener != null) {
|
|
onCloseIconClickListener.onClick(this);
|
|
result = true;
|
|
} else {
|
|
result = false;
|
|
}
|
|
|
|
touchHelper.sendEventForVirtualView(
|
|
CLOSE_ICON_VIRTUAL_ID, AccessibilityEvent.TYPE_VIEW_CLICKED);
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean onTouchEvent(MotionEvent event) {
|
|
boolean handled = false;
|
|
|
|
int action = event.getActionMasked();
|
|
boolean eventInCloseIcon = getCloseIconTouchBounds().contains(event.getX(), event.getY());
|
|
switch (action) {
|
|
case MotionEvent.ACTION_DOWN:
|
|
if (eventInCloseIcon) {
|
|
setCloseIconPressed(true);
|
|
handled = true;
|
|
}
|
|
break;
|
|
case MotionEvent.ACTION_MOVE:
|
|
if (closeIconPressed) {
|
|
if (!eventInCloseIcon) {
|
|
setCloseIconPressed(false);
|
|
}
|
|
handled = true;
|
|
}
|
|
break;
|
|
case MotionEvent.ACTION_UP:
|
|
if (closeIconPressed) {
|
|
performCloseIconClick();
|
|
handled = true;
|
|
}
|
|
// Fall-through.
|
|
case MotionEvent.ACTION_CANCEL:
|
|
setCloseIconPressed(false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return handled || super.onTouchEvent(event);
|
|
}
|
|
|
|
@Override
|
|
public boolean onHoverEvent(MotionEvent event) {
|
|
int action = event.getActionMasked();
|
|
switch (action) {
|
|
case MotionEvent.ACTION_HOVER_MOVE:
|
|
setCloseIconHovered(getCloseIconTouchBounds().contains(event.getX(), event.getY()));
|
|
break;
|
|
case MotionEvent.ACTION_HOVER_EXIT:
|
|
setCloseIconHovered(false);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return super.onHoverEvent(event);
|
|
}
|
|
|
|
@Override
|
|
protected boolean dispatchHoverEvent(MotionEvent event) {
|
|
return touchHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
|
|
}
|
|
|
|
@Override
|
|
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
|
|
if (focused) {
|
|
// If we've gained focus from another view, always focus the chip first.
|
|
setFocusedVirtualView(ExploreByTouchHelper.HOST_ID);
|
|
} else {
|
|
setFocusedVirtualView(ExploreByTouchHelper.INVALID_ID);
|
|
}
|
|
invalidate();
|
|
|
|
super.onFocusChanged(focused, direction, previouslyFocusedRect);
|
|
}
|
|
|
|
@Override
|
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
|
// We need to handle focus change within the Chip because we are simulating multiple Views. The
|
|
// left/right arrow keys will move between the chip and the close icon. Focus
|
|
// up/down/forward/back jumps out of the Chip to the next focusable View in the hierarchy.
|
|
boolean focusChanged = false;
|
|
switch (event.getKeyCode()) {
|
|
case KeyEvent.KEYCODE_DPAD_LEFT:
|
|
if (event.hasNoModifiers()) {
|
|
focusChanged = moveFocus(ViewUtils.isLayoutRtl(this));
|
|
}
|
|
break;
|
|
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
|
if (event.hasNoModifiers()) {
|
|
focusChanged = moveFocus(!ViewUtils.isLayoutRtl(this));
|
|
}
|
|
break;
|
|
case KeyEvent.KEYCODE_DPAD_CENTER:
|
|
case KeyEvent.KEYCODE_ENTER:
|
|
switch (focusedVirtualView) {
|
|
case ExploreByTouchHelper.HOST_ID:
|
|
performClick();
|
|
return true;
|
|
case CLOSE_ICON_VIRTUAL_ID:
|
|
performCloseIconClick();
|
|
return true;
|
|
case ExploreByTouchHelper.INVALID_ID:
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case KeyEvent.KEYCODE_TAB:
|
|
int focusChangeDirection = 0;
|
|
if (event.hasNoModifiers()) {
|
|
focusChangeDirection = View.FOCUS_FORWARD;
|
|
} else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
|
|
focusChangeDirection = View.FOCUS_BACKWARD;
|
|
}
|
|
if (focusChangeDirection != 0) {
|
|
final ViewParent parent = getParent();
|
|
// Move focus out of this view.
|
|
View nextFocus = this;
|
|
do {
|
|
nextFocus = nextFocus.focusSearch(focusChangeDirection);
|
|
} while (nextFocus != null && nextFocus != this && nextFocus.getParent() == parent);
|
|
if (nextFocus != null) {
|
|
nextFocus.requestFocus();
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (focusChanged) {
|
|
invalidate();
|
|
return true;
|
|
} else {
|
|
return super.onKeyDown(keyCode, event);
|
|
}
|
|
}
|
|
|
|
private boolean moveFocus(boolean positive) {
|
|
ensureFocus();
|
|
boolean focusChanged = false;
|
|
if (positive) {
|
|
if (focusedVirtualView == ExploreByTouchHelper.HOST_ID) {
|
|
setFocusedVirtualView(CLOSE_ICON_VIRTUAL_ID);
|
|
focusChanged = true;
|
|
}
|
|
} else {
|
|
if (focusedVirtualView == CLOSE_ICON_VIRTUAL_ID) {
|
|
setFocusedVirtualView(ExploreByTouchHelper.HOST_ID);
|
|
focusChanged = true;
|
|
}
|
|
}
|
|
return focusChanged;
|
|
}
|
|
|
|
private void ensureFocus() {
|
|
if (focusedVirtualView == ExploreByTouchHelper.INVALID_ID) {
|
|
setFocusedVirtualView(ExploreByTouchHelper.HOST_ID);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void getFocusedRect(Rect r) {
|
|
if (focusedVirtualView == CLOSE_ICON_VIRTUAL_ID) {
|
|
r.set(getCloseIconTouchBoundsInt());
|
|
} else {
|
|
super.getFocusedRect(r);
|
|
}
|
|
}
|
|
|
|
private void setFocusedVirtualView(@VirtualId int virtualView) {
|
|
if (focusedVirtualView != virtualView) {
|
|
if (focusedVirtualView == CLOSE_ICON_VIRTUAL_ID) {
|
|
setCloseIconFocused(false);
|
|
}
|
|
focusedVirtualView = virtualView;
|
|
if (virtualView == CLOSE_ICON_VIRTUAL_ID) {
|
|
setCloseIconFocused(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void setCloseIconPressed(boolean pressed) {
|
|
if (closeIconPressed != pressed) {
|
|
closeIconPressed = pressed;
|
|
refreshDrawableState();
|
|
}
|
|
}
|
|
|
|
private void setCloseIconHovered(boolean hovered) {
|
|
if (closeIconHovered != hovered) {
|
|
closeIconHovered = hovered;
|
|
refreshDrawableState();
|
|
}
|
|
}
|
|
|
|
private void setCloseIconFocused(boolean focused) {
|
|
if (closeIconFocused != focused) {
|
|
closeIconFocused = focused;
|
|
refreshDrawableState();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void drawableStateChanged() {
|
|
super.drawableStateChanged();
|
|
|
|
boolean changed = false;
|
|
|
|
if (chipDrawable != null && chipDrawable.isCloseIconStateful()) {
|
|
changed = chipDrawable.setCloseIconState(createCloseIconDrawableState());
|
|
}
|
|
|
|
if (changed) {
|
|
invalidate();
|
|
}
|
|
}
|
|
|
|
private int[] createCloseIconDrawableState() {
|
|
int count = 0;
|
|
if (isEnabled()) {
|
|
count++;
|
|
}
|
|
if (closeIconFocused) {
|
|
count++;
|
|
}
|
|
if (closeIconHovered) {
|
|
count++;
|
|
}
|
|
if (closeIconPressed) {
|
|
count++;
|
|
}
|
|
if (isChecked()) {
|
|
count++;
|
|
}
|
|
|
|
int[] stateSet = new int[count];
|
|
int i = 0;
|
|
|
|
if (isEnabled()) {
|
|
stateSet[i] = android.R.attr.state_enabled;
|
|
i++;
|
|
}
|
|
if (closeIconFocused) {
|
|
stateSet[i] = android.R.attr.state_focused;
|
|
i++;
|
|
}
|
|
if (closeIconHovered) {
|
|
stateSet[i] = android.R.attr.state_hovered;
|
|
i++;
|
|
}
|
|
if (closeIconPressed) {
|
|
stateSet[i] = android.R.attr.state_pressed;
|
|
i++;
|
|
}
|
|
if (isChecked()) {
|
|
stateSet[i] = android.R.attr.state_selected;
|
|
i++;
|
|
}
|
|
return stateSet;
|
|
}
|
|
|
|
private boolean hasCloseIcon() {
|
|
return chipDrawable != null && chipDrawable.getCloseIcon() != null;
|
|
}
|
|
|
|
private RectF getCloseIconTouchBounds() {
|
|
rectF.setEmpty();
|
|
|
|
if (hasCloseIcon()) {
|
|
// noinspection ConstantConditions
|
|
chipDrawable.getCloseIconTouchBounds(rectF);
|
|
}
|
|
|
|
return rectF;
|
|
}
|
|
|
|
private Rect getCloseIconTouchBoundsInt() {
|
|
RectF bounds = getCloseIconTouchBounds();
|
|
rect.set((int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom);
|
|
return rect;
|
|
}
|
|
|
|
@Override
|
|
@TargetApi(VERSION_CODES.N)
|
|
public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
|
|
if (getCloseIconTouchBounds().contains(event.getX(), event.getY()) && isEnabled()) {
|
|
return PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_HAND);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** Provides a virtual view hierarchy for the close icon. */
|
|
private class ChipTouchHelper extends ExploreByTouchHelper {
|
|
|
|
ChipTouchHelper(Chip view) {
|
|
super(view);
|
|
}
|
|
|
|
@Override
|
|
protected int getVirtualViewAt(float x, float y) {
|
|
return (hasCloseIcon() && getCloseIconTouchBounds().contains(x, y))
|
|
? CLOSE_ICON_VIRTUAL_ID
|
|
: HOST_ID;
|
|
}
|
|
|
|
@Override
|
|
protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
|
|
virtualViewIds.add(HOST_ID);
|
|
if (hasCloseIcon()) {
|
|
virtualViewIds.add(CLOSE_ICON_VIRTUAL_ID);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onPopulateNodeForVirtualView(
|
|
int virtualViewId, AccessibilityNodeInfoCompat node) {
|
|
if (hasCloseIcon()) {
|
|
node.setContentDescription(
|
|
getContext().getString(R.string.mtrl_chip_close_icon_content_description));
|
|
node.setBoundsInParent(getCloseIconTouchBoundsInt());
|
|
node.addAction(AccessibilityActionCompat.ACTION_CLICK);
|
|
node.setEnabled(isEnabled());
|
|
} else {
|
|
node.setContentDescription("");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onPopulateNodeForHost(AccessibilityNodeInfoCompat node) {
|
|
node.setCheckable(chipDrawable != null && chipDrawable.isCheckable());
|
|
node.setClassName(Chip.class.getName());
|
|
node.setContentDescription(
|
|
Chip.class.getSimpleName()
|
|
+ ". "
|
|
+ (chipDrawable != null ? chipDrawable.getChipText() : ""));
|
|
}
|
|
|
|
@Override
|
|
protected boolean onPerformActionForVirtualView(
|
|
int virtualViewId, int action, Bundle arguments) {
|
|
if (action == AccessibilityNodeInfoCompat.ACTION_CLICK
|
|
&& virtualViewId == CLOSE_ICON_VIRTUAL_ID) {
|
|
return performCloseIconClick();
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Getters and setters for attributes.
|
|
|
|
@Nullable
|
|
public ColorStateList getChipBackgroundColor() {
|
|
return chipDrawable != null ? chipDrawable.getChipBackgroundColor() : null;
|
|
}
|
|
|
|
public void setChipBackgroundColorResource(@ColorRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipBackgroundColorResource(id);
|
|
}
|
|
}
|
|
|
|
public void setChipBackgroundColor(@Nullable ColorStateList chipBackgroundColor) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipBackgroundColor(chipBackgroundColor);
|
|
}
|
|
}
|
|
|
|
public float getChipMinHeight() {
|
|
return chipDrawable != null ? chipDrawable.getChipMinHeight() : 0;
|
|
}
|
|
|
|
public void setChipMinHeightResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipMinHeightResource(id);
|
|
}
|
|
}
|
|
|
|
public void setChipMinHeight(float minHeight) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipMinHeight(minHeight);
|
|
}
|
|
}
|
|
|
|
public float getChipCornerRadius() {
|
|
return chipDrawable != null ? chipDrawable.getChipCornerRadius() : 0;
|
|
}
|
|
|
|
public void setChipCornerRadiusResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipCornerRadiusResource(id);
|
|
}
|
|
}
|
|
|
|
public void setChipCornerRadius(float chipCornerRadius) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipCornerRadius(chipCornerRadius);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public ColorStateList getChipStrokeColor() {
|
|
return chipDrawable != null ? chipDrawable.getChipStrokeColor() : null;
|
|
}
|
|
|
|
public void setChipStrokeColorResource(@ColorRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipStrokeColorResource(id);
|
|
}
|
|
}
|
|
|
|
public void setChipStrokeColor(@Nullable ColorStateList chipStrokeColor) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipStrokeColor(chipStrokeColor);
|
|
}
|
|
}
|
|
|
|
public float getChipStrokeWidth() {
|
|
return chipDrawable != null ? chipDrawable.getChipStrokeWidth() : 0;
|
|
}
|
|
|
|
public void setChipStrokeWidthResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipStrokeWidthResource(id);
|
|
}
|
|
}
|
|
|
|
public void setChipStrokeWidth(float chipStrokeWidth) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipStrokeWidth(chipStrokeWidth);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public ColorStateList getRippleColor() {
|
|
return chipDrawable != null ? chipDrawable.getRippleColor() : null;
|
|
}
|
|
|
|
public void setRippleColorResource(@ColorRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setRippleColorResource(id);
|
|
}
|
|
}
|
|
|
|
public void setRippleColor(@Nullable ColorStateList rippleColor) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setRippleColor(rippleColor);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public CharSequence getChipText() {
|
|
return chipDrawable != null ? chipDrawable.getChipText() : null;
|
|
}
|
|
|
|
public void setChipTextResource(@StringRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipTextResource(id);
|
|
}
|
|
}
|
|
|
|
public void setChipText(@Nullable CharSequence chipText) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipText(chipText);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public TextAppearance getTextAppearance() {
|
|
return chipDrawable != null ? chipDrawable.getTextAppearance() : null;
|
|
}
|
|
|
|
public void setTextAppearanceResource(@StyleRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setTextAppearanceResource(id);
|
|
}
|
|
}
|
|
|
|
public void setTextAppearance(@Nullable TextAppearance textAppearance) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setTextAppearance(textAppearance);
|
|
}
|
|
}
|
|
|
|
public boolean isChipIconEnabled() {
|
|
return chipDrawable != null && chipDrawable.isChipIconEnabled();
|
|
}
|
|
|
|
public void setChipIconEnabledResource(@BoolRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipIconEnabledResource(id);
|
|
}
|
|
}
|
|
|
|
public void setChipIconEnabled(boolean chipIconEnabled) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipIconEnabled(chipIconEnabled);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public Drawable getChipIcon() {
|
|
return chipDrawable != null ? chipDrawable.getChipIcon() : null;
|
|
}
|
|
|
|
public void setChipIconResource(@DrawableRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipIconResource(id);
|
|
}
|
|
}
|
|
|
|
public void setChipIcon(@Nullable Drawable chipIcon) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipIcon(chipIcon);
|
|
}
|
|
}
|
|
|
|
public float getChipIconSize() {
|
|
return chipDrawable != null ? chipDrawable.getChipIconSize() : 0;
|
|
}
|
|
|
|
public void setChipIconSizeResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipIconSizeResource(id);
|
|
}
|
|
}
|
|
|
|
public void setChipIconSize(float chipIconSize) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipIconSize(chipIconSize);
|
|
}
|
|
}
|
|
|
|
public boolean isCloseIconEnabled() {
|
|
return chipDrawable != null && chipDrawable.isCloseIconEnabled();
|
|
}
|
|
|
|
public void setCloseIconEnabledResource(@BoolRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCloseIconEnabledResource(id);
|
|
}
|
|
}
|
|
|
|
public void setCloseIconEnabled(boolean closeIconEnabled) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCloseIconEnabled(closeIconEnabled);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public Drawable getCloseIcon() {
|
|
return chipDrawable != null ? chipDrawable.getCloseIcon() : null;
|
|
}
|
|
|
|
public void setCloseIconResource(@DrawableRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCloseIconResource(id);
|
|
}
|
|
}
|
|
|
|
public void setCloseIcon(@Nullable Drawable closeIcon) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCloseIcon(closeIcon);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public ColorStateList getCloseIconTint() {
|
|
return chipDrawable != null ? chipDrawable.getCloseIconTint() : null;
|
|
}
|
|
|
|
public void setCloseIconTintResource(@ColorRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCloseIconTintResource(id);
|
|
}
|
|
}
|
|
|
|
public void setCloseIconTint(@Nullable ColorStateList closeIconTint) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCloseIconTint(closeIconTint);
|
|
}
|
|
}
|
|
|
|
public float getCloseIconSize() {
|
|
return chipDrawable != null ? chipDrawable.getCloseIconSize() : 0;
|
|
}
|
|
|
|
public void setCloseIconSizeResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCloseIconSizeResource(id);
|
|
}
|
|
}
|
|
|
|
public void setCloseIconSize(float closeIconSize) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCloseIconSize(closeIconSize);
|
|
}
|
|
}
|
|
|
|
public boolean isCheckable() {
|
|
return chipDrawable != null && chipDrawable.isCheckable();
|
|
}
|
|
|
|
public void setCheckableResource(@BoolRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCheckableResource(id);
|
|
}
|
|
}
|
|
|
|
public void setCheckable(boolean checkable) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCheckable(checkable);
|
|
}
|
|
}
|
|
|
|
public boolean isCheckedIconEnabled() {
|
|
return chipDrawable != null && chipDrawable.isCheckedIconEnabled();
|
|
}
|
|
|
|
public void setCheckedIconEnabledResource(@BoolRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCheckedIconEnabledResource(id);
|
|
}
|
|
}
|
|
|
|
public void setCheckedIconEnabled(boolean checkedIconEnabled) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCheckedIconEnabled(checkedIconEnabled);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public Drawable getCheckedIcon() {
|
|
return chipDrawable != null ? chipDrawable.getCheckedIcon() : null;
|
|
}
|
|
|
|
public void setCheckedIconResource(@DrawableRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCheckedIconResource(id);
|
|
}
|
|
}
|
|
|
|
public void setCheckedIcon(@Nullable Drawable checkedIcon) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCheckedIcon(checkedIcon);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public MotionSpec getShowMotionSpec() {
|
|
return chipDrawable != null ? chipDrawable.getShowMotionSpec() : null;
|
|
}
|
|
|
|
public void setShowMotionSpecResource(@AnimatorRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setShowMotionSpecResource(id);
|
|
}
|
|
}
|
|
|
|
public void setShowMotionSpec(@Nullable MotionSpec showMotionSpec) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setShowMotionSpec(showMotionSpec);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public MotionSpec getHideMotionSpec() {
|
|
return chipDrawable != null ? chipDrawable.getHideMotionSpec() : null;
|
|
}
|
|
|
|
public void setHideMotionSpecResource(@AnimatorRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setHideMotionSpecResource(id);
|
|
}
|
|
}
|
|
|
|
public void setHideMotionSpec(@Nullable MotionSpec hideMotionSpec) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setHideMotionSpec(hideMotionSpec);
|
|
}
|
|
}
|
|
|
|
public float getChipStartPadding() {
|
|
return chipDrawable != null ? chipDrawable.getChipStartPadding() : 0;
|
|
}
|
|
|
|
public void setChipStartPaddingResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipStartPaddingResource(id);
|
|
}
|
|
}
|
|
|
|
public void setChipStartPadding(float chipStartPadding) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipStartPadding(chipStartPadding);
|
|
}
|
|
}
|
|
|
|
public float getIconStartPadding() {
|
|
return chipDrawable != null ? chipDrawable.getIconStartPadding() : 0;
|
|
}
|
|
|
|
public void setIconStartPaddingResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setIconStartPaddingResource(id);
|
|
}
|
|
}
|
|
|
|
public void setIconStartPadding(float iconStartPadding) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setIconStartPadding(iconStartPadding);
|
|
}
|
|
}
|
|
|
|
public float getIconEndPadding() {
|
|
return chipDrawable != null ? chipDrawable.getIconEndPadding() : 0;
|
|
}
|
|
|
|
public void setIconEndPaddingResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setIconEndPaddingResource(id);
|
|
}
|
|
}
|
|
|
|
public void setIconEndPadding(float iconEndPadding) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setIconEndPadding(iconEndPadding);
|
|
}
|
|
}
|
|
|
|
public float getTextStartPadding() {
|
|
return chipDrawable != null ? chipDrawable.getTextStartPadding() : 0;
|
|
}
|
|
|
|
public void setTextStartPaddingResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setTextStartPaddingResource(id);
|
|
}
|
|
}
|
|
|
|
public void setTextStartPadding(float textStartPadding) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setTextStartPadding(textStartPadding);
|
|
}
|
|
}
|
|
|
|
public float getTextEndPadding() {
|
|
return chipDrawable != null ? chipDrawable.getTextEndPadding() : 0;
|
|
}
|
|
|
|
public void setTextEndPaddingResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setTextEndPaddingResource(id);
|
|
}
|
|
}
|
|
|
|
public void setTextEndPadding(float textEndPadding) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setTextEndPadding(textEndPadding);
|
|
}
|
|
}
|
|
|
|
public float getCloseIconStartPadding() {
|
|
return chipDrawable != null ? chipDrawable.getCloseIconStartPadding() : 0;
|
|
}
|
|
|
|
public void setCloseIconStartPaddingResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCloseIconStartPaddingResource(id);
|
|
}
|
|
}
|
|
|
|
public void setCloseIconStartPadding(float closeIconStartPadding) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCloseIconStartPadding(closeIconStartPadding);
|
|
}
|
|
}
|
|
|
|
public float getCloseIconEndPadding() {
|
|
return chipDrawable != null ? chipDrawable.getCloseIconEndPadding() : 0;
|
|
}
|
|
|
|
public void setCloseIconEndPaddingResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCloseIconEndPaddingResource(id);
|
|
}
|
|
}
|
|
|
|
public void setCloseIconEndPadding(float closeIconEndPadding) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setCloseIconEndPadding(closeIconEndPadding);
|
|
}
|
|
}
|
|
|
|
public float getChipEndPadding() {
|
|
return chipDrawable != null ? chipDrawable.getChipEndPadding() : 0;
|
|
}
|
|
|
|
public void setChipEndPaddingResource(@DimenRes int id) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipEndPaddingResource(id);
|
|
}
|
|
}
|
|
|
|
public void setChipEndPadding(float chipEndPadding) {
|
|
if (chipDrawable != null) {
|
|
chipDrawable.setChipEndPadding(chipEndPadding);
|
|
}
|
|
}
|
|
}
|