Android Custom player control VideoView

  • 2021-01-25 07:54:52
  • OfStack

introduce

Recently to use the player to do a simple video play function, began to learn VideoView, in the horizontal and vertical screen switch encountered a bit of trouble, but after consulting information is finally solved. When writing VideoView to play video definition control code all written in Actvity, write 1 see I depend on code good chaos, so wrote a custom player control, support specified size, can switch horizontal and vertical screen, manual slide around fast forward fast back. Okay, let's get started.

The renderings are a bit stuck, I don't know why...

VideoView introduction

This is the most important control we achieve video playback, a detailed introduction to baidu to see, here introduce a few common methods.

Used to play video files. The ES21en class can read images from different sources (such as resource files or content providers), calculate and maintain the screen size of a video for any layout manager, and provide display options such as scaling, shading, and so on.

A few common methods of VideoView

public int getDuration ()

Gets the total time of the video played

public int getCurrentPosition ()

Get the current location, which we can use to set the display of the playback time

public int getCurrentPosition ()

Get the current location, which we can use to set the display of the playback time

public int pause ()

On TV

public int seekTo ()

Set the playback position, which we can use when we're always fast-forward

public int setOnCompletionListener(MediaPlayer.OnCompletionListener l)

Register the callback function to be called when the media file is finished playing.

public int setOnErrorListener (MediaPlayer.OnErrorListener l)

Registers callback functions that are called when an error occurs during setup or playback. If no callback function is specified, or if the callback function returns false, 1 dialog will pop up to prompt the user not to play

public void setOnPreparedListener (MediaPlayer.OnPreparedListener l)

Register the callback function to be called when the media file is loaded and ready to play.

public void setVideoURI (Uri uri)

Set the source of the video to play. You can also specify a local file using setVideoPath

public void start ()

Start playing

getHolder().setFixedSize(width,height);

Set the resolution of ES106en. If we change the layout size of ES108en from portrait to landscape, we will need to reset the resolution. Otherwise, you will find that the video inside ES109en is the same size as before.

Customize player ideas

In fact, it is nothing more than combining these ES116en and other controls used for display in 1, and then handle its event interaction internally. We need to do the following steps: 1, write the layout of the whole space. 2, in the custom control inside the control to get the entire control inside each small control, and set some initialization events for them. 3. Write your own event handling based on your own logic and desired effect, providing methods and interfaces to interact with the outside world. Finally, use the test effect. Well, let's follow these 4 steps to achieve it!

The specific implementation

1. Step 1: Write your own layout file

The desired effect is to put a status bar at the bottom to display information such as time, play progress, enter the full screen, and put a fast forward and fast back status in the middle. The layout code is as follows:


<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/viewBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="beforeDescendants">
<com.qiangyu.test.commonvideoview.MyVideoView
android:id="@+id/videoView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
// Bottom status bar 
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#CC282828"
android:padding="3dip"
android:id="@+id/videoControllerLayout"
android:gravity="center"
android:layout_gravity="bottom">
<LinearLayout android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:id="@+id/videoPauseBtn"
android:paddingRight="10dip"
android:paddingLeft="10dp">
<ImageView android:layout_width="22dp"
android:layout_height="22dp"
android:id="@+id/videoPauseImg" />
</LinearLayout>
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingRight="0dip">
<SeekBar android:layout_width="fill_parent"
android:id="@+id/videoSeekBar"
android:layout_weight="1"
style="@android:style/Widget.Holo.SeekBar"
android:layout_height="wrap_content"/>
<TextView android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center"
android:text="00:00"
android:textSize="12dp"
android:id="@+id/videoCurTime"
android:textColor="#FFF"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="12dp"
android:textColor="#FFF"
android:text="/"/>
<TextView android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center"
android:text="00:00"
android:textSize="12dp"
android:id="@+id/videoTotalTime"
android:textColor="#FFF"
android:layout_marginRight="10dp"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/screen_status_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center">
<ImageView
android:id="@+id/screen_status_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/iconfont_enter_32"/>
</LinearLayout>
</LinearLayout>
//VideoVIEW The start button and the progress bar in the middle and the prompt for fast forward and fast backward 
<ProgressBar android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="@+id/progressBar"
style="@android:style/Widget.Holo.ProgressBar.Small"/>
<ImageView android:layout_width="30dip"
android:layout_height="30dip"
android:id="@+id/videoPlayImg"
android:layout_gravity="center"
android:src="@mipmap/video_box_play"/>
<LinearLayout
android:id="@+id/touch_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:visibility="invisible"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:background="#000">
<ImageView android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:id="@+id/touchStatusImg"/>
<TextView
android:id="@+id/touch_time"
android:layout_width="wrap_content"
android:text="25:00/59:00"
android:textSize="12sp"
android:textColor="#fff"
android:layout_height="wrap_content"/>
</LinearLayout>
</FrameLayout> 

