Android中通过view.getContext获取Activity时得到TintContextWrapper的问题及解决public class MainActivity extends AppC

本文探讨了在Android开发中使用AppCompatActivity时遇到的问题,包括Button类型自动转换为AppCompatButton及通过View获取Activity实例的难点,并提供了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       这篇博客主要介绍Android在使用AppCompatActivity时遇到的一些出乎意料的问题,下面将问题及解决方案记录如下,便于查询。

问题

 

       在使用Androidstudio开发中,默认创建的activity都继承自AppCompatActivity,如下图所示,然后在布局中使用也使用很简单的一个布局如下图所示。

public class MainActivity extends AppCompatActivity implementsView.OnClickListener{}

       然后我想在别的工具类中(该工具类中有该view的引用)通过view.getContext 获得view所在activity的实例对象。于是乎有了如下的简单代码。

(1)MainActivity 中

Button button = (Button) findViewById(R.id.btn);
button.setOnClickListener(MainActivity.this);

if(button instanceof AppCompatButton){
  Log.d("HHHHH","====button is appCompatButton====");
}

(2)工具类中

public static void getActivityInfo(View view){
    Context context=view.getContext();
    Log.d("HHHHH","getActivityInfo-getContext="+context);
    if(context instanceof Activity){
        Activity activity= (Activity) context;
        Log.d("HHHHH","getActivityInfo-name="+activity.getClass().getSimpleName());
    }
}

结果日志是这样的。。。

what??? 

(1)我的Button怎么成了 AppCompatButton ???  (2)我的view.getContext得到的是TintContextWrapper,而不是Activity??这又是什么鬼.......

于是乎。。。查源码发现,我们的activity继承自AppCompatActivity时,也就是引用的是v7包时,我们的button就自动转为AppCompatButton 了(具体的可以参见AppCompatButton 源码注释)。同时在AppCompatButton 中我们的Context就变成了图中第二个框中的TintContextWrapper了。。。。

解决方案

       原来在继承自AppCompatActivity时,Android底层会将我们应用的控件转为v7包中对应的控件,Context就被替换成了TintContextWrapper(他也继承自ContextWrapper)。所以我们可以通过如下代码获取想要的activity。

public static Activity getActivityFromView(View view) {
  if (null != view) {
    Context context = view.getContext();
    while (context instanceof ContextWrapper) {
      if (context instanceof Activity) {
        return (Activity) context;
      }
      context = ((ContextWrapper) context).getBaseContext();
    }
  }
  return null;
}

 

其他和AppCompatActivity相关的问题可以参考如下文章。

AppCompatActivity

Android 从 View 中获取 Activity 时遇到 TintContextWrapper cannot be cast to 的问题

