【001】【视频开发】ijkplayer
1.将ijkplayer的引入








【声明全局的编译环境的指定】


【生成类库】

【拷贝example下的wiget下的media文件夹】

【拷贝文件】


【暂时不使用exoplayer-删除】

【拷贝string字符串及stringPref文件】


【代码块编译gradle编译<--没有问题之后拷贝.so】


【引入权限】

【添加控件】
、
【初始化】




【说明】后续还会有一些文件的缺失,自行拷贝即可。
2. progressBar的引入
【源码】【video_loading.xml】
1 <rotate xmlns:android="http://schemas.android.com/apk/res/android" 2 android:fromDegrees="0" 3 android:toDegrees="1080" 4 android:pivotX="50%" 5 android:pivotY="50%"> 6 7 <!-- android:innerRadiusRatio="3" 表示内环半径 --> 8 <!-- android:thicknessRatio="10" 表示环厚度半径 --> 9 <!-- android:useLevel="false" 表示有渐变--> 10 11 <shape 12 android:shape="ring" 13 android:innerRadiusRatio="3" 14 android:thicknessRatio="10" 15 android:useLevel="false"> 16 17 <!-- gradient 梯度--> 18 <!-- android:centerY="0.5" 表示渐变中心y的相对位置--> 19 <!-- android:type="sweep" sweep表示扫描式, linear表示线性, radial辐射式--> 20 <gradient 21 android:startColor="@color/loading_start_color" 22 android:centerColor="@color/loading_center_color" 23 android:endColor="@color/loading_end_color" 24 android:centerY="0.5" 25 android:useLevel="false" 26 android:type="sweep"/> 27 28 </shape> 29 30 </rotate>
【activity_play.xml】
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:id="@+id/activity_play" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:background="@android:color/black" //+++++++ 8 android:gravity="center" //+++++++ 9 android:keepScreenOn="true"> //+++++++ 播放不熄灭屏幕 10 11 <FrameLayout //+++++++ 12 android:id="@+id/fl_surface_container" 13 android:layout_width="match_parent" 14 android:layout_height="match_parent"> 15 16 <com.hejunlin.imooc_supervideo.widget.media.IjkVideoView 17 android:id="@+id/video_view" 18 android:layout_width="match_parent" 19 android:layout_height="match_parent" 20 android:layout_gravity="center"/> 21 22 //加载转圈的动画 23 <RelativeLayout 24 android:id="@+id/rl_loading_layout" 25 android:layout_width="match_parent" 26 android:layout_height="match_parent"> 27 28 <TextView 29 android:id="@+id/tv_loading_info" 30 android:layout_width="wrap_content" 31 android:layout_height="wrap_content" 32 android:layout_below="@+id/pb_loading" 33 android:layout_centerInParent="true" 34 android:layout_marginTop="@dimen/dimen_12dp" 35 android:textSize="@dimen/dimen_32sp" 36 android:textColor="@color/white"/> 37 38 <!--android:indeterminate 表示不确定的进度--> 39 <ProgressBar 40 android:id="@+id/pb_loading" 41 android:layout_width="@dimen/dimen_120dp" 42 android:layout_height="@dimen/dimen_120dp" 43 android:layout_centerInParent="true" 44 android:indeterminate="false" 45 android:indeterminateDrawable="@drawable/video_loading" 46 android:padding="@dimen/dimen_10dp" 47 android:layout_marginTop="@dimen/dimen_120dp"/> 48 49 50 </RelativeLayout> 51 52 <TextView 53 android:id="@+id/tv_horiontal_gesture" 54 android:layout_width="match_parent" 55 android:layout_height="wrap_content" 56 android:shadowDx="1.0" 57 android:shadowDy="1.0" 58 android:shadowColor="@color/black" 59 android:textColor="@color/white" 60 android:textSize="@dimen/dimen_64sp" 61 android:visibility="gone" 62 android:textStyle="bold" 63 android:gravity="center"/> 64 65 <TextView 66 android:id="@+id/tv_vertical_gesture" 67 android:layout_width="wrap_content" 68 android:layout_height="wrap_content" 69 android:layout_gravity="center" 70 android:gravity="center_horizontal" 71 android:background="@drawable/black_bg" 72 android:paddingLeft="@dimen/dimen_100dp" 73 android:paddingRight="@dimen/dimen_100dp" 74 android:paddingTop="@dimen/dimen_40dp" 75 android:paddingBottom="@dimen/dimen_40dp" 76 android:textSize="@dimen/dimen_64sp" 77 android:textStyle="bold" 78 android:textColor="@color/white" 79 android:visibility="gone"/> 80 155 224 </RelativeLayout>

3.播放功能的添加
【播放】


【运行点击播放】


【bug】
原因是没有将InfoHodlerVideo移除掉,只移除了一部分


【解决办法】将InfoHodlerVideo所有源码注释掉



4.设置为启动进入之后是横屏

5.progressBar 在正常播放之后消失
【问题】

【PlayActivity.java】
1 @Override 2 protected void initView() { 3 ...... 4 IjkMediaPlayer.native_profileBegin("libijkplayer.so"); 5 mLoadingLayout = bindViewId(R.id.rl_loading_layout); 6 mLoadingText = bindViewId(R.id.tv_loading_info); 7 mLoadingText.setText("正在加载中..."); 8 mVideoView.setVideoURI(Uri.parse(mUrl)); 9 mVideoView.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() { 10 @Override 11 public void onPrepared(IMediaPlayer mp) { 12 mVideoView.start(); 13 } 14 }); 15 //设置监听 16 mVideoView.setOnInfoListener(new IMediaPlayer.OnInfoListener() { 17 @Override 18 public boolean onInfo(IMediaPlayer mp, int what, int extra) { 19 switch (what) { 20 case IjkMediaPlayer.MEDIA_INFO_BUFFERING_START: //开始加载,则loading显示 21 mLoadingLayout.setVisibility(View.VISIBLE); 22 break; 23 case IjkMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START://开始播放或者是缓存结束,则loading不显示 24 case IjkMediaPlayer.MEDIA_INFO_BUFFERING_END: 25 mLoadingLayout.setVisibility(View.GONE); 26 break; 27 } 28 return false; 29 } 30 }); 31 ............ 32 }
【测试】在自己播放之后自己横屏显示,并且loading动画消失

