Android, setting background color of button loses ripple effect

后端 未结 7 1042
北恋
北恋 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:49

    You can add the ripple effect & background color with an additionnal ripple drawable:

    your layout :

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <Button
            android:id="@+id/button_connect"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dip"
            android:fontFamily="sans-serif"
            android:text="Connect"
            android:background="@drawable/ripple"
            android:textColor="#FFFFFF"
            android:textSize="18sp" />
    
    </LinearLayout>
    

    ripple.xml (this is where you can add background color in addition to the ripple effect) :

    <?xml version="1.0" encoding="utf-8"?>
    <!-- in drawable folder-->
    <ripple
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="?android:colorControlHighlight">
    
        <item android:id="@android:id/mask">
            <shape android:shape="rectangle">
                <solid android:color="?android:colorAccent" />
            </shape>
        </item>
    
        <item>
            <shape android:shape="rectangle">
                <!-- put your background color here-->
                <solid android:color="@color/default_color" />
            </shape>
        </item>
    
    </ripple>
    
    0 讨论(0)
  • 2020-12-07 00:51

    Actually, you can use <layer-list> 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 <layer-list>, 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:

    <resources>
        <declare-styleable name="MyView">
            <attr name="selectableBackground" format="reference"/>
            <attr name="backgroundDrawable" format="reference"/>
        </declare-styleable>
    </resources>
    

    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.

    0 讨论(0)
  • 2020-12-07 00:51

    You should use styles

    <style parent="Theme.AppCompat.Light" name="RaisedButtonGreen">
        <item name="colorButtonNormal">@color/green</item>
        <item name="colorControlHighlight">@color/greenLight</item>
        <item name="android:textColor">@android:color/white</item>
    </style>
    

    this is the optimal solution

    <Button
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Button with ripple for style"
        android:theme="@style/RaisedButtonGreen"/>
    
    0 讨论(0)
  • 2020-12-07 00:54

    A very simple and straight forward way of doing this is to set ?attr/selectableItemBackground to android:foreground attribute of your button. Following xml is perfectly valid and works

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:foreground="?attr/selectableItemBackground"/>
    
    0 讨论(0)
  • 2020-12-07 00:54

    Just use :

    android:backgroundTint="#4CAF50"
    

    Instead of:

    android:background="#4CAF50"
    

    Don't forget to change your Button to android.support.v7.widget.AppCompatButton

    0 讨论(0)
  • 2020-12-07 00:59

    Don't change the background of Button. Change the theme.

    <style name="ButtonGray">
        <item name="colorButtonNormal">@color/gray</item>
    </style>
    

    and in your xml file

    <Button
         android:id="@+id/accept_button"
         android:layout_height="wrap_content"
         android:layout_width="0dp"
         android:layout_weight="1"
         android:text="@string/button_accept_group"
         android:theme="@style/ButtonGray"/>
    

    Or you can add it in your main app theme

    <style name="AppTheme"
               parent="Theme.AppCompat.Light.NoActionBar">
            <item name="colorButtonNormal">@color/primary_color</item>
    </style>
    

    And don't need change button background.

    If you want totally custom background you need create your selector. And you can set there ripple effect.

    0 讨论(0)
提交回复
热议问题