本文讲解的是flutter与android原生通信,调用视屏播放和音乐播放的功能,如果要使用音乐播放那么肯定是需要用到网络资源的,在写代码之前先将权限添加,添加了这些权限即可以使用网络资源也可以使用本地资源,但是要使用网络资源还需要添加另外的资源
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />读取权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 写入权限
<uses-permission android:name="android.permission.INTERNET" /> 网络权限
在res文件下下添加xml文件夹,再在里面添加一个network_security_config.xml 资源文件,应用程序不访问有些网络资源比如https的资源他使用不了,所以需要添加这个文件,它是用来配置网络安全策略,然后在AndroidManifest.xml中的Application里面将这个资源文件添加到应用程序当中,这样就能使用所有网络资源啦
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true"/>
</network-security-config>
<application
android:icon="@mipmap/ic_launcher"
android:label="flutter_android"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/Theme.AppCompat.NoActionBar">
接下来开始写代码
首先是视屏播放
需要写原生代码,使用VideoView这个控件来展示播放视频
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:background="@android:color/white"
tools:context=".PlayVideoActivity"
android:layout_width="match_parent"
android:layout_height="match_parent">
<VideoView
android:id="@+id/mVideoViewLand"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
然后再写java代码 ,代码中已经详细注释了说明,这边就不过多赘述
package com.example.flutter_android;
import android.content.Intent;
import android.content.res.Configuration;
import android.hardware.SensorManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.view.OrientationEventListener;
import android.widget.MediaController;
import android.widget.VideoView;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
public class PlayVideoActivity extends FlutterActivity {
private VideoView mVideoView;
private MediaController mediaController;
private String path;
private OrientationEventListener orientationEventListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
path = getIntent().getStringExtra("path");
if(mediaController == null){
initView();
}
isOrientation();
}
private void isOrientation(){
则合格是用来判断手机的横竖屏的
int currentOrientation = getResources().getConfiguration().orientation;
if(currentOrientation == Configuration.ORIENTATION_PORTRAIT){
setContentView(R.layout.activiti_play_video_land);
mVideoView = findViewById(R.id.mVideoViewLand);
initData();
System.out.println("竖屏");
}else{
setContentView(R.layout.activity_play_video);
mVideoView = findViewById(R.id.mVideoView);
initData();
System.out.println("横屏");
}
}
//当屏幕方向发生改变的时候调用这个方法
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
setContentView(R.layout.activiti_play_video_land);
mVideoView = findViewById(R.id.mVideoViewLand);
initData();
}else{
setContentView(R.layout.activity_play_video);
mVideoView = findViewById(R.id.mVideoView);
initData();
}
}
private void initView(){
初始化控制器,用来控制视屏的播放和视屏的进度
mediaController = new MediaController(this);
}
private void initData() {
在视屏播放器中将播放控制器添加进去
mVideoView.setMediaController(mediaController);
//这个是使用本地资源视屏
Uri videoUri = Uri.parse("android.resource://" + getPackageName() + "/raw/play1");
mVideoView.setVideoURI(videoUri);
//如果想使用网络资源那么将上方代码替换成下方代码
mVideoView.setVideoPath(path);
//监听播放是否完成
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mVideoView.suspend();
backResult("1");
}
});
mVideoView.start();
}
//结果返回的方法
private void backResult(String result){
Intent intent = new Intent();
intent.putExtra("result",result);
setResult(RESULT_OK,intent);
finish();
}
//当意外返回的时候将自动调用backResult将返回值设置为0表示播放未完成
@Override
protected void onDestroy() {
mVideoView.suspend();
backResult("0");
super.onDestroy();
}
}
当进入之后屏幕方向发生改变的时候会重新更新Activitiy,那么就会退回到flutter页面中,我想要的效果是当切换屏幕方向时,只需要切换到另一个xml文件视图,这样的话就需要在AndroidManifest再添加一行代码
configChanges="orientation | screenSize" 告诉系统不要销毁当前活动,而是自行处理配置更改。
<activity
android:name=".PlayVideoActivity"
android:configChanges="orientation|screenSize"
android:exported="false" />
调用音乐播放功能
这是界面代码,用来展示音乐播放的界面,当中写了一个动画,用来当音乐播放时,图片进行旋转,控件的作用已在代码中注释
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical"
tools:context=".PlayMusic">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="24dp"
android:layout_marginTop="40dp"
android:textColor="@android:color/black"
android:text="标题"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="16dp"
android:textColorHint="@android:color/darker_gray"
android:textColor="@android:color/black"
android:text="歌手"/>
//将音乐图片设置为原型
<androidx.cardview.widget.CardView
android:layout_height="200dp"
android:layout_width="200dp"
android:layout_gravity="center"
app:cardCornerRadius="100dp"
app:cardElevation="50dp"
android:layout_marginVertical="20dp">
<ImageView
android:id="@+id/mImgView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@mipmap/music_logo"
android:scaleType="fitXY"/>
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
//展示播放视屏的当前进度
<TextView
android:id="@+id/mPosition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="16dp"
android:textColorHint="@android:color/black"
android:textColor="@android:color/black"
android:text="歌手"/>
//用来控制播放视屏的进度
<SeekBar
android:id="@+id/mSeekBar"
android:layout_width="300dp"
style="?android:attr/progressBarStyleHorizontal"
android:layout_gravity="center"
android:layout_height="wrap_content">
</SeekBar>
//视屏播放的最大进度
<TextView
android:id="@+id/mDuration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="16dp"
android:textColorHint="@android:color/black"
android:textColor="@android:color/black"
android:text="00:00"/>
</LinearLayout>
用来控制播放或暂停
<ImageButton
android:layout_marginTop="20dp"
android:id="@+id/mImgBtn"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitXY"
android:src="@mipmap/play_icon"
android:background="@android:color/white"
android:layout_gravity="center"/>
</LinearLayout>
现在开始写java代码,同样的以便于理解我将说明都写在了代码注释上
package com.example.flutter_android;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.MediaController;
import android.widget.SeekBar;
import android.widget.TextView;
import java.util.Locale;
public class PlayMusic extends AppCompatActivity {
private TextView mPosition;
private TextView mDuration;
private SeekBar mSeekBar;
private ImageView mImgView;
private ImageButton mImgBtn;
private MediaPlayer mediaPlayer;
private ObjectAnimator rotationAnimator;
private Handler handler;
private Runnable mRunnable;
private long runTime;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_music);
initView();
}
private void initView() {
//初始化通过查找id获取界面中的控件
mPosition = findViewById(R.id.mPosition);
mDuration = findViewById(R.id.mDuration);
mSeekBar = findViewById(R.id.mSeekBar);
mImgView = findViewById(R.id.mImgView);
mImgBtn = findViewById(R.id.mImgBtn);
handler = new Handler();//初始化线程通信, 他是用来重复使用一个线程更新ui
mediaPlayer = MediaPlayer.create(this,R.raw.music2); //初始化音乐播放控件,资源文件选择本地
如果想要使用网络资源那么将以上代码替换成下方代码
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource("path"); //将网络地址放入
mediaPlayer.prepareAsync(); //异步在后台进行准备工作,这样可以避免在网络请求过程中主线程被阻塞,提高流畅性。
}catch (Exception e){
e.printStackTrace();
}
//添加播放按钮事件
mImgBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
playMusic();
}
});
//监听设置好资源后初始化数据,直接播放
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mDuration.setText(formatTime(mp.getDuration()));
mSeekBar.setMax(mp.getDuration());
handler.post(mRunnable); //将代码块放入主线程队列中
playMusic(); //播放
}
});
//播放完成监听
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
backResult();
}
});
//监听进度条修改时进行的操作
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
//当进度条被改变时
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
mPosition.setText(formatTime(progress));
}
//当用户拉动进度条时
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mPosition.setText(formatTime(seekBar.getProgress()));
mediaPlayer.pause();
//由于动画会重新播放,所以保存上一次动画的进度
runTime = rotationAnimator.getCurrentPlayTime();
rotationAnimator.cancel();
mImgBtn.setImageResource(R.mipmap.play_icon);
}
//当拉动进度条完成时
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mediaPlayer.seekTo(seekBar.getProgress());
mediaPlayer.start();
mImgBtn.setImageResource(R.mipmap.pause_icon);
setImgView();
}
});
将需要在单独线程中执行的代码逻辑放在其中
mRunnable = new Runnable() {
@Override
public void run() {
if(mediaPlayer.isPlaying()){
mSeekBar.setProgress(mediaPlayer.getCurrentPosition());
}
//每隔一秒执行当前代码块
handler.postDelayed(this,1000);
}
};
}
private void backResult() {
setResult(RESULT_OK);
handler.removeCallbacks(mRunnable);
rotationAnimator.cancel();
mediaPlayer.release();
finish();
}
//用来处理时间返回字符串
private String formatTime(int duration) {
int minute = (duration / (1000 * 60)) % 60;
int seconds = (duration / 1000) % 60;
return String.format(
Locale.getDefault(),
"%02d:%02d",minute,seconds
);
}
private void playMusic() {
//当歌曲正在播放时进行的操作
if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
//这个变量在进度条监听的时候讲过
runTime = rotationAnimator.getCurrentPlayTime();
rotationAnimator.cancel();
mImgBtn.setImageResource(R.mipmap.play_icon);
}else{
mediaPlayer.start();
mImgBtn.setImageResource(R.mipmap.pause_icon);
setImgView();
}
}
@Override
protected void onDestroy() {
backResult();
super.onDestroy();
}
private void setImgView() {
rotationAnimator = ObjectAnimator.ofFloat(mImgView,"rotation",0f,360f);
rotationAnimator.setDuration(10000);
rotationAnimator.setRepeatCount(ValueAnimator.INFINITE);
rotationAnimator.setInterpolator(new LinearInterpolator());
rotationAnimator.setCurrentPlayTime(runTime);
rotationAnimator.start();
}
}