The above layout is very simple, VideoView uses customization because when the layout changes, VideoView needs to retrieve the layout position and set its resolution to full screen. The code for VideoView is as follows


public class MyVideoView extends VideoView {
public MyVideoView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyVideoView(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = getDefaultSize(0, widthMeasureSpec);
int height = getDefaultSize(0, heightMeasureSpec);
this.getHolder().setFixedSize(width,height);// Set resolution 
setMeasuredDimension(width, height);
}
}

Okay, now that the layout is written I'm going to step 2, get the internal control initialization event

2. Step 2, get the internal control in onFinishInflate() and do the initialization work

The onFinishInflate method calls back the xml method when the xml method has been parsed, and is most commonly used when making composite controls


@Override
protected void onFinishInflate() {
super.onFinishInflate();
initView();
}
private void initView() {
View view = LayoutInflater.from(context).inflate(R.layout.common_video_view,null);
viewBox = (FrameLayout) view.findViewById(R.id.viewBox);
videoView = (MyVideoView) view.findViewById(R.id.videoView);
videoPauseBtn = (LinearLayout) view.findViewById(R.id.videoPauseBtn);
screenSwitchBtn = (LinearLayout) view.findViewById(R.id.screen_status_btn);
videoControllerLayout = (LinearLayout) view.findViewById(R.id.videoControllerLayout);
touchStatusView = (LinearLayout) view.findViewById(R.id.touch_view);
touchStatusImg = (ImageView) view.findViewById(R.id.touchStatusImg);
touchStatusTime = (TextView) view.findViewById(R.id.touch_time);
videoCurTimeText = (TextView) view.findViewById(R.id.videoCurTime);
videoTotalTimeText = (TextView) view.findViewById(R.id.videoTotalTime);
videoSeekBar = (SeekBar) view.findViewById(R.id.videoSeekBar);
videoPlayImg = (ImageView) view.findViewById(R.id.videoPlayImg);
videoPlayImg.setVisibility(GONE);
videoPauseImg = (ImageView) view.findViewById(R.id.videoPauseImg);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
videoPauseBtn.setOnClickListener(this);
videoSeekBar.setOnSeekBarChangeListener(this);
videoPauseBtn.setOnClickListener(this);
videoView.setOnPreparedListener(this);
videoView.setOnCompletionListener(this);
screenSwitchBtn.setOnClickListener(this);
videoPlayImg.setOnClickListener(this);
// Registers callback functions that are called when an error occurs during setup or playback. If the callback function is not specified, or the callback function returns false . VideoView  The user is notified that an error has occurred. 
videoView.setOnErrorListener(this);
viewBox.setOnTouchListener(this);
viewBox.setOnClickListener(this);
addView(view);
} 

videoSeekBar.setOnSeekBarChangeListener(this), viewBox.setOnTouchListener(this), viewBox.setOnTouchListener(this), onTouch (setOnSeekBarChangeListener), videoSeekBar.setOnSeekBarChangeListener(this),

(this), viewBox.setOnTouchListener(this),

(this), viewBox.setOnTouchListener(this). Handling drag seekbar fast forward fast back.

3. Step 3, event and effect processing

viewBox.setOnTouchListener(this);

1, onTouch to achieve fast forward fast back


@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
// Don't handle it when it's not playing 
if (!videoView.isPlaying()){
return false;
}
float downX = event.getRawX();
touchLastX = downX;
Log.d("FilmDetailActivity", "downX" + downX);
// Save the current playback location for event display 
this.position = videoView.getCurrentPosition();
break;
case MotionEvent.ACTION_MOVE:
// Don't handle it when it's not playing 
if (!videoView.isPlaying()){
return false;
}
float currentX = event.getRawX();
float deltaX = currentX - touchLastX;
float deltaXAbs = Math.abs(deltaX);
if (deltaXAbs>1){// Forward moving. Fast forward 
if (touchStatusView.getVisibility()!=View.VISIBLE){
touchStatusView.setVisibility(View.VISIBLE);
// Displays the fast-forward time view
}
touchLastX = currentX;
Log.d("FilmDetailActivity","deltaX"+deltaX);
if (deltaX > 1) {
position += touchStep;
if (position > duration) {
position = duration;
}
touchPosition = position;
touchStatusImg.setImageResource(R.mipmap.ic_fast_forward_white_24dp);
int[] time = getMinuteAndSecond(position);
touchStatusTime.setText(String.format("%02d:%02d/%s", time[0], time[1],formatTotalTime));
} else if (deltaX < -1) {// Retreat quickly 
position -= touchStep;
if (position < 0) {
position = 0;
}
touchPosition = position;
touchStatusImg.setImageResource(R.mipmap.ic_fast_rewind_white_24dp);
int[] time = getMinuteAndSecond(position);
touchStatusTime.setText(String.format("%02d:%02d/%s", time[0], time[1],formatTotalTime));
//mVideoView.seekTo(position);
}
}
break;
case MotionEvent.ACTION_UP:
if (touchPosition!=-1){
videoView.seekTo(touchPosition);
// When releasing the finger, fast forward or fast back to the time position determined by the slide  touchStatusView.setVisibility(View.GONE);
touchPosition = -1;
if (videoControllerShow){
return true;
}
}
break;
}
return false;
}

