问题
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