<think>在ViewPager2中,每个页面实际上是一个RecyclerView的item,因此我们可以通过自定义ViewHolder来设置view的tag。具体步骤:1.创建自定义的FragmentStateAdapter,并重写onCreateViewHolder和onBindViewHolder方法。2.在onCreateViewHolder中,创建包含FragmentContainerView的布局,并给这个容器设置一个tag(可以是位置或其他标识)。3.在动画代码中,通过ViewPager2的findViewHolderForAdapterPosition方法获取ViewHolder,进而获取view并读取tag。但是,注意:ViewPager2内部使用RecyclerView,我们无法直接访问每个页面的根视图。因此,我们需要在创建Fragment,在Fragment的onViewCreated中给根视图设置tag。另一种方法:在Fragment中,当创建视图,给根视图设置tag。然后在Activity中,通过Fragment的getView()获取根视图,再通过getTag()获取标识。然而,根据问题描述,我们更希望在做动画通过view.getTag()获取标识。因此,我们可以在Fragment的onViewCreated中设置tag。下面提供两种方案:方案一:在Fragment中设置根视图的tag步骤:-在Fragment的onViewCreated中,调用view.setTag(tag)设置标识。-在Activity中,获取到Fragment后,通过fragment.getView().getTag()获取标识。方案二:在FragmentStateAdapter中设置容器视图的tag由于ViewPager2内部使用FragmentContainerView来容纳Fragment,我们可以给这个容器设置tag。但是,方案二需要自定义ViewHolder,因为默认的FragmentStateAdapter使用FragmentViewHolder,它内部只有一个FragmentContainerView。下面分别实现两种方案:方案一:在Fragment中设置tag(推荐,因为直接操作Fragment的根视图)代码示例:Fragment中:```javapublicclassMyFragmentextendsFragment{privatestaticfinalStringARG_POSITION="position";publicstaticMyFragmentnewInstance(intposition){MyFragmentfragment=newMyFragment();Bundleargs=newBundle();args.putInt(ARG_POSITION,position);fragment.setArguments(args);returnfragment;}@OverridepublicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,BundlesavedInstanceState){returninflater.inflate(R.layout.fragment_my,container,false);}@OverridepublicvoidonViewCreated(@NonNullViewview,@NullableBundlesavedInstanceState){super.onViewCreated(view,savedInstanceState);intposition=getArguments().getInt(ARG_POSITION,-1);//设置tag,这里我们将位置作为tag,也可以使用其他标识view.setTag(position);}}```在Activity获取tag:```javaprivatevoiddoAnimationForFragment(intposition){Fragmentfragment=getFragmentAt(position);if(fragment!=null){ViewfragmentView=fragment.getView();if(fragmentView!=null){inttag=(int)fragmentView.getTag();//获取在Fragment中设置的tag//使用tag标识进行动画处理}}}```方案二:在FragmentStateAdapter中设置容器tag自定义Adapter:```javapublicclassMyFragmentStateAdapterextendsFragmentStateAdapter{publicMyFragmentStateAdapter(@NonNullFragmentActivityfragmentActivity){super(fragmentActivity);}@NonNull@OverridepublicFragmentcreateFragment(intposition){returnMyFragment.newInstance(position);}@OverridepublicintgetItemCount(){return3;}@NonNull@OverridepublicFragmentViewHolderonCreateViewHolder(@NonNullViewGroupparent,intviewType){//默认实现是创建一个FragmentContainerViewFrameLayoutcontainer=newFrameLayout(parent.getContext());container.setLayoutParams(newViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));container.setId(ViewCompat.generateViewId());//设置tag,这里我们使用位置作为tag,但注意此位置还未绑定,需要在绑定设置//由于位置是动态的,我们无法在创建设置,所以改为在onBindViewHolder中设置returnnewFragmentViewHolder(container);}@OverridepublicvoidonBindViewHolder(@NonNullFragmentViewHolderholder,intposition){super.onBindViewHolder(holder,position);//给容器设置tagholder.itemView.setTag(position);}}```在Activity中,我们可以通过ViewPager2的findViewHolderForAdapterPosition方法获取ViewHolder,然后获取itemView的tag:```java//在需要的地方,比如页面切换监听中ViewPager2.OnPageChangeCallbackcallback=newViewPager2.OnPageChangeCallback(){@OverridepublicvoidonPageSelected(intposition){//获取当前页面的ViewHolderRecyclerView.ViewHolderviewHolder=viewPager.findViewHolderForAdapterPosition(position);if(viewHolder!=null){Viewview=viewHolder.itemView;//这就是容器Viewinttag=(int)view.getTag();//这里获取到在adapter中设置的tag//使用tag}}};viewPager.registerOnPageChangeCallback(callback);```注意:方案二中,由于RecyclerView的回收机制,当页面被回收再重新创建,onBindViewHolder会被调用,因此tag会被重新设置。两种方案对比:方案一:更直接,在Fragment内部设置,与ViewPager2的实现无关,但需要先获取Fragment。方案二:在容器视图设置tag,不需要获取Fragment,但需要自定义Adapter,并且需要确保在onBindViewHolder中设置tag。根据需求,如果动画是在Activity中统一控制,并且需要快速通过位置获取tag,方案二可能更高效,因为不需要先获取Fragment。但方案一更符合常规的Fragment使用方式。因此,我推荐使用方案一,因为:1.它不依赖于ViewPager2的内部实现(RecyclerView)。2.代码更清晰,将tag设置放在Fragment内部,与视图绑定。但是,如果需要在Fragment外部(Activity)中直接通过位置获取视图并做动画,方案二更方便,因为可以直接通过RecyclerViewViewHolder获取视图。根据问题描述,在做动画通过view.getTag()获取标识,两种方案都可以。下面我们以方案一为例,结合之前的动画接口,给出一个完整示例。完整代码示例(方案一):Fragment部分:```javapublicclassMyFragmentextendsFragmentimplementsFragmentAnimator{privateViewrootView;privateFragmentAnimationHelperanimationHelper;publicstaticMyFragmentnewInstance(intposition){MyFragmentfragment=newMyFragment();Bundleargs=newBundle();args.putInt("position",position);fragment.setArguments(args);returnfragment;}@Nullable@OverridepublicViewonCreateView(@NonNullLayoutInflaterinflater,@NullableViewGroupcontainer,@NullableBundlesavedInstanceState){rootView=inflater.inflate(R.layout.fragment_my,container,false);animationHelper=newFragmentAnimationHelper(rootView,requireContext());returnrootView;}@OverridepublicvoidonViewCreated(@NonNullViewview,@NullableBundlesavedInstanceState){super.onViewCreated(view,savedInstanceState);//设置tag,使用位置intposition=getArguments().getInt("position",-1);view.setTag(position);}//...实现FragmentAnimator接口的方法}```Activity中触发动画获取tag:```javaviewPager.registerOnPageChangeCallback(newViewPager2.OnPageChangeCallback(){@OverridepublicvoidonPageSelected(intposition){Fragmentfragment=getFragmentAt(position);if(fragment!=null&&fragmentinstanceofFragmentAnimator){FragmentAnimatoranimator=(FragmentAnimator)fragment;ViewfragmentView=fragment.getView();if(fragmentView!=null){inttag=(int)fragmentView.getTag();//根据tag做特定处理,比如不同的页面使用不同的动画参数//例如:根据tag设置不同的动画参数if(tag==0){animator.setFadeInParams(400,newAccelerateDecelerateInterpolator(),200f);}else{animator.setFadeInParams(366,newPathInterpolator(0.11f,0.06f,0.07f,0.99f),300f);}animator.fadeIn();}}}});```这样,我们就可以根据tag(即位置)来定制不同的动画效果。注意:在onPageSelected中,我们获取到的fragment可能为null(如果还未创建),所以需要处理这种情况。之前的方案中已经提供了延迟处理的方法。总结:在Fragment的onViewCreated中给根视图设置tag,然后在需要的地方通过fragment.getView().getTag()获取,是一种简单可靠的方式。§§相关问题§§:1.ViewPager2中,如何给Fragment中的特定子View设置tag?2.当Fragment被回收后重建,如何保持tag的值?3.除了使用tag,还有哪些方法可以在Activity中识别不同的Fragment?4.ViewPager2中,如何实现非当前页面的预加载并设置tag?</think># 为 ViewPager2 中的视图设置标签以标识 Fragment 在 ViewPager2 中为视图设置标签是一个很好的实践,可以方便在动画期间快速识别和操作特定 Fragment 的视图。以下是几种实现方法: ## 方法一:在 Fragment 中直接设置视图标签 这是最简单直接的方法,在 Fragment 的 `onViewCreated` 中设置根视图的标签: ```java public class MyFragment extends Fragment { private View rootView; public static MyFragment newInstance(int position) { MyFragment fragment = new MyFragment(); Bundle args = new Bundle(); args.putInt("position", position); fragment.setArguments(args); return fragment; } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { rootView = inflater.inflate(R.layout.fragment_my, container, false); return rootView; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 设置视图标签 int position = getArguments().getInt("position", -1); rootView.setTag(R.id.fragment_position_tag, position); // 或者使用复合标签 Map<String, Object> tagMap = new HashMap<>(); tagMap.put("position", position); tagMap.put("fragment_id", getId()); rootView.setTag(R.id.fragment_custom_tag, tagMap); } // 获取标签的辅助方法 public int getFragmentPosition() { if (rootView != null) { Object tag = rootView.getTag(R.id.fragment_position_tag); if (tag instanceof Integer) { return (Integer) tag; } } return -1; } } ``` 在 `res/values/ids.xml` 中定义标签 ID: ```xml <?xml version="1.0" encoding="utf-8"?> <resources> <item name="fragment_position_tag" type="id" /> <item name="fragment_custom_tag" type="id" /> </resources> ``` ## 方法二:通过自定义 FragmentStateAdapter 设置标签 这种方法在适配器层面统一管理标签设置: ```java public class TaggedFragmentStateAdapter extends FragmentStateAdapter { private final SparseArray<Fragment> fragmentCache = new SparseArray<>(); public TaggedFragmentStateAdapter(@NonNull FragmentActivity fragmentActivity) { super(fragmentActivity); } @NonNull @Override public Fragment createFragment(int position) { Fragment fragment = MyFragment.newInstance(position); fragmentCache.put(position, fragment); return fragment; } @Override public void onBindViewHolder(@NonNull FragmentViewHolder holder, int position) { super.onBindViewHolder(holder, position); // 设置容器视图的标签 holder.itemView.setTag(R.id.fragment_position_tag, position); // 获取 Fragment 并设置视图标签 Fragment fragment = fragmentCache.get(position); if (fragment != null && fragment.getView() != null) { fragment.getView().setTag(R.id.fragment_position_tag, position); } } @Override public void onViewRecycled(@NonNull FragmentViewHolder holder) { super.onViewRecycled(holder); int position = holder.getAbsoluteAdapterPosition(); fragmentCache.remove(position); } public Fragment getFragment(int position) { return fragmentCache.get(position); } @Override public int getItemCount() { return 3; } } ``` ## 方法三:使用自定义 FragmentContainerView 创建自定义容器视图,在构造自动设置标签: ```java public class TaggedFragmentContainerView extends FragmentContainerView { private int position = -1; public TaggedFragmentContainerView(@NonNull Context context) { super(context); } public TaggedFragmentContainerView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public TaggedFragmentContainerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void setPosition(int position) { this.position = position; setTag(R.id.fragment_position_tag, position); } public int getPosition() { return position; } } ``` 在适配器中使用自定义容器: ```java @NonNull @Override public FragmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // 创建自定义容器视图 TaggedFragmentContainerView container = new TaggedFragmentContainerView(parent.getContext()); container.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); container.setId(ViewCompat.generateViewId()); return new FragmentViewHolder(container); } @Override public void onBindViewHolder(@NonNull FragmentViewHolder holder, int position) { super.onBindViewHolder(holder, position); // 设置位置标签 if (holder.itemView instanceof TaggedFragmentContainerView) { ((TaggedFragmentContainerView) holder.itemView).setPosition(position); } } ``` ## 在动画中使用标签 在 Activity 的页面切换监听中,通过标签获取视图并执行动画: ```java viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { // 方法1:通过适配器获取视图 RecyclerView.ViewHolder viewHolder = viewPager.findViewHolderForAdapterPosition(position); if (viewHolder != null) { View fragmentView = viewHolder.itemView; Object tag = fragmentView.getTag(R.id.fragment_position_tag); if (tag instanceof Integer) { int fragmentPosition = (Integer) tag; animateFragmentView(fragmentView, fragmentPosition); } } // 方法2:通过Fragment获取视图 Fragment fragment = getFragmentAt(position); if (fragment != null && fragment.getView() != null) { View fragmentView = fragment.getView(); Object tag = fragmentView.getTag(R.id.fragment_position_tag); if (tag instanceof Integer) { int fragmentPosition = (Integer) tag; animateFragmentView(fragmentView, fragmentPosition); } } } }); private void animateFragmentView(View view, int position) { // 使用标签信息定制动画 if (position == 0) { // 首页特殊动画 view.animate() .translationX(0) .alpha(1) .setDuration(300) .start(); } else { // 其他页面通用动画 view.animate() .translationX(100) .alpha(0.8f) .setDuration(200) .start(); } } ``` ## 处理复杂标签场景 ### 1. 使用复合标签 ```java // 创建复合标签 public class FragmentTag { private final int position; private final String fragmentType; private final long creationTime; public FragmentTag(int position, String fragmentType) { this.position = position; this.fragmentType = fragmentType; this.creationTime = System.currentTimeMillis(); } // Getters and other methods } // 在Fragment中设置 @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { FragmentTag tag = new FragmentTag(position, "my_fragment"); view.setTag(R.id.fragment_custom_tag, tag); } // 在动画中使用 Object tag = fragmentView.getTag(R.id.fragment_custom_tag); if (tag instanceof FragmentTag) { FragmentTag fragmentTag = (FragmentTag) tag; if (fragmentTag.getFragmentType().equals("my_fragment")) { // 执行特定类型Fragment的动画 } } ``` ### 2. 标签生命周期管理 ```java public class BaseFragment extends Fragment { private static final String KEY_TAG_DATA = "tag_data"; private FragmentTag fragmentTag; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 恢复标签状态 if (savedInstanceState != null) { fragmentTag = savedInstanceState.getParcelable(KEY_TAG_DATA); } } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { // 初始化标签 if (fragmentTag == null) { int position = getArguments().getInt("position", -1); fragmentTag = new FragmentTag(position, getClass().getSimpleName()); } // 设置标签 view.setTag(R.id.fragment_custom_tag, fragmentTag); } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); // 保存标签状态 outState.putParcelable(KEY_TAG_DATA, fragmentTag); } } ``` ### 3. 通过标签查找 Fragment ```java public Fragment findFragmentByTag(int position) { RecyclerView recyclerView = (RecyclerView) viewPager.getChildAt(0); for (int i = 0; i < recyclerView.getChildCount(); i++) { View view = recyclerView.getChildAt(i); Object tag = view.getTag(R.id.fragment_position_tag); if (tag instanceof Integer && (Integer) tag == position) { // 找到匹配的视图 Fragment fragment = getFragmentAt(position); if (fragment != null) { return fragment; } } } return null; } ``` ## 最佳实践建议 1. **统一标签管理**: ```java public class FragmentTagManager { private static final int[] TAG_IDS = { R.id.fragment_position_tag, R.id.fragment_custom_tag }; public static void clearTags(View view) { for (int id : TAG_IDS) { view.setTag(id, null); } } public static void setPositionTag(View view, int position) { view.setTag(R.id.fragment_position_tag, position); } public static int getPositionTag(View view) { Object tag = view.getTag(R.id.fragment_position_tag); return (tag instanceof Integer) ? (Integer) tag : -1; } } ``` 2. **在动画中使用标签的通用方法**: ```java public void applyAnimationBasedOnTag(View view) { int position = FragmentTagManager.getPositionTag(view); if (position == -1) return; switch (position % 3) { case 0: applySlideAnimation(view); break; case 1: applyFadeAnimation(view); break; case 2: applyScaleAnimation(view); break; } } ``` 3. **处理视图回收**: ```java @Override public void onViewRecycled(@NonNull FragmentViewHolder holder) { super.onViewRecycled(holder); // 清除标签引用 FragmentTagManager.clearTags(holder.itemView); // 或者重置为初始值 FragmentTagManager.setPositionTag(holder.itemView, -1); } ``` ## 总结 通过为 ViewPager2 中的视图设置标签,您可以: 1. 在动画期间快速识别特定 Fragment 的视图 2. 根据位置、类型或其他标识符定制动画效果 3. 简化 Fragment 与 Activity 之间的通信 4. 提高代码的可读性和可维护性 建议使用第一种方法(在 Fragment 中直接设置视图标签)作为主要实现,因为它: - 不依赖于 ViewPager2 内部实现 - 与 Fragment 生命周期自然集成 - 便于在 Fragment 内部访问标签信息 - 易于扩展和自定义
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值