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

原创 2016年09月22日 16:20:16

影片详情开发也是通过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版本,点击忽略即可),目录结构如下:

android项目结构

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

实现JAVA端模块

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

public class OrientationModule extends ReactContextBaseJavaModule{

    public OrientationModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "Orientation";
    }

    @Nullable
    @Override
    public Map<String, Object> getConstants() {
        return null;
    }
}

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

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

@ReactMethod
public void setOrientation(final int orientation){
    UiThreadUtil.runOnUiThread(new Runnable() {
        @Override
        public void run() {
            Activity activity = getCurrentActivity();
            if(activity == null){
                return;
            }
            if(orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){
                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                //设置全屏
                Window window = activity.getWindow();
                WindowManager.LayoutParams params = window.getAttributes();
                params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
                window.setAttributes(params);

            }else if(orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){
                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                //设置非全屏
                Window window = activity.getWindow();
                WindowManager.LayoutParams params = window.getAttributes();
                params.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
                window.setAttributes(params);
            }
        }
    });

}

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

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

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

private static final String ORIENTATION_LANDSCAPE_KEY = "LANDSCAPE";//横屏
private static final String ORIENTATION_PORTRAIT_KEY = "PORTRAIT";//竖屏


@Nullable
@Override
public Map<String, Object> getConstants() {//定义返回值常量
    Map<String,Object> constants = MapBuilder.newHashMap();
    constants.put(ORIENTATION_LANDSCAPE_KEY, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    constants.put(ORIENTATION_PORTRAIT_KEY, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    return constants;
}

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

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

public class OrientationPackage implements ReactPackage{
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        return Arrays.<NativeModule>asList(
                new OrientationModule(reactContext)
        );
    }

    @Override
    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

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

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

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

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    protected boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
          new OrientationPackage()
      );
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
      return mReactNativeHost;
  }
}

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

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

实现对应的JS模块

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

import React,{Component} from 'react';
import {
    View,
    WebView,
    NativeModules
} from 'react-native';
var Orientation = NativeModules.Orientation;

export default class VideoPlayScene extends Component{
    constructor(props){
        super(props);
    }

    componentWillMount(){
        Orientation.setOrientation(Orientation.LANDSCAPE);
    }

    componentWillUnmount(){
        Orientation.setOrientation(Orientation.PORTRAIT);
    }
}

我们需要import NativeModules这个模块

var Orientation = NativeModules.Orientation;

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

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

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

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

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

js中调用

componentWillMount(){
    Orientation.getRequestedOrientation((orientation)=>{
        console.log('current orientation :'+ orientation);
    });
    //Orientation.setOrientation(Orientation.LANDSCAPE);
}

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

public class OrientationModule extends ReactContextBaseJavaModule{
    private static final String ORIENTATION_LANDSCAPE_KEY = "LANDSCAPE";//横屏
    private static final String ORIENTATION_PORTRAIT_KEY = "PORTRAIT";//竖屏

    public OrientationModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "Orientation";
    }

    @Nullable
    @Override
    public Map<String, Object> getConstants() {//定义返回值
        Map<String,Object> constants = MapBuilder.newHashMap();
        constants.put(ORIENTATION_LANDSCAPE_KEY, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        constants.put(ORIENTATION_PORTRAIT_KEY, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        constants.put("initOrientation",getReactApplicationContext().getResources().getConfiguration().orientation);
        return constants;
    }

    @ReactMethod
    public void setOrientation(final int orientation){
        UiThreadUtil.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Activity activity = getCurrentActivity();
                if(activity == null){
                    return;
                }
                if(orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){
                    activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    //设置全屏
                    Window window = activity.getWindow();
                    WindowManager.LayoutParams params = window.getAttributes();
                    params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
                    window.setAttributes(params);

                }else if(orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){
                    activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    //设置非全屏
                    Window window = activity.getWindow();
                    WindowManager.LayoutParams params = window.getAttributes();
                    params.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
                    window.setAttributes(params);
                }
            }
        });

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

总结

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

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

横竖屏切换问题(不使用onConfigurationChanged)

有这样个需求:要求父Activity横屏(或竖屏),但在弹出的窗口(例如系统配置项)能根据用户的当前手机状态(横屏或者竖屏)进行横竖屏切换 方案1:使用Dialog样式的Activity进行弹窗,然...

【稀饭】react native 实战系列教程之项目初始化

项目前期准备工作本系列课程实战,默认是你已经安装好react native所需的一切环境配置,如果你还没配置好,请事先看下中文官网。这里提一下,如果你在安装Chocolatey过程中出错了,一般是权限...

【稀饭】react native 实战系列教程之Navigator实现页面跳转

主界面开发上一节,我们已经完成了首页的开发,现在,我们继续完成主界面的开发,就是添加底部‘首页’和‘我的’两个tabbar。在js/文件夹下,新建MainScene.js文件import React,...

【稀饭】react native 实战系列教程之自定义原生UI组件

上一节,讲了关于RN的自定义原生模块,本节是关于自定义原生UI组件,学习完本节,你将了解到原生UI组件的开发流程,以及js如何向native发送命令和native如何向js发送事件。原生UI组件之Vi...

【稀饭】react native 实战系列教程之热更新原理分析与实现

很多人在技术选型的时候,会选择RN是因为它具有热更新,而且这是它的一个特性,所以实现起来会相对比较简单,不像原生那样,原生的热更新是一个大工程。那就目前来看,RN的热更新方案已有的,有微软的CodeP...

【稀饭】react native 实战系列教程之首页列表UI实现

首页设计与实现首先,这章节讲的是首页内容的设计与实现,不包括主界面的设计,因为一开始入手,我希望能立马获取到数据并能展示出来,后面再来搭木积似的一步一步把整体框架做起来。设计(图丑,莫见怪~)主界面的...

【稀饭】react native 实战系列教程之完成首页

首页功能前面,我们已经完成了影视信息组件的开发,接下来,我们要用该组件来完成首页界面功能的开发,如下图可以看到,首页顶部一个标题栏,下面是‘最新’、‘最热’两个选项卡。我们要完成的有标题栏、选项卡、以...

【稀饭】react native 实战系列教程之项目介绍

写之前当你在看该系列教程时,我想你应该和我一样起码是有些基础了。本人是Android原生开发的,业余时间学习了下react native。学习的初衷是:很多时候,移动开发要求既要会Android也要会...

【稀饭】react native 实战系列教程之数据存储

概述在开发一款APP,对于数据的存储是在正常不过了,在此之前,【稀饭】这个应用还没有用到存储数据的地方,为了学习研究React Native的数据存储,打算给应用增加【我的收藏】和【观看历史】这两个功...

【稀饭】react native 系列教程之已有项目接入React Native

概述本文是基于目前公司的一个真实项目编写的,由于是边实践边记录,遇到什么问题和如何解决的,所以你看这篇文章的时候,可能有时候会觉得不是很流畅,特此说明。引入React Nativebuild.grad...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【稀饭】react native 实战系列教程之自定义原生模块
举报原因:
原因补充:

(最多只允许输入30个字)