700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > ReactNative封装Android原生UI

ReactNative封装Android原生UI

时间:2022-07-13 16:33:57

相关推荐

ReactNative封装Android原生UI

上篇博客,笔者介绍了如何封装Android原生模块供rn端调用,一共分为四个步骤。这篇我们来讲解下如何封装Android原生UI供rn端调用。

官方文档,请参考下面链接:/docs/native-components-android。如果有些同学懒得看官方文档或是看完之后有些模糊不清的地方,可以参考下本篇博客列举的几个案例。

我们通过两个案例来讲解如何封装原生UI给rn端调用

封装TextView封装Webview

封装TextView

第一步,集成SimpleViewManager,如下所示:

package com.example.demo.reactnative.base.viewManager;import android.util.Log;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import android.widget.Toast;import androidx.annotation.NonNull;import androidx.annotation.Nullable;import com.facebook.react.bridge.Arguments;import com.facebook.react.bridge.ReactApplicationContext;import com.facebook.react.bridge.WritableMap;import com.facebook.react.modules.core.DeviceEventManagerModule;import com.facebook.react.uimanager.SimpleViewManager;import com.facebook.react.uimanager.ThemedReactContext;import com.facebook.react.uimanager.annotations.ReactProp;import com.facebook.react.uimanager.events.RCTEventEmitter;import org.w3c.dom.Text;import java.util.Map;/*** Created by brett.li* on /10/18*///SimpleViewManager<T>,T可以是Android原生的view如:TextView、WebView或者是Android原生的自定义viewpublic class MyTextviewManager extends SimpleViewManager<TextView> {private ReactApplicationContext context;public MyTextviewManager(ReactApplicationContext reactApplicationContext){context = reactApplicationContext;}@NonNull@Overridepublic String getName() {return "RCTTextView";}@NonNull@Overrideprotected TextView createViewInstance(@NonNull ThemedReactContext reactContext) {TextView textView = new TextView(context);textView.post(new Runnable() {@Overridepublic void run() {ViewGroup.LayoutParams lp = textView.getLayoutParams();lp.width = 100;lp.height = 100;textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));}});textView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {WritableMap event = Arguments.createMap();event.putString("message", "MyMessage"); //原生事件通知rn,我们可以在原生中处理点击事件,如点击TextView,我们直接弹出一个Toast,但是如何想要在rn端响应这个事件,那么可以通过下面这种方式reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("onClick",event);}});return textView;}@Overridepublic void onDropViewInstance(@NonNull TextView view) {super.onDropViewInstance(view);Log.e("RCTTextView","onDropViewInstance view is "+view);}@ReactProp(name = "text")public void setText(TextView view,String text){Log.e("RCTTextView","text is "+text);view.setText(text);view.setTextSize(10);Log.e("RCTTextView","layoutParams is "+view.getLayoutParams());}}

第二步,把MyTextviewManager添加到ReactPackage中:

