Android support library 23.4.0: android.support.v7

2019-06-16 18:36发布

So I updated to the latest support libraries, and got a crash I am not able to fix. My build.gradle now has these dependencies:

dependencies {
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:gridlayout-v7:23.4.0'
    compile 'com.android.support:support-v4:23.4.0'
    compile 'com.android.support:cardview-v7:23.4.0'
    compile 'com.android.support:recyclerview-v7:23.4.0'
    compile 'com.android.support:design:23.4.0'
    // More stuff...
}

I had a working listener that is used to catch clicks and start a new Activity. This was working fine in support libraries v. 23.1.0, but not in 23.4.0 (and 23.3.0):

public class IngredientItemOnClickListener implements OnClickListener
{
    private Ingredient mIngredient;

    public IngredientItemOnClickListener(Ingredient ingredient)
    {
        mIngredient= ingredient;
    }

    @Override
    public void onClick(View view)
    {
        MyActivity myActivity = (MyActivity) view.getContext(); // <-- crash here
        myActivity.showIngredientActivity(mIngredient);
    }
}

This listener is simply attached to an ImageButton and thereafter the color of the Button is tinted, like this:

Ingredient ingredient = getIngredient();
myImageButton.setOnClickListener(new IngredientItemOnClickListener(ingredient));
Drawable drawable = Tinting.tint(myActivity, R.drawable.my_icon, R.color.red);
myImageButton.setImageDrawable(drawable);

where Tinting.tint() is my own tinting function:

public class Tinting
{
    @Nullable
    public static Drawable tint(Context context, int drawableId, int colorId)
    {
        final Drawable drawable = ContextCompat.getDrawable(context, drawableId);
        if (drawable != null)
        {
            final Drawable wrapped = DrawableCompat.wrap(drawable);
            drawable.mutate();
            DrawableCompat.setTint(wrapped, ContextCompat.getColor(context, colorId));
        }
        return drawable;
    }
}

Previously when I clicked the button everything worked as expected, but now the Context of the View seems to have changed to TintContextWrapper which I can find little information about. I found this issue, but the project member advises to ask here on StackOverflow, so here it is.

What have I tried?

Since the project member in the Google issue stated You will need to obtain the activity from the wrapped context. I tried casting to TintContextWrapper instead of MyActivity, which works fine, but I cannot figure out how to get MyActivity from TintContextWrapper.

So my questions are:

  1. How can I get MyActivity from the TintContextWrapper?
  2. Why is my ImageButton suddenly wrapped in a TintContextWrapper.
  3. Should this behavior really be expected?

Definition of ImageButton in xml is simply:

<ImageButton
    android:id="@+id/my_id"
    android:src="@drawable/my_icon" />

Stack trace:

java.lang.ClassCastException: android.support.v7.widget.TintContextWrapper cannot be cast to com.my.app.activities.MyActivity
    at com.my.app.listeners.IngredientItemOnClickListener.onClick(IngredientItemOnClickListener.java:21)
    at android.view.View.performClick(View.java:4475)
    at android.view.View$PerformClick.run(View.java:18786)
    at android.os.Handler.handleCallback(Handler.java:730)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:176)
    at android.app.ActivityThread.main(ActivityThread.java:5419)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:525)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
    at dalvik.system.NativeStart.main(Native Method)

7条回答
爷的心禁止访问
2楼-- · 2019-06-16 18:46

My suggestion is to pass a reference of your activity into the onClickListener to avoid the issue with TintContextWrapper. Giving your class a reference to MyActivity is simple and avoids possible casting issues.

查看更多
成全新的幸福
3楼-- · 2019-06-16 18:51

@Krøllebølle

Android support library 23.4.0: android.support.v7.widget.TintContextWrapper cannot be cast to Activity

Answer to you question is : first your click event code should be like:

@Override
public void onClick(View view)
{
    MyActivity myActivity = getRequiredActivity(view);
    myActivity.showIngredientActivity(mIngredient);
}

and then write function getRequiredActivity():

private Activity getRequiredActivity(View req_view) {
    Context context = req_view.getContext();
    while (context instanceof ContextWrapper) {
        if (context instanceof Activity) {
            return (Activity)context;
        }
        context = ((ContextWrapper)context).getBaseContext();
    }
    return null;
}

and your Crash/Exception is fixed :)

查看更多
放荡不羁爱自由
4楼-- · 2019-06-16 18:53
  1. both activity n TintContextWRapper comes from ContextWrapper. ContextWrapper have a method getBaseContext(). It should be easy to create a loop method that checks instanceof WrapContext, gets base context and then checks instanceof Activity. (If you have problems with this method comment here that I'll dig on some project of mine and paste here to u)

  2. Because AppCompat wraps your context to be able to inject "compat" views and "compat" tinting and other "compat" stuff. That's normal.

  3. Yes. That's how AppCompat does its thing.

查看更多
兄弟一词,经得起流年.
5楼-- · 2019-06-16 18:59

You can try

Activity activity = (Activity) view.getRootView().getContext()

Gets the context that contains this view without the wrapper of android.support.v7.widget.TintContextWrapper.

查看更多
霸刀☆藐视天下
6楼-- · 2019-06-16 19:01
  1. How can I get MyActivity from the TintContextWrapper?

You really shouldn't. There's no guaranteed that a View's Context will be an Activity -- definitely not a specific Activity. Where are you setting your OnClickListener? I'm assuming that at the place where you set the listener, you will have access to the Activity. For example, if you're setting the listener from an Activity:

public class MainActivity extends AppCompatActivity {
    @Override 
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        findViewById(R.id.ingredientButton).setOnClickListener(
                new IngredientItemOnClickListener(yourIngredient));
    }

    void showIngredientActivity(Ingredient ingredient) {
        // Do your stuff
    }

    public class IngredientItemOnClickListener implements OnClickListener {
        private Ingredient mIngredient;

        public IngredientItemOnClickListener(Ingredient ingredient) {
            mIngredient = ingredient;
        }

        @Override
        public void onClick(View view) {
            showIngredientActivity(mIngredient);
        }
    }
}
查看更多
地球回转人心会变
7楼-- · 2019-06-16 19:02

I faced the same issue and is resolved with
- Android Support Library, revision 24.2.1 (September 2016)
- compileSdkVersion 24
- buildToolsVersion "24.0.3"

查看更多
登录 后发表回答