web-dev-qa-db-fra.com

Android Nougat: TextureView ne prend pas en charge l'affichage d'un arrière-plan dessinable

J'utilise une TextureView dans mon Android, et cela fonctionnait bien. Tout récemment, j'ai testé mon code sur un appareil Android avec Android API 25 (7.1.2). Le même code ne fonctionne plus et génère l'erreur, Java.lang.UnsupportedOperationException: TextureView doesn't support displaying a background drawable.

Je sais que void setBackgroundDrawable (Drawable background) avait été obsolète depuis longtemps , et maintenant il doit avoir été supprimé. Mais je ne le mets même pas seul.

J'utilise les derniers buildTools et SDK. Donc, je me demande pourquoi l'implémentation interne de textureView n'a pas été mise à jour.

Voici la trace de pile appropriée:

Java.lang.UnsupportedOperationException: TextureView doesn't support displaying a background drawable
at Android.view.TextureView.setBackgroundDrawable(TextureView.Java:315)
at Android.view.View.setBackground(View.Java:18124)
at Android.view.View.<init>(View.Java:4573)
at Android.view.View.<init>(View.Java:4082)
at Android.view.TextureView.<init>(TextureView.Java:159)
at com.abdulwasaetariq.xyz.ui.customView.AutoFitTextureView.<init>(AutoFitTextureView.Java:24)
at com.abdulwasaetariq.xyz.ui.customView.AutoFitTextureView.<init>(AutoFitTextureView.Java:20)
at Sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[...]
at Java.lang.Thread.run(Thread.Java:745)

Voici comment j'utilise mon TextureView personnalisé (pas encore personnalisé):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    tools:context="com.abdulwasaetariq.xyz.ui.activity.MainActivity">

    <com.abdulwasaetariq.xyz.ui.customView.AutoFitTextureView
        Android:id="@+id/texture"
        Android:layout_width="1080px"
        Android:layout_height="1080px"
        Android:layout_alignParentStart="true"
        Android:layout_alignParentTop="true" />

</RelativeLayout>

Voici mon AutoFitTextureView.Java pertinent :enter code here

public class AutoFitTextureView extends TextureView {

private int mRatioWidth = 0;
private int mRatioHeight = 0;

public AutoFitTextureView(Context context) {
    this(context, null);
}

public AutoFitTextureView(Context context, AttributeSet attrs) {
    this(context, attrs, 0); //(LINE#20)
}

public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle); //(LINE#24)
}

public void setAspectRatio(int width, int height) {
    if (width < 0 || height < 0) {
        throw new IllegalArgumentException("Size cannot be negative.");
    }
    mRatioWidth = width;
    mRatioHeight = height;
    requestLayout();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    if (0 == mRatioWidth || 0 == mRatioHeight) {
        setMeasuredDimension(width, height);
    } else {
        if (width < height * mRatioWidth / mRatioHeight) {
            setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
        } else {
            setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
        }
    }
}}

Ainsi, comme vous pouvez le voir, les exceptions se produisent au niveau des méthodes super(), ce qui signifie que mon TextureView personnalisé n'est pas responsable de cette exception. C'est un appel intérieur.

Voici ma config gradle:

apply plugin: 'com.Android.application'

Android {
    compileSdkVersion 25
    buildToolsVersion '25.0.2'
    defaultConfig {
        applicationId "com.abdulwasaetariq.xyz"
        minSdkVersion 21
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "Android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-Android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    androidTestCompile('com.Android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.Android.support', module: 'support-annotations'
        })
    compile 'com.Android.support.constraint:constraint-layout:1.0.0-alpha8'
    compile 'com.github.hotchemi:permissionsdispatcher:2.3.2'
    annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:2.3.2'
}

Avez-vous une idée de la raison pour laquelle cela peut se produire? Toutes les notes de version de Android API 25, où cette modification est-elle évoquée?

25
Abdul Wasae

Si vous regardez la source de la vue de texture pour l'API 24, vous verrez ce qui suit:

/**
 * Subclasses of TextureView cannot do their own rendering
 * with the {@link Canvas} object.
 *
 * @param canvas The Canvas to which the View is rendered.
 */
@Override
public final void draw(Canvas canvas) {
    // NOTE: Maintain this carefully (see View#draw)
    mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

    /* Simplify drawing to guarantee the layer is the only thing drawn - so e.g. no background,
    scrolling, or fading edges. This guarantees all drawing is in the layer, so drawing
    properties (alpha, layer Paint) affect all of the content of a TextureView. */

    if (canvas.isHardwareAccelerated()) {
        DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;

        HardwareLayer layer = getHardwareLayer();
        if (layer != null) {
            applyUpdate();
            applyTransformMatrix();

            mLayer.setLayerPaint(mLayerPaint); // ensure layer Paint is up to date
            displayListCanvas.drawHardwareLayer(layer);
        }
    }
}

