mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 11:01:21 +08:00
feat(android): vector drawable support (#9464)
This commit is contained in:
@ -13,6 +13,7 @@
|
||||
<Button text="css-playground" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
|
||||
<Button text="visibility-vs-hidden" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
|
||||
<Button text="image-async" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
|
||||
<Button text="vector-image" tap="{{ viewDemo }}" class="btn btn-primary btn-view-demo"/>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</StackLayout>
|
||||
|
5
apps/toolbox/src/pages/vector-image.xml
Normal file
5
apps/toolbox/src/pages/vector-image.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<Page xmlns="http://schemas.nativescript.org/tns.xsd" class="page">
|
||||
<StackLayout>
|
||||
<Image src="res://outline_face_24"/>
|
||||
</StackLayout>
|
||||
</Page>
|
Binary file not shown.
@ -4,20 +4,6 @@
|
||||
<dict>
|
||||
<key>AvailableLibraries</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>DebugSymbolsPath</key>
|
||||
<string>dSYMs</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>TNSWidgets.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>DebugSymbolsPath</key>
|
||||
<string>dSYMs</string>
|
||||
@ -35,6 +21,20 @@
|
||||
<key>SupportedPlatformVariant</key>
|
||||
<string>simulator</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>DebugSymbolsPath</key>
|
||||
<string>dSYMs</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>TNSWidgets.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XFWK</string>
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -34,7 +34,7 @@
|
||||
</data>
|
||||
<key>Info.plist</key>
|
||||
<data>
|
||||
Xj1cG5BPcNRo2kinT2TzpxSuwlc=
|
||||
WsLLFjQ3sruW6pvEfLRTXorurcg=
|
||||
</data>
|
||||
<key>Modules/module.modulemap</key>
|
||||
<data>
|
||||
|
Binary file not shown.
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
|
||||
|
@ -79,6 +79,7 @@ dependencies {
|
||||
implementation 'androidx.fragment:fragment:' + androidxVersion
|
||||
implementation 'androidx.transition:transition:' + androidxVersion
|
||||
implementation "androidx.exifinterface:exifinterface:1.3.2"
|
||||
implementation "androidx.appcompat:appcompat:1.1.0"
|
||||
} else {
|
||||
println 'Using support library'
|
||||
implementation 'com.android.support:support-v4:' + computeSupportVersion()
|
||||
|
@ -7,6 +7,9 @@ import android.content.Context;
|
||||
import android.graphics.*;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.shapes.RoundRectShape;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
import org.nativescript.widgets.image.BitmapOwner;
|
||||
import org.nativescript.widgets.image.Fetcher;
|
||||
@ -14,7 +17,7 @@ import org.nativescript.widgets.image.Worker;
|
||||
/**
|
||||
* @author hhristov
|
||||
*/
|
||||
public class ImageView extends android.widget.ImageView implements BitmapOwner {
|
||||
public class ImageView extends androidx.appcompat.widget.AppCompatImageView implements BitmapOwner {
|
||||
private static final double EPSILON = 1E-05;
|
||||
|
||||
private Path path = new Path();
|
||||
@ -36,6 +39,10 @@ public class ImageView extends android.widget.ImageView implements BitmapOwner {
|
||||
private Worker.OnImageLoadedListener mListener;
|
||||
private boolean mAttachedToWindow = false;
|
||||
|
||||
static {
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
||||
}
|
||||
|
||||
public float getRotationAngle() {
|
||||
return rotationAngle;
|
||||
}
|
||||
@ -214,7 +221,9 @@ public class ImageView extends android.widget.ImageView implements BitmapOwner {
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
BorderDrawable background = this.getBackground() instanceof BorderDrawable ? (BorderDrawable) this.getBackground() : null;
|
||||
|
||||
if(this.mBitmap == null && this.getDrawable() != null) {
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
if (this.mBitmap != null) {
|
||||
float borderTopLeftRadius, borderTopRightRadius, borderBottomRightRadius, borderBottomLeftRadius;
|
||||
|
||||
|
@ -2,6 +2,7 @@ package org.nativescript.widgets;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
@ -18,6 +19,7 @@ import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
|
||||
import org.json.JSONException;
|
||||
@ -31,7 +33,18 @@ import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
|
||||
public class Utils {
|
||||
public static Drawable getDrawable(String uri, Context context){
|
||||
String resPath = uri.substring("res://".length());
|
||||
int resId = context.getResources().getIdentifier(resPath, "drawable", context.getPackageName());
|
||||
if (resId > 0) {
|
||||
return AppCompatResources.getDrawable(context, resId);
|
||||
} else {
|
||||
Log.v("JS", "Missing Image with resourceID: " + uri);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static void drawBoxShadow(View view, String value) {
|
||||
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
|
||||
return;
|
||||
|
@ -20,16 +20,15 @@ import android.annotation.TargetApi;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
import android.graphics.Matrix;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
@ -382,7 +381,6 @@ public class Fetcher extends Worker {
|
||||
try {
|
||||
final TypedValue value = new TypedValue();
|
||||
is = res.openRawResource(resId, value);
|
||||
|
||||
bitmap = BitmapFactory.decodeResourceStream(res, value, is, null, options);
|
||||
} catch (Exception e) {
|
||||
/* do nothing.
|
||||
@ -391,8 +389,10 @@ public class Fetcher extends Worker {
|
||||
*/
|
||||
}
|
||||
|
||||
if (bitmap == null && options != null && options.inBitmap != null) {
|
||||
throw new IllegalArgumentException("Problem decoding into existing bitmap");
|
||||
|
||||
if (bitmap == null) {
|
||||
// throw new IllegalArgumentException("Problem decoding into existing bitmap");
|
||||
return null;
|
||||
}
|
||||
|
||||
ExifInterface ei = getExifInterface(is);
|
||||
@ -449,7 +449,7 @@ public class Fetcher extends Worker {
|
||||
/**
|
||||
* Decode and sample down a bitmap from a file to the requested width and height.
|
||||
*
|
||||
* @param filename The full path of the file to decode
|
||||
* @param fileName The full path of the file to decode
|
||||
* @param reqWidth The requested width of the resulting bitmap
|
||||
* @param reqHeight The requested height of the resulting bitmap
|
||||
* @param cache The Cache used to find candidate bitmaps for use with inBitmap
|
@ -26,9 +26,10 @@ import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
import android.widget.ImageView;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import org.nativescript.widgets.Utils;
|
||||
|
||||
/**
|
||||
* This class wraps up completing some arbitrary long running work when loading a bitmap to an
|
||||
* ImageView. It handles things like using a memory and disk cache, running the work in a background
|
||||
@ -52,6 +53,7 @@ public abstract class Worker {
|
||||
protected boolean mPauseWork = false;
|
||||
protected Resources mResources;
|
||||
protected ContentResolver mResolver;
|
||||
protected Context mContext;
|
||||
private static final int MESSAGE_CLEAR = 0;
|
||||
private static final int MESSAGE_INIT_DISK_CACHE = 1;
|
||||
private static final int MESSAGE_FLUSH = 2;
|
||||
@ -62,6 +64,7 @@ public abstract class Worker {
|
||||
protected Worker(Context context) {
|
||||
mResources = context.getResources();
|
||||
mResolver = context.getContentResolver();
|
||||
mContext = context;
|
||||
// Negative means not initialized.
|
||||
if (debuggable < 0) {
|
||||
try {
|
||||
@ -102,7 +105,7 @@ public abstract class Worker {
|
||||
return;
|
||||
}
|
||||
|
||||
Bitmap value = null;
|
||||
Object value = null;
|
||||
String cacheUri = uri;
|
||||
|
||||
if (debuggable > 0) {
|
||||
@ -124,9 +127,14 @@ public abstract class Worker {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "loadImage.addBitmapToCache: " + owner + ", src: " + cacheUri);
|
||||
}
|
||||
mCache.addBitmapToCache(cacheUri, value);
|
||||
mCache.addBitmapToCache(cacheUri, (Bitmap) value);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try loading as drawable */
|
||||
if(value == null){
|
||||
value = Utils.getDrawable(uri, mContext);
|
||||
}
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
@ -134,7 +142,12 @@ public abstract class Worker {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "Set ImageBitmap on: " + owner + " to: " + uri);
|
||||
}
|
||||
owner.setBitmap(value);
|
||||
if(value instanceof Drawable){
|
||||
owner.setDrawable((Drawable) value);
|
||||
}else {
|
||||
owner.setBitmap((Bitmap) value);
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "OnImageLoadedListener on: " + owner + " to: " + uri);
|
||||
@ -145,6 +158,8 @@ public abstract class Worker {
|
||||
final BitmapWorkerTask task = new BitmapWorkerTask(uri, owner, decodeWidth, decodeHeight, keepAspectRatio, useCache, listener);
|
||||
final AsyncDrawable asyncDrawable =
|
||||
new AsyncDrawable(mResources, mLoadingBitmap, task);
|
||||
|
||||
|
||||
owner.setDrawable(asyncDrawable);
|
||||
|
||||
// NOTE: This uses a custom version of AsyncTask that has been pulled from the
|
||||
@ -277,7 +292,7 @@ public abstract class Worker {
|
||||
/**
|
||||
* The actual AsyncTask that will asynchronously process the image.
|
||||
*/
|
||||
private class BitmapWorkerTask extends AsyncTask<Void, Void, Bitmap> {
|
||||
private class BitmapWorkerTask extends AsyncTask<Void, Void, Object> {
|
||||
private int mDecodeWidth;
|
||||
private int mDecodeHeight;
|
||||
private boolean mKeepAspectRatio;
|
||||
@ -306,13 +321,13 @@ public abstract class Worker {
|
||||
* Background processing.
|
||||
*/
|
||||
@Override
|
||||
protected Bitmap doInBackground(Void... params) {
|
||||
protected Object doInBackground(Void... params) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "doInBackground - starting work: " + imageViewReference.get() + ", on: " + mUri);
|
||||
}
|
||||
|
||||
|
||||
Bitmap bitmap = null;
|
||||
Object bitmap = null;
|
||||
|
||||
// Wait here if work is paused and the task is not cancelled
|
||||
synchronized (mPauseWorkLock) {
|
||||
@ -327,8 +342,7 @@ public abstract class Worker {
|
||||
// another thread and the ImageView that was originally bound to this task is still
|
||||
// bound back to this task and our "exit early" flag is not set, then call the main
|
||||
// process method (as implemented by a subclass)
|
||||
if (bitmap == null && !isCancelled() && getAttachedOwner() != null
|
||||
&& !mExitTasksEarly) {
|
||||
if (!isCancelled() && getAttachedOwner() != null && !mExitTasksEarly) {
|
||||
bitmap = processBitmap(mUri, mDecodeWidth, mDecodeHeight, mKeepAspectRatio, mCacheImage);
|
||||
}
|
||||
|
||||
@ -341,10 +355,15 @@ public abstract class Worker {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "addBitmapToCache: " + imageViewReference.get() + ", src: " + mCacheUri);
|
||||
}
|
||||
mCache.addBitmapToCache(mCacheUri, bitmap);
|
||||
mCache.addBitmapToCache(mCacheUri, (Bitmap) bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try loading as Drawable */
|
||||
if (bitmap == null){
|
||||
bitmap = Utils.getDrawable(mUri, mContext);
|
||||
}
|
||||
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "doInBackground - finished work");
|
||||
}
|
||||
@ -356,7 +375,7 @@ public abstract class Worker {
|
||||
* Once the image is processed, associates it to the imageView
|
||||
*/
|
||||
@Override
|
||||
protected void onPostExecute(Bitmap value) {
|
||||
protected void onPostExecute(Object value) {
|
||||
boolean success = false;
|
||||
// if cancel was called on this task or the "exit early" flag is set then we're done
|
||||
if (isCancelled() || mExitTasksEarly) {
|
||||
@ -377,7 +396,11 @@ public abstract class Worker {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "Set ImageDrawable on: " + owner + " to: " + mUri);
|
||||
}
|
||||
owner.setBitmap(value);
|
||||
if(value instanceof Drawable){
|
||||
owner.setDrawable((Drawable) value);
|
||||
} else if(value instanceof Bitmap){
|
||||
owner.setBitmap((Bitmap) value);
|
||||
}
|
||||
}
|
||||
|
||||
if (mOnImageLoadedListener != null) {
|
||||
@ -389,7 +412,7 @@ public abstract class Worker {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(Bitmap value) {
|
||||
protected void onCancelled(Object value) {
|
||||
super.onCancelled(value);
|
||||
synchronized (mPauseWorkLock) {
|
||||
mPauseWorkLock.notifyAll();
|
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFA500"
|
||||
android:pathData="M10.25,13c0,0.69 -0.56,1.25 -1.25,1.25S7.75,13.69 7.75,13s0.56,-1.25 1.25,-1.25 1.25,0.56 1.25,1.25zM15,11.75c-0.69,0 -1.25,0.56 -1.25,1.25s0.56,1.25 1.25,1.25 1.25,-0.56 1.25,-1.25 -0.56,-1.25 -1.25,-1.25zM22,12c0,5.52 -4.48,10 -10,10S2,17.52 2,12 6.48,2 12,2s10,4.48 10,10zM10.66,4.12C12.06,6.44 14.6,8 17.5,8c0.46,0 0.91,-0.05 1.34,-0.12C17.44,5.56 14.9,4 12,4c-0.46,0 -0.91,0.05 -1.34,0.12zM4.42,9.47c1.71,-0.97 3.03,-2.55 3.66,-4.44C6.37,6 5.05,7.58 4.42,9.47zM20,12c0,-0.78 -0.12,-1.53 -0.33,-2.24 -0.7,0.15 -1.42,0.24 -2.17,0.24 -3.13,0 -5.92,-1.44 -7.76,-3.69C8.69,8.87 6.6,10.88 4,11.86c0.01,0.04 0,0.09 0,0.14 0,4.41 3.59,8 8,8s8,-3.59 8,-8z"/>
|
||||
</vector>
|
Reference in New Issue
Block a user