自定义VideoView的演进
在编写简单自定义VideoView中尝试编写自己的VideoView实现类。这样对VideoView的实现机制有了一个比较深入的理解。经过整理发现,其实要自定义需求,还真不一定需要重新自己的VideoView实现。在本文中将原来的CustomerVideoView的方法全部删除,并继承VideoView,发现功能上没有什么不能实现的。继承的CustomerVideoView最后其实只剩下继承来的构造方法,也就是说直接使用VideoView也没问题。
这次演进,实现了自定义的播放控制条:
这个播放控制条,实际是替代了编写简单自定义VideoView中的MediaController。这样就可以自定义各种样式和风格的控制条界面了。
另外,本文示例中默认不出现播放控制条,当识别到横向手势的时候,才显示该滚动条,并且根据横向手势的x轴位移前进或者后退播放视频的位置。
如果手指单击视频,则停止播放,再单击继续播放。
CustomerVideoView已经简化到可以直接使用VideoView替代:
package com.easymorse.videoplayer;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.VideoView;public class CustomerVideoView extends VideoView {
private static String TAG = "customer.videoplayer";
public CustomerVideoView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}public CustomerVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
}public CustomerVideoView(Context context) {
super(context);
}}
保留自定义CustomerVideoView的目的是:
- 以后可以通过覆盖方法增加更加个性化的功能;
- 为什么要继承VideoView,是否能直接写自己的VideoView实现?这是一个问题,当然是可行的,比如mVideoPlayer就是这么干的,但是再查看VideoView源代码的时候,发现一些在Android源代码中公开的(public)类,在android sdk api中并没有,比如找不到android.media.Metadata类,调用MediaPlayer的时候也无法找到源代码中公开的(public)方法getMetadata方法,因此直接使用继承可以获得很多好处,间接使用未公开的api。
有关播放控制条的布局:
<RelativeLayout android:id="@+id/mediaControllerLayout"
android:layout_height="55dip" android:layout_width="fill_parent"
android:visibility="invisible" android:layout_alignParentBottom="true">
<View android:background="#50878787" android:layout_width="fill_parent"
android:layout_height="1dip" />
<ImageButton android:id="@+id/playButton"
android:layout_width="wrap_content" android:src="@drawable/pause_button_gray"
android:layout_height="wrap_content" android:layout_marginRight="15.0dip"
android:layout_alignParentRight="true"
android:layout_centerVertical="true" />
<RelativeLayout android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_marginLeft="15.0dip"
android:layout_marginRight="15.0dip" android:layout_centerVertical="true"
android:layout_toLeftOf="@id/playButton">
<SeekBar android:id="@+id/videoSeekBar" android:focusable="false"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_marginBottom="5.0dip"
android:layout_alignParentBottom="true" />
</RelativeLayout>
</RelativeLayout>
在这里还能加入其他需要的信息,比如当前播放时间,总时间等等。SeekBar是使用默认样式的,可以指定自己的样式和thumb小图标。
为了实现横向手势指定播放进度功能,需要让activity实现OnGestureListener接口,以前写过一个简单的示例,编写android简单的手势切换视图示例,可先参考了解。
在OnGestureListener中主要实现了以下方法。
onFling:
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
mediaControllerLayout.setVisibility(View.VISIBLE);
handler.postDelayed(new Runnable() {
@Override
public void run() {
mediaControllerLayout.setVisibility(View.INVISIBLE);
}
}, 1000);
return false;
}
其实这个方法中的内容也可以实现在下面提到的onScroll方法中。onFling主要是处理有有关速度横向和纵向的手势。这里用来触发显示播放控制条界面。并且通过postDelayed方法在1秒钟后让控制条不可见。这里还可以改进,用动画来处理出现和消失的效果。
onSingleTapUp:
@Override
public boolean onSingleTapUp(MotionEvent e) {
Toast.makeText(this, "taped", Toast.LENGTH_SHORT).show();
if (videoView.isPlaying()) {
videoView.pause();
} else {
videoView.start();
}
return false;
}
用来识别单击屏幕的手势,并做视频的暂停和继续播放。
onScroll:
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
if (distanceX > 0) {
this.videoSeekBar
.setProgress(this.videoSeekBar.getProgress() – 1);
} else {
this.videoSeekBar
.setProgress(this.videoSeekBar.getProgress() + 1);
}
videoView.seekTo((int) (this.videoSeekBar.getProgress() * 1.0
/ videoSeekBar.getMax() * videoView.getDuration()));
return false;
}
识别手势横向左右位移,这里实现的很简单,进度条根据动作固定的为进度条加1%或者减1%。视频再根据滚动条做调整。正式的代码,应该根据位移的长度来适当的定位滚动条的位置。
手势的监听器要实现,还需要两件事情。
实例化GestureDetector:
private GestureDetector gestureDetector;
this.gestureDetector = new GestureDetector(this);
覆盖Activity的onTouchEvent方法:
@Override
public boolean onTouchEvent(MotionEvent event) {
return this.gestureDetector.onTouchEvent(event);
}
这样才会把触摸事件转到手势监听器。
另外,也可以手动拨动滚动条上的thumb,需要通过下面方法支持:
@Override
public void onProgressChanged(SeekBar seekBar,
int progress, boolean fromUser) {
if (fromUser) {
videoView.seekTo((int) (progress * 1.0
/ seekBar.getMax() * videoView
.getDuration()));
seekBar.setProgress(progress);
}
}
源代码见:
进一步可以做的是:
- 在播放器中保存用户播放视频中止时的播放位置,再次播放的时候从中止的位置播放;
- 设置menu项,用户可以清除保存的位置,这样可以从头播放;
- 屏幕的适配,目前是按照视频原生大小播放,可以增加拉伸适配全屏功能,并通过menu项切换;
- 视频的横屏和竖屏切花;
- 设置intent,这样可以在用户播放视频的时候,可以出现应用列表,用户可以选择android内置播放器以外的应用播放;
- 做一个比较好的动画,在单击暂停的时候播放。
1 Comment to “自定义VideoView的演进”
这篇文章上的评论的 RSS feed TrackBack URI
By virginie, 2011年08月31日 @ 18:51
請問直接extends VideoView的方法, 要如何access到private member “mMediaPlayer” 呢?
我想要自訂一個video view可以export MediaPlayer.onSeekCompletedListener, 該怎麼做才好呢? 如果重新寫一個, 就會遇到你說的android.media.Metadata的問題…唉唉…