web-dev-qa-db-fra.com

Le thème/style n'est pas appliqué lorsque inflater est utilisé avec ApplicationContext

J'ai un thème qui spécifie textColor pour TextView en rouge.

J'utilise LayoutInflater pour instancier TextView. Le problème est que les styles ne sont pas appliqués à TextView lors de la création d'un inflater à l'aide de ApplicationContext - la couleur n'est pas rouge. Tout fonctionne bien lorsque LayoutInflater est créé à l'aide de activity.

Pourquoi cela se produit et comment peut-on résoudre ce problème?

/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"
    >

Code:

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_width="fill_parent"
    Android:layout_height="wrap_content"
    Android:orientation="vertical">

    <Button 
        Android:text="Add with AppContext" 
        Android:id="@+id/add_with_appContext" 
        Android:layout_width="wrap_content" 
        Android:layout_height="wrap_content" 
        />

    <Button 
        Android:text="Add with Activity" 
        Android:id="@+id/add_with_activity" 
        Android:layout_width="wrap_content" 
        Android:layout_height="wrap_content" 
        />

    <LinearLayout
        Android:id="@+id/container"
        Android:layout_width="fill_parent"
        Android:layout_height="wrap_content"
        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_width="fill_parent" 
    Android:layout_height="wrap_content"
    >

    <TextView
        Android:id="@+id/text"
        Android:text="Some text..."
        Android:layout_width="fill_parent" 
        Android:layout_height="wrap_content"
    />

</LinearLayout>
43
alex2k8

Solution n ° 1

La méthode inflate accepte l'argument optionnel 'ViewGroup root':

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

Si nous avons une valeur à passer en tant que paramètre 'root', nous pouvons l'utiliser pour obtenir un 'contexte d'activité' d'où nous pouvons obtenir le correct LayoutInflater:

ViewGroup root > activity context > LayoutInflater

Donc, mon code pourrait être:

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

Solution n ° 2

Je viens d'essayer de définir le thème de contexte d'application par programme, et il fonctionne :

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

Je pense qu'il était logique de s'attendre à ce balisage:

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

pour le définir automatiquement, mais ce n'est pas le cas.

50
alex2k8

N'utilisez jamais un contexte d'application pour gonfler des vues, car le style ne fonctionne pas avec ce contexte. Utilisez toujours le contexte d'une activité lorsque vous jouez avec des vues. La seule exception concerne les cas où vous devez créer RemoteViews à partir d'un service.

Plus d’informations sur les différents types de contextes et leurs capacités sont disponibles dans cet excellent article .

33
BladeCoder

Je rencontre généralement ce problème en gonflant une vue personnalisée. Voici ce que je fais personnellement pour garder le même thème de l'activité dans le 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       
}   
}
0
abedfar