kovli

kovli

V2EX 第 351330 号会员,加入于 2018-09-21 16:36:38 +08:00
一个追逐全栈的程序员。12年开始从事iOS原生开发,目前的工作内容是iOS、安卓平台的APP设计开发及服务端的开发和运维,专注于跨平台开发、JavaScript、Node.js等技术。我喜欢敲代码,也喜欢写博客。
kovli 最近回复了
2019-04-28 14:16:26 +08:00
回复了 kovli 创建的主题 React ReactNative 开发笔记(持续更新...)
- 如何在原生端( iOS 和 android 两个平台)使用 ReactNative 里的本地图片(路径类似 require('./xxximage.png'))。
在 ReactNative 开发过程中,有时需要在原生端显示 RN 里的图片,这样的好处是可以通过热更新来更新 APP 里的图片,而不需要发布原生版本,而 ReactNative 里图片路径是相对路径,类似'./xxximage.png'的写法,原生端是无法解析这类路径,那么如果将 RN 的图片传递给原生端呢?

解决方案:

1、图片如果用网络图,那只需要将 url 字符串地址传递给原生即可,这种做法需要时间和网络环境加载图片,不属于本地图片,不是本方案所追求的最佳方式。

2、懒人做法是把 RN 的本地图片生成 base64 字符串然后传递给原生再解析,这种做法如果图片太大,字符串会相当长,同样不认为是最佳方案。

其实 RN 提供了相关的解决方法,如下:

RN 端

const myImage = require('./my-image.png');
const resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource');
const resolvedImage = resolveAssetSource(myImage);
NativeModules.NativeBridge.showRNImage(resolvedImage);

iOS 端

#import <React/RCTConvert.h>


RCT_EXPORT_METHOD(showRNImage:(id)rnImageData){
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *rnImage = [RCTConvert UIImage:rnImageData];
...
});
}

安卓端

第一步,从桥接文件获取到 uri 地址


@ReactMethod
public static void showRNImage(Activity activity, ReadableMap params){
String rnImageUri;
try {
//图片地址
rnImageUri = params.getString("uri");
Log.i("Jumping", "uri : " + uri);

...

} catch (Exception e) {
return;
}
}

第二步,创建 JsDevImageLoader.java


package com.XXX;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.StrictMode;
import android.support.annotation.NonNull;
import android.util.Log;


import com.XXX.NavigationApplication;

import java.io.IOException;
import java.net.URL;

public class JsDevImageLoader {
private static final String TAG = "JsDevImageLoader";
public static Drawable loadIcon(String iconDevUri) {
try {
StrictMode.ThreadPolicy threadPolicy = StrictMode.getThreadPolicy();
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitNetwork().build());

Drawable drawable = tryLoadIcon(iconDevUri);

StrictMode.setThreadPolicy(threadPolicy);
return drawable;
} catch (Exception e) {
Log.e(TAG, "Unable to load icon: " + iconDevUri);
return new BitmapDrawable();
}
}

@NonNull
private static Drawable tryLoadIcon(String iconDevUri) throws IOException {
URL url = new URL(iconDevUri);
Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
return new BitmapDrawable(NavigationApplication.instance.getResources(), bitmap);
}
}
第三步,导入 ResourceDrawableIdHelper.java


package com.xg.navigation.react;// Copyright 2004-present Facebook. All Rights Reserved.

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;

import com.facebook.common.util.UriUtil;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Nullable;

/**
* Direct copy paste from react-native, because they made that class package scope. -_-"
* Can be deleted in react-native ^0.29
*/
public class ResourceDrawableIdHelper {
public static final ResourceDrawableIdHelper instance = new ResourceDrawableIdHelper();

private Map<String, Integer> mResourceDrawableIdMap;

public ResourceDrawableIdHelper() {
mResourceDrawableIdMap = new HashMap<>();
}

public int getResourceDrawableId(Context context, @Nullable String name) {
if (name == null || name.isEmpty()) {
return 0;
}
name = name.toLowerCase().replace("-", "_");
if (mResourceDrawableIdMap.containsKey(name)) {
return mResourceDrawableIdMap.get(name);
}
int id = context.getResources().getIdentifier(
name,
"drawable",
context.getPackageName());
mResourceDrawableIdMap.put(name, id);
return id;
}

@Nullable
public Drawable getResourceDrawable(Context context, @Nullable String name) {
int resId = getResourceDrawableId(context, name);
return resId > 0 ? context.getResources().getDrawable(resId) : null;
}

public Uri getResourceDrawableUri(Context context, @Nullable String name) {
int resId = getResourceDrawableId(context, name);
return resId > 0 ? new Uri.Builder()
.scheme(UriUtil.LOCAL_RESOURCE_SCHEME)
.path(String.valueOf(resId))
.build() : Uri.EMPTY;
}
}

