首頁 微信小程式 微信開發 使用Android實現微信小錄影功能詳細介紹

使用Android實現微信小錄影功能詳細介紹

Mar 23, 2017 pm 01:39 PM

這篇文章主要介紹了Android 微信小視頻錄製功能實現詳解的相關資料,這裡提供了具體的實現思路及代碼,需要的朋友可以參考下

Android 微信小視頻錄製功能

開發之前

這幾天接觸了一下和視頻相關的控件, 所以, 繼之前的微信搖一搖, 我想到了來實現一下微信小視頻錄製的功能, 它的功能點比較多, 我每天都抽出點時間來寫寫, 說實話, 有些東西還是比較費勁, 希望大家認真看看, 說得不對的地方還請大家在評論中指正. 廢話不多說, 進入正題.

開發環境

最近剛更新的, 沒更新的小夥伴們抓緊了

  1. Android Studio 2.2.2

  2. JDK1.7

  3. #API 24

  4. Gradle 2.2 .2

相關知識點

  1. #影片錄製介面SurfaceView 的使用

## Camera的使用

相機的對焦, 變焦

  1. #影片錄製控制項MediaRecorder的使用

  2. 簡單自訂View

  3. GestureDetector(手勢偵測)的使用

  4. 用到的東西真不少, 不過別急, 咱們一個一個來.

    開始開發
  5. 案例分析

  6. #大家可以打開自己微信裡面的小影片, 一塊簡單的分析一下它的功能點有哪些?

基本的影片預覽功能

# 長按「按住拍」實現影片的錄製

錄製過程中的進度條從兩側向中間變短


#當鬆手或進度條走到盡頭影片停止錄製並保存

從「按住拍攝」 上滑取消影片的錄製


雙擊螢幕變焦放大


根據上述的分析, 我們一步一步的完成

搭建佈局

佈局介面的實作還可以, 難度不大

<?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_gravity="bottom|center_horizontal"
    android:layout_marginBottom="150dp"
    android:elevation="1dp"
    android:text="双击放大"
    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"/>
    <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="0dp"
      android:layout_weight="1"
      android: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.weichatsamplevideo.BothWayProgressBar
          android:id="@+id/main_progress_bar"
          android:layout_width="match_parent"
          android:layout_height="2dp"
          android:background="#000"/>
        <TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_centerInParent="true"
          android:text="按住拍"
          android:textAppearance="@style/TextAppearance.AppCompat.Large"
          android:textColor="#00ff00"/>
      </RelativeLayout>
    </LinearLayout>
  </LinearLayout>
</FrameLayout>
登入後複製

影片預覽的實作

step1: 得到SufaceView控制項, 設定基本屬性和對應監聽(該控制項的建立是異步的, 只有在真正」準備」好之後才能呼叫)


mSurfaceView = (SurfaceView) findViewById(R.id.main_surface_view);
 //设置屏幕分辨率
mSurfaceHolder.setFixedSize(videoWidth, videoHeight);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mSurfaceHolder.addCallback(this);
登入後複製

step2: 實作介面的方法, surfaceCreated方法中開啟影片的預覽, 在surfaceDestroyed中銷毀


//////////////////////////////////////////////
// SurfaceView回调
/////////////////////////////////////////////
@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: ");
    //停止预览并释放摄像头资源
    mCamera.stopPreview();
    mCamera.release();
    mCamera = null;
  }
  if (mMediaRecorder != null) {
    mMediaRecorder.release();
    mMediaRecorder = null;
  }
}
登入後複製

step3: 實作影片預覽的方法


/**
 * 开启预览
 *
 * @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();
      //实现Camera自动对焦
      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: 上面新增了自動對焦的程式碼, 但是部分手機可能不支援


#自訂雙向縮減的進度條

有些像我一樣的初學者一看到自訂某某View, 就覺得比較牛X. 其實呢, Google已經替我們寫好了很多程式碼, 所以我們用就行了.而且咱們的這個進度條也沒啥, 不就是一根線, 今天咱就來說說.

step1: 繼承View, 完成初始化
  1. private static final String TAG = "BothWayProgressBar";
    //取消状态为红色bar, 反之为绿色bar
    private boolean isCancel = false;
    private Context mContext;
    //正在录制的画笔
    private Paint mRecordPaint;
    //上滑取消时的画笔
    private Paint mCancelPaint;
    //是否显示
    private int mVisibility;
    // 当前进度
    private int progress;
    //进度条结束的监听
    private OnProgressEndListener mOnProgressEndListener;
    
    public BothWayProgressBar(Context context) {
       super(context, null);
    }
    public BothWayProgressBar(Context 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);
    }
    登入後複製
  2. Note: OnProgressEndListener, 主要用於當進度條走到中間了, 好通知相機停止錄製, 介面如下:
  3. public interface OnProgressEndListener{
      void onProgressEndListener();
    }
    /**
     * 当进度条结束后的 监听
     * @param onProgressEndListener
     */
    public void setOnProgressEndListener(OnProgressEndListener onProgressEndListener) {
      mOnProgressEndListener = onProgressEndListener;
    }
    登入後複製
  4. step2 :設定Setter方法用於通知我們的Progress改變狀態

  5. #
    /**
     * 设置进度
     * @param progress
     */
    public void setProgress(int progress) {
      this.progress = progress;
      invalidate();
    }
    
    /**
     * 设置录制状态 是否为取消状态
     * @param isCancel
     */
    public void setCancel(boolean isCancel) {
      this.isCancel = isCancel;
      invalidate();
    }
    /**
     * 重写是否可见方法
     * @param visibility
     */
    @Override
    public void setVisibility(int visibility) {
      mVisibility = visibility;
      //重新绘制
      invalidate();
    }
    登入後複製

    step3 :最重要的一步, 畫出我們的進度條,使用的就是View中的onDraw(Canvas canvas)方法


