基本介绍:
混合跨平台技术框架有多种,如web开发、Hybrid开发、React Native、Flutter,此处使用Hybrid实现web与原生的混合开发。
原生与web之间需要搭建通道,才能进行通信,此处用到dsBridge框架实现两者的数据通信,以短视频播放为示例讲解混合开发Hybrid的运用。
慕课网学习视屏地址:https://www.imooc.com/video/22050 ---- 我也是看视屏学习的,但没有源码
dsBridge的使用:
先学习dsBridge的基本使用,实现两者的通信是本次案例的重点。
两端的官方使用文档地址中,详细讲述了dsBridge的集成与基本使用。
dsBridge-android端官方使用文档地址:https://github.com/wendux/DSBridge-Android/blob/master/readme-chs.md
dsBridge-IOS端官方使用文档地址:https://github.com/wendux/DSBridge-IOS
一、dsBridge的集成
① 本地依赖
将文件路径为 https://github.com/wendux/DSBridge-Android/tree/master/dsbridge 下载出来,去掉npm,作为module引入项目中。(npm文件夹中放的事dsBridge.js)
② 仓库依赖
添加 JitPack repository 到gradle脚本中
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
//compile 'com.github.wendux:DSBridge-Android:3.0-SNAPSHOT'
//support the x5 browser core of tencent
//compile 'com.github.wendux:DSBridge-Android:x5-3.0-SNAPSHOT'
}
短视频播放案例代码:
目录结构:
一、H5页面
<!DOCTYPE html>
<html>
<!--
简体中文页面:html lang=zh-cmn-Hans
繁体中文页面:html lang=zh-cmn-Hant
英语页面:html lang=en -->
<head lang="zh-cmn-Hans">
<!-- 兼容搜素引擎 webkit -->
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome">
<!--
width:可视区域的宽度,值可为数字或关键词device-width
height:同width
intial-scale:页面首次被显示是可视区域的缩放级别,取值1.0则页面按实际尺寸显示,无任何缩放
maximum-scale=1.0, minimum-scale=1.0;可视区域的缩放级别,
maximum-scale用户可将页面放大的程序,1.0将禁止用户放大到实际尺寸之上。
user-scalable:是否可对页面进行缩放,no 禁止缩放 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<script src="dsbridge.js"></script>
</head>
<style>
*{padding 0; margin:0}
.poster{
position : relative;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: flex-start;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
object-fit: contain;
z-index: 2;
}
.button{
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
position: absolute;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
border-style: solid;
z-index: 3;
}
.btn{
position: relative;
text-align: center;
background: #D8D8D8;
color: #222;
padding: 20px;
margin: 70px;
font-size: 24px;
border-radius: 4px;
box-shadow: 4px 2px 10px #999;
}
.btn:active{
opacity: .7;
box-shadow: 4px 2px 10px #555;
}
</style>
<body>
<div id="play" class="poster">
<img src="poster.jpg" width="100%" width="auto" height="auto"/>
<div class="button" onclick="play()">
<img class="playImg" src="play.png" />
</div>
</div>
<div class="btn" onclick="pause()">暂停播放</div>
<div class="btn" onclick="resume()">继续播放</div>
<div class="btn" onclick="seekForward()">前进10秒</div>
<div class="btn" onclick="seekBack()">后退10秒</div>
<div class="btn" onclick="fullScreen()">全屏</div>
<div class="btn" onclick="exitFullScreen()">退出全屏</div>
</body>
<script>
function play(){
//url old : http://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4
var videoInfo = "{\n" +
"\t\"url\": \"http://vfx.mtime.cn/Video/2019/03/21/mp4/190321153853126488.mp4\",\n"+
"\t\"width\": 1080,\n"+
"\t\"height\": 750\n"+
"}"
bridge.call("play",videoInfo,function(v){
alert(v);
// document.getElementById("play").style.display= "none";
});
}
function pause(){
bridge.call("pause","test syn call",function(v){
//document.getElementById("play").style.display= "block";
alert(v);
});
}
function resume(){
bridge.call("resume","test asyn call",function(v){
alert(v);
});
}
function seekForward(){
bridge.call("seek","10",function(v){
alert(v);
});
}
function seekBack(){
bridge.call("seek","-10",function(v){
alert(v);
});
}
function fullScreen(){
bridge.call("fullScreen","test syn call",function(v){
alert(v);
});
}
function exitFullScreen(){
bridge.call("exitFullScreen","test syn call",function(v){
alert(v);
});
}
</script>
</html>
二、H5与Android绑定
MainActivity.java
package com.xyhe.shortvideo;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import wendu.dsbridge.DWebView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final DWebView dWebView = findViewById(R.id.dWebView);
//将WebView与Html页面进行绑定
dWebView.addJavascriptObject(new VideoApi(this), "");
dWebView.loadUrl("file:///android_asset/video.html");
dWebView.setWebContentsDebuggingEnabled(true);
getWindow().setFormat(PixelFormat.TRANSLUCENT);//设置半透明
}
@Override
public void onBackPressed() {
if(!VideoApi.onBackPressed()){
super.onBackPressed();
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<wendu.dsbridge.DWebView
android:id="@+id/dWebView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
三、用于h5使用的方法
VideoApi.java
package com.xyhe.shortvideo;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.widget.FrameLayout;
import org.json.JSONObject;
import wendu.dsbridge.CompletionHandler;
public class VideoApi {
private Context mContext;
private boolean mIsAttach = false;//是否在播放
private static VideoPlayerView mVideoPlay;
public VideoApi(Context context){
mContext = context;
mVideoPlay = new VideoPlayerView(mContext);
}
/**
* 请求播放视屏,并且添加VideoView到当前View中
*/
@JavascriptInterface
public String play(Object msg){
if(mIsAttach){
return "video already play.";
}
String url = "";
int width = 0;
int height = 0;
try{
JSONObject videoInfo = new JSONObject((String)msg);
url = videoInfo.getString("url");
width = videoInfo.getInt("width");
height = videoInfo.getInt("height");
}catch (Exception e){
}
//将VideoPlay视图添加到View中
final Activity acticity = (Activity)mContext;
final int finalWidth = width;
final int finalHeight = height;
final String finalUrl = url;
acticity.runOnUiThread(new Runnable() {
@Override
public void run() {
FrameLayout frameLayout = acticity.findViewById(R.id.frameLayout);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(finalWidth, finalHeight);
frameLayout.addView(mVideoPlay, params);
mIsAttach = true;
mVideoPlay.play(finalUrl);
}
});
return "play success";
}
/**
* 暂停播放
* @param msg
* @return
*/
@JavascriptInterface
public String pause(Object msg){
if(mVideoPlay!=null){
mVideoPlay.pause();
}
return "call pause success";
}
/**
* 继续播放
* @param msg
* @return
*/
@JavascriptInterface
public String resume(Object msg){
if(mVideoPlay!=null){
mVideoPlay.resume();
}
return "call resume success";
}
/**
* 后退或前进
* @param msg
* @return
*/
@JavascriptInterface
public String seek(Object msg){
if(mVideoPlay!=null){
mVideoPlay.seek(Integer.valueOf((String)msg));
}
return "call seek success";
}
/**
* 全屏
* @param msg
* @return
*/
@JavascriptInterface
public String fullScreen(Object msg){
if(mVideoPlay!=null){
mVideoPlay.fullScreen();
}
return "call fullScreen success";
}
/**
* 退出全屏
* @param msg
* @return
*/
@JavascriptInterface
public String exitFullScreen(Object msg){
if(mVideoPlay!=null){
mVideoPlay.exitFullScreen();
}
return "call exitFullScreen success";
}
/**
* android返回按钮:先退出全屏
* @return
*/
public static boolean onBackPressed() {
if(mVideoPlay!=null){
return mVideoPlay.onBack();
}
return false;
}
}
四、重写播放器控件
VideoPlayView.java
package com.xyhe.shortvideo;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.media.MediaPlayer;
import android.util.Log;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.MediaController;
import android.widget.VideoView;
/**
*客户端播放器
*/
public class VideoPlayerView extends FrameLayout {
private VideoView mVideoView;
private boolean mIsFull = false;//是否全屏
private int lastWidth;//全屏圈的宽
private int lastHeight;//全屏圈的高
public VideoPlayerView(Context context) {
super(context);
}
/**
* 播放视频
* @param url
*/
public void play(String url){
mVideoView = new VideoView(getContext());
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
addView(mVideoView,params);
MediaController controller = new MediaController(getContext());
mVideoView.setMediaController(controller);
controller.setAnchorView(mVideoView);
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
Log.v("VIDEO","onPrepare()");
}
});
mVideoView.setVideoPath(url);
mVideoView.start();
}
/**
* 暂停播放
*/
public void pause(){
if(mVideoView!=null){
mVideoView.pause();
}
}
/**
* 继续播放
*/
public void resume(){
if(mVideoView!=null){
mVideoView.start();
}
}
/**
* 前进或倒退几秒
* @param second
*/
public void seek(int second){
if(mVideoView!=null){
mVideoView.seekTo(mVideoView.getCurrentPosition()+second*1000);
}
}
/**
* 获取当前播放进度
* @return
*/
public int getCurrentPosition(){
if(mVideoView!=null){
return mVideoView.getCurrentPosition();
}
return 0;
}
/**
* 全屏
*/
public void fullScreen(){
if(mIsFull){
return;
}
final Activity activity = (Activity) getContext();
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
//做成横屏,并且全屏
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
//留存现有的宽和高
ViewGroup.LayoutParams params = mVideoView.getLayoutParams();
lastWidth = params.width;
lastHeight = params.height;
//设置全屏宽高度
params.width = LayoutParams.MATCH_PARENT;
params.height = LayoutParams.MATCH_PARENT;
setLayoutParams(params);
mIsFull = true;
}
});
}
/**
* 退出全屏
*/
public void exitFullScreen(){
if(!mIsFull){
return;
}
final Activity activity = (Activity) getContext();
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
//做成竖屏
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
//设置全屏宽高度
ViewGroup.LayoutParams params = getLayoutParams();
params.width = lastWidth;
params.height = lastHeight;
setLayoutParams(params);
mIsFull = false;
}
});
}
/**
* 返回按钮:先退出全屏
* @return
*/
public boolean onBack() {
if(mIsFull){
exitFullScreen();
return true;
}
return false;
}
}
总结:
代码粘起来就几分钟,但是所有代码都是看视屏一点点写出来的。
这个案例重在将H5和原生开发啊(Android或IOS)结合起来,算是打开了一扇新的门。技术永远在进步,学海无涯,继续加油,我去开启下一个课程学习了。