Android原生实现控件Ripple方案(API28及以上)

Android控件的水波纹效果的实现方式有很多种,比如使用ripple文件,这里介绍一下另一种Android原生的水波纹实现方案(API28及以上)。

我们利用RippleDrawable来实现一个带Ripple的Button。RippleDrawable可以通过xml 中定义 ripple来实现,或者通过代码中动态创建RippleDrawable设置给控件。

自定义属性

添加ripple相关的自定义属性

 <declare-styleable name="shape_button">
		<attr name="carbon_rippleColor" />
        <attr name="carbon_rippleStyle" />
        <attr name="carbon_rippleHotspot" />
        <attr name="carbon_rippleRadius" />
        ...
</declare-styleable>
<!-- 两种类型的Ripple,background表示在控件背景上显示水波纹,borderless表示在控件背后显示水波纹 -->
 <attr name="carbon_rippleStyle" format="enum">
        <enum name="background" value="0" />
        <enum name="borderless" value="1" />
    </attr>

RippleView接口

创建RippleView通用接口

public interface RippleView {
    /**
     * Gets the ripple drawable.
     *
     * @return the ripple drawable. Can be null.
     */
    RippleDrawable getRippleDrawable();

    /**
     * Sets the ripple drawable. This method doesn't break the background.
     *
     * @param rippleDrawable the ripple drawable. Can be null
     */
    void setRippleDrawable(RippleDrawable rippleDrawable);
}

RippleView接口中的RippleDrawable也是一个接口,里面管理了RippleDrawable文件对象的创建。

RippleDrawable接口

public interface RippleDrawable{

    Drawable getBackground();

    enum Style {
        Background, Borderless
    }

    boolean setState(int[] stateSet);

    void draw(Canvas canvas);
    Style getStyle();

    boolean isHotspotEnabled();

    void setHotspotEnabled(boolean useHotspot);

    void setBounds(int left, int top, int right, int bottom);

    void setBounds(Rect bounds);

    void setHotspot(float x, float y);

    boolean isStateful();

    void setCallback(Drawable.Callback cb);

    ColorStateList getColor();

    void setRadius(int radius);

    int getRadius();

    static RippleDrawable create(ColorStateList color, Style style, View view, boolean useHotspot, int radius) {
        RippleDrawable rippleDrawable = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            rippleDrawable = new RippleDrawableMarshmallow(color, style == Style.Background ? view.getBackground() : null, style);
        } else if (Carbon.IS_LOLLIPOP_OR_HIGHER) {
            rippleDrawable = new RippleDrawableLollipop(color, style == Style.Background ? view.getBackground() : null, style);
        }
        rippleDrawable.setCallback(view);
        rippleDrawable.setHotspotEnabled(useHotspot);
        rippleDrawable.setRadius(radius);
        return rippleDrawable;
    }

    static RippleDrawable create(ColorStateList color, Style style, View view, Drawable background, boolean useHotspot, int radius) {
        RippleDrawable rippleDrawable = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            rippleDrawable = new RippleDrawableMarshmallow(color, background, style);
        } else if (Carbon.IS_LOLLIPOP_OR_HIGHER) {
            rippleDrawable = new RippleDrawableLollipop(color, background, style);
        }
        rippleDrawable.setCallback(view);
        rippleDrawable.setHotspotEnabled(useHotspot);
        rippleDrawable.setRadius(radius);
        return rippleDrawable;
    }
}

