视频放在assets文件下,这里的文件不会被编辑,但是使用的时候,需要用流转到本地,然后再使用
关于视频的相关测试数据,在文章最后
需要权限:
目录结构:
代码:
1、准备工作:
1.1、
import android.os.Environment; /** * 全局变量 */ public class CHEN { // 引导视频存放路径 public static String GuideVideoSavePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/chen/guide_video/"; // 引导视频播放路径 public static String GuideVideoPlayPath = ""; }
1.2、工具类
import android.text.TextUtils; public class Utils { /** * 检查字符串是否是空 * * @param str * @return true:字符串为空 */ public static boolean checkStringIsEmpty(String str) { if (TextUtils.isEmpty(str) || "null".equals(str)) { return true; } else { return false; } } }
1.3、styles.xml文件下:这个属性给展示视频的activity,让视频全屏播放
2、自定义的videoview(保证能全屏的videoview)
import android.content.Context; import android.util.AttributeSet; import android.widget.VideoView; public class CustomVideoView extends VideoView { private Context context; public CustomVideoView(Context context) { this(context, null); } public CustomVideoView(Context context, AttributeSet attrs) { this(context, attrs, -1); } public CustomVideoView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); this.context = context; init(); } private void init() { //do something... } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //重新计算高度。保证全屏显示 int width = getDefaultSize(0, widthMeasureSpec); int height = getDefaultSize(0, heightMeasureSpec); setMeasuredDimension(width, height); } }
3、使用:
3.1、一开始的MainActivity,布局文件我不做任何处理,就用新建时默认的就行,核心是下一个展示视频的activity
import android.app.Activity; import android.content.Intent; import android.content.res.AssetManager; import android.os.Bundle; import android.os.Handler; import android.util.Log; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; public class MainActivity extends Activity { private int time = 3000; public Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case 100: if (!Utils.checkStringIsEmpty(CHEN.GuideVideoPlayPath)) { //播放视频成功生成了 startActivity(new Intent(MainActivity.this, VideoGuideActivity.class)); MainActivity.this.finish(); } else { //引导视频没有生成 //do something... } break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); creatGuideVideo(); //sendEmptyMessageDelayed(int what, long delayMillis) //延时time,发送一个what=100的值 mHandler.sendEmptyMessageDelayed(100, time); } /** * 创建视频引导页文件 */ private void creatGuideVideo() { new Thread() { public void run() { try { File path_file = new File(CHEN.GuideVideoSavePath); if (!path_file.exists()) { try { path_file.mkdirs(); } catch (Exception e) { CHEN.GuideVideoPlayPath = ""; } } long start = System.currentTimeMillis(); //第二个参数是生成的文件的名字(包括后缀名),一般和assets文件中要打开的一致 File file = new File(CHEN.GuideVideoSavePath, "../201510/GridViewLiveTiles.html"); if (!file.exists()) { AssetManager assetManager = getAssets();// 获取asset管理者 InputStream in = null; FileOutputStream out = null; try { //assets文件中要打开的文件的名字(包括后缀名) in = assetManager.open("../201510/GridViewLiveTiles.html"); out = new FileOutputStream(file); byte[] b = new byte[1024]; int len = -1; while ((len = in.read(b)) != -1) { out.write(b, 0, len); } } catch (IOException e) { e.printStackTrace(); CHEN.GuideVideoPlayPath = ""; } finally { if (out != null) { out.close(); } if (in != null) { in.close(); } } } long end = System.currentTimeMillis(); Log.e("创建视频引导页播放视频用时===", "" + (end - start)); CHEN.GuideVideoPlayPath = file.getAbsolutePath(); } catch (Exception e) { CHEN.GuideVideoPlayPath = ""; } } }.start(); } }
3.2、VideoGuideActivity(为了保证全屏,在清单文件中)
import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; /** * 视频引导页 */ public class VideoGuideActivity extends Activity { CustomVideoView video_view; private int videoIndex = -1; /** * 监听是否点击了home键将客户端推到后台 */ private BroadcastReceiver homeAndLockReceiver = new BroadcastReceiver() { String SYSTEM_REASON = "reason"; String SYSTEM_HOME_KEY = "homekey"; @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) { String reason = intent.getStringExtra(SYSTEM_REASON); if (TextUtils.equals(reason, SYSTEM_HOME_KEY)) { //表示按了home键,程序到了后台 video_view.pause(); //得到当前视频播放的位置。单位是毫秒 videoIndex = video_view.getCurrentPosition(); Log.e("videoIndex", videoIndex + ""); } } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { //按下锁屏键 video_view.pause(); //得到当前视频播放的位置。单位是毫秒 videoIndex = video_view.getCurrentPosition(); Log.e("videoIndex", videoIndex + ""); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.video_guide_layout); try { //注册广播,监听home键及锁屏键的按下 IntentFilter filter = new IntentFilter(); //锁屏广播,由系统发出 filter.addAction(Intent.ACTION_SCREEN_ON); //锁屏广播,由系统发出 filter.addAction(Intent.ACTION_SCREEN_OFF); //点击home键广播,由系统发出 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); registerReceiver(homeAndLockReceiver, filter); //注册广播结束 video_view = (CustomVideoView) findViewById(R.id.video_view); /** * 视频或者音频到结尾时触发的方法 */ video_view.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { doSomething(); } }); /** * 视频或者音频播放出错触发的方法 */ video_view.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { doSomething(); return false; } }); video_view.setVideoURI(Uri.parse(CHEN.GuideVideoPlayPath)); //播放 video_view.start(); } catch (Exception e) { doSomething(); } } @Override protected void onRestart() { super.onRestart(); Log.e("onRestart", videoIndex + ""); //video_view.start(); video_view.seekTo(videoIndex == -1 ? 0 : videoIndex); //先start,再seekTo,home键回来是重播;先seekTo,再start,都是续播。但是如果距离开始时间较短(经测试,大约10以内,可能有误差),还是会出现重播现象。这些细节,在用的时候,多测试测试。对效果影响不大 video_view.start(); } @Override protected void onDestroy() { super.onDestroy(); if (homeAndLockReceiver != null) { unregisterReceiver(homeAndLockReceiver); } } //对返回键的监听 @Override public void onBackPressed() { super.onBackPressed(); doSomething(); } /** * 播放失败、异常时做的事。一般都是跳转到下个页面 */ private void doSomething() { //do something... Toast.makeText(this, "do something...", Toast.LENGTH_SHORT).show(); } }
4、测试数据:
一般的使用视频引导页,之前还会有一个图片启动页,最好在图启动页展示的时候,在本地创建视频,然后在视频引导页直接展示。时间上有要求,既要展示启动页,还要尽可能的给本地生成视频文件留下时间:
我测试了3组数据(单位毫秒):
assets中视频文件大小为5.6M时: 创建视频引导页播放视频用时===: 570 assets中视频文件大小为6.6M时: 创建视频引导页播放视频用时===: 648 assets中视频文件大小为16M时: 创建视频引导页播放视频用时===: 1697
注:我使用的是小米Note,我再使用魅族系列的一个低配手机的时候,出现过一次6.6M视频,在本地创建,时间为2700毫秒左右的情况。同一个手机,每次创建也会有差距,以上数据仅供参考