第四步,创建 BitmapUtil.java


package com.XXX;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.MediaStore;
import android.text.TextUtils;

import com.XXX.NavigationApplication;
import com.XXX.JsDevImageLoader;
import com.XXX.ResourceDrawableIdHelper;

import java.io.IOException;


public class BitmapUtil {

private static final String FILE_SCHEME = "file";

public static Drawable loadImage(String iconSource) {

if (TextUtils.isEmpty(iconSource)) {
return null;
}

if (NavigationApplication.instance.isDebug()) {
return JsDevImageLoader.loadIcon(iconSource);
} else {
Uri uri = Uri.parse(iconSource);
if (isLocalFile(uri)) {
return loadFile(uri);
} else {
return loadResource(iconSource);
}
}
}

private static boolean isLocalFile(Uri uri) {
return FILE_SCHEME.equals(uri.getScheme());
}

private static Drawable loadFile(Uri uri) {
Bitmap bitmap = BitmapFactory.decodeFile(uri.getPath());
return new BitmapDrawable(NavigationApplication.instance.getResources(), bitmap);
}

private static Drawable loadResource(String iconSource) {
return ResourceDrawableIdHelper.instance.getResourceDrawable(NavigationApplication.instance, iconSource);
}

public static Bitmap getBitmap(Activity activity, String uri) {

if (activity == null || uri == null || TextUtils.isEmpty(uri)) {
return null;
}

Uri mImageCaptureUri;
try {
mImageCaptureUri = Uri.parse(uri);
} catch (Exception e) {
e.printStackTrace();
return null;
}

if (mImageCaptureUri == null) {
return null;
}

Bitmap bitmap = null;
try {
bitmap = MediaStore.Images.Media.getBitmap(activity.getContentResolver(), mImageCaptureUri);
} catch (IOException e) {
e.printStackTrace();
return null;
}

return bitmap;
}
}

第五步,使用第一步里的 rnImageUri 地址

...
BitmapUtil.loadImage(rnImageUri)
...

第六步,显示图片


import android.widget.RelativeLayout;
import android.support.v7.widget.AppCompatImageView;
import android.graphics.drawable.Drawable;

...
final RelativeLayout item = (RelativeLayout) mBottomBar.getChildAt(i);
final AppCompatImageView itemIcon = (AppCompatImageView) item.getChildAt(0);
itemIcon.setImageDrawable(BitmapUtil.loadImage(rnImageUri));

...
2019-01-26 10:50:10 +08:00
回复了 kovli 创建的主题 React ReactNative 开发笔记(持续更新...)
不可以编辑主题,好吧,以后通过回复来更新我的博文。

- 升级旧 RN 版本到目前最新的 0.57.8 如果采用手动升级需要注意如下。
I upgraded from react-naitve 0.55.4 to react-native 0.57.0 and I get this error bundling failed: Error: The 'decorators' plugin requires a 'decoratorsBeforeExport' option, whose value must be a boolean. If you are migrating from Babylon/Babel 6 or want to use the old decorators proposal, you should use the 'decorators-legacy' plugin instead of 'decorators'.

解决方案:参考如下例子

First install the new proposal decorators with npm install @babel/plugin-proposal-decorators --save-dev or yarn add @babel/plugin-proposal-decorators --dev

Then, inside of your .babelrc file, change this:

{
"presets": ["react-native"],
"plugins": ["transform-decorators-legacy"]
}
To this:

{
"presets": [
"module:metro-react-native-babel-preset",
"@babel/preset-flow"
],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy" : true }]
]
}

EDIT:

After you've updated your .babelrc file, make sure to add preset-flow as well with the command yarn add @babel/preset-flow --dev or npm install @babel/preset-flow --save-dev
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2815 人在线   最高记录 6543   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 13ms · UTC 14:56 · PVG 22:56 · LAX 07:56 · JFK 10:56
Developed with CodeLauncher
♥ Do have faith in what you're doing.