mirror of
https://github.com/material-components/material-components-android.git
synced 2026-01-16 09:52:53 +08:00
217 lines
6.8 KiB
Java
217 lines
6.8 KiB
Java
/*
|
|
* Copyright 2019 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 com.google.android.material.ripple;
|
|
|
|
import android.content.res.ColorStateList;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.ColorFilter;
|
|
import android.graphics.PorterDuff;
|
|
import android.graphics.Rect;
|
|
import android.graphics.drawable.Drawable;
|
|
import androidx.annotation.ColorInt;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.RestrictTo;
|
|
import androidx.annotation.RestrictTo.Scope;
|
|
import androidx.core.graphics.drawable.TintAwareDrawable;
|
|
import androidx.dynamicanimation.animation.SpringForce;
|
|
import com.google.android.material.shape.MaterialShapeDrawable;
|
|
import com.google.android.material.shape.ShapeAppearanceModel;
|
|
import com.google.android.material.shape.Shapeable;
|
|
import com.google.android.material.shape.StateListShapeAppearanceModel;
|
|
|
|
/**
|
|
* A compat {@link Drawable} that is used to provide an overlay for pressed, focused, and hovered
|
|
* states (only when in enabled). This is intended to be used pre-Lollipop.
|
|
*
|
|
* <p>This Drawable is a {@link MaterialShapeDrawable} so that it can be shaped to match a
|
|
* MaterialShapeDrawable background.
|
|
*
|
|
* <p>Unlike the framework {@link android.graphics.drawable.RippleDrawable}, this will <b>not</b>
|
|
* apply different alphas for pressed, focused, and hovered states and it does not provide a ripple
|
|
* animation for the pressed state.
|
|
*/
|
|
@RestrictTo(Scope.LIBRARY_GROUP)
|
|
public class RippleDrawableCompat extends Drawable implements Shapeable, TintAwareDrawable {
|
|
|
|
private RippleDrawableCompatState drawableState;
|
|
|
|
/**
|
|
* Creates a {@link RippleDrawableCompat} with the given shape that will only draw when enabled
|
|
* and at least one of: pressed, focused, or hovered.
|
|
*
|
|
* @param shapeAppearanceModel The shape for the ripple.
|
|
*/
|
|
public RippleDrawableCompat(ShapeAppearanceModel shapeAppearanceModel) {
|
|
this(new RippleDrawableCompatState(new MaterialShapeDrawable(shapeAppearanceModel)));
|
|
}
|
|
|
|
private RippleDrawableCompat(RippleDrawableCompatState state) {
|
|
super();
|
|
this.drawableState = state;
|
|
}
|
|
|
|
@Override
|
|
public void setTint(@ColorInt int tintColor) {
|
|
drawableState.delegate.setTint(tintColor);
|
|
}
|
|
|
|
@Override
|
|
public void setTintMode(@Nullable PorterDuff.Mode tintMode) {
|
|
drawableState.delegate.setTintMode(tintMode);
|
|
}
|
|
|
|
@Override
|
|
public void setTintList(@Nullable ColorStateList tintList) {
|
|
drawableState.delegate.setTintList(tintList);
|
|
}
|
|
|
|
@Override
|
|
public void setShapeAppearanceModel(@NonNull ShapeAppearanceModel shapeAppearanceModel) {
|
|
drawableState.delegate.setShapeAppearanceModel(shapeAppearanceModel);
|
|
}
|
|
|
|
/**
|
|
* Get the {@link ShapeAppearanceModel} containing the path that will be rendered in this
|
|
* drawable.
|
|
*
|
|
* @return the current model.
|
|
*/
|
|
@Override
|
|
@NonNull
|
|
public ShapeAppearanceModel getShapeAppearanceModel() {
|
|
return drawableState.delegate.getShapeAppearanceModel();
|
|
}
|
|
|
|
public void setStateListShapeAppearanceModel(
|
|
@NonNull StateListShapeAppearanceModel stateListShapeAppearanceModel) {
|
|
drawableState.delegate.setShapeAppearance(stateListShapeAppearanceModel);
|
|
}
|
|
|
|
@Nullable
|
|
public StateListShapeAppearanceModel getStateListShapeAppearanceModel() {
|
|
return drawableState.delegate.getStateListShapeAppearanceModel();
|
|
}
|
|
|
|
public void setCornerSpringForce(@NonNull SpringForce springForce) {
|
|
drawableState.delegate.setCornerSpringForce(springForce);
|
|
}
|
|
|
|
@Nullable
|
|
public SpringForce getCornerSpringForce() {
|
|
return drawableState.delegate.getCornerSpringForce();
|
|
}
|
|
|
|
/*
|
|
* This is always stateful as it draws on the canvas only when enabled and (pressed, focused, or
|
|
* hovered).
|
|
*/
|
|
@Override
|
|
public boolean isStateful() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected boolean onStateChange(@NonNull int[] stateSet) {
|
|
boolean changed = super.onStateChange(stateSet);
|
|
if (drawableState.delegate.setState(stateSet)) {
|
|
changed = true;
|
|
}
|
|
boolean shouldDrawRipple = RippleUtils.shouldDrawRippleCompat(stateSet);
|
|
// If shouldDrawRipple is changing, this needs to be redrawn even if the paint / tint values
|
|
// are not changing in order to support setting a ColorStateList with a single color.
|
|
if (drawableState.shouldDrawDelegate != shouldDrawRipple) {
|
|
drawableState.shouldDrawDelegate = shouldDrawRipple;
|
|
changed = true;
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
@Override
|
|
public void draw(Canvas canvas) {
|
|
// Only draw the delegate Drawable when enabled and at least one of: pressed, focused, hovered.
|
|
if (drawableState.shouldDrawDelegate) {
|
|
drawableState.delegate.draw(canvas);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onBoundsChange(@NonNull Rect bounds) {
|
|
super.onBoundsChange(bounds);
|
|
drawableState.delegate.setBounds(bounds);
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public ConstantState getConstantState() {
|
|
return drawableState;
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public RippleDrawableCompat mutate() {
|
|
RippleDrawableCompatState newDrawableState = new RippleDrawableCompatState(drawableState);
|
|
drawableState = newDrawableState;
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public void setAlpha(int alpha) {
|
|
drawableState.delegate.setAlpha(alpha);
|
|
}
|
|
|
|
@Override
|
|
public void setColorFilter(@Nullable ColorFilter colorFilter) {
|
|
drawableState.delegate.setColorFilter(colorFilter);
|
|
}
|
|
|
|
@Override
|
|
public int getOpacity() {
|
|
return drawableState.delegate.getOpacity();
|
|
}
|
|
|
|
/**
|
|
* A {@link ConstantState} for {@link RippleDrawableCompat}
|
|
*/
|
|
static final class RippleDrawableCompatState extends ConstantState {
|
|
|
|
@NonNull MaterialShapeDrawable delegate;
|
|
boolean shouldDrawDelegate;
|
|
|
|
public RippleDrawableCompatState(MaterialShapeDrawable delegate) {
|
|
this.delegate = delegate;
|
|
this.shouldDrawDelegate = false;
|
|
}
|
|
|
|
public RippleDrawableCompatState(@NonNull RippleDrawableCompatState orig) {
|
|
this.delegate = (MaterialShapeDrawable) orig.delegate.getConstantState().newDrawable();
|
|
this.shouldDrawDelegate = orig.shouldDrawDelegate;
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public RippleDrawableCompat newDrawable() {
|
|
return new RippleDrawableCompat(new RippleDrawableCompatState(this));
|
|
}
|
|
|
|
@Override
|
|
public int getChangingConfigurations() {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|