【稀饭】react native 实战系列教程之自定义原生模块

前端之家收集整理的这篇文章主要介绍了【稀饭】react native 实战系列教程之自定义原生模块前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

影片详情开发也是通过Cheerio抓取并分析网页获取到的详情数据,本节就不作为详细内容来讲解了,详细的代码可以看下我的github效果如下:

在点击播放时,会跳转到播放界面,并且横屏显示退出播放界面时,会恢复到竖屏状态。但是,react native并没有给我们提供设置横竖屏的API,因此,我们需要自己使用原生的代码来完成此功能

使用原生代码,我们可以为react native作什么呢?

  • 一个是功能性上的(模块),比如提供横竖屏设置、数据库存储等
  • 一个是UI层面上的(UI组件),比如可以自定义一个VideoView视频播放器等

就目前项目而言,详情页开发涉及到的两个正好是上面的两种情况,横竖屏设置(功能)和使用原生VideoView播放视频(UI)。

原生模块之横竖屏设置功能开发

在我们项目目录下XiFan/android 存放的是android项目的代码,需要使用Android Studio来开发,如果你不是android开发者,那么你可能需要先搭建android开发环境,这里就不介绍了,可自行网上搜索查阅资料。这里放一个工具下载地址 http://www.androiddevtools.cn/

打开android项目之后(打开之后会弹窗提示更改gradle版本,点击忽略即可),目录结构如下:

module是新建的一个包名,专门存放自定义的组件。开发一个功能组件,需要配对实现一个Module和Package。

实现JAVA端模块

在module包下,新建OrientationModule类,并继承ReactContextBaseJavaModule

  1. public class OrientationModule extends ReactContextBaseJavaModule{
  2.  
  3. public OrientationModule(ReactApplicationContext reactContext) {
  4. super(reactContext);
  5. }
  6.  
  7. @Override
  8. public String getName() {
  9. return "Orientation";
  10. }
  11.  
  12. @Nullable
  13. @Override
  14. public Map<String,Object> getConstants() {
  15. return null;
  16. }
  17. }

getName方法返回的是该组件的名称(该名称可以加RCT前缀,如RCTOrientation,但JS中调用的还是没有加前缀的Orientation),在JS中供NativeModules使用;getConstants方法返回一组key/value常量属性值,可供JS中调用。具体看实现吧。

首先我们需要提供一个方法,供JS中调用设置应用的横竖屏。要让JS中能调用,需要在方法上使用ReactMethod注解。

  1. @ReactMethod
  2. public void setOrientation(final int orientation){
  3. UiThreadUtil.runOnUiThread(new Runnable() {
  4. @Override
  5. public void run() {
  6. Activity activity = getCurrentActivity();
  7. if(activity == null){
  8. return;
  9. }
  10. if(orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){
  11. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  12. @H_301_103@//设置全屏
  13. Window window = activity.getWindow();
  14. WindowManager.LayoutParams params = window.getAttributes();
  15. params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
  16. window.setAttributes(params);
  17.  
  18. }else if(orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){
  19. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
  20. @H_301_103@//设置非全屏
  21. Window window = activity.getWindow();
  22. WindowManager.LayoutParams params = window.getAttributes();
  23. params.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
  24. window.setAttributes(params);
  25. }
  26. }
  27. });
  28.  
  29. }

这个方法接收一个orientation参数,只支持ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE和ActivityInfo.SCREEN_ORIENTATION_PORTRAIT常量参数值。由于react native是运行在JavaBridge线程,所以在使用android一些功能时,我们需要将在android UI线程里进行操作,因此这里调用UiThreadUtil.runOnUiThread。

那么,我们如何让使用者更方便的使用setOrientation这个方便,而不用知道orientation参数要传什么具体值呢?

这里我们就需要使用上面提到的getConstants方法来定义常量值了。

  1. private static final String ORIENTATION_LANDSCAPE_KEY = "LANDSCAPE";@H_301_103@//横屏
  2. private static final String ORIENTATION_PORTRAIT_KEY = "PORTRAIT";@H_301_103@//竖屏
  3.  
  4.  
  5. @Nullable
  6. @Override
  7. public Map<String,Object> getConstants() {@H_301_103@//定义返回值常量
  8. Map<String,Object> constants = MapBuilder.newHashMap();
  9. constants.put(ORIENTATION_LANDSCAPE_KEY,ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  10. constants.put(ORIENTATION_PORTRAIT_KEY,ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
  11. return constants;
  12. }

定义了LANDSCAPE 和 PORTRAIT两个属性常量,并分别赋予值ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE 和 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT

写完了Module,我们需要再写Package。新建OrientationPackage类,并实现ReactPackage接口。

  1. public class OrientationPackage implements ReactPackage{
  2. @Override
  3. public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
  4. return Arrays.<NativeModule>asList(
  5. new OrientationModule(reactContext)
  6. );
  7. }
  8.  
  9. @Override
  10. public List<Class<? extends JavaScriptModule>> createJSModules() {
  11. return Collections.emptyList();
  12. }
  13.  
  14. @Override
  15. public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
  16. return Collections.emptyList();
  17. }
  18. }

OrientationModule是属于NativeModule,所以,我们在createNativeModules方法中返回我们已经实现了的OrientationModule。

