web-dev-qa-db-fra.com

Pourquoi l'attribut `Android: foreground` ne fonctionne-t-il pas?

Jetez un coup d'oeil à cette petite application Android:

MainActivity.Java:

package io.github.gsaga.toucheventtest;

import Android.support.v7.app.AppCompatActivity;
import Android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

activity_main:

<ImageView Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:foreground="@drawable/ic_launcher_background"
    xmlns:Android="http://schemas.Android.com/apk/res/Android" />

L'image pointée par Android:foreground n'est pas affichée, mais elle apparaît si je change foreground en src ou background dans activity_main.xml. Ce code semble suivre les instructions décrites ici:

https://developer.Android.com/reference/Android/view/View.html#attr_Android:foreground

Pourquoi la balise Android:foreground ne fonctionne-t-elle pas dans le code ci-dessus?

REMARQUE:

minSdkVersion est 19, je lance cette application sur Android 5.1 (API level 22)

4
saga

Réponse courte

Ceci est dû à un bug existant dans Android depuis le niveau 23 de l'API.


Plus de détails sur le comportement

Voici la liste de toutes les méthodes et des attributs XML correspondants liés à la définition de vues dessinables au premier plan avec les niveaux d'API introduits via FrameLayout. Cependant, ceux-ci sont plus tard déplacés dans View dans l'API de niveau 23.

╔════════════════════════════╦═════════════════════════════════════════════════╦═════════════╗
║           Methods          ║                  XML attributes                 ║             ║
║                            ║                                                 ║   Added in  ║
║                            ║                                                 ║ (API level) ║
╠════════════════════════════╬═════════════════════════════════════════════════╬═════════════╣
║ Android:foreground         ║ setForeground(Drawable)                         ║ 1           ║
╠════════════════════════════╬═════════════════════════════════════════════════╬═════════════╣
║ Android:foregroundGravity  ║ setForegroundGravity(int gravity)               ║ 1           ║
╠════════════════════════════╬═════════════════════════════════════════════════╬═════════════╣
║ Android:foregroundTint     ║ setForegroundTintMode(PorterDuff.Mode tintMode) ║ 21          ║
╠════════════════════════════╬═════════════════════════════════════════════════╬═════════════╣
║ Android:foregroundTintMode ║ setForegroundTintMode(PorterDuff.Mode tintMode) ║ 21          ║
╚════════════════════════════╩═════════════════════════════════════════════════╩═════════════╝
  • La documentation Android indique que setForeground(Drawable) est ajouté dans l’API 1 et que setForegroundTintList (ColorStateList tint) et setForegroundTintMode (PorterDuff.Mode tintMode) sont ajoutés dans l’API de niveau 21 en View. Il est faux. Il y en avait dans FrameLayout jusqu'à l'API 23.

  • Au niveau de l'API <23, vous recevrez un avertissement même s'il n'est pas requis. Vous pouvez simplement le supprimer. Voir this .


Examinons maintenant comment ces propriétés fonctionnent sur différentes versions.

╔═══════════╦══════════════════╦══════════════════╗
║ API level ║      By code     ║     Using XML    ║
╠═══════════╬══════════════════╬══════════════════╣
║ <23       ║ FrameLayout only ║ FrameLayout only ║
╠═══════════╬══════════════════╬══════════════════╣
║ >=23      ║ FrameLayout only ║ All views        ║
╚═══════════╩══════════════════╩══════════════════╝


La cause du bug

Lorsque ces propriétés ont été déplacées à View dans l'API de niveau 23, elles ont apporté des modifications étranges qui peuvent être appelées un bogue. Lors du chargement des propriétés à partir de XML, il vérifie si la View est une FrameLayout qui n'est pas présente dans les méthodes que nous pouvons utiliser dans le même but.

Constructeur de vue, niveau 23 de l'API:

case R.styleable.View_foreground:
    if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
        setForeground(a.getDrawable(attr));
    }
    break;
6
Anees

Pour utiliser Android:foreground sur Android 5.1 c'est-à-dire API level 22, vous n'utilisez pas Android:foreground

Comme son nom indique clairement que vous pouvez définir le dessin sur le dessus/le premier plan de tout contenu tel que la superposition, c’est-à-dire que vous pouvez placer une vue dans FrameLayout dans laquelle vous pouvez utiliser Android:foreground. Dans cette FrameLayout, ajoutez votre ImageView.

Documentation :

Définit le dessinable à dessiner sur le contenu. Ceci peut être utilisé comme une superposition. Le premier plan dessinable participe au rembourrage de le contenu si la gravité est définie pour se remplir.

Voici un exemple d'utilisation:

<FrameLayout
    Android:id="@+id/share"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:foreground="@drawable/ic_launcher_background>

    // your ImageView here
    <ImageView
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"/>

</FrameLayout>

Remarque: Pour API level > 23, cela fonctionnera sans FrameLayout.

J'espère que cela t'aidera.

2
VicJordan

Il semble qu’à un moment donné (API <23), Android:foreground ne fonctionnerait qu’avec FrameLayout, comme le suggère VicJordan. Cependant, pour l’API 23+, il apparaît que Android:foreground fonctionnera pour tout type de vue. Voir this selection de la source View.Java:

case R.styleable.View_foreground:
    if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
        setForeground(a.getDrawable(attr));
}

Voici un exemple de Android:foreground travaillant sur l'API 28 avec la présentation suivante:

<androidx.constraintlayout.widget.ConstraintLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:foreground="@drawable/ic_launcher_foreground"
        Android:src="@Android:drawable/ic_delete"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

 enter image description here

Pourtant, sur API 22, nous voyons ceci:

 enter image description here

Aucune image de premier plan. Donc, Android:foreground ne fonctionne que lorsque le niveau de l'API est de 23+. Je conviens que cela n’est pas vraiment documenté, mais c’est comme ça.

Update: API 23 semble avoir un problème avec Android:foreground, alors supposons que Android:foreground fonctionne sur API 24+ pour les vues générales.

Deuxième mise à jour: Je suis tombé sur quelques articles traitant de la même question concernant setForeground()ici et ici . Dans la réponse acceptée à la deuxième question, CommonsWare identifie cela comme un "bogue de documentation".

1
Cheticamp