package com.example.demo.reactnative.base.hook;import androidx.annotation.NonNull;import com.example.demo.reactnative.base.module.RNBaseReactModule;import com.example.demo.reactnative.base.module.StorageModule;import com.example.demo.reactnative.base.module.ToastModule;import com.example.demo.reactnative.base.viewManager.MyTextviewManager;import com.example.demo.reactnative.base.viewManager.MyWebViewManager;import com.facebook.react.ReactPackage;import com.facebook.react.bridge.NativeModule;import com.facebook.react.bridge.ReactApplicationContext;import com.facebook.react.uimanager.ViewManager;import java.util.ArrayList;import java.util.Collections;import java.util.List;/*** Created by brett.li* on /10/17*/public class RNBasePackage implements ReactPackage {@NonNull@Overridepublic List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {List<NativeModule> modules = new ArrayList<>();//modules.add(RNBaseReactModule.getInstance(reactContext).setReactApplicationContext(reactContext));//modules.add(new ToastModule(reactContext));// modules.add(new StorageModule(reactContext));return modules;}@NonNull@Overridepublic List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {List<ViewManager> modules = new ArrayList<>();modules.add(new MyTextviewManager(reactContext));return modules;}}

第三步,将RNBasePackage添加到ReactNativeHost,这一步更封装原生模块一样。

package com.example.demo;import android.app.Application;import android.content.Context;import com.facebook.soloader.SoLoader;/*** Created by Brett.li on /9/21.*/public class MyReactApplication extends Application implements ReactApplication {@Overridepublic ReactNativeHost getReactNativeHost() {return new ReactNativeHost(this) {@Overridepublic boolean getUseDeveloperSupport() {return BuildConfig.DEBUG;}@Overrideprotected List<ReactPackage> getPackages() {List<ReactPackage> packages= new PackageList(this).getPackages();packages.add(new RNBasePackage());return packages;}@Nullable@Overrideprotected String getBundleAssetName() {//就是我们打包出来的bundle的名字,不能写错,不然就加载不到bundlereturn "main.bundle";//bundle的名字,默认是index.android.bundle}@Overrideprotected String getJSMainModuleName() {//即打包脚本中--entry-file后面的参数名。不能写错return "index";}};}@Overridepublic void onCreate() {super.onCreate();SoLoader.init(this, /* native exopackage */ false);}}//application中需要做两件事//1.实现getReactNativeHost接口//2.添加SoLoader.init(this, /* native exopackage */ false);这句代码

第四步,rn端调用

import React from "react";import {DeviceEventEmitter,NativeEventEmitter,NativeModules,Platform,requireNativeComponent,StyleSheet,Text, ToastAndroid,TouchableOpacity,View} from "react-native";//1.添加该代码,确保rn端能够找到该原生uiconst RCTTextView = (Platform.OS === 'android') ? requireNativeComponent('RCTTextView') : null;const NWebview = (Platform.OS === 'android') ? requireNativeComponent('WebView') : null;const styles = StyleSheet.create({container: {// flex: 1,justifyContent: 'center',alignItems:'center'},hello: {fontSize: 20,textAlign: 'center',margin: 10}});interface Props{test:string}interface State{test:string}export class HelloWorld extends ponent<Props,State> {constructor(props:Props) {super(props);this.state={test:props.test}//响应原生端事件,“onClick”需要和原生的emit方法的第一个参数对应DeviceEventEmitter.addListener("onClick",(msg)=>{ToastAndroid.show(msg.message,ToastAndroid.SHORT);})}render() {return (<View style={styles.container}><TouchableOpacity onPress={()=>{NativeModules.CustomToast.show('Brett', NativeModules.CustomToast.SHORT);}}><Text style={styles.hello}>{this.state.test}</Text></TouchableOpacity><TouchableOpacity onPress={()=>{this.setDataFromStorage("brett","1")// NativeModules.BaseStorageModule.setStringData("brett","1")}}><Text>{"setData"}</Text></TouchableOpacity>//2.注意,必须为原生ui添加width,height不然是显示不出来的,//style是每个rn的view都有的属性,如果是自定义的原生ui,编译器可能没有提示,但是还是可以使用style属性的。text属性则是我们在原生中@ReactProp(name = "text")的那么对应的值<RCTTextView style={{width:100,height:100}} text={"Hello!!!"}/><NWebview style = {{width:500,height:500}} url={""}/></View>);}}

完成上面这四步,运行程序则可以看到我们自定义的view了。

小提示:如果编译出错,找不到该原生ui。首先,确认上面这四步是否有遗漏,如果没有那么请删掉node_modules文件夹,重新yarn下载依赖,并且原生程序sync一下。

封装Webview

其实,笔者认为封装原生ui这个功能最大的好处是可以用于webview中。因为现在的app很大一部分都是混合开发的,通过webview加载一些前端的页面,通过webview客户端可以注入一些事件给前端,如果原生已经完成了这一套逻辑,难道rn端还要在实现一套吗?当然rn端有提供一个成熟的webview控件,但是如果我们自己封装webview那么就可以省去重新编写js事件这部分代码,直接复用原生的。

例如:我们可以在rn端控制webview加载的url、大小等,原生则是自己进行webview的一些设置、WebViewClient的监听等,监听完成需要发送什么事件则通过RCTDeviceEventEmitte通知rn端,在rn端实现相关的功能。有相关需求的同学可以参考下笔者的思路。

package com.example.demo.reactnative.base.viewManager;import android.util.Log;import android.view.ViewGroup;import android.webkit.WebResourceRequest;import android.webkit.WebView;import android.webkit.WebViewClient;import androidx.annotation.NonNull;import com.facebook.react.bridge.ReactApplicationContext;import com.facebook.react.uimanager.SimpleViewManager;import com.facebook.react.uimanager.ThemedReactContext;import com.facebook.react.uimanager.annotations.ReactProp;/*** Created by brett.li* on /10/18*/public class MyWebViewManager extends SimpleViewManager<WebView> {private ReactApplicationContext context;public MyWebViewManager(ReactApplicationContext context){this.context = context;}@NonNull@Overridepublic String getName() {return "WebView";}@NonNull@Overrideprotected WebView createViewInstance(@NonNull ThemedReactContext reactContext) {WebView webView = new WebView(reactContext);webView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));return webView;}@ReactProp(name = "url")public void setUrl(WebView webView,String url){Log.e("WebView","url is "+url);initWebViewSetting(webView);webView.loadUrl(url);}private void initWebViewSetting(WebView webView){webView.setWebViewClient(new WebViewClient(){@Overridepublic boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {String url = request.getUrl().toString();if (url.startsWith("http://") || url.startsWith("https://")) {view.loadUrl(url);return true;}return false;}});}}

这里就只贴出原生的代码,剩下的步骤更笔者上面封装TextView的一样的。

最后

其实,笔者上面提供的案例都是封装view的,并没有封装viewgroup。后面笔者会专门抽出一个章节来讲解如何封装一个瀑布流控件。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。