与 ApplicationContext 一起使用充气机时,不应用主题/样式

Posted

技术标签:

【中文标题】与 ApplicationContext 一起使用充气机时,不应用主题/样式【英文标题】:Theme/Style is not applied when inflater used with ApplicationContext 【发布时间】:2011-01-08 06:18:08 【问题描述】:

我的主题将 TextView 的 textColor 指定为红色。

我正在使用 LayoutInflater 来实例化 TextView。问题是当使用 ApplicationContext 创建充气器时,样式不会应用于 TextView - 颜色不是红色。使用活动创建 LayoutInflater 时一切正常。

为什么会发生这种情况,如何解决?

/res/values/styles.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="MyTheme">
        <item name="android:textViewStyle">@style/MyTextView</item>
    </style>

    <style name="MyTextView" parent="@android:style/Widget.TextView">
        <item name="android:textColor">#f00</item>
    </style>
</resources>

AndroidManifest.xml:

<application 
    android:icon="@drawable/icon" 
    android:label="@string/app_name"
    android:theme="@style/MyTheme"
    >

代码:

public class A extends Activity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_a);

        final LayoutInflater goodInflater = getInflater((Activity)this); 
        final LayoutInflater badInflater = getInflater(getApplicationContext());
        final LinearLayout container = (LinearLayout)findViewById(R.id.container);

        findViewById(R.id.add_with_appContext).setOnClickListener(new OnClickListener() 
            @Override
            public void onClick(View v) 
                add(container, badInflater); // Creates gray TextView
                        
        );

        findViewById(R.id.add_with_activity).setOnClickListener(new OnClickListener() 
            @Override
            public void onClick(View v) 
                add(container, goodInflater); // Creates red TextView
                        
        );
    

    private LayoutInflater getInflater(Context context) 
        return (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    

    private void add(LinearLayout container, LayoutInflater inflater) 
        inflater.inflate(R.layout.my_template, container, true);
    

/res/layout/test_a.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:orientation="vertical">

    <Button 
        android:text="Add with AppContext" 
        android:id="@+id/add_with_appContext" 
        android:layout_ 
        android:layout_ 
        />

    <Button 
        android:text="Add with Activity" 
        android:id="@+id/add_with_activity" 
        android:layout_ 
        android:layout_ 
        />

    <LinearLayout
        android:id="@+id/container"
        android:layout_
        android:layout_
        android:orientation="vertical"  
        />

</LinearLayout>

/res/layout/my_template.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_ 
    android:layout_
    >

    <TextView
        android:id="@+id/text"
        android:text="Some text..."
        android:layout_ 
        android:layout_
    />

</LinearLayout>

【问题讨论】:

【参考方案1】:

解决方案 #1

inflate 方法接受可选的“ViewGroup root”参数:

public View inflate (int resource, ViewGroup root, boolean attachToRoot)

如果我们有值作为“根”参数传递,那么我们可以使用它来获取“活动上下文”,从中我们可以获得正确的 LayoutInflater:

ViewGroup root > activity context > LayoutInflater

所以我的代码可能是:

private void add(LinearLayout container) 
    LayoutInflater inflater = getInflater(container.getContext());
    inflater.inflate(R.layout.my_template, container, true);

解决方案 #2

刚刚尝试以编程方式设置应用程序上下文主题,它有效

getApplicationContext().setTheme(R.style.MyTheme);

我认为期待这个标记是合乎逻辑的:

<application 
    android:icon="@drawable/icon" 
    android:label="@string/app_name"
    android:theme="@style/MyTheme"
    >

自动设置,但不会。

【讨论】:

这是大错特错。它不能像你认为的那样工作是有原因的:它不应该那样工作。 标签上的主题属性只是应用于所有活动的默认值。许多其他的事情并不能真正与应用程序上下文一起使用,例如,您将泄漏响应配置更改而更改的资源等。 解决方案 #2 就像一个魅力。我的应用程序现在看起来很漂亮。谢谢! 永远不要使用应用程序上下文来膨胀视图 (***.com/a/25116293/570168) 在自定义对话框中表现出色......你是我这一周的英雄【参考方案2】:

永远不要使用应用程序上下文来膨胀视图,因为样式不适用于此上下文。玩视图时始终使用 Activity 的上下文。唯一的例外是当您需要从服务创建 RemoteViews 时。

有关不同类型的上下文及其功能的更多信息,请访问in this excellent article

【讨论】:

这不是真的,还建议使用应用程序上下文创建视图以避免我现在正在处理的内存泄漏。 取决于如何使用您的视图。如果您的视图不是 Activity 的一部分,那么您当然不应该使用 Activity 的上下文。 哇。这成功了。我遇到了 Spinner 项目不符合主题的问题,结果证明我犯了这个错误,而且在 ArrayAdapter 中我使用getContext() 来膨胀子视图而不是通过调用parent.getContext() 来使用父视图的上下文其中parent 的类型为ViewGroup @David NO NO,亲爱的上帝,NO。你在做什么?!您可能正在避免泄漏资源,但您仍然在泄漏这些视图,或者充其量正在创建一堆您永远不会使用的视图,如果您一直持有相同的视图引用。没有哪个世界可以使用应用程序上下文来避免由于您不理解的糟糕代码而导致的内存泄漏是一件好事,更不用说推荐了。【参考方案3】:

您可能使用的上下文不是具有主题的上下文。

要解决它,请使用以下内容:

val inflater= LayoutInflater.from(context).cloneInContext(ContextThemeWrapper(context, R.style.some_activity_theme))

你可以阅读更多关于这个here

【讨论】:

用这个 E / ThemeUtils 解决了我的问题:查看类 com.airbnb.lottie.LottieAnimationView 是一个 AppCompat 小部件,只能与 Theme.AppCompat 主题(或后代)一起使用 还使用 RemoteViews,从服务中膨胀。谢谢。【参考方案4】:

我通常在扩展自定义视图时遇到这个问题。这是我个人为在 CustomView 中保持相同的活动主题所做的工作

public class CustomView extends ViewGroup

public CustomView (Context context) 
    super(context);
    init(context);


public CustomView (Context context, AttributeSet attrs) 
    super(context, attrs);
    init(context);


public CustomView (Context context, AttributeSet attrs, int defStyle) 
    super(context, attrs, defStyle);
    init(context);


@TargetApi(21)
public CustomView (Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) 
    super(context, attrs, defStyleAttr, defStyleRes);
    init(context);


private void init(Context context) 
    LayoutInflater  mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View v = mInflater.inflate(R.layout.review_list_item, this, true);
    //rest of view initialization       
   

【讨论】:

以上是关于与 ApplicationContext 一起使用充气机时,不应用主题/样式的主要内容,如果未能解决你的问题,请参考以下文章

Spring ApplicationContext 容器

Beanfactory与ApplicationContext

单例模式&BeanFactory与 ApplicationContext之间的区别

转:Spring系列之beanFactory与ApplicationContext

Spring的ApplicationContext学习

spring源码分析系列5:ApplicationContext的初始化与Bean生命周期