通过提供出去的create方法可以生成一个RippleDrawable文件对象,根据版本号生成不同的RippleDrawableMarshmallow对象 或 RippleDrawableLollipop对象。

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class RippleDrawableMarshmallow extends android.graphics.drawable.RippleDrawable implements RippleDrawable {

    private final ColorStateList color;
    private final Drawable background;
    private Style style;
    private boolean useHotspot;

    public RippleDrawableMarshmallow(ColorStateList color, Drawable background, Style style) {
        super(color, background, style == Style.Borderless ? null : new ColorDrawable(0xffffffff));
        this.style = style;
        this.color = color;
        this.background = background;
    }

    @Override
    public Drawable getBackground() {
        return background;
    }

    @Override
    public Style getStyle() {
        return style;
    }

    @Override
    public boolean isHotspotEnabled() {
        return useHotspot;
    }

    @Override
    public void setHotspotEnabled(boolean useHotspot) {
        this.useHotspot = useHotspot;
    }

    @Override
    public ColorStateList getColor() {
        return color;
    }
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class RippleDrawableLollipop extends android.graphics.drawable.RippleDrawable implements RippleDrawable {

    private final ColorStateList color;
    private final Drawable background;
    private Style style;
    private boolean useHotspot;
    private int radius;

    public RippleDrawableLollipop(ColorStateList color, Drawable background, Style style) {
        super(color, background, style == Style.Borderless ? null : new ColorDrawable(0xffffffff));
        this.style = style;
        this.color = color;
        this.background = background;
    }

    @Override
    public Drawable getBackground() {
        return background;
    }

    @Override
    public Style getStyle() {
        return style;
    }

    @Override
    public boolean isHotspotEnabled() {
        return useHotspot;
    }

    @Override
    public void setHotspotEnabled(boolean useHotspot) {
        this.useHotspot = useHotspot;
    }

    @Override
    public ColorStateList getColor() {
        return color;
    }

    @Override
    public void setRadius(int radius) {
        this.radius = radius;
        try {
            Method setMaxRadiusMethod = android.graphics.drawable.RippleDrawable.class.getDeclaredMethod("setMaxRadius", int.class);
            setMaxRadiusMethod.invoke(this, radius);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public int getRadius() {
        return radius;
    }
}

ShapeButton

ShapeButton 实现RippleView接口:

public class ShapeButton extends AppCompatButton
        implements ShadowView,
        ShapeModelView,
        RippleView {
   

  ...

    private static int[] rippleIds = new int[]{
            R.styleable.shape_button_carbon_rippleColor,
            R.styleable.shape_button_carbon_rippleStyle,
            R.styleable.shape_button_carbon_rippleHotspot,
            R.styleable.shape_button_carbon_rippleRadius
    };

    private void initButton(AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.shape_button, defStyleAttr, defStyleRes);
        Carbon.initElevation(this, a, elevationIds);
        Carbon.initCornerCutRadius(this,a,cornerCutRadiusIds);
        // 初始化ripple相关属性
        Carbon.initRippleDrawable(this,a,rippleIds);
        a.recycle();
    }



    // -------------------------------
    // shadow
    // -------------------------------
    
   ... ... ...

    // -------------------------------
    // shape
    // -------------------------------
   
   ... ... ...

    // -------------------------------
    // ripple
    // -------------------------------

    private RippleDrawable rippleDrawable;

    @Override
    public boolean dispatchTouchEvent(@NonNull MotionEvent event) {
		// 为rippledrawable设置扩散点
        if (rippleDrawable != null && event.getAction() == MotionEvent.ACTION_DOWN)
            rippleDrawable.setHotspot(event.getX(),event.getY());

        return super.dispatchTouchEvent(event);
    }

    @Override
    public RippleDrawable getRippleDrawable() {
        return rippleDrawable;
    }

    @Override
    public void setRippleDrawable(RippleDrawable newRipple) {
       // todo -- 为控件设置RippleDrawable作为背景
    }

}

初始化ripple相关属性:

    public static void initRippleDrawable(RippleView rippleView, TypedArray a, int[] ids) {
        int carbon_rippleColor = ids[0];
        int carbon_rippleStyle = ids[1];
        int carbon_rippleHotspot = ids[2];
        int carbon_rippleRadius = ids[3];

        View view = (View) rippleView;
        if (view.isInEditMode())
            return;

        ColorStateList color = a.getColorStateList(carbon_rippleColor);

        if (color != null) {
            RippleDrawable.Style style = RippleDrawable.Style.values()[a.getInt(carbon_rippleStyle, RippleDrawable.Style.Background.ordinal())];
            boolean useHotspot = a.getBoolean(carbon_rippleHotspot, true);
            int radius = (int) a.getDimension(carbon_rippleRadius, -1);

            rippleView.setRippleDrawable(RippleDrawable.create(color, style, view, useHotspot, radius));
        }
    }

Background

我们首先实现Style为background的情况:

    @Override
    public void setRippleDrawable(RippleDrawable newRipple) {
        if (newRipple != null) {
            newRipple.setCallback(this);
            newRipple.setBounds(0, 0, getWidth(), getHeight());
            newRipple.setState(getDrawableState());
            ((Drawable) newRipple).setVisible(getVisibility() == VISIBLE, false);
            // 其实就只是为控件设置了一个background,这个background是一个rippleDrawable图片
            if (newRipple.getStyle() == RippleDrawable.Style.Background)
                super.setBackgroundDrawable((Drawable) newRipple);
        }

        rippleDrawable = newRipple;
    }

如何使用:

            <com.chinatsp.demo1.shadow.ShapeButton
                android:id="@+id/show_dialog_btn"
                android:layout_width="wrap_content"
                android:layout_height="36dp"
                android:layout_margin="@dimen/carbon_padding"
                android:background="#ffffff"
                android:stateListAnimator="@null"
                android:text="TOM"
                app:carbon_cornerRadius="10dp"
                android:elevation="2dp"
                app:carbon_elevationShadowColor="@color/carbon_red_700"
                app:carbon_rippleColor="@color/carbon_green_700"
                app:carbon_rippleStyle="background"
                app:carbon_rippleRadius="40dp"
                />

Borderless

要实现borderless效果,我们首先需要为rippleDrawable添加状态,例如按压状态state_press

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        if (rippleDrawable != null && rippleDrawable.getStyle() != RippleDrawable.Style.Background)
            rippleDrawable.setState(getDrawableState());
    }

其次,borderless要求超出控件边界,其实是把子控件的rippledrawable显示在了父控件上,因此在刷新子控件背景时需要刷新父控件:

 @Override
    public void invalidateDrawable(@NonNull Drawable drawable) {
        super.invalidateDrawable(drawable);
        invalidateParentIfNeeded();
    }

    @Override
    public void invalidate(@NonNull Rect dirty) {
        super.invalidate(dirty);
        invalidateParentIfNeeded();
    }

    @Override
    public void invalidate(int l, int t, int r, int b) {
        super.invalidate(l, t, r, b);
        invalidateParentIfNeeded();
    }

    @Override
    public void invalidate() {
        super.invalidate();
        invalidateParentIfNeeded();
    }

    private void invalidateParentIfNeeded() {
        if (getParent() == null || !(getParent() instanceof View))
            return;

        if (rippleDrawable != null && rippleDrawable.getStyle() == RippleDrawable.Style.Borderless)
            ((View) getParent()).invalidate();
    }

假如当前Button的父控件是LinearLayout,则需要我们重写LinearLayout的drawChild方法来显示子控件的rippleDrawable:

public class LinearLayout extends LinearLayoutCompat {
    public LinearLayout(@NonNull Context context) {
        super(context);
    }

    public LinearLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public LinearLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        if (child instanceof RippleView) {
            RippleView rippleView = (RippleView) child;
            RippleDrawable rippleDrawable = rippleView.getRippleDrawable();
            if (rippleDrawable != null && rippleDrawable.getStyle() == RippleDrawable.Style.Borderless) {
                int saveCount = canvas.save();
                canvas.translate(child.getLeft() + child.getWidth()/2f, child.getTop() + child.getHeight()/2f);
                canvas.concat(child.getMatrix());
                // 在子控件中间显示子控件的rippledrawable
                rippleDrawable.draw(canvas);
                canvas.restoreToCount(saveCount);
            }
        }

        return super.drawChild(canvas, child, drawingTime);
    }
}

