Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Nougat: TextureView doesn't support displaying a background drawable

I have been using a TextureView in my android app, and it was working fine. Just recently I tested my code on an Android device with Android API 25 (7.1.2). Same code now does not work and throws the error, java.lang.UnsupportedOperationException: TextureView doesn't support displaying a background drawable.

I know that void setBackgroundDrawable (Drawable background) had been deprecated for a long time, and now it must have been removed. But I am not even setting it by myself.

I am using latest buildTools and SDK. So, I wonder why hasn't the textureView internal implementation been updated.

Here is the relevant stack trace:

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) 

Here is how i use my (not-yet customized) custom TextureView:

<?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> 

Here is my relevant AutoFitTextureView.java: 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);         }     } }} 

So, as you can see, the exceptions occur at the super() methods, which means that my custom TextureView is not responsible for this exception. It's an inside call.

Here is my gradle config:

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' } 

Any ideas why this may be happening? Any release-notes of Android API 25, where this change is talked about?

like image 953
Abdul Wasae Avatar asked Apr 30 '17 10:04

Abdul Wasae


1 Answers

The following are snippets from the source code for View for Android Nougat:

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

In the single-argument constructor (called from all the others):

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

In the View constructor where your exception is thrown:

...         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     } 

The actual definition of 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); } 

Then the override of setBackgroundDrawable in TextureView:

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

So what you can piece together from all that is: 1) You have a target SDK N (Nougat) - obvious from your build file; 2) The constructor from View determines a non-null background (I cannot explain this part at the moment).

That's all it takes for this to be an actual problem. I do not see that you have managed to define a drawable in your xml, so overriding setBackground or setBackgroundDrawable seems to be the most sensible possibility to resolve the issue to me. There may be another workaround (or maybe "suggested usage" would be a better terminology) whereby you can manage to coerce the background variable in the constructor to remain null.

like image 113
Dave Avatar answered Sep 23 '22 10:09

Dave