Android, setting background color of button loses ripple effect

后端 未结 7 1095
北恋
北恋 2020-12-07 00:40

After adding color to an android button, it loses its ripple effect that makes the user feel like there is a responsive click. How do I fix this? I\'ve searched through many

7条回答
  •  天命终不由人
    2020-12-07 00:51

    Actually, you can use of drawables to combine ripple effect with any other drawable. This is a universal solution also for pre-lolipop: I've tested it in many configurations.

    The only problem is that pre-lolipop crashes when ?selectableItemBackground appears inside , so we have to create LayerDrawable programmatically.

    A very fast simple solution looks like:

    Specify for your View

    android:background="?selectableItemBackground"
    

    Then anywhere in the code create mySpecialDrawable and do the trick:

    Drawable[] layers = {mySpecialDrawable, getBackground()};
    setBackgroundDrawable(new LayerDrawable(layers).mutate()); 
    

    Please note that .mutate() for LayeredDrawable is essential here!

    A more complex solution could be useful when you already have your custom View and prefer rather extend its functionality and compatibility than add extra empty FrameLayout as a parent.

    Inside attrs.xml put:

    
        
            
            
        
    
    

    then inside your View-descendant class:

    private Drawable selectableBackground;
    private Drawable backgroundDrawable;
    
    public MyView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    
        try {
            TypedArray attributeArray;
            attributeArray = context.obtainStyledAttributes(attrs, R.styleable.MyView);
    
            int id = attributeArray.getResourceId(R.styleable.MyView_selectableBackground, -1);
            if (id != -1) {
                selectableBackground = ResourcesCompat.getDrawable(getResources(), id, context.getTheme());
            }
            id = attributeArray.getResourceId(R.styleable.MyView_backgroundDrawable, -1);
            if (id != -1) {
                backgroundDrawable = ResourcesCompat.getDrawable(getResources(), id, context.getTheme());
            }
    
            constructBackground();
            attributeArray.recycle();
        } catch (Exception e) {
            Log.e(this.toString(), "Attributes initialization error", e);
            throw e;
        }
    }
    
    void setSelectableBackground(Drawable drawable) {
        selectableBackground = drawable;
        constructBackground();
    }
    
    void setDrawable(Drawable drawable) {
        backgroundDrawable = drawable;
        constructBackground();
    }
    
    private void constructBackground() {
        if (selectableBackground != null) {
            if (backgroundDrawable != null) {
                Drawable[] layers = {backgroundDrawable, selectableBackground};
                setBackgroundDrawable(new LayerDrawable(layers).mutate());      // Both, using layers
            } else setBackgroundDrawable(selectableBackground);                 // Selectable only
        } else setBackgroundDrawable(backgroundDrawable);                       // Background only or null
    }
    

    I prefer this approach because it has no issues like android:foreground attribute which is 23+ or extra overhead of enclosing clickable views inside FrameLayout.

提交回复
热议问题