【问题标题】:Android support library 23.4.0: android.support.v7.widget.TintContextWrapper cannot be cast to ActivityAndroid 支持库 23.4.0:android.support.v7.widget.TintContextWrapper 无法转换为 Activity
【发布时间】:2016-05-12 19:54:32
【问题描述】:

所以我更新到了最新的支持库,但出现了一个我无法修复的崩溃。我的 build.gradle 现在有这些依赖项:

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...
}

我有一个工作监听器,用于捕获点击并启动一个新的 Activity。这在支持库 v. 23.1.0 中运行良好,但在 23.4.0(和 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);
    }
}

这个监听器简单地附加到ImageButton,然后按钮的颜色被着色,如下所示:

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

Tinting.tint() 是我自己的着色函数:

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;
    }
}

以前当我单击按钮时,一切都按预期工作,但现在视图的上下文似乎已更改为 TintContextWrapper,我几乎找不到相关信息。我找到了this issue,但是项目成员建议在StackOverflow上问这里,所以在这里。

我尝试了什么?

由于 Google 问题中的项目成员声明 您将需要从包装的上下文中获取活动。 我尝试转换为 TintContextWrapper 而不是 MyActivity,效果很好,但我无法弄清楚如何从TintContextWrapper 获取MyActivity

所以我的问题是:

  1. 如何从TintContextWrapper 获取MyActivity
  2. 为什么我的ImageButton 突然被TintContextWrapper 包裹起来了。
  3. 真的应该期待这种行为吗?

xml中ImageButton的定义很简单:

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

堆栈跟踪:

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)

【问题讨论】:

  • 日志描述 MyItemOnClickListener 。它在哪里?
  • 对不起,我这边出现错误,我更新了堆栈跟踪。崩溃发生在问题中的IngredientItemOnClickListener

标签: android android-support-library


【解决方案1】:
  1. 两个活动 n TintContextWRapper 都来自ContextWrapperContextWrapper 有一个方法 getBaseContext()。应该很容易创建一个循环方法来检查instanceof WrapContext,获取基本上下文,然后检查instanceof Activity。 (如果您对此方法有问题,请在此处评论,我将挖掘我的一些项目并粘贴到您这里)

  2. 因为 AppCompat 包装了您的上下文,以便能够注入“compat”视图和“compat”着色以及其他“compat”内容。这很正常。

  3. 是的。这就是 AppCompat 的工作方式。

【讨论】:

  • 谢谢,我从来没有意识到 ContextWrapper 类的工作原理以及原因。
  • “检查instanceof WrapContext的循环方法”的代码可以在这里找到:stackoverflow.com/a/32973351
【解决方案2】:

@Krøllebølle

Android 支持库 23.4.0: android.support.v7.widget.TintContextWrapper 无法转换为 活动

你的问题的答案是: 首先你的点击事件代码应该是这样的:

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

然后编写函数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;
}

并且您的崩溃/异常已修复 :)

【讨论】:

    【解决方案3】:

    我的建议是将您的活动的引用传递给 onClickListener 以避免TintContextWrapper 的问题。为您的班级提供对 MyActivity 的引用很简单,并且可以避免可能的转换问题。

    【讨论】:

      【解决方案4】:

      你可以试试

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

      在没有 android.support.v7.widget.TintContextWrapper 的包装器的情况下获取包含此视图的上下文。

      【讨论】:

      • 解决了我的问题的简单解决方案,谢谢!
      【解决方案5】:
      1. 如何从 TintContextWrapper 获取 MyActivity?

      你真的不应该。不能保证 View 的 Context 会是一个 Activity——绝对不是一个特定的 Activity。你在哪里设置你的 OnClickListener?我假设在您设置监听器的地方,您将可以访问 Activity。例如,如果您从 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);
              }
          }
      }
      

      【讨论】:

      • 是的,侦听器始终设置在扩展 AppCompatActivity 的给定 Activity 中,更具体地是在该 Activity 中的 RecyclerView 的 Adapter 中设置。 RecyclerView 中的多个项目将具有此 ImageButton,但成分项目将随按钮的每个实例而变化。在这种情况下,上下文可能是别的东西吗?正如@Budiud 建议的那样,直接投射到 MyActivity 似乎可以正常工作。
      • 我会说那是“不正确的事情”。它可能有效,但 API 中没有任何内容可以保证它不会在另一个库版本中再次中断。对于您的情况,我建议向您的 RecyclerView 添加一个接口(类似于 OnIngredientClickedListener)并提供一个设置器,您的 Activity 将提供一个实例。然后,在 RecyclerView 中有一个私有的 OnClickListener,它将把点击转发到 OnIngredientClickedListener。这消除了您对 MainActivity 的硬依赖。
      • 有趣的方法。我将尝试在我的代码中以及在其他几个地方做类似的事情。然而值得注意的是,this issue 中的项目成员建议从包装的上下文中获取 Activity。当然,那个人可能是“错误的”。
      【解决方案6】:

      更新 Android 支持库后,它对我有用。

      【讨论】:

        【解决方案7】:

        我遇到了同样的问题,并通过
        解决 - Android 支持库,修订版 24.2.1(2016 年 9 月)
        - compileSdkVersion 24
        - buildToolsVersion "24.0.3"

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-07-23
          • 2016-06-29
          • 2017-02-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-09-03
          相关资源
          最近更新 更多