Le commentaire dans le corps de draw() donne la justification du changement que vous avez vu. Ceci est la seule documentation que j'ai trouvée. Comparez cela à TextureView de l'API 23:

/**
 * Subclasses of TextureView cannot do their own rendering
 * with the {@link Canvas} object.
 *
 * @param canvas The Canvas to which the View is rendered.
 */
@Override
public final void draw(Canvas canvas) {
    // NOTE: Maintain this carefully (see View.Java)
    mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

    applyUpdate();
    applyTransformMatrix();
}

L'API 24 a également introduit des substitutions pour les méthodes "définir l'arrière-plan" qui ne sont pas remplacées dans l'API 23. La définition d'un arrière-plan est désormais clairement déconseillée et n'est tout simplement pas autorisée. Si vous voyez l'exception d'opération non prise en charge et que vous ne définissez pas explicitement un arrière-plan, cela se faufile probablement dans vos styles. Essayez de définir Android:background="@null" Dans votre XML pour forcer l'arrière-plan à être nul pour contourner l'erreur. Vous pouvez également ajouter le code suivant à votre vue personnalisée pour conserver les fonctionnalités des versions qui prennent en charge la définition d'un arrière-plan:

@Override
public void setBackgroundDrawable(Drawable background) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N && background != null) {
        setBackgroundDrawable(background);
    }
}

Il est difficile de savoir comment remplacer la fonctionnalité que vous avez perdue pour API 24+ ou si vous en avez même besoin, mais que vous souhaitez simplement avoir l'outil d'arrière-plan dans votre arsenal.

3
Cheticamp

Voici des extraits du code source pour View pour Android Nougat:

/**
 * Allow setForeground/setBackground to be called (and ignored) on a textureview,
 * without throwing
 */
static boolean sTextureViewIgnoresDrawableSetters = false;

Dans le constructeur à argument unique (appelé de tous les autres):

        // Prior to N, TextureView would silently ignore calls to setBackground/setForeground.
        // On N+, we throw, but that breaks compatibility with apps that use these methods.
        sTextureViewIgnoresDrawableSetters = targetSdkVersion <= M;

Dans le constructeur View où votre exception est levée:

...
        switch (attr) {
            case com.Android.internal.R.styleable.View_background:
                background = a.getDrawable(attr);
                break;
...
    if (background != null) {
        setBackground(background);  // <--- this is the problematic line, apparently "background" is not null here
    }

La définition réelle de setBackground:

/**
 * Set the background to a given Drawable, or remove the background. If the
 * background has padding, this View's padding is set to the background's
 * padding. However, when a background is removed, this View's padding isn't
 * touched. If setting the padding is desired, please use
 * {@link #setPadding(int, int, int, int)}.
 *
 * @param background The Drawable to use as the background, or null to remove the
 *        background
 */
public void setBackground(Drawable background) {
    //noinspection deprecation
    setBackgroundDrawable(background);
}

Ensuite, le remplacement de setBackgroundDrawable dans TextureView :

@Override
public void setBackgroundDrawable(Drawable background) {
    if (background != null && !sTextureViewIgnoresDrawableSetters) {
        throw new UnsupportedOperationException(
                "TextureView doesn't support displaying a background drawable");
    }
}

Donc, ce que vous pouvez rassembler à partir de tout ce qui est: 1) Vous avez un SDK N cible (Nougat) - évident à partir de votre fichier de construction; 2) Le constructeur de View détermine un fond non nul (je ne peux pas expliquer cette partie pour le moment).

C'est tout ce qu'il faut pour que ce soit un problème réel. Je ne vois pas que vous avez réussi à définir un dessinable dans votre xml, donc remplacer setBackground ou setBackgroundDrawable semble être la possibilité la plus judicieuse pour résoudre le problème pour moi. Il peut y avoir une autre solution de contournement (ou peut-être une "utilisation suggérée" serait une meilleure terminologie) grâce à laquelle vous pouvez réussir à contraindre la variable background dans le constructeur à rester nulle.

8
Dave

Juste pour mentionner, pas seulement TextureView: j'ai trouvé que GridLayout ne prend pas non plus en charge l'affichage d'un arrière-plan dessinable depuis l'API 24.

J'ai essayé:

A) gridLayout.setBackgroundResource(R.drawable.board_960x960px_border_in_bg);

B) Resources res = getResources(); Drawable drawable = res.getDrawable(R.drawable.board_960x960px_border_in_bg); gridLayout.setBackground(drawable);

Aucun des éléments ci-dessus ne semble fonctionner au-dessus de l'API 23.

Cependant, l'arrière-plan de TableLayout ne disparaîtra même pas à l'API 24+, j'ai donc réécrit tout mon code pertinent de GridLayout à TableLayout et maintenant c'est OK.

1
Hardzsi