@Override
protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  if (mVisibility == View.VISIBLE) {
    int height = getHeight();
    int width = getWidth();
    int mid = width / 2;


    //画出进度条
    if (progress < mid){
      canvas.drawRect(progress, 0, width-progress, height, isCancel ? mCancelPaint : mRecordPaint);
    } else {
      if (mOnProgressEndListener != null) {
        mOnProgressEndListener.onProgressEndListener();
      }
    }
  } else {
    canvas.drawColor(Color.argb(0, 0, 0, 0));
  }
}
登入後複製

錄製事件的處理

錄製中觸發的事件包括四個:


長按錄製

抬起儲存


上滑取消






####雙擊放大(變焦) ############ 現在對這4個事件逐一分析: ###前三這個事件, 我都放在了一個onTouch()回呼方法中了###對於第4個, 我們待會談###我們先把onTouch()中局部變數列舉一下:############
@Override
public boolean onTouch(View v, MotionEvent event) {
  boolean ret = false;
  int action = event.getAction();
  float ey = event.getY();
  float ex = event.getX();
  //只监听中间的按钮处
  int vW = v.getWidth();
  int left = LISTENER_START;
  int right = vW - LISTENER_START;
  float downY = 0;
  // ...
}
登入後複製
######長按錄製#########長按錄製我們需要監聽ACTION_DOWN事件, 使用執行緒延遲發送Handler來實現進度列的更新###############
switch (action) {
 case MotionEvent.ACTION_DOWN:
   if (ex > left && ex < right) {
     mProgressBar.setCancel(false);
     //显示上滑取消
     mTvTip.setVisibility(View.VISIBLE);
     mTvTip.setText("↑ 上滑取消");
     //记录按下的Y坐标
     downY = ey;
     // TODO: 2016/10/20 开始录制视频, 进度条开始走
     mProgressBar.setVisibility(View.VISIBLE);
     //开始录制
     Toast.makeText(this, "开始录制", 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()這個方法先不說, 我們只需要知道執行了它就可以錄製了, 但是Handler事件還是要說的, 它只負責更新進度條的進度############
////////////////////////////////////////////////////
// Handler处理
/////////////////////////////////////////////////////
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(Message msg) {
    switch (msg.what) {
      case 0:
        mActivity.mProgressBar.setProgress(mActivity.mProgress);
        break;
    }
  }
}
登入後複製
######抬起保存#########同樣我們這兒需要監聽ACTION_UP事件, 但是要考慮當用戶抬起過快時(錄製的時間過短), 不需要保存. 而且,在這個事件中包含了取消狀態的抬起, 解釋一下: 就是當上滑取消時抬起的一瞬間取消錄製, 大家看代碼###############
case MotionEvent.ACTION_UP:
 if (ex > left && ex < right) {
   mTvTip.setVisibility(View.INVISIBLE);
   mProgressBar.setVisibility(View.INVISIBLE);
   //判断是否为录制结束, 或者为成功录制(时间过短)
   if (!isCancel) {
     if (mProgress < 50) {
       //时间太短不保存
       stopRecordUnSave();
       Toast.makeText(this, "时间太短", Toast.LENGTH_SHORT).show();
       break;
     }
     //停止录制
     stopRecordSave();
   } else {
     //现在是取消状态,不保存
     stopRecordUnSave();
     isCancel = false;
     Toast.makeText(this, "取消录制", Toast.LENGTH_SHORT).show();
     mProgressBar.setCancel(false);
   }

   ret = false;
 }
 break;
登入後複製
## #Note: 同樣的, 內部的stopRecordUnSave()和stopRecordSave();大家先不要考慮, 我們會在後面介紹, 他倆從名字就能看出前者用來停止錄製但不保存, 後者停止錄製並保存############上滑取消#########配合上一部分說得抬起取消事件, 實作上滑取消############
case MotionEvent.ACTION_MOVE:
 if (ex > left && ex < right) {
   float currentY = event.getY();
   if (downY - currentY > 10) {
     isCancel = true;
     mProgressBar.setCancel(true);
   }
 }
 break;
登入後複製

Note: 主要原理不难, 只要按下并且向上移动一定距离 就会触发,当手抬起时视频录制取消

双击放大(变焦)

这个事件比较特殊, 使用了Google提供的GestureDetector手势检测 来判断双击事件

step1: 对SurfaceView进行单独的Touch事件监听, why? 因为GestureDetector需要Touch事件的完全托管, 如果只给它传部分事件会造成某些事件失效


mDetector = new GestureDetector(this, new ZoomGestureListener());
/**
 * 单独处理mSurfaceView的双击事件
 */
mSurfaceView.setOnTouchListener(new View.OnTouchListener() {
  @Override
  public boolean onTouch(View v, MotionEvent event) {
    mDetector.onTouchEvent(event);
    return true;
  }
});
登入後複製

step2: 重写GestureDetector.SimpleOnGestureListener, 实现双击事件


///////////////////////////////////////////////////////////////////////////
// 变焦手势处理类
///////////////////////////////////////////////////////////////////////////
class ZoomGestureListener extends GestureDetector.SimpleOnGestureListener {
  //双击手势事件
  @Override
  public boolean onDoubleTap(MotionEvent e) {
    super.onDoubleTap(e);
    Log.d(TAG, "onDoubleTap: 双击事件");
    if (mMediaRecorder != null) {
      if (!isZoomIn) {
        setZoom(20);
        isZoomIn = true;
      } else {
        setZoom(0);
        isZoomIn = false;
      }
    }
    return true;
  }
}
登入後複製

step3: 实现相机的变焦的方法


/**
 * 相机变焦
 *
 * @param zoomValue
 */
public void setZoom(int zoomValue) {
  if (mCamera != null) {
    Camera.Parameters parameters = mCamera.getParameters();
    if (parameters.isZoomSupported()) {//判断是否支持
      int maxZoom = parameters.getMaxZoom();
      if (maxZoom == 0) {
        return;
      }
      if (zoomValue > maxZoom) {
        zoomValue = maxZoom;
      }
      parameters.setZoom(zoomValue);
      mCamera.setParameters(parameters);
    }
  }

}
登入後複製

Note: 至此我们已经完成了对所有事件的监听, 看到这里大家也许有些疲惫了, 不过不要灰心, 现在完成我们的核心部分, 实现视频的录制

实现视频的录制

说是核心功能, 也只不过是我们不知道某些API方法罢了, 下面代码中我已经加了详细的注释, 部分不能理解的记住就好^v^


/**
 * 开始录制
 */
private void startRecord() {
  if (mMediaRecorder != null) {
    //没有外置存储, 直接停止录制
    if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
      return;
    }
    try {
      //mMediaRecorder.reset();
      mCamera.unlock();
      mMediaRecorder.setCamera(mCamera);
      //从相机采集视频
      mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
      // 从麦克采集音频信息
      mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
      // TODO: 2016/10/20 设置视频格式
      mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
      mMediaRecorder.setVideoSize(videoWidth, videoHeight);
      //每秒的帧数
      mMediaRecorder.setVideoFrameRate(24);
      //编码格式
      mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
      mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
      // 设置帧频率,然后就清晰了
      mMediaRecorder.setVideoEncodingBitRate(1 * 1024 * 1024 * 100);
      // TODO: 2016/10/20 临时写个文件地址, 稍候该!!!
      File targetDir = Environment.
          getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
      mTargetFile = new File(targetDir,
          SystemClock.currentThreadTimeMillis() + ".mp4");
      mMediaRecorder.setOutputFile(mTargetFile.getAbsolutePath());
      mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
      mMediaRecorder.prepare();
      //正式录制
      mMediaRecorder.start();
      isRecording = true;
    } catch (Exception e) {
      e.printStackTrace();
    }

  }
}
登入後複製

实现视频的停止

大家可能会问, 视频的停止为什么单独抽出来说呢? 仔细的同学看上面代码会看到这两个方法: stopRecordSave和stopRecordUnSave, 一个停止保存, 一个是停止不保存, 接下来我们就补上这个坑

停止并保存


private void stopRecordSave() {
  if (isRecording) {
    isRunning = false;
    mMediaRecorder.stop();
    isRecording = false;
    Toast.makeText(this, "视频已经放至" + mTargetFile.getAbsolutePath(), Toast.LENGTH_SHORT).show();
  }
}
登入後複製

停止不保存


private void stopRecordUnSave() {
  if (isRecording) {
    isRunning = false;
    mMediaRecorder.stop();
    isRecording = false;
    if (mTargetFile.exists()) {
      //不保存直接删掉
      mTargetFile.delete();
    }
  }
}
登入後複製

Note: 这个停止不保存是我自己的一种想法, 如果大家有更好的想法, 欢迎大家到评论中指出, 不胜感激

以上是使用Android實現微信小錄影功能詳細介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1665
14
CakePHP 教程
1424
52
Laravel 教程
1321
25
PHP教程
1269
29
C# 教程
1249
24