2. Handling seekBar drag events


@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int[] time = getMinuteAndSecond(progress);
videoCurTimeText.setText(String.format("%02d:%02d", time[0], time[1]));
// Set the bottom time display 
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
videoView.pause();
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
videoView.seekTo(videoSeekBar.getProgress());
videoView.start();
videoPlayImg.setVisibility(View.INVISIBLE);
videoPauseImg.setImageResource(R.mipmap.icon_video_pause) ; 
// Drag to the specified time location 
} 

3, videoView callback event


@Override
public void onPrepared(MediaPlayer mp) {
duration = videoView.getDuration();
int[] time = getMinuteAndSecond(duration);
videoTotalTimeText.setText(String.format("%02d:%02d", time[0], time[1]));
formatTotalTime = String.format("%02d:%02d", time[0], time[1]);
videoSeekBar.setMax(duration);
progressBar.setVisibility(View.GONE);
mp.start();
videoPauseBtn.setEnabled(true);
videoSeekBar.setEnabled(true);
videoPauseImg.setImageResource(R.mipmap.icon_video_pause);
timer.schedule(timerTask, 0, 1000);
// Total initialization time, etc 1 Some interface display, use at the same time timer Regularly modify the schedule textView
}
@Override
public void onCompletion(MediaPlayer mp) {
videoView.seekTo(0);
videoSeekBar.setProgress(0);
videoPauseImg.setImageResource(R.mipmap.icon_video_play);
videoPlayImg.setVisibility(View.VISIBLE);
}

There are a few other click time is not put, are pause, play, click show hide bottom status bar, full screen switch and so on. Now that we're done with the event, we're going to have to figure out how to play the video? We provide an external method for playback


// Start playing 
public void start(String url){
videoPauseBtn.setEnabled(false);
videoSeekBar.setEnabled(false);
videoView.setVideoURI(Uri.parse(url));
videoView.start();
}
// Called when it goes full screen 
public void setFullScreen(){
touchStatusImg.setImageResource(R.mipmap.iconfont_exit);
this.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
videoView.requestLayout();
}
// Called when exiting full screen 
public void setNormalScreen(){
touchStatusImg.setImageResource(R.mipmap.iconfont_enter_32);
this.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,400));
videoView.requestLayout();
} 

setFullScreen() and setNormalScreen() need to be called from the callback method of onConfigurationChanged(Configuration newConfig). Note that the parent space of view is LinearLayout, so you can change it.

4. Use of controls

All we need to do is call the start method in the fetch space, and then call the setFullScreen and setNormalScreen methods in the onConfigurationChanged method.

layout


<?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:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.qiangyu.test.commonvideoview.MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
<com.qiangyu.test.commonvideoview.CommonVideoView
android:id="@+id/common_videoView"
android:layout_width="match_parent"
android:layout_height="300dp" />
</LinearLayout>

activity code


public class MainActivity extends AppCompatActivity {
CommonVideoView videoView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
videoView = (CommonVideoView) findViewById(R.id.common_videoView);
videoView.start(" Your server video address ");
}
@Override public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
videoView.setFullScreen();
}else {
videoView.setNormalScreen();
}
}
}

Finally, don't forget to configure the Activity file in AndroidManifest.xml file in order to prevent your Activity from being recreated when switching from horizontal to vertical

android: configChanges = "orientation | screenSize | screenLayout", if you have doubts here � you can refer to my article > In-depth understanding of Activity- life cycle


<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"
android:configChanges="orientation|screenSize|screenLayout">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

The above is this site to share Android custom player control VideoView related knowledge, I hope to help you.


Related articles: