私はAndroidアプリでTextureViewを使用していますが、正常に機能していました。つい最近、AndroidデバイスでAndroid API 25(7.1.2)を使用してコードをテストしました。現在、同じコードは機能せず、エラー_Java.lang.UnsupportedOperationException: TextureView doesn't support displaying a background drawable
_をスローします。
void setBackgroundDrawable (Drawable background)
が 長い間非推奨 であったことを知っていますが、今では削除されているはずです。しかし、私は自分で設定することすらしていません。
私は最新のbuildToolsとSDKを使用しています。だから、textureViewの内部実装がなぜ更新されていないのだろうか
関連するスタックトレースは次のとおりです:
_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)
_
ここで、(まだカスタマイズされていない)カスタム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>
_
関連する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);
}
}
}}
_
したがって、ご覧のとおり、例外はsuper()
メソッドで発生します。つまり、カスタムのTextureViewはこの例外を処理しません。それは内部呼び出しです。
私の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'
}
_
なぜこれが起こっているのか?この変更について言及されているAndroid API 25のリリースノート
API 24のテクスチャビューのソースを見ると、次のように表示されます。
_/**
* 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);
}
}
}
_
draw()
の本文のコメントは、あなたが見た変更の根拠を与えます。これは私が見つけた唯一のドキュメントです。これをAPI 23のTextureView
と比較してください:
_/**
* 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();
}
_
API 24では、API 23でオーバーライドされない「バックグラウンドの設定」メソッドのオーバーライドも導入されました。バックグラウンドの設定は明らかに推奨されておらず、許可されていません。サポートされていない操作の例外が表示され、明示的に背景を設定していない場合は、おそらくスタイルを介してこっそりと侵入しています。 XMLに_Android:background="@null"
_を設定して、エラーを回避するために背景を強制的にnullにしてみてください。次のコードをカスタムビューに追加して、背景の設定をサポートするバージョンの機能を保持することもできます。
_@Override
public void setBackgroundDrawable(Drawable background) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N && background != null) {
setBackgroundDrawable(background);
}
}
_
API 24+で失われた機能をどのように置き換えるか、それが必要な場合でもバックグラウンドツールを武器にしたいだけなのかは不明です。
以下は、 View
for Android Nougat :)のソースコードからの抜粋です。
/**
* Allow setForeground/setBackground to be called (and ignored) on a textureview,
* without throwing
*/
static boolean sTextureViewIgnoresDrawableSetters = false;
単一引数コンストラクター(他のすべてから呼び出される):
// 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;
例外がスローされるView
コンストラクター内:
...
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
}
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);
}
次に、 setBackgroundDrawable
のTextureView
のオーバーライド:
@Override
public void setBackgroundDrawable(Drawable background) {
if (background != null && !sTextureViewIgnoresDrawableSetters) {
throw new UnsupportedOperationException(
"TextureView doesn't support displaying a background drawable");
}
}
1)ターゲットSDK N(Nougat)があります-ビルドファイルから明らかです。 2)View
のコンストラクターはnull以外の背景を決定します(現時点ではこの部分を説明できません)。
これが実際の問題になるのに必要なことはそれだけです。 xmlでドロウアブルを定義できたとは思わないので、setBackground
またはsetBackgroundDrawable
をオーバーライドすることが、問題を解決するための最も賢明な可能性のようです。コンストラクターでbackground
変数を強制的にnullに強制することができる別の回避策があるかもしれません(または、「推奨される使用法」がより良い用語かもしれません)。
TextureViewだけでなく、言うまでもなく、GridLayoutはAPI 24以降、背景のドロアブルの表示もサポートしていません。
私は試した:
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);
上記のどちらもAPI 23を超えて機能しているようには見えません。
ただし、TableLayoutの背景はAPI 24以降でも消えないので、関連するすべてのコードをGridLayoutからTableLayoutに書き直して、今では大丈夫です。