影片详情开发也是通过Cheerio抓取并分析网页获取到的详情数据,本节就不作为详细内容来讲解了,详细的代码可以看下我的github,效果如下:
在点击播放时,会跳转到播放界面,并且横屏显示,退出播放界面时,会恢复到竖屏状态。但是,react native并没有给我们提供设置横竖屏的API,因此,我们需要自己使用原生的代码来完成此功能。
使用原生代码,我们可以为react native作什么呢?
就目前项目而言,详情页开发涉及到的两个正好是上面的两种情况,横竖屏设置(功能)和使用原生VideoView播放视频(UI)。
原生模块之横竖屏设置功能开发
在我们项目目录下XiFan/android 存放的是android项目的代码,需要使用Android Studio来开发,如果你不是android开发者,那么你可能需要先搭建android开发环境,这里就不介绍了,可自行网上搜索查阅资料。这里放一个工具下载地址 http://www.androiddevtools.cn/
打开android项目之后(打开之后会弹窗提示更改gradle版本,点击忽略即可),目录结构如下:
module是新建的一个包名,专门存放自定义的组件。开发一个功能组件,需要配对实现一个Module和Package。
实现JAVA端模块
在module包下,新建OrientationModule类,并继承ReactContextBaseJavaModule
public@H_403_28@ class@H_403_28@ OrientationModule@H_403_28@ extends@H_403_28@ ReactContextBaseJavaModule@H_403_28@{@H_403_28@
public@H_403_28@ OrientationModule@H_403_28@(ReactApplicationContext reactContext) {
super@H_403_28@(reactContext);
}
@Override@H_403_28@
public@H_403_28@ String getName@H_403_28@() {
return@H_403_28@ "Orientation"@H_403_28@;
}
@Nullable@H_403_28@
@Override@H_403_28@
public@H_403_28@ Map<String,Object> getConstants@H_403_28@() {
return@H_403_28@ null@H_403_28@;
}
}
getName方法返回的是该组件的名称(该名称可以加RCT前缀,如RCTOrientation,但JS中调用的还是没有加前缀的Orientation),在JS中供NativeModules使用;getConstants方法返回一组key/value常量属性值,可供JS中调用。具体看实现吧。
首先我们需要提供一个方法,供JS中调用设置应用的横竖屏。要让JS中能调用,需要在方法上使用ReactMethod注解。
@ReactMethod@H_403_28@
public@H_403_28@ void@H_403_28@ setOrientation@H_403_28@(final@H_403_28@ int@H_403_28@ orientation){
UiThreadUtil.runOnUiThread(new@H_403_28@ Runnable() {
@Override@H_403_28@
public@H_403_28@ void@H_403_28@ run@H_403_28@() {
Activity activity = getCurrentActivity();
if@H_403_28@(activity == null@H_403_28@){
return@H_403_28@;
}
if@H_403_28@(orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
//设置全屏@H_403_28@
Window window = activity.getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
window.setAttributes(params);
}else@H_403_28@ if@H_403_28@(orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//设置非全屏@H_403_28@
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@H_403_28@ static@H_403_28@ final@H_403_28@ String ORIENTATION_LANDSCAPE_KEY = "LANDSCAPE"@H_403_28@;//横屏@H_403_28@
private@H_403_28@ static@H_403_28@ final@H_403_28@ String ORIENTATION_PORTRAIT_KEY = "PORTRAIT"@H_403_28@;//竖屏@H_403_28@
@Nullable@H_403_28@
@Override@H_403_28@
public@H_403_28@ Map<String,Object> getConstants@H_403_28@() {//定义返回值常量@H_403_28@
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@H_403_28@ constants;
}
定义了LANDSCAPE 和 PORTRAIT两个属性常量,并分别赋予值ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE 和 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
写完了Module,我们需要再写Package。新建OrientationPackage类,并实现ReactPackage接口。
public@H_403_28@ class@H_403_28@ OrientationPackage@H_403_28@ implements@H_403_28@ ReactPackage@H_403_28@{@H_403_28@
@Override@H_403_28@
public@H_403_28@ List<NativeModule> createNativeModules@H_403_28@(ReactApplicationContext reactContext) {
return@H_403_28@ Arrays.<NativeModule>asList(
new@H_403_28@ OrientationModule(reactContext)
);
}
@Override@H_403_28@
public@H_403_28@ List<Class<? extends JavaScriptModule>> createJSModules@H_403_28@() {
return@H_403_28@ Collections.emptyList();
}
@Override@H_403_28@
public@H_403_28@ List<ViewManager> createViewManagers@H_403_28@(ReactApplicationContext reactContext) {
return@H_403_28@ Collections.emptyList();
}
}
OrientationModule是属于NativeModule,所以,我们在createNativeModules方法中返回我们已经实现了的OrientationModule。
最后,我们需要将OrientationPackage注册到react native中,让它能识别到我们的Module。
打开MainApplication类,并将OrientationPackage添加到ReactPackage
public@H_403_28@ class@H_403_28@ MainApplication@H_403_28@ extends@H_403_28@ Application@H_403_28@ implements@H_403_28@ ReactApplication@H_403_28@ {@H_403_28@
private@H_403_28@ final@H_403_28@ ReactNativeHost mReactNativeHost = new@H_403_28@ ReactNativeHost(this@H_403_28@) {
@Override@H_403_28@
protected@H_403_28@ boolean@H_403_28@ getUseDeveloperSupport@H_403_28@() {
return@H_403_28@ BuildConfig.DEBUG;
}
@Override@H_403_28@
protected@H_403_28@ List<ReactPackage> getPackages@H_403_28@() {
return@H_403_28@ Arrays.<ReactPackage>asList(
new@H_403_28@ MainReactPackage(),new@H_403_28@ OrientationPackage()
);
}
};
@Override@H_403_28@
public@H_403_28@ ReactNativeHost getReactNativeHost@H_403_28@() {
return@H_403_28@ mReactNativeHost;
}
}
在getPackages方法中增加了我们的OrientationPackage。
接下来就是在JS中使用它了。
实现对应的JS模块
新建VideoPlayScene.js,用于播放视频的场景。我们需要在应用进入该页面时,横屏显示,退出时恢复竖屏。
import React,{Component} from 'react'@H_403_28@;
import {
View,WebView,NativeModules
} from 'react-native'@H_403_28@;
var@H_403_28@ Orientation = NativeModules.Orientation;
export default@H_403_28@ class@H_403_28@ VideoPlayScene extends Component{
constructor(props){
super(props);
}
componentWillMount(){
Orientation.setOrientation(Orientation.LANDSCAPE);
}
componentWillUnmount(){
Orientation.setOrientation(Orientation.PORTRAIT);
}
}
我们需要import NativeModules这个模块
var@H_403_28@ Orientation = NativeModules.Orientation;
NativeModules点后面的Orientation就是我们在原生代码OrientationModule中getName返回的字符串值。
在componentWillMount 和 componentWillUnmount生命周期中调用setOrientation来实现需求。其中Orientation.LANDSCAPE 和 Orientation.PORTRAIT 就是我们在OrientationModule$getConstants 定义的两个常量值。
那如果在js中需要接收一个回调方法,那么原生代码需要怎么写呢?
在OrientationModule在定义一个方法,并接收一个Callback参数
@ReactMethod@H_403_28@
public@H_403_28@ void@H_403_28@ getRequestedOrientation@H_403_28@(Callback callback){
int@H_403_28@ orientation = getReactApplicationContext().getResources().getConfiguration().orientation;
callback.invoke(orientation);
}
js中调用
componentWillMount(){
Orientation.getRequestedOrientation((orientation)=>{
console.log('current orientation :'@H_403_28@+ orientation);
});
//Orientation.setOrientation(Orientation.LANDSCAPE);@H_403_28@
}
这样就完成了横竖屏功能的开发了(不好的是,自定义的Module在IDE下并不能代码提示,有知道如何让它提示的话,请告知一下哈),贴下OrientationModule.java的完整代码
public@H_403_28@ class@H_403_28@ OrientationModule@H_403_28@ extends@H_403_28@ ReactContextBaseJavaModule@H_403_28@{@H_403_28@
private@H_403_28@ static@H_403_28@ final@H_403_28@ String ORIENTATION_LANDSCAPE_KEY = "LANDSCAPE"@H_403_28@;//横屏@H_403_28@
private@H_403_28@ static@H_403_28@ final@H_403_28@ String ORIENTATION_PORTRAIT_KEY = "PORTRAIT"@H_403_28@;//竖屏@H_403_28@
public@H_403_28@ OrientationModule@H_403_28@(ReactApplicationContext reactContext) {
super@H_403_28@(reactContext);
}
@Override@H_403_28@
public@H_403_28@ String getName@H_403_28@() {
return@H_403_28@ "Orientation"@H_403_28@;
}
@Nullable@H_403_28@
@Override@H_403_28@
public@H_403_28@ Map<String,Object> getConstants@H_403_28@() {//定义返回值@H_403_28@
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"@H_403_28@,getReactApplicationContext().getResources().getConfiguration().orientation);
return@H_403_28@ constants;
}
@ReactMethod@H_403_28@
public@H_403_28@ void@H_403_28@ setOrientation@H_403_28@(final@H_403_28@ int@H_403_28@ orientation){
UiThreadUtil.runOnUiThread(new@H_403_28@ Runnable() {
@Override@H_403_28@
public@H_403_28@ void@H_403_28@ run@H_403_28@() {
Activity activity = getCurrentActivity();
if@H_403_28@(activity == null@H_403_28@){
return@H_403_28@;
}
if@H_403_28@(orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
//设置全屏@H_403_28@
Window window = activity.getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
window.setAttributes(params);
}else@H_403_28@ if@H_403_28@(orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//设置非全屏@H_403_28@
Window window = activity.getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
window.setAttributes(params);
}
}
});
}
@ReactMethod@H_403_28@
public@H_403_28@ void@H_403_28@ getRequestedOrientation@H_403_28@(Callback callback){
int@H_403_28@ orientation = getReactApplicationContext().getResources().getConfiguration().orientation;
callback.invoke(orientation);
}
}
总结
本节我们完成了android的自定义模块,使RN能够使用原生提供的能力。而关于js如何向native发送命令以及native如何向js发送事件的问题,原生模块和原生UI组件它们的通信都是一样的,所以我们把这个问题留在下一节来讲述。