最后,我们需要将OrientationPackage注册到react native中,让它能识别到我们的Module。

打开MainApplication类,并将OrientationPackage添加到ReactPackage

  1. public class MainApplication extends Application implements ReactApplication {
  2.  
  3. private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
  4. @Override
  5. protected boolean getUseDeveloperSupport() {
  6. return BuildConfig.DEBUG;
  7. }
  8.  
  9. @Override
  10. protected List<ReactPackage> getPackages() {
  11. return Arrays.<ReactPackage>asList(
  12. new MainReactPackage(),new OrientationPackage()
  13. );
  14. }
  15. };
  16.  
  17. @Override
  18. public ReactNativeHost getReactNativeHost() {
  19. return mReactNativeHost;
  20. }
  21. }

在getPackages方法增加了我们的OrientationPackage。

接下来就是在JS中使用它了。

实现对应的JS模块

新建VideoPlayScene.js,用于播放视频的场景。我们需要在应用进入该页面时,横屏显示退出时恢复竖屏。

  1. import React,{Component} from 'react';
  2. import {
  3. View,WebView,NativeModules
  4. } from 'react-native';
  5. var Orientation = NativeModules.Orientation;
  6.  
  7. export default class VideoPlayScene extends Component{
  8. constructor(props){
  9. super(props);
  10. }
  11.  
  12. componentWillMount(){
  13. Orientation.setOrientation(Orientation.LANDSCAPE);
  14. }
  15.  
  16. componentWillUnmount(){
  17. Orientation.setOrientation(Orientation.PORTRAIT);
  18. }
  19. }

我们需要import NativeModules这个模块

  1. var Orientation = NativeModules.Orientation;

NativeModules点后面的Orientation就是我们在原生代码OrientationModule中getName返回的字符串值。

在componentWillMount 和 componentWillUnmount生命周期中调用setOrientation来实现需求。其中Orientation.LANDSCAPE 和 Orientation.PORTRAIT 就是我们在OrientationModule$getConstants 定义的两个常量值。

那如果在js中需要接收一个回调方法,那么原生代码需要怎么写呢?

在OrientationModule在定义一个方法,并接收一个Callback参数

  1. @ReactMethod
  2. public void getRequestedOrientation(Callback callback){
  3. int orientation = getReactApplicationContext().getResources().getConfiguration().orientation;
  4. callback.invoke(orientation);
  5. }

js中调用

  1. componentWillMount(){
  2. Orientation.getRequestedOrientation((orientation)=>{
  3. console.log('current orientation :'+ orientation);
  4. });
  5. @H_301_103@//Orientation.setOrientation(Orientation.LANDSCAPE);
  6. }

这样就完成了横竖屏功能的开发了(不好的是,自定义的Module在IDE下并不能代码提示,有知道如何让它提示的话,请告知一下哈),贴下OrientationModule.java的完整代码

  1. public class OrientationModule extends ReactContextBaseJavaModule{
  2. private static final String ORIENTATION_LANDSCAPE_KEY = "LANDSCAPE";@H_301_103@//横屏
  3. private static final String ORIENTATION_PORTRAIT_KEY = "PORTRAIT";@H_301_103@//竖屏
  4.  
  5. public OrientationModule(ReactApplicationContext reactContext) {
  6. super(reactContext);
  7. }
  8.  
  9. @Override
  10. public String getName() {
  11. return "Orientation";
  12. }
  13.  
  14. @Nullable
  15. @Override
  16. public Map<String,Object> getConstants() {@H_301_103@//定义返回值
  17. Map<String,Object> constants = MapBuilder.newHashMap();
  18. constants.put(ORIENTATION_LANDSCAPE_KEY,ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  19. constants.put(ORIENTATION_PORTRAIT_KEY,ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
  20. constants.put("initOrientation",getReactApplicationContext().getResources().getConfiguration().orientation);
  21. return constants;
  22. }
  23.  
  24. @ReactMethod
  25. public void setOrientation(final int orientation){
  26. UiThreadUtil.runOnUiThread(new Runnable() {
  27. @Override
  28. public void run() {
  29. Activity activity = getCurrentActivity();
  30. if(activity == null){
  31. return;
  32. }
  33. if(orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){
  34. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  35. @H_301_103@//设置全屏
  36. Window window = activity.getWindow();
  37. WindowManager.LayoutParams params = window.getAttributes();
  38. params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
  39. window.setAttributes(params);
  40.  
  41. }else if(orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){
  42. activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
  43. @H_301_103@//设置非全屏
  44. Window window = activity.getWindow();
  45. WindowManager.LayoutParams params = window.getAttributes();
  46. params.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
  47. window.setAttributes(params);
  48. }
  49. }
  50. });
  51.  
  52. }
  53. @ReactMethod
  54. public void getRequestedOrientation(Callback callback){
  55. int orientation = getReactApplicationContext().getResources().getConfiguration().orientation;
  56. callback.invoke(orientation);
  57. }
  58. }

总结

本节我们完成了android的自定义模块,使RN能够使用原生提供的能力。而关于js如何向native发送命令以及native如何向js发送事件的问题,原生模块和原生UI组件它们的通信都是一样的,所以我们把这个问题留在下一节来讲述。

猜你在找的React相关文章