6.mediaplayer状态机
【说明】必须学习的内容
【箭头】表示工作流的工作的过程;
【椭圆】表示状态;
【箭头旁边的方法】执行此方法才能到达的状态;

7.播放的流程图

8.上下panle的布局添加

1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:id="@+id/activity_play" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:background="@android:color/black" //+++++++ 8 android:gravity="center" //+++++++ 9 android:keepScreenOn="true"> //+++++++ 10 11 <FrameLayout //+++++++ 12 android:id="@+id/fl_surface_container" 13 android:layout_width="match_parent" 14 android:layout_height="match_parent"> 15 16 <com.hejunlin.imooc_supervideo.widget.media.IjkVideoView 17 android:id="@+id/video_view" 18 android:layout_width="match_parent" 19 android:layout_height="match_parent" 20 android:layout_gravity="center"/> 21 22 //加载转圈的动画 23 <RelativeLayout 24 android:id="@+id/rl_loading_layout" 25 android:layout_width="match_parent" 26 android:layout_height="match_parent"> 27 28 <TextView 29 android:id="@+id/tv_loading_info" 30 android:layout_width="wrap_content" 31 android:layout_height="wrap_content" 32 android:layout_below="@+id/pb_loading" 33 android:layout_centerInParent="true" 34 android:layout_marginTop="@dimen/dimen_12dp" 35 android:textSize="@dimen/dimen_32sp" 36 android:textColor="@color/white"/> 37 38 <!--android:indeterminate 表示不确定的进度--> 39 <ProgressBar 40 android:id="@+id/pb_loading" 41 android:layout_width="@dimen/dimen_120dp" 42 android:layout_height="@dimen/dimen_120dp" 43 android:layout_centerInParent="true" 44 android:indeterminate="false" 45 android:indeterminateDrawable="@drawable/video_loading" 46 android:padding="@dimen/dimen_10dp" 47 android:layout_marginTop="@dimen/dimen_120dp"/> 48 49 50 </RelativeLayout> 51 52 <TextView 53 android:id="@+id/tv_horiontal_gesture" 54 android:layout_width="match_parent" 55 android:layout_height="wrap_content" 56 android:shadowDx="1.0" 57 android:shadowDy="1.0" 58 android:shadowColor="@color/black" 59 android:textColor="@color/white" 60 android:textSize="@dimen/dimen_64sp" 61 android:visibility="gone" 62 android:textStyle="bold" 63 android:gravity="center"/> 64 65 <TextView 66 android:id="@+id/tv_vertical_gesture" 67 android:layout_width="wrap_content" 68 android:layout_height="wrap_content" 69 android:layout_gravity="center" 70 android:gravity="center_horizontal" 71 android:background="@drawable/black_bg" 72 android:paddingLeft="@dimen/dimen_100dp" 73 android:paddingRight="@dimen/dimen_100dp" 74 android:paddingTop="@dimen/dimen_40dp" 75 android:paddingBottom="@dimen/dimen_40dp" 76 android:textSize="@dimen/dimen_64sp" 77 android:textStyle="bold" 78 android:textColor="@color/white" 79 android:visibility="gone"/> 80 81 <FrameLayout //顶部相关的panel 82 android:id="@+id/fl_player_top_container" 83 android:layout_width="match_parent" 84 android:layout_height="@dimen/dimen_70dp" 85 android:layout_gravity="top" 86 android:background="@color/player_panel_background_color" 87 android:paddingTop="@dimen/dimen_10dp" 88 android:paddingBottom="@dimen/dimen_10dp"> 89 90 <LinearLayout 91 android:layout_width="wrap_content" 92 android:layout_height="wrap_content" 93 android:layout_gravity="center_vertical" 94 android:orientation="horizontal"> 95 96 <ImageView 97 android:id="@+id/iv_player_close" 98 android:layout_width="wrap_content" 99 android:layout_height="wrap_content" 100 android:paddingLeft="@dimen/dimen_15dp" 101 android:paddingRight="@dimen/dimen_15dp" 102 android:layout_gravity="center_vertical" 103 android:src="@drawable/titlebar_return_white"/> 104 105 <TextView 106 android:id="@+id/tv_player_video_name" 107 android:layout_width="wrap_content" 108 android:layout_height="wrap_content" 109 android:layout_gravity="center_vertical" 110 android:ellipsize="end" 111 android:singleLine="true" 112 android:textColor="@color/white" 113 android:textSize="@dimen/dimen_32sp"/> 114 115 </LinearLayout> 116 117 <LinearLayout 118 android:layout_width="wrap_content" 119 android:layout_height="wrap_content" 120 android:layout_gravity="right|center_vertical" 121 android:orientation="horizontal"> 122 123 <ImageView //电池电量 124 android:id="@+id/iv_battery" 125 android:layout_width="wrap_content" 126 android:layout_height="wrap_content" 127 android:layout_marginRight="@dimen/dimen_10dp" 128 android:layout_gravity="center_vertical" 129 android:background="@drawable/ic_battery_10"/> 130 131 <TextView 132 android:id="@+id/tv_sys_time" //系统时间 133 android:layout_width="wrap_content" 134 android:layout_height="wrap_content" 135 android:layout_gravity="center_vertical" 136 android:layout_marginRight="@dimen/dimen_10dp" 137 android:text="16:56" 138 android:textColor="@color/white" 139 android:textSize="@dimen/dimen_32sp"/> 140 141 142 </LinearLayout> 143 144 </FrameLayout> 145 146 <ImageView 147 android:id="@+id/iv_player_center_pause" //暂停的图标 148 android:layout_width="wrap_content" 149 android:layout_height="wrap_content" 150 android:background="@drawable/player_pause_selector" 151 android:layout_gravity="center" 152 android:visibility="gone"/> //只有在暂停的时候才出现 153 </FrameLayout> 154 155 <LinearLayout //底部相关的panel 156 android:id="@+id/ll_player_bottom_layout" 157 android:layout_width="match_parent" 158 android:layout_height="@dimen/dimen_70dp" 159 android:orientation="horizontal" 160 android:layout_alignParentBottom="true" 161 android:background="@color/player_panel_background_color" 162 android:gravity="center"> 163 164 <!--选中之后的某个状态的切换???--> 165 <CheckBox 166 android:id="@+id/cb_play_pause" 167 android:layout_width="wrap_content" 168 android:layout_height="wrap_content" 169 android:layout_marginLeft="@dimen/dimen_26dp" 170 android:button="@drawable/player_playbtn_selector" 171 android:checked="true"/> 172 173 <ImageView 174 android:id="@+id/iv_next_video" 175 android:layout_width="wrap_content" 176 android:layout_height="wrap_content" 177 android:layout_marginLeft="@dimen/dimen_10dp" 178 android:src="@drawable/panel_next_selector"/> 179 180 <!--当前播放的时间--> 181 <TextView 182 android:id="@+id/tv_current_video_time" 183 android:layout_width="wrap_content" 184 android:layout_height="wrap_content" 185 android:layout_gravity="center_vertical" 186 android:text="03:00" 187 android:textColor="@color/white" 188 android:textSize="@dimen/dimen_20sp"/> 189 190 <SeekBar 191 android:id="@+id/sb_player_seekbar" 192 android:layout_width="match_parent" 193 android:layout_height="wrap_content" 194 style="@style/playerSeekBarStyle" //指定样式,其中使用到了drawable-list 195 android:layout_gravity="center_vertical" 196 android:layout_weight="1.0" 197 android:indeterminate="false" 198 android:max="100" 199 android:progress="0" 200 android:paddingRight="@dimen/dimen_10dp" 201 android:paddingLeft="@dimen/dimen_10dp" 202 android:thumbOffset="@dimen/dimen_10dp" /> 203 <!--视频总时间--> 204 <TextView 205 android:id="@+id/tv_total_video_time" 206 android:layout_width="wrap_content" 207 android:layout_height="wrap_content" 208 android:text="15:00" 209 android:textSize="@dimen/dimen_20sp" 210 android:textColor="@color/white" 211 android:layout_marginLeft="@dimen/dimen_10dp"/> 212 213 <!--码流属性--> 214 <TextView 215 android:id="@+id/tv_bitstream" 216 android:layout_width="wrap_content" 217 android:layout_height="wrap_content" 218 android:layout_marginLeft="@dimen/dimen_10dp" 219 android:text="高清" 220 android:textColor="@color/white" 221 android:textSize="@dimen/dimen_20sp" 222 android:layout_marginRight="@dimen/dimen_10dp"/> 223 224 </LinearLayout> 225 </RelativeLayout>
9.上下panel的处理
9.1 初始化底部paneld布局
【PlayActivity.java】
1 private void initTopAndBottomView() { 2 mTopLayout = bindViewId(R.id.fl_player_top_container); 3 mBottomLayout = bindViewId(R.id.ll_player_bottom_layout); 4 mBackButton = bindViewId(R.id.iv_player_close);//返回按钮 5 mVideoNameView = bindViewId(R.id.tv_player_video_name);//video标题 6 mBatteryView = bindViewId(R.id.iv_battery); 7 mSysTimeView = bindViewId(R.id.tv_sys_time);//系统时间 8 mBigPauseButton = bindViewId(R.id.iv_player_center_pause);//屏幕中央暂停按钮 9 mPlayOrPauseButton = bindViewId(R.id.cb_play_pause);//底部播放暂停按钮 10 mVideoCurrentTime = bindViewId(R.id.tv_current_video_time);//当前播放进度 11 mVideoTotalTime = bindViewId(R.id.tv_total_video_time);//视频总时长 12 mBitStreamView = bindViewId(R.id.tv_bitstream);//码流 13 mSeekBar = bindViewId(R.id.sb_player_seekbar); 14 mSeekBar.setMax(1000); 15 mSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener); 16 mFormatterBuilder = new StringBuilder(); 17 mFormatter = new Formatter(mFormatterBuilder,Locale.getDefault()); 18 }
9.2 初始化listener
【说明】主要是对按钮事件的监听设置,此处遗留了handlePlayPause()方法
1 private void initListener() { 2 mBackButton.setOnClickListener(new View.OnClickListener() { 3 @Override 4 public void onClick(View v) { 5 finish(); 6 } 7 }); 8 mBigPauseButton.setOnClickListener(new View.OnClickListener() { 9 @Override 10 public void onClick(View v) { 11 mVideoView.start(); 12 updatePlayPauseStatus(true); 13 } 14 }); 15 mPlayOrPauseButton.setOnClickListener(new View.OnClickListener() { 16 @Override 17 public void onClick(View v) { 18 handlePlayPause(); //处理pause事件 19 } 20 }); 21 }
9.3 数据的初始化
【设置播放的视频的name】
1 @Override 2 protected void initData() { 3 Log.d(TAG, ">> initData mVideo=" + mVideo); 4 if (mVideo != null) { 5 Log.d(TAG, ">> initData mVideoName" + mVideo.getVideoName()); 6 mVideoNameView.setText(mVideo.getVideoName()); 7 } 8 }
9.4 系统时间的显示
【获取系统时间】属于java的基本知识
1 package com.hejunlin.imooc_supervideo.utils; 2 3 import java.text.SimpleDateFormat; 4 import java.util.Date; 5 6 public class DateUtils { 7 8 public static String getCurrentTime() { 9 SimpleDateFormat format = new SimpleDateFormat("HH:mm"); 10 Date curDate = new Date(System.currentTimeMillis()); 11 String str = format.format(curDate); 12 return str; 13 } 14 15 }
【定义】常量
1 private static final int CHECK_TIME = 1; 2 private static final int CHECK_BATTERY = 2; 3 private static final int CHECK_PROGRESS = 3;
【定义handler】
1 class EventHandler extends Handler { 2 public EventHandler(Looper looper) { 3 super(looper); 4 } 5 6 @Override 7 public void handleMessage(Message msg) { 8 super.handleMessage(msg); 9 switch (msg.what) { 10 case CHECK_TIME: 11 runOnUiThread(new Runnable() { 12 @Override 13 public void run() { 14 mSysTimeView.setText(DateUtils.getCurrentTime()); //设置时间 15 } 16 }); 17 break;36 } 37 } 38 }
1 @Override 2 protected void initView() { 3 ...... 4 mEventHandler = new EventHandler(Looper.myLooper()); //EventHandler 的初始化 5 ...... 6 }
9.5 显示/隐藏上下panel
1 private EventHandler mEventHandler; 2 private boolean mIsPanelShowing = false; //判断是否显示
3 private static final int AUTO_HIDE_TIME = 10000; //显示上下panle的时间
1 /** 2 * 开关是否显示上下panel 3 */ 4 private void toggleTopAndBottomLayout() { 5 if (mIsPanelShowing) { 6 hideTopAndBottomLayout(); 7 } else { 8 showTopAndBottomLayout(); 9 //先显示,没有任何操作,就5s后隐藏 10 mEventHandler.postDelayed(new Runnable() { 11 @Override 12 public void run() { 13 hideTopAndBottomLayout(); 14 } 15 }, AUTO_HIDE_TIME); 16 } 17 } 18
【上下panel调用的时机】
1 @Override 2 protected void initView() {
..... 3 toggleTopAndBottomLayout();
4 }
9.6 隐藏上下panel
1 /** 2 * 隐藏上下的panel 3 */ 4 private void hideTopAndBottomLayout() { 5 if (mIsDragging == true) { 6 return; 7 } 8 mIsPanelShowing = false; 9 mTopLayout.setVisibility(View.GONE); 10 mBottomLayout.setVisibility(View.GONE); 11 }
9.7 显示上下的panel-系统时间的显示
1 /** 2 * 显示上下的panel 3 */ 4 private void showTopAndBottomLayout() { 5 mIsPanelShowing = true; 6 mTopLayout.setVisibility(View.VISIBLE); 7 mBottomLayout.setVisibility(View.VISIBLE); 8 9 if (mEventHandler != null) { 10 mEventHandler.removeMessages(CHECK_TIME); //先移除信息 11 Message msg = mEventHandler.obtainMessage(CHECK_TIME); 12 mEventHandler.sendMessage(msg);//发送信息 13 } 14 15 }
1 class EventHandler extends Handler { 2 public EventHandler(Looper looper) { 3 super(looper); 4 } 5 6 @Override 7 public void handleMessage(Message msg) { 8 super.handleMessage(msg); 9 switch (msg.what) { 10 case CHECK_TIME: 11 runOnUiThread(new Runnable() { 12 @Override 13 public void run() { 14 mSysTimeView.setText(DateUtils.getCurrentTime()); 15 } 16 }); 17 break; 18 } 19 } 20 }
9.8 电池电量的显示
【通过广播获取系统的电量】
1 /** 2 * 通过广播获取系统电量情况 3 */ 4 private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { 5 @Override 6 public void onReceive(Context context, Intent intent) { 7 mBatteryLevel = intent.getIntExtra("level", 0); 8 Log.d(TAG, ">> mBatteryReceiver onReceive mBatteryLevel=" + mBatteryLevel); 9 } 10 };
【注册广播】
1 @Override 2 protected void initView() { 3 ...... 4 registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); //注册获取系统电量的广播 5 ..... 6 }
【注销广播】
1 class EventHandler extends Handler { 2 public EventHandler(Looper looper) { 3 super(looper); 4 } 5 6 @Override 7 protected void onPause() { 8 super.onPause(); 9 if (mBatteryReceiver != null) { 10 unregisterReceiver(mBatteryReceiver); 11 mBatteryReceiver = null; 12 } 13 }
【电量信息的发送】
1 /** 2 * 显示上下的panel 3 */ 4 private void showTopAndBottomLayout() { 5 6 if (mEventHandler != null) { 7 ...... 8 mEventHandler.removeMessages(CHECK_BATTERY); 9 Message batterymsg = mEventHandler.obtainMessage(CHECK_BATTERY); 10 mEventHandler.sendMessage(batterymsg); 11 ...... 12 } 13 }
【电量信息的设置】
1 class EventHandler extends Handler { 2 public EventHandler(Looper looper) { 3 super(looper); 4 } 5 6 @Override 7 public void handleMessage(Message msg) { 8 super.handleMessage(msg); 9 switch (msg.what) { 10 ...... 11 case CHECK_BATTERY: 12 runOnUiThread(new Runnable() { 13 @Override 14 public void run() { 15 setCurrentBattery(); 16 } 17 }); 18 break; 19 ...... 20 } 21 } 22 }
【设置】
1 /** 2 * 根据不同的电量的状态设置不同的drawable文件夹下的图片 3 */ 4 private void setCurrentBattery() { 5 Log.d(TAG, ">> setCurrentBattery level " + mBatteryLevel); 6 if ( 0 < mBatteryLevel && mBatteryLevel <= 10) { 7 mBatteryView.setBackgroundResource(R.drawable.ic_battery_10); 8 } else if (10 < mBatteryLevel && mBatteryLevel <= 20) { 9 mBatteryView.setBackgroundResource(R.drawable.ic_battery_20); 10 } else if (20 < mBatteryLevel && mBatteryLevel <= 50) { 11 mBatteryView.setBackgroundResource(R.drawable.ic_battery_50); 12 } else if (50 < mBatteryLevel && mBatteryLevel <= 80) { 13 mBatteryView.setBackgroundResource(R.drawable.ic_battery_80); 14 } else if (80 < mBatteryLevel && mBatteryLevel <= 100) { 15 mBatteryView.setBackgroundResource(R.drawable.ic_battery_100); 16 } 17 }
9.9 video 标题的设置
【标题的设置】
1 @Override 2 protected void initData() { 3 Log.d(TAG, ">> initData mVideo=" + mVideo); 4 if (mVideo != null) { 5 Log.d(TAG, ">> initData mVideoName" + mVideo.getVideoName()); 6 mVideoNameView.setText(mVideo.getVideoName()); 7 } 8 }
【标题的获取】从网络的json数据获取并设置,分为两个路径,一个是letv,一个是sohu
【letv】
1 //取video相关信息 2 public void onGetVideo(final Album album, int pageSize, int pageNo, final OnGetVideoListener listener) { 3 final String url = String.format(ALBUM_VIDEOS_URL_FORMAT, album.getAlbumId(), pageNo, pageSize, "-1", "1"); 4 OkHttpUtils.excute(url, new Callback() { 5 .... 6 @Override 7 public void onResponse(Call call, Response response) throws IOException { 8 ...... 9 //videoInfo表示每个视频info 10 JSONObject videoInfo = jsonArray.getJSONObject(i); 11 video.setAid(Long.parseLong(album.getAlbumId())); 12 video.setSite(album.getSite().getSiteId()); 13 //nameCn: "择天记03" 14 if (!TextUtils.isEmpty(videoInfo.optString("nameCn"))) { //信息获取的设置 15 video.setVideoName(videoInfo.optString("nameCn")); 16 } 17 //mid: "64271196" 表示解释乐视视频源需要的 18 if (!TextUtils.isEmpty(videoInfo.optString("mid"))) { 19 video.setMid(Long.parseLong(videoInfo.optString("mid"))); 20 } 21 if (!TextUtils.isEmpty(videoInfo.optString("id"))) { 22 video.setVid(Long.parseLong(videoInfo.optString("id"))); 23 } 24 videoList.add(video); 25 }
9.10 码流的设置
【码流的获取】从哪里获取的???从上一个页面的设置的type中获取的;
1 @Override 2 protected void initView() { 3 mUrl = getIntent().getStringExtra("url"); 4 mLiveTitle = getIntent().getStringExtra("title"); 5 mStreamType = getIntent().getIntExtra("type", 0); //获取码流的类型 6 mCurrentPosition = getIntent().getIntExtra("currentPosition", 0); 7 mVideo = getIntent().getParcelableExtra("video");
【码流的设置】
1 /** 2 * 显示上下的panel 3 */ 4 private void showTopAndBottomLayout() { 5 ...... 6 switch (mStreamType) { 7 case AlbumDetailActivity.StreamType.SUPER: 8 mBitStreamView.setText(getResources().getString(R.string.stream_super));//超清 9 break; 10 case AlbumDetailActivity.StreamType.NORMAL: 11 mBitStreamView.setText(getResources().getString(R.string.stream_normal));//标清 12 break; 13 case AlbumDetailActivity.StreamType.HIGH: 14 mBitStreamView.setText(getResources().getString(R.string.stream_high));//高清 15 break; 16 default: 17 break; 18 } 19 ...... 20 }
9.11 播放/暂停
1 @Override 2 protected void initView() { 3 ...... 4 mVideoView.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() { 5 @Override 6 public void onPrepared(IMediaPlayer mp) { 7 mVideoView.start(); //开始播放 8 } 9 }); 10 ..... 11 }
1 private void initListener() { 2 ...... 3 mBigPauseButton.setOnClickListener(new View.OnClickListener() { 4 @Override 5 public void onClick(View v) { 6 mVideoView.start();// 暂停按键再次按下的时候,开始播放 7 updatePlayPauseStatus(true); //更新按键的显示 8 } 9 }); 10 mPlayOrPauseButton.setOnClickListener(new View.OnClickListener() { 11 @Override 12 public void onClick(View v) { 13 handlePlayPause(); //处理pause事件 14 } 15 }); 16 } 17 18 19 private void updatePlayPauseStatus(boolean isPlaying) { 20 mBigPauseButton.setVisibility(isPlaying ? View.GONE : View.VISIBLE); 21 mPlayOrPauseButton.invalidate(); // 重绘 22 mPlayOrPauseButton.setChecked(isPlaying); //图标切换为选中 23 mPlayOrPauseButton.refreshDrawableState(); //更新drable的状态 24 } 25 26 27 private void handlePlayPause() { 28 if (mVideoView.isPlaying()) {//视频正在播放 29 mVideoView.pause(); 30 updatePlayPauseStatus(false); 31 } else { 32 mVideoView.start(); 33 updatePlayPauseStatus(true); 34 } 35 }
9.11 视频拖拽功能
【说明】从服务器获取的时间格式需要重新格式化,使用到javaUtils中的Formatter
1 private SeekBar mSeekBar; 2 private Formatter mFormatter; //直接从服务器获取的 3 private StringBuilder mFormatterBuilder; //字符的拼接
【seekBar的初始化】
1 private void initTopAndBottomView() { 2 ...... 3 mSeekBar = bindViewId(R.id.sb_player_seekbar); 4 mSeekBar.setMax(1000); 5 mSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener); 6 mFormatterBuilder = new StringBuilder(); 7 mFormatter = new Formatter(mFormatterBuilder,Locale.getDefault()); 8 ...... 9 }
【seekBar的状态的监听】
1 private SeekBar.OnSeekBarChangeListener mSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() { 2 // seekbar进度发生变化时回调 3 @Override 4 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 5 if (!fromUser) { 6 return; 7 } 8 long duration = mVideoView.getDuration();//视频时长 9 long nowPosition = (duration * progress) / 1000L; 10 mVideoCurrentTime.setText(stringForTime((int) nowPosition)); //需要专门的时间的格式转化 11 } 12 13 // seekbar开始拖动时回调 14 @Override 15 public void onStartTrackingTouch(SeekBar seekBar) { 16 mIsDragging = true; //显示panel 17 } 18 19 // seekbar拖动完成后回调 20 @Override 21 public void onStopTrackingTouch(SeekBar seekBar) { 22 mIsDragging = false; 23 int progress = seekBar.getProgress();//最后拖动停止的进度 24 long duration = mVideoView.getDuration();//视频时长 25 long newPosition = (duration * progress) / 1000L;//当前的进度 26 mVideoView.seekTo((int) newPosition); //视频跳转到对应的视频位置 27 mEventHandler.postDelayed(new Runnable() { 28 @Override 29 public void run() { 30 hideTopAndBottomLayout(); //视频seek到指定的进度值之后,panel在3秒后消失 31 } 32 },AFTER_DRAGGLE_HIDE_TIME); 33 } 34 };
【时间格式的转换】
1 private String stringForTime(int timeMs) { 2 int totalSeconds = timeMs / 1000; 3 int seconds = totalSeconds % 60; //换成秒 4 int minutes = (totalSeconds / 60) % 60; 5 int hours = (totalSeconds / 3600); 6 mFormatterBuilder.setLength(0); 7 if (hours > 0) { 8 return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString(); 9 } else { 10 return mFormatter.format("%02d:%02d", minutes, seconds).toString(); 11 } 12 }
【进度的时机】
1 /** 2 * 显示上下的panel 3 */ 4 private void showTopAndBottomLayout() { 5 ...... 6 mBottomLayout.setVisibility(View.VISIBLE); 7 updateProgress(); //在panel更新进度 8 if (mEventHandler != null) { 9 mEventHandler.removeMessages(CHECK_TIME); 10 Message msg = mEventHandler.obtainMessage(CHECK_TIME); 11 ......
【更新进度】
1 private void updateProgress() { 2 int currentPosition = mVideoView.getCurrentPosition();//当前的视频位置 3 int duration = mVideoView.getDuration();//视频时长 4 if (mSeekBar != null) { 5 if (duration > 0) { 6 //转成long型,避免溢出 7 long pos = currentPosition * 1000L/ duration; 8 mSeekBar.setProgress((int) pos); 9 } 10 int perent = mVideoView.getBufferPercentage();//已经缓冲的进度 11 mSeekBar.setSecondaryProgress(perent);//设置缓冲进度 12 mVideoCurrentTime.setText(stringForTime(currentPosition)); 13 mVideoTotalTime.setText(stringForTime(duration)); 14 } 15 }
【实时监测视频的位置】
1 class EventHandler extends Handler { 2 public EventHandler(Looper looper) { 3 super(looper); 4 } 5 6 @Override 7 public void handleMessage(Message msg) { 8 super.handleMessage(msg); 9 switch (msg.what) { 10 ...... 11 case CHECK_PROGRESS: 12 runOnUiThread(new Runnable() { 13 @Override 14 public void run() { 15 long duration = mVideoView.getDuration(); 16 long nowduration = (mSeekBar.getProgress() * duration)/1000L; 17 mVideoCurrentTime.setText(stringForTime((int)nowduration)); 18 } 19 }); 20 break; 21 } 22 } 23 } 24
【进度的监测】在显示panel时进行progress的检测
1 /** 2 * 显示上下的panel 3 */ 4 private void showTopAndBottomLayout() { 5 ...... 6 if (mEventHandler != null) { 7 mEventHandler.removeMessages(CHECK_PROGRESS); 8 Message progressmsg = mEventHandler.obtainMessage(CHECK_PROGRESS); 9 mEventHandler.sendMessage(progressmsg); 10 } 11 ...... 12 }
10. 视频手势相关的操作
10.1 GestureDetector 类的实现
【说明】新建一个类:GestureDetector.OnGestureListener,并overide一系列的方法
1 public class GestureDetectorController implements GestureDetector.OnGestureListener { 2 3 private static final String TAG = GestureDetectorController.class.getSimpleName(); 4 private GestureDetector mGestureDetector; 5 private IGestureListener mGestureListener; 6 private int mWidth; 7 private ScrollType mCurrentType; 8 9 public GestureDetectorController(Context context, IGestureListener listener) { 10 /** 11 * 需要判断坐标值,因此需要知道屏幕的宽度 12 */ 13 mWidth = context.getResources().getDisplayMetrics().widthPixels; 14 mGestureListener = listener; 15 mGestureDetector = new GestureDetector(context, this); 16 } 17 18 ...... 19 /** 20 * 手指滑动的类型 21 */ 22 public enum ScrollType { 23 NOTHING, //滑动无操作 24 VERTICAL_LEFT, //左半边竖滑亮度 25 VERTICAL_RIGH, //右半边竖滑音量 26 HORIZONTAL //水平滑动,seek进度调整 27 } 28 29 30 public interface IGestureListener { 31 void onScrollStart(ScrollType type);//开始滑动 32 void onScrollHorizontal(float x1, float x2);//水平滑动 33 void onScrollVerticalLeft(float y1, float y2);//左滑 34 void onScrollVerticalRight(float y1, float y2);//右滑 35 } 36 }
【其他动作的处理】
1 //在按下的时候设置类型为无动作响应 2 @Override 3 public boolean onDown(MotionEvent e) { 4 mCurrentType = NOTHING; 5 return true; 6 } 7 8 @Override 9 public void onShowPress(MotionEvent e) { 10 11 } 12 13 @Override 14 public boolean onSingleTapUp(MotionEvent e) { 15 return false; 16 } 17 18 @Override 19 public void onLongPress(MotionEvent e) { 20 21 } 22 23 @Override 24 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 25 return true; // 返回true 26 }
【滑动相关的参数的传递】
【1】滑动的类型的设置
【2】位置坐标的传递
1 @Override 2 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 3 if (mGestureListener != null) { 4 if (mCurrentType != NOTHING) { 5 switch (mCurrentType) { 6 case VERTICAL_LEFT: 7 mGestureListener.onScrollVerticalLeft(distanceY, e1.getY() - e2.getY()); 8 break; 9 case VERTICAL_RIGH: 10 mGestureListener.onScrollVerticalRight(distanceY, e1.getY() - e2.getY()); 11 break; 12 case HORIZONTAL: 13 mGestureListener.onScrollHorizontal(distanceX, e2.getX() - e1.getX()); 14 break; 15 } 16 return false; 17 } 18 } 19 //水平方向上滑动判断 20 if (Math.abs(distanceY) <= Math.abs(distanceX)) { 21 mCurrentType = ScrollType.HORIZONTAL; 22 mGestureListener.onScrollStart(mCurrentType); 23 return false; 24 } 25 26 //将屏幕分为1/3判断 27 int i = mWidth / 3; 28 //左滑判断 29 if (e1.getX() <= i) { 30 mCurrentType = ScrollType.VERTICAL_LEFT; 31 mGestureListener.onScrollStart(mCurrentType); 32 } else if (e1.getX() > i*2) {//右滑判断 33 mCurrentType = ScrollType.VERTICAL_RIGH; 34 mGestureListener.onScrollStart(mCurrentType); 35 } else { 36 mCurrentType = NOTHING; 37 } 38 return false; 39 }
10.2 GestureDetector 类的使用
【实现接口并复写方法】
1 public class PlayActivity extends BaseActivity implements GestureDetectorController.IGestureListener{ 2 3 private static final String TAG = PlayActivity.class.getSimpleName(); 4 private static final int CHECK_TIME = 1;
【初始化GestureDetector 】
1 private void initGesture() { 2 mGestureController = new GestureDetectorController(this, this); 3 }
【布局当中添加信息的显示】音量的改变的大小值,亮度改变的大小值等等
1 <TextView 2 android:id="@+id/tv_horiontal_gesture" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" 5 android:shadowDx="1.0" 6 android:shadowDy="1.0" 7 android:shadowColor="@color/black" 8 android:textColor="@color/white" 9 android:textSize="@dimen/dimen_64sp" 10 android:visibility="gone" 11 android:textStyle="bold" 12 android:gravity="center"/> 13 14 <TextView 15 android:id="@+id/tv_vertical_gesture" 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:layout_gravity="center" 19 android:gravity="center_horizontal" 20 android:background="@drawable/black_bg" 21 android:paddingLeft="@dimen/dimen_100dp" 22 android:paddingRight="@dimen/dimen_100dp" 23 android:paddingTop="@dimen/dimen_40dp" 24 android:paddingBottom="@dimen/dimen_40dp" 25 android:textSize="@dimen/dimen_64sp" 26 android:textStyle="bold" 27 android:textColor="@color/white" 28 android:visibility="gone"/>
10.3 水平滑动的初始响应
【常用变量的定义】此处省略了TextView滑动信息显示的控件的查找
1 private GestureDetectorController mGestureController; 2 private TextView mDragHorizontalView; 3 private TextView mDragVerticalView; 4 private long mScrollProgress; //滑动的进度值 5 private boolean mIsHorizontalScroll; //标识是否为水平滑动 6 private boolean mIsVerticalScroll; //标识是否为竖直滑动 7 private int mCurrentLight; 8 private int mMaxLight = 255; 9 private int mCurrentVolume; 10 private int mMaxVolume = 10
【水平滑动的响应】
1 @Override 2 public void onScrollStart(GestureDetectorController.ScrollType type) { 3 mIsMove = true; //是否在屏幕上滑动 4 ...... 5 switch (type) { 6 case HORIZONTAL: 7 mDragHorizontalView.setVisibility(View.VISIBLE); 8 mScrollProgress = -1; 9 mIsHorizontalScroll = true;//水平滑动标识 10 break; 11 ...... 12 } 13 }
10.4 左半边部分区域的初始响应
【组合图片和文字】
1 //用于组合图片及文字 2 private void setComposeDrawableAndText(TextView textView, int drawableId, Context context) { 3 Drawable drawable = context.getResources().getDrawable(drawableId); 4 //这四个参数表示把drawable绘制在矩形区域 5 drawable.setBounds(0,0, drawable.getMinimumWidth(), drawable.getMinimumHeight()); 6 //设置图片在文字的上方 7 //The Drawables must already have had drawable.setBounds called. 8 textView.setCompoundDrawables(null, drawable, null , null); 9 }
【更新亮度TextView】需要转化为百分比的显示
1 //更新垂直方向上滑动时的百分比 2 private void updateVerticalText(int current, int total) { 3 NumberFormat formater = NumberFormat.getPercentInstance(); 4 formater.setMaximumFractionDigits(0);//设置整数部分允许最大小数位 66.5%->66% 5 String percent = formater.format((double)(current)/(double) total); 6 mDragVerticalView.setText(percent); 7 }
【左半边部分区域的垂直的动作的响应】
1 @Override 2 public void onScrollStart(GestureDetectorController.ScrollType type) { 3 mIsMove = true; 4 switch (type) { 5 ...... 6 case VERTICAL_LEFT: 7 setComposeDrawableAndText(mDragVerticalView, R.drawable.ic_light, this); 8 mDragVerticalView.setVisibility(View.VISIBLE); 9 updateVerticalText(mCurrentLight, mMaxLight); 10 mIsVerticalScroll = true; 11 break; 12 ...... 13 }
10.5 右半边部分区域的初始响应
1 @Override 2 public void onScrollStart(GestureDetectorController.ScrollType type) { 3 mIsMove = true; 4 switch (type) { 5 ...... 6 case VERTICAL_RIGH: 7 if (mCurrentVolume > 0) { 8 setComposeDrawableAndText(mDragVerticalView, R.drawable.volume_normal, this); 9 } else { 10 setComposeDrawableAndText(mDragVerticalView, R.drawable.volume_no, this); 11 } 12 mDragVerticalView.setVisibility(View.VISIBLE); 13 updateVerticalText(mCurrentVolume, mMaxVolume); 14 mIsVerticalScroll = true; 15 break; 16 } 17 ...... 18 }
10.6 屏幕左半边垂直滑动更新系统的亮度
【系统获取的亮度值变量】默认值是系统的值,即使获取出来也是一样的。
1 private int mMaxLight = 255; 2 private int mCurrentVolume; 3 private int mMaxVolume = 10;
【更新系统的亮度】
1 @Override 2 public void onScrollVerticalLeft(float y1, float y2) { 3 int height = getResources().getDisplayMetrics().heightPixels; 4 int offset = (int) (mMaxLight * y1)/ height; 5 if (Math.abs(offset) > 0) { 6 mCurrentLight += offset;//得到变化后的亮度 7 mCurrentLight = Math.max(0, Math.min(mMaxLight, mCurrentLight)); 8 // 更新系统亮度 9 SysUtils.setBrightness(this, mCurrentLight); 10 SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit(); 11 editor.putInt("shared_preferences_light", mCurrentLight); 12 editor.commit(); 13 updateVerticalText(mCurrentLight, mMaxLight); 14 } 15 }
【SysUtils类】
1 public class SysUtils { 2 3 //获取亮度 4 public static int getBrightness(Context context) { 5 return Settings.System.getInt(context.getContentResolver(),"screen_brightness", -1); 6 } 7 //设置亮度 8 public static void setBrightness(Context context, int param) { 9 Settings.System.putInt(context.getContentResolver(), "screen_brightness", param); 10 } 11 //获取亮度的sharedPreferences文件--系统默认的信息保存文件 12 public static int getDefaultBrightness(Context context) { 13 return PreferenceManager.getDefaultSharedPreferences(context).getInt("shared_preferences_light", -1); 14 } 15 16 }
【亮度的初始化】
1 private void initLight() { 2 mCurrentLight = SysUtils.getDefaultBrightness(this); 3 if (mCurrentLight == -1) {//获取不到亮度sharedpreferences文件 4 mCurrentLight = SysUtils.getBrightness(this); 5 } 6 }
10.7 屏幕右半边垂直滑动更新系统的音量
【屏幕右半边垂直滑动的参数的设置】
1 @Override 2 public void onScrollVerticalRight(float y1, float y2) { 3 int height = getResources().getDisplayMetrics().heightPixels; 4 int offset = (int) (mMaxVolume * y1)/ height; 5 if (Math.abs(offset) > 0) { 6 mCurrentVolume += offset;//得到变化后的声音 7 mCurrentVolume = Math.max(0, Math.min(mMaxVolume, mCurrentVolume)); 8 // 更新系统声音 9 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, mCurrentVolume/10, 0); 10 updateVerticalText(mCurrentVolume, mMaxVolume); 11 } 12 }
【初始化音量 】
1 private void initAudio() { 2 mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE); 3 setVolumeControlStream(AudioManager.STREAM_MUSIC); 4 mAudioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); 5 mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) * 10;// 系统声音取值是0-10,*10为了和百分比相关 6 mCurrentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC) * 10; 7 }
【释放声音abandonAudioFocus】
1 @Override 2 protected void onPause() { 3 super.onPause(); 4 if (mBatteryReceiver != null) { 5 unregisterReceiver(mBatteryReceiver); 6 mBatteryReceiver = null; 7 } 8 //释放audiofocus 9 mAudioManager.abandonAudioFocus(null); 10 }
10.8 水平方向滑动更新进度
【水平方向滑动动作】
1 // 更新进度 2 @Override 3 public void onScrollHorizontal(float x1, float x2) { 4 int width = getResources().getDisplayMetrics().widthPixels; 5 int MAX_SEEK_STEP = 300000;//最大滑动5分钟 6 int offset = (int) (x2 / width * MAX_SEEK_STEP) + mVideoView.getCurrentPosition(); 7 long progress = Math.max(0, Math.min(mVideoView.getDuration(), offset)); 8 mScrollProgress = progress; 9 updateHorizontalText(progress); 10 }
【updateHorizontalText】
1 //更新水平方向seek的进度, duration表示变化后的duration 2 private void updateHorizontalText(long duration) { 3 String text = stringForTime((int)duration) + "/" + stringForTime(mVideoView.getDuration()); 4 mDragHorizontalView.setText(text); 5 }
10.9 TouchEvent事件的处理
1 @Override 2 public boolean onTouchEvent(MotionEvent event) { 3 if (event.getAction() == MotionEvent.ACTION_UP) { 4 if (mIsMove == false) { 5 toggleTopAndBottomLayout(); 6 } else { 7 mIsMove = false; 8 } 9 //水平方向,up时,seek到对应位置播放 10 if (mIsHorizontalScroll) { 11 mIsHorizontalScroll = false; 12 mVideoView.seekTo((int)mScrollProgress); 13 //一次down,up结束后mDragHorizontalView隐藏 14 mDragHorizontalView.setVisibility(View.GONE); 15 } 16 if (mIsVerticalScroll) { 17 mDragVerticalView.setVisibility(View.GONE); 18 mIsVerticalScroll = false; 19 } 20 } 21 return mGestureController.onTouchEvent(event); 22 }
浙公网安备 33010602011771号