Strange behavior of inflated buttons when changing one's color

↘锁芯ラ 提交于 2019-12-19 10:19:08

问题


I am trying to implement a dynamic button inflation for my Android application, based on an input specified by the user in real time. When clicked, button changes its color from blue to red.

The code responsible for this goes as follows:

LayoutInflater layoutsInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.layout_for_buttons);

// Let's say that now we need only 3 buttons:
for (int i = 0; i < 3; i++) {
    RelativeLayout buttonLayout = (RelativeLayout) layoutsInflater
            .inflate(R.layout.button_layout, linearLayout, false);
    Button button = (Button) buttonLayout.getChildAt(0);
    button.setText("Button " + i);

    button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View buttonView) {
            Button button = (Button) buttonView;
            GradientDrawable gradientDrawable = (GradientDrawable) button
                    .getBackground();
            gradientDrawable.setColor(Color.RED);
            gradientDrawable.invalidateSelf();
        }
    });

    linearLayout.addView(buttonLayout);
    linearLayout.invalidate();
}

Layout that I append buttons to is a simple LinearLayout:

<LinearLayout
    android:id="@+id/layout_for_buttons"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
</LinearLayout>

Inflated button_layout is a RelativeLayout consisting of a Button and a TextView:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="10sp" >

    <Button
        android:id="@+id/button_view"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/button_bg"
        android:gravity="left"
        android:paddingBottom="7dip"
        android:paddingLeft="50sp"
        android:paddingTop="7dip"
        android:textColor="#FFFFFF"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/label_view"
        android:layout_width="24dip"
        android:layout_height="24dip"
        android:layout_marginLeft="13dip"
        android:layout_marginTop="8dip"
        android:gravity="center"
        android:text="Hi"
        android:textColor="#FFFFFF"
        android:textSize="20sp"
        android:textStyle="bold" />

</RelativeLayout>

@drawable/button_bg is a shape in blue color:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:padding="10dp"
    android:shape="rectangle" >
    <solid android:color="#0000ff" />
    <corners android:radius="10dp" />
</shape>

Now, when I run the application everything seems to be fine. After being clicked, first button unsurprisingly turns red:

When I close and re-run the application every single button is blue, and that is a behavior that matches up to my expectations. The problem is, when I re-run the application for the second time, every button turns red (as if it has been clicked):

As it is written in a documentation, inflate method should inflate a new view hierarchy based on a given XML, so what could be responsible for this? I thought that the same GradientDrawable might be shared among every inflated button, but debugger shows that each button has its own GradientDrawable instance.


回答1:


It seems that I've already solved my problem. That was a GradientDrawable.mutate() method which had to be called to prevent such behavior:

Button button = (Button) buttonView;
GradientDrawable gradientDrawable = (GradientDrawable) button.getBackground();
gradientDrawable.mutate(); // needed line
gradientDrawable.setColor(Color.RED);
gradientDrawable.invalidateSelf();

It guarantees that an initialized Drawable won't share its state with Drawables inflated from the same XML, as it was stated in a documentation:

Make this drawable mutable. This operation cannot be reversed. A mutable drawable is guaranteed to not share its state with any other drawable. This is especially useful when you need to modify properties of drawables loaded from resources. By default, all drawables instances loaded from the same resource share a common state; if you modify the state of one instance, all the other instances will receive the same modification. Calling this method on a mutable Drawable will have no effect.




回答2:


call invalidate on linear layout after adding the view,

linearLayout.addView(buttonLayout);
linearLayout.invalidate();


来源:https://stackoverflow.com/questions/16345756/strange-behavior-of-inflated-buttons-when-changing-ones-color

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!