Fix crash where some android Drawables doesn't implement getConstantState... (#4742)

Changed all places where getConstantState was used.
This commit is contained in:
Hristo Hristov
2017-08-24 11:06:33 +03:00
committed by GitHub
parent d6689a04b2
commit b6d5510627
4 changed files with 64 additions and 22 deletions

View File

@@ -459,7 +459,16 @@ export class View extends ViewCommon {
[backgroundInternalProperty.getDefault](): android.graphics.drawable.Drawable { [backgroundInternalProperty.getDefault](): android.graphics.drawable.Drawable {
const nativeView = this.nativeViewProtected; const nativeView = this.nativeViewProtected;
const drawable = nativeView.getBackground(); const drawable = nativeView.getBackground();
return drawable ? drawable.getConstantState().newDrawable(nativeView.getResources()) : null; if (drawable) {
const constantState = drawable.getConstantState();
if (constantState) {
return constantState.newDrawable(nativeView.getResources());
} else {
return drawable;
}
}
return null;
} }
[backgroundInternalProperty.setNative](value: android.graphics.drawable.Drawable | Background) { [backgroundInternalProperty.setNative](value: android.graphics.drawable.Drawable | Background) {
this._redrawNativeBackground(value); this._redrawNativeBackground(value);

View File

@@ -12,15 +12,15 @@ const R_ATTR_STATE_SELECTED = 0x010100a1;
const TITLE_TEXT_VIEW_ID = 16908310; // http://developer.android.com/reference/android/R.id.html#title const TITLE_TEXT_VIEW_ID = 16908310; // http://developer.android.com/reference/android/R.id.html#title
interface TabChangeListener { interface TabChangeListener {
new (owner: SegmentedBar): android.widget.TabHost.OnTabChangeListener; new(owner: SegmentedBar): android.widget.TabHost.OnTabChangeListener;
} }
interface TabContentFactory { interface TabContentFactory {
new (owner: SegmentedBar): android.widget.TabHost.TabContentFactory; new(owner: SegmentedBar): android.widget.TabHost.TabContentFactory;
} }
interface TabHost { interface TabHost {
new (context: android.content.Context, attrs: android.util.AttributeSet): android.widget.TabHost; new(context: android.content.Context, attrs: android.util.AttributeSet): android.widget.TabHost;
} }
let apiLevel: number; let apiLevel: number;
@@ -144,17 +144,18 @@ export class SegmentedBarItem extends SegmentedBarItemBase {
this.nativeViewProtected.setTypeface(value instanceof Font ? value.getAndroidTypeface() : value); this.nativeViewProtected.setTypeface(value instanceof Font ? value.getAndroidTypeface() : value);
} }
[selectedBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable.ConstantState { [selectedBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable {
const viewGroup = <android.view.ViewGroup>this.nativeViewProtected.getParent(); const viewGroup = <android.view.ViewGroup>this.nativeViewProtected.getParent();
return viewGroup.getBackground().getConstantState(); return viewGroup.getBackground();
} }
[selectedBackgroundColorProperty.setNative](value: Color | android.graphics.drawable.Drawable.ConstantState) { [selectedBackgroundColorProperty.setNative](value: Color | android.graphics.drawable.Drawable) {
const viewGroup = <android.view.ViewGroup>this.nativeViewProtected.getParent(); const nativeView = this.nativeViewProtected;
const viewGroup = <android.view.ViewGroup>nativeView.getParent();
if (value instanceof Color) { if (value instanceof Color) {
const color = value.android; const color = value.android;
const backgroundDrawable = viewGroup.getBackground(); const backgroundDrawable = viewGroup.getBackground();
if (apiLevel > 21 && backgroundDrawable && typeof backgroundDrawable.setColorFilter === "function") { if (apiLevel > 21 && backgroundDrawable) {
const newDrawable = backgroundDrawable.getConstantState().newDrawable(); const newDrawable = tryCloneDrawable(backgroundDrawable, nativeView.getResources());
newDrawable.setColorFilter(color, android.graphics.PorterDuff.Mode.SRC_IN); newDrawable.setColorFilter(color, android.graphics.PorterDuff.Mode.SRC_IN);
org.nativescript.widgets.ViewHelper.setBackground(viewGroup, newDrawable); org.nativescript.widgets.ViewHelper.setBackground(viewGroup, newDrawable);
} else { } else {
@@ -164,15 +165,26 @@ export class SegmentedBarItem extends SegmentedBarItemBase {
arr[0] = R_ATTR_STATE_SELECTED; arr[0] = R_ATTR_STATE_SELECTED;
stateDrawable.addState(arr, colorDrawable); stateDrawable.addState(arr, colorDrawable);
stateDrawable.setBounds(0, 15, viewGroup.getRight(), viewGroup.getBottom()); stateDrawable.setBounds(0, 15, viewGroup.getRight(), viewGroup.getBottom());
org.nativescript.widgets.ViewHelper.setBackground(viewGroup, stateDrawable); org.nativescript.widgets.ViewHelper.setBackground(viewGroup, stateDrawable);
} }
} else { } else {
org.nativescript.widgets.ViewHelper.setBackground(viewGroup, value.newDrawable()); const backgroundDrawable = tryCloneDrawable(value, nativeView.getResources());
org.nativescript.widgets.ViewHelper.setBackground(viewGroup, backgroundDrawable);
} }
} }
} }
function tryCloneDrawable(value: android.graphics.drawable.Drawable, resources: android.content.res.Resources): android.graphics.drawable.Drawable {
if (value) {
const constantState = value.getConstantState();
if (constantState) {
return constantState.newDrawable(resources);
}
}
return value;
}
export class SegmentedBar extends SegmentedBarBase { export class SegmentedBar extends SegmentedBarBase {
nativeViewProtected: android.widget.TabHost; nativeViewProtected: android.widget.TabHost;
private _tabContentFactory: android.widget.TabHost.TabContentFactory; private _tabContentFactory: android.widget.TabHost.TabContentFactory;

View File

@@ -6,7 +6,7 @@ import * as application from "../../application";
export * from "./background-common" export * from "./background-common"
interface AndroidView { interface AndroidView {
_cachedDrawableConstState: android.graphics.drawable.Drawable.ConstantState; _cachedDrawable: android.graphics.drawable.Drawable.ConstantState | android.graphics.drawable.Drawable;
} }
// TODO: Change this implementation to use // TODO: Change this implementation to use
@@ -41,8 +41,9 @@ export module ad {
const drawable = nativeView.getBackground(); const drawable = nativeView.getBackground();
const androidView = <any>view as AndroidView; const androidView = <any>view as AndroidView;
// use undefined as not set. getBackground will never return undefined only Drawable or null; // use undefined as not set. getBackground will never return undefined only Drawable or null;
if (androidView._cachedDrawableConstState === undefined && drawable) { if (androidView._cachedDrawable === undefined && drawable) {
androidView._cachedDrawableConstState = drawable.getConstantState(); const constantState = drawable.getConstantState();
androidView._cachedDrawable = constantState || drawable;
} }
if (isSetColorFilterOnlyWidget(nativeView) if (isSetColorFilterOnlyWidget(nativeView)
@@ -77,10 +78,19 @@ export module ad {
} }
} }
} else { } else {
// TODO: newDrawable for BitmapDrawable will fail if we don't specify resource. Use the other overload. const cachedDrawable = androidView._cachedDrawable;
const defaultDrawable = androidView._cachedDrawableConstState ? androidView._cachedDrawableConstState.newDrawable(nativeView.getResources()) : null; let defaultDrawable: android.graphics.drawable.Drawable;
if (cachedDrawable instanceof android.graphics.drawable.Drawable.ConstantState) {
defaultDrawable = cachedDrawable.newDrawable(nativeView.getResources())
} else if (cachedDrawable instanceof android.graphics.drawable.Drawable) {
defaultDrawable = cachedDrawable;
} else {
defaultDrawable = null;
}
org.nativescript.widgets.ViewHelper.setBackground(nativeView, defaultDrawable); org.nativescript.widgets.ViewHelper.setBackground(nativeView, defaultDrawable);
androidView._cachedDrawableConstState = undefined; // TODO: Do we need to clear the drawable here? Can't we just reuse it again?
androidView._cachedDrawable = undefined;
if (cache.layerType !== undefined) { if (cache.layerType !== undefined) {
cache.setLayerType(cache.layerType, null); cache.setLayerType(cache.layerType, null);

View File

@@ -412,14 +412,14 @@ export class TabView extends TabViewBase {
selectedIndexProperty.coerce(this); selectedIndexProperty.coerce(this);
} }
[tabBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable.ConstantState { [tabBackgroundColorProperty.getDefault](): android.graphics.drawable.Drawable {
return this._tabLayout.getBackground().getConstantState(); return this._tabLayout.getBackground();
} }
[tabBackgroundColorProperty.setNative](value: android.graphics.drawable.Drawable.ConstantState | Color) { [tabBackgroundColorProperty.setNative](value: android.graphics.drawable.Drawable | Color) {
if (value instanceof Color) { if (value instanceof Color) {
this._tabLayout.setBackgroundColor(value.android); this._tabLayout.setBackgroundColor(value.android);
} else { } else {
this._tabLayout.setBackground(value ? value.newDrawable() : null); this._tabLayout.setBackground(tryCloneDrawable(value, this.nativeViewProtected.getResources));
} }
} }
@@ -447,4 +447,15 @@ export class TabView extends TabViewBase {
const color = value instanceof Color ? value.android : value; const color = value instanceof Color ? value.android : value;
tabLayout.setSelectedIndicatorColors([color]); tabLayout.setSelectedIndicatorColors([color]);
} }
}
function tryCloneDrawable(value: android.graphics.drawable.Drawable, resources: android.content.res.Resources): android.graphics.drawable.Drawable {
if (value) {
const constantState = value.getConstantState();
if (constantState) {
return constantState.newDrawable(resources);
}
}
return value;
} }