Android Mini-letter video recording function
Before development
I've been in touch with video-related controls these days, so, following the micro-letter before shaking, I thought of to realize the micro-letter Small video recording function, it's more functional points, I spend some time every day to write, to tell the truth, some things are still more laborious, I hope we take a serious look, The wrong place and please correct me in the comments. Don't say much nonsense, get to the point.
Development environment
Recently updated, no update of the small partners to seize the
- Android Studio 2.2.2
- JDK1.7
- API 24
- Gradle 2.2.2
Related knowledge points
- The use of video recording interface Surfaceview
- The use of camera
- Camera Focus, Zoom
- Use of video recording control Mediarecorder
- Simple Custom View
- The use of gesturedetector (gesture detection)
There are a lot of things to use, but don't worry, let's come alone.
Start developing
Case analysis
You can open your micro-letter inside the small video, a simple analysis of its function points have what?
- Basic Video preview feature
- Long press "hold and clap" to achieve video recording
- The progress bar in the recording process is shortened from both sides to the middle
- When you let go or the progress bar goes to the end the video stops recording and saves
- Slide off video recording from Press and hold
- Double click on screen Zoom zoom in
In the light of the above analysis, we have done this step-by-step
Build a layout
The implementation of the layout interface is also possible, not very difficult
<?xml version= "1.0" encoding= "Utf-8"?> <framelayout xmlns:android= "http://schemas.android.com/apk/res/" Android "Android:layout_width=" Match_parent "android:layout_height=" match_parent "> <textview android:id=" @ +id/main_tv_tip "android:layout_width=" wrap_content "android:layout_height=" Wrap_content "Android:layout_gravi"
Ty= "Bottom|center_horizontal" android:layout_marginbottom= "150DP" android:elevation= "1DP" android:text= "double click to enlarge" Android:textcolor= "#FFFFFF"/> <linearlayout android:layout_width= "Match_parent" android:layout_height=
"Match_parent" android:orientation= "vertical" > <surfaceview android:id= "@+id/main_surface_view" Android:layout_width= "Match_parent" android:layout_height= "0DP" android:layout_weight= "3"/> <LinearL Ayout android:layout_width= "match_parent" android:layout_height= "0DP" android:layout_weight= "1" an droid:background= "@color/colorapP "android:orientation=" vertical "> <relativelayout android:id=" @+id/main_press_control " Android:layout_width= "Match_parent" android:layout_height= "match_parent" > <com.lulu.weichatsamplevid Eo. Bothwayprogressbar android:id= "@+id/main_progress_bar" android:layout_width= "Match_parent" a ndroid:layout_height= "2DP" android:background= "#000"/> <textview android:layout_width= "W Rap_content "android:layout_height=" Wrap_content "android:layout_centerinparent=" true "Andro id:text= "android:textappearance=" @style/textappearance.appcompat.large "android:textcolor=" #00ff0
0 "/> </RelativeLayout> </LinearLayout> </LinearLayout> </FrameLayout>
Implementation of video preview
Step1: Get the Sufaceview control, set the basic properties and listen for it (the control is created asynchronously and can only be invoked after it is really "ready")
Msurfaceview = (Surfaceview) Findviewbyid (R.id.main_surface_view);
Set screen resolution
msurfaceholder.setfixedsize (Videowidth, videoheight);
Msurfaceholder.settype (surfaceholder.surface_type_push_buffers);
Msurfaceholder.addcallback (this);
Step2: Method of implementing the interface, surfacecreated the preview of the video in the method, destroying in the surfacedestroyed
//Surfaceview callback
////////////////////////////////////////
@Override public
void surfacecreated (Surfaceholder holder) {
msurfaceholder = holder;
Startpreview (holder);
}
@Override public
void surfacechanged (surfaceholder holder, int format, int width, int height) {
}
@ Override public
void surfacedestroyed (Surfaceholder holder) {
if (Mcamera!= null) {
LOG.D (TAG, " surfacedestroyed: ");
Stop previewing and releasing camera Resources
Mcamera.stoppreview ();
Mcamera.release ();
Mcamera = null;
}
if (Mmediarecorder!= null) {
mmediarecorder.release ();
Mmediarecorder = null;
}
}
Step3: A way to achieve video preview
/** * Open Preview * * @param holder/private void Startpreview (Surfaceholder holder) {
LOG.D (TAG, "Startpreview:");
if (Mcamera = = null) {Mcamera = Camera.open (Camera.CameraInfo.CAMERA_FACING_BACK);
} if (Mmediarecorder = = null) {Mmediarecorder = new Mediarecorder ();
} if (Mcamera!= null) {mcamera.setdisplayorientation (90);
try {mcamera.setpreviewdisplay (holder);
Camera.parameters Parameters = Mcamera.getparameters ();
Realize camera autofocus list<string> focusmodes = Parameters.getsupportedfocusmodes ();
if (focusmodes!= null) {for (String mode:focusmodes) {mode.contains ("Continuous-video");
Parameters.setfocusmode ("Continuous-video");
} mcamera.setparameters (parameters);
Mcamera.startpreview ();
catch (IOException e) {e.printstacktrace (); }
}
}
Note: The code for autofocus is added above, but some phones may not support
Customize the two-way reduced progress bar
Some beginners like me, when they see a custom so-and-so view, think they compare the cow x. In fact, Google has written a lot of code for us, so we can use it. And we have no more of this progress bar, is not a line, today I say.
Step1: Inheriting view, completing initialization
private static final String TAG = "Bothwayprogressbar";
The cancellation status is Red bar, whereas Green bar
private Boolean iscancel = false;
Private context Mcontext;
The brush being recorded
private Paint mrecordpaint;
The brush on the cancel when
private Paint mcancelpaint;
Whether to show
private int mvisibility;
Current Progress
private int progress;
Progress bar end of the monitoring
private Onprogressendlistener Monprogressendlistener;
Public Bothwayprogressbar {
Super (context, NULL);
}
Public Bothwayprogressbar (context, AttributeSet attrs) {
Super (context, attrs);
Mcontext = context;
Init ();
}
private void Init () {
mvisibility = invisible;
Mrecordpaint = new Paint ();
Mrecordpaint.setcolor (color.green);
Mcancelpaint = new Paint ();
Mcancelpaint.setcolor (color.red);
}
Note:onprogressendlistener, mainly used when the progress bar went to the middle, good to inform the camera to stop recording, the interface is as follows:
public interface onprogressendlistener{
void Onprogressendlistener ();
}
/**
*
@param onprogressendlistener
/public void Setonprogressendlistener when the progress bar is closed
( Onprogressendlistener onprogressendlistener) {
monprogressendlistener = Onprogressendlistener;
}
Step2: Set Setter method to notify us of progress change status
/**
* Set Progress
* @param progress
/public void setprogress (int progress) {
this.progress = progress;
Invalidate ();
}
/**
* Set whether recording status is canceled
* @param iscancel
/public
void Setcancel (Boolean iscancel) {
This.iscancel = Iscancel;
Invalidate ();
}
/**
* Overrides whether the method is visible
* @param visibility * *
@Override public
void setvisibility (int visibility) { c33/>mvisibility = visibility;
Redraw
invalidate ();
}
Step3: The most important step is to draw our progress bar, using the OnDraw (Canvas Canvas) method in view
@Override
protected void OnDraw (Canvas Canvas) {
super.ondraw (Canvas);
if (mvisibility = = view.visible) {
int height = getheight ();
int width = getwidth ();
int mid = WIDTH/2;
Draw the progress bar
if (Progress < mid) {
Canvas.drawrect (progress, 0, width-progress, height, iscancel? mcancelpaint:mr) ecordpaint);
} else {
if (Monprogressendlistener!= null) {
monprogressendlistener.onprogressendlistener ();
}
}
else {
canvas.drawcolor (color.argb (0, 0, 0, 0));
}
Handling of recording events
The events that are triggered in the recording include four:
- Long Press Recording
- Lift Up Save
- Up-Slide Cancel
- Double click to enlarge (zoom)
Now analyze each of these 4 events individually:
The first three of these events, I put them in a Ontouch () callback method.
For the 4th one, we'll talk.
Let's first enumerate the local variables in Ontouch ():
@Override Public
Boolean Ontouch (View V, motionevent event) {
Boolean ret = false;
int action = Event.getaction ();
float ey = event.gety ();
float ex = Event.getx ();
Only listens to the middle button place
int vW = V.getwidth ();
int left = Listener_start;
int right = Vw-listener_start;
float DownY = 0;
// ...
}
Long Press Recording
Long press Recording we need to monitor the Action_down event and use thread delay to send handler to implement the progress bar update
switch (action) {case MotionEvent.ACTION_DOWN:if (ex > left && ex < right) {
Mprogressbar.setcancel (FALSE);
Display up-slip cancellation mtvtip.setvisibility (view.visible);
Mtvtip.settext ("↑ Up-slip-Cancel");
Record press Y-coordinate downY = ey;
TODO:2016/10/20 began to record video, the progress bar began to walk mprogressbar.setvisibility (view.visible);
Start Recording Toast.maketext (this, start recording, Toast.length_short). Show ();
Startrecord ();
Mprogressthread = new Thread () {@Override public void run () {super.run ();
try {mprogress = 0;
IsRunning = true;
while (isrunning) {mprogress++;
Mhandler.obtainmessage (0). Sendtotarget ();
Thread.Sleep (20);
} catch (Interruptedexception e) {e.printstacktrace ();
}
}
};
Mprogressthread.start ();
ret = true;
} break;
. return true; }
Note:startrecord () This method is not to say, we only need to know that it can be recorded, but the handler event is still to be said, it is only responsible for updating progress bar progress
//handler processing
//////////////////////////////////////
private static class MyHandler extends Handler {
private weakreference<mainactivity> mreference;
Private mainactivity mactivity;
Public MyHandler (mainactivity activity) {
mreference = new weakreference<mainactivity> (activity);
Mactivity = Mreference.get ();
}
@Override public
void Handlemessage (msg) {
switch (msg.what) {case
0:
MActivity.mProgressBar.setProgress (mactivity.mprogress);
break;
}}}
Lift Up Save
Also, we need to listen for ACTION_UP events, but consider that when the user lifts too fast (the recording time is too short), it does not need to be saved. And, in this event contains the cancellation of the lifting of the state, explained: is when the slip lifted the moment to cancel the recording, we look at the code
Case MOTIONEVENT.ACTION_UP:
if (ex > left && ex < right) {
mtvtip.setvisibility (view.invisible); C3/>mprogressbar.setvisibility (view.invisible);
Determines whether the recording ends, or if the successfully recorded (short time)
if (!iscancel) {
if (Mprogress <) {
//time is too short to save
Stoprecordunsave ();
Toast.maketext (This, "time is too Short", Toast.length_short). Show ();
break;
Stop recording
Stoprecordsave ();
} else {
//Now is canceled, do not save
Stoprecordunsave ();
Iscancel = false;
Toast.maketext (this, cancel recording, Toast.length_short). Show ();
Mprogressbar.setcancel (false);
ret = false;
}
Break
Note: The same, the internal Stoprecordunsave () and Stoprecordsave (); we'll introduce them later, and they can tell by name the first to stop recording but not save, which stops recording and saves
Up-Slide Cancel
With the previous part of the lifting of the cancellation event, implementation of the slip cancellation
Case Motionevent.action_move:
if (ex > left && ex < right) {
float currenty = event.gety ();
if (Downy-currenty >) {
iscancel = true;
Mprogressbar.setcancel (True);
}
Break
Note: The main principle is not difficult, as long as you press and move up a certain distance will trigger, when the hand is raised when the video recording canceled
Double click to enlarge (zoom)
This event is very special, using Google's gesturedetector gesture detection to determine the double click event
Step1: Surfaceview A separate touch event monitor, why? Because Gesturedetector needs the full hosting of the touch event, if only a partial event is passed to it, it will cause some event to fail.
Mdetector = new Gesturedetector (This, new Zoomgesturelistener ());
/**
* Handle Msurfaceview Double-click event individually *
*
msurfaceview.setontouchlistener (new View.ontouchlistener () {
@ Override Public
Boolean Ontouch (View V, motionevent event) {
mdetector.ontouchevent (event);
return true;
}
});
Step2: Overriding Gesturedetector.simpleongesturelistener, implementing double click events
//Zoom gesture Processing class
/////////////////
class Zoomgesturelistener extends Gesturedetector.simpleongesturelistener {
//double-click Gesture Event
@Override public
boolean Ondoubletap (Motionevent e) {
Super.ondoubletap (e);
LOG.D (TAG, "ONDOUBLETAP: Double click event");
if (Mmediarecorder!= null) {
if (!iszoomin) {
setzoom);
Iszoomin = true;
} else {
setzoom (0);
Iszoomin = false;
}
}
return true;
}
Step3: The method of realizing the zoom of camera
/**
* Camera Zoom
*
* @param zoomvalue */public
void setzoom (int zoomvalue) {
if (Mcamera!= null) {
camera.parameters Parameters = Mcamera.getparameters ();
if (parameters.iszoomsupported ()) {//Determine whether to support
int maxzoom = Parameters.getmaxzoom ();
if (Maxzoom = = 0) {return
;
}
if (Zoomvalue > Maxzoom) {
zoomvalue = maxzoom;
}
Parameters.setzoom (zoomvalue);
Mcamera.setparameters (parameters);}}
Note: So far we have completed the monitoring of all events, see here Everyone may be a little tired, but do not lose heart, now complete our core part, the realization of video recording
To implement video recording
Say is the core function, it is just that we do not know some API methods, the following code I have added a detailed annotation, some can not understand the memory is good ^v^
/** * Start recording/private void Startrecord () {if (Mmediarecorder!= null) {//No external storage, stop recording directly if (!)
Environment.getexternalstoragestate (). Equals (environment.media_mounted)) {return;
try {//mmediarecorder.reset ();
Mcamera.unlock ();
Mmediarecorder.setcamera (Mcamera);
Capture video from Camera Mmediarecorder.setvideosource (MediaRecorder.VideoSource.CAMERA);
Collect audio information from Mike Mmediarecorder.setaudiosource (MediaRecorder.AudioSource.MIC);
TODO:2016/10/20 Set Video format Mmediarecorder.setoutputformat (MediaRecorder.OutputFormat.MPEG_4);
Mmediarecorder.setvideosize (Videowidth, videoheight);
Number of frames per second mmediarecorder.setvideoframerate (24);
Encoding format mmediarecorder.setvideoencoder (MediaRecorder.VideoEncoder.DEFAULT);
Mmediarecorder.setaudioencoder (MediaRecorder.AudioEncoder.AMR_NB);
Set the frame frequency and then clear the mmediarecorder.setvideoencodingbitrate (1 * 1024 * 1024 * 100); TODO:2016/10/20 temporarily write a file address, wait for the!!!
File TargetDir = environment.
Getexternalstoragepublicdirectory (environment.directory_movies);
Mtargetfile = new File (TargetDir, Systemclock.currentthreadtimemillis () + ". mp4");
Mmediarecorder.setoutputfile (Mtargetfile.getabsolutepath ());
Mmediarecorder.setpreviewdisplay (Msurfaceholder.getsurface ());
Mmediarecorder.prepare ();
Formal recording of Mmediarecorder.start ();
IsRecording = true;
catch (Exception e) {e.printstacktrace ();
}
}
}
Implement video Stop
You may ask, why is the video stop being withdrawn separately? The careful classmate looks at the above code to see these two methods: Stoprecordsave and Stoprecordunsave, one stops saves, one is stops does not save, next we will fill up this pit
Stop and save
private void Stoprecordsave () {
if (isrecording) {
isrunning = false;
Mmediarecorder.stop ();
IsRecording = false;
Toast.maketext (This, "video has been placed" + Mtargetfile.getabsolutepath (), Toast.length_short). Show ();
}
Stop without saving
private void Stoprecordunsave () {
if (isrecording) {
isrunning = false;
Mmediarecorder.stop ();
IsRecording = false;
if (mtargetfile.exists ()) {
//without saving directly delete
mtargetfile.delete ();}
}
Note: This stop does not save is an idea of my own, if you have a better idea, welcome to the comments to point out, appreciate
Complete code
SOURCE I have put on the GitHub, blogging is not easy! Writing a decent blog is not easy, I hope that we support
Summarize
Finally finished writing!!! This is the most I want to say, from the beginning of the case to now has been a long time. This is the longest I wrote a blog, it is difficult to express my own ideas, this is my biggest feeling!!!
To be honest, this case is not very difficult, but it is very good for beginners like me to practice practicing, thanks to Jack's blog and a lot of help for me.
Thank you for reading, I hope to help you, thank you for your support for this site!