如何使用:

<com.chinatsp.demo1.ripple.LinearLayout
            android:layout_width="match_parent"
            android:layout_height="500dp"
            android:orientation="horizontal">
            
            <com.chinatsp.demo1.shadow.ShapeButton
                android:id="@+id/show_dialog_btn"
                android:layout_width="wrap_content"
                android:layout_height="36dp"
                android:layout_margin="@dimen/carbon_padding"
                android:background="#ffffff"
                android:stateListAnimator="@null"
                android:text="TOM"
                app:carbon_cornerRadius="10dp"
                android:elevation="2dp"
                app:carbon_elevationShadowColor="@color/carbon_red_700"
                app:carbon_rippleColor="@color/carbon_green_700"
                app:carbon_rippleStyle="borderless"
                />
</com.chinatsp.demo1.ripple.LinearLayout>

完整代码:

public class ShapeButton extends AppCompatButton
        implements ShadowView,
        ShapeModelView,
        RippleView {
    public ShapeButton(@NonNull Context context) {
        super(context);
        initButton(null, android.R.attr.buttonStyle, R.style.carbon_Button);
    }

    public ShapeButton(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initButton(attrs, android.R.attr.buttonStyle, R.style.carbon_Button);
    }

    public ShapeButton(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initButton(attrs, defStyleAttr, R.style.carbon_Button);
    }

    public ShapeButton(Context context, String text, OnClickListener listener) {
        super(context);
        initButton(null, android.R.attr.buttonStyle, R.style.carbon_Button);
        setText(text);
        setOnClickListener(listener);
    }

    private static int[] elevationIds = new int[]{
            R.styleable.shape_button_carbon_elevation,
            R.styleable.shape_button_carbon_elevationShadowColor,
            R.styleable.shape_button_carbon_elevationAmbientShadowColor,
            R.styleable.shape_button_carbon_elevationSpotShadowColor
    };

    private static int[] cornerCutRadiusIds = new int[]{
            R.styleable.shape_button_carbon_cornerRadiusTopStart,
            R.styleable.shape_button_carbon_cornerRadiusTopEnd,
            R.styleable.shape_button_carbon_cornerRadiusBottomStart,
            R.styleable.shape_button_carbon_cornerRadiusBottomEnd,
            R.styleable.shape_button_carbon_cornerRadius,
            R.styleable.shape_button_carbon_cornerCutTopStart,
            R.styleable.shape_button_carbon_cornerCutTopEnd,
            R.styleable.shape_button_carbon_cornerCutBottomStart,
            R.styleable.shape_button_carbon_cornerCutBottomEnd,
            R.styleable.shape_button_carbon_cornerCut
    };

    private static int[] rippleIds = new int[]{
            R.styleable.shape_button_carbon_rippleColor,
            R.styleable.shape_button_carbon_rippleStyle,
            R.styleable.shape_button_carbon_rippleHotspot,
            R.styleable.shape_button_carbon_rippleRadius
    };

    protected TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);

    private void initButton(AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.shape_button, defStyleAttr, defStyleRes);
        Carbon.initElevation(this, a, elevationIds);
        Carbon.initCornerCutRadius(this,a,cornerCutRadiusIds);
        Carbon.initRippleDrawable(this,a,rippleIds);
        a.recycle();
    }



    // -------------------------------
    // shadow
    // -------------------------------
    private float elevation = 0;
    private float translationZ = 0;
    private ColorStateList ambientShadowColor, spotShadowColor;
    @Override
    public float getElevation() {
        return elevation;
    }

    @Override
    public void setElevation(float elevation) {
        if (Carbon.IS_PIE_OR_HIGHER) {
            super.setElevation(elevation);
            super.setTranslationZ(translationZ);
        } else if (Carbon.IS_LOLLIPOP_OR_HIGHER) {
            if (ambientShadowColor == null || spotShadowColor == null) {
                super.setElevation(elevation);
                super.setTranslationZ(translationZ);
            } else {
                super.setElevation(0);
                super.setTranslationZ(0);
            }
        } else if (elevation != this.elevation && getParent() != null) {
            ((View) getParent()).postInvalidate();
        }
        this.elevation = elevation;
    }

    @Override
    public float getTranslationZ() {
        return translationZ;
    }

    public void setTranslationZ(float translationZ) {
        if (translationZ == this.translationZ)
            return;
        if (Carbon.IS_PIE_OR_HIGHER) {
            super.setTranslationZ(translationZ);
        } else if (Carbon.IS_LOLLIPOP_OR_HIGHER) {
            if (ambientShadowColor == null || spotShadowColor == null) {
                super.setTranslationZ(translationZ);
            } else {
                super.setTranslationZ(0);
            }
        } else if (translationZ != this.translationZ && getParent() != null) {
            ((View) getParent()).postInvalidate();
        }
        this.translationZ = translationZ;
    }

    @Override
    public ColorStateList getElevationShadowColor() {
        return ambientShadowColor;
    }

    @Override
    public void setElevationShadowColor(ColorStateList shadowColor) {
        ambientShadowColor = spotShadowColor = shadowColor;
        setElevation(elevation);
        setTranslationZ(translationZ);
    }

    @Override
    public void setElevationShadowColor(int color) {
        ambientShadowColor = spotShadowColor = ColorStateList.valueOf(color);
        setElevation(elevation);
        setTranslationZ(translationZ);
    }

    @Override
    public void setOutlineAmbientShadowColor(ColorStateList color) {
        ambientShadowColor = color;
        if (Carbon.IS_PIE_OR_HIGHER) {
            super.setOutlineAmbientShadowColor(color.getColorForState(getDrawableState(), color.getDefaultColor()));
        } else {
            setElevation(elevation);
            setTranslationZ(translationZ);
        }
    }

    @Override
    public void setOutlineAmbientShadowColor(int color) {
        setOutlineAmbientShadowColor(ColorStateList.valueOf(color));
    }

    @Override
    public int getOutlineAmbientShadowColor() {
        return ambientShadowColor.getDefaultColor();
    }

    @Override
    public void setOutlineSpotShadowColor(int color) {
        setOutlineSpotShadowColor(ColorStateList.valueOf(color));
    }

    @Override
    public void setOutlineSpotShadowColor(ColorStateList color) {
        spotShadowColor = color;
        if (Carbon.IS_PIE_OR_HIGHER) {
            super.setOutlineSpotShadowColor(color.getColorForState(getDrawableState(), color.getDefaultColor()));
        } else {
            setElevation(elevation);
            setTranslationZ(translationZ);
        }

    }

    @Override
    public int getOutlineSpotShadowColor() {
        return ambientShadowColor.getDefaultColor();
    }

    @Override
    public boolean hasShadow() {
        return false;
    }

    @Override
    public void drawShadow(Canvas canvas) {

    }

    @Override
    public void draw(Canvas canvas) {
        boolean c = !Carbon.isShapeRect(shapeModel, boundsRect);

        if (Carbon.IS_PIE_OR_HIGHER) {
            if (spotShadowColor != null)
                super.setOutlineSpotShadowColor(spotShadowColor.getColorForState(getDrawableState(), spotShadowColor.getDefaultColor()));
            if (ambientShadowColor != null)
                super.setOutlineAmbientShadowColor(ambientShadowColor.getColorForState(getDrawableState(), ambientShadowColor.getDefaultColor()));
        }

        // 判断如果不是圆角矩形,需要使用轮廓Path,绘制一下Path,不然显示会很奇怪
        if (getWidth() > 0 && getHeight() > 0 && ((c && !Carbon.IS_LOLLIPOP_OR_HIGHER) || !shapeModel.isRoundRect(boundsRect))) {
            int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
            super.draw(canvas);
            paint.setXfermode(Carbon.CLEAR_MODE);
            if (c) {
                cornersMask.setFillType(Path.FillType.INVERSE_WINDING);
                canvas.drawPath(cornersMask, paint);
            }
            canvas.restoreToCount(saveCount);
            paint.setXfermode(null);
        }else{
            super.draw(canvas);
        }
    }

    // -------------------------------
    // shape
    // -------------------------------
    private ShapeAppearanceModel shapeModel = new ShapeAppearanceModel();
    private MaterialShapeDrawable shadowDrawable = new MaterialShapeDrawable(shapeModel);
    @Override
    public void setShapeModel(ShapeAppearanceModel shapeModel) {
        this.shapeModel = shapeModel;
        shadowDrawable = new MaterialShapeDrawable(shapeModel);
        if (getWidth() > 0 && getHeight() > 0)
            updateCorners();
        if (!Carbon.IS_LOLLIPOP_OR_HIGHER)
            postInvalidate();
    }
    // View的轮廓形状
    private RectF boundsRect = new RectF();
    // View的轮廓形状形成的Path路径
    private Path cornersMask = new Path();

    /**
     * 更新圆角
     */
    private void updateCorners() {
        if (Carbon.IS_LOLLIPOP_OR_HIGHER) {
            // 如果不是矩形,裁剪View的轮廓
            if (!Carbon.isShapeRect(shapeModel, boundsRect)){
                setClipToOutline(true);
            }
            //该方法返回一个Outline对象,它描述了该视图的形状。
            setOutlineProvider(new ViewOutlineProvider() {
                @Override
                public void getOutline(View view, Outline outline) {
                    if (Carbon.isShapeRect(shapeModel, boundsRect)) {
                        outline.setRect(0, 0, getWidth(), getHeight());
                    } else {
                        shadowDrawable.setBounds(0, 0, getWidth(), getHeight());
                        shadowDrawable.setShadowCompatibilityMode(MaterialShapeDrawable.SHADOW_COMPAT_MODE_NEVER);
                        shadowDrawable.getOutline(outline);
                    }
                }
            });
        }
        // 拿到圆角矩形的形状
        boundsRect.set(shadowDrawable.getBounds());
        // 拿到圆角矩形的Path
        shadowDrawable.getPathForSize(getWidth(), getHeight(), cornersMask);
    }

    @Override
    public ShapeAppearanceModel getShapeModel() {
        return this.shapeModel;
    }

    @Override
    public void setCornerCut(float cornerCut) {
        shapeModel = ShapeAppearanceModel.builder().setAllCorners(new CutCornerTreatment(cornerCut)).build();
        setShapeModel(shapeModel);
    }

    @Override
    public void setCornerRadius(float cornerRadius) {
        shapeModel = ShapeAppearanceModel.builder().setAllCorners(new RoundedCornerTreatment(cornerRadius)).build();
        setShapeModel(shapeModel);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        if (!changed)
            return;

        if (getWidth() == 0 || getHeight() == 0)
            return;

        updateCorners();

    }

    // -------------------------------
    // ripple
    // -------------------------------

    private RippleDrawable rippleDrawable;

    @Override
    public boolean dispatchTouchEvent(@NonNull MotionEvent event) {

        if (rippleDrawable != null && event.getAction() == MotionEvent.ACTION_DOWN)
            rippleDrawable.setHotspot(event.getX(),event.getY());

        return super.dispatchTouchEvent(event);
    }

    @Override
    public RippleDrawable getRippleDrawable() {
        return rippleDrawable;
    }

    @Override
    public void setRippleDrawable(RippleDrawable newRipple) {
        if (newRipple != null) {
            newRipple.setCallback(this);
            newRipple.setBounds(0, 0, getWidth(), getHeight());
            newRipple.setState(getDrawableState());
            ((Drawable) newRipple).setVisible(getVisibility() == VISIBLE, false);
            if (newRipple.getStyle() == RippleDrawable.Style.Background)
                super.setBackgroundDrawable((Drawable) newRipple);
        }

        rippleDrawable = newRipple;
    }

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        if (rippleDrawable != null && rippleDrawable.getStyle() != RippleDrawable.Style.Background)
            rippleDrawable.setState(getDrawableState());
    }


    @Override
    public void invalidateDrawable(@NonNull Drawable drawable) {
        super.invalidateDrawable(drawable);
        invalidateParentIfNeeded();
    }

    @Override
    public void invalidate(@NonNull Rect dirty) {
        super.invalidate(dirty);
        invalidateParentIfNeeded();
    }

    @Override
    public void invalidate(int l, int t, int r, int b) {
        super.invalidate(l, t, r, b);
        invalidateParentIfNeeded();
    }

    @Override
    public void invalidate() {
        super.invalidate();
        invalidateParentIfNeeded();
    }

    private void invalidateParentIfNeeded() {
        if (getParent() == null || !(getParent() instanceof View))
            return;

        if (rippleDrawable != null && rippleDrawable.getStyle() == RippleDrawable.Style.Borderless)
            ((View) getParent()).invalidate();
    }

}

猜你喜欢

转载自blog.csdn.net/jxq1994/article/details/133687352