Heim  >  Artikel  >  WeChat-Applet  >  Detaillierte Einführung in die Verwendung von Android zur Implementierung der WeChat-Funktion zur kurzen Videoaufzeichnung

Detaillierte Einführung in die Verwendung von Android zur Implementierung der WeChat-Funktion zur kurzen Videoaufzeichnung

高洛峰
高洛峰Original
2017-03-23 13:39:442202Durchsuche

In diesem Artikel werden hauptsächlich relevante Informationen zur Implementierung der Android WeChat-Kurzvideoaufzeichnungsfunktion vorgestellt. Spezifische Implementierungsideen und Codes finden Sie hier.

Android WeChat-Kurzvideoaufzeichnung Funktion

Vor der Entwicklung

Ich war in den letzten Tagen videobezogenen Kontrollen ausgesetzt, daher dachte ich nach dem vorherigen WeChat-Shake darüber nach, ein WeChat-Kurzvideo zu implementieren Ich nehme mir jeden Tag die Zeit, darüber zu schreiben. Ich hoffe, dass sich jeder in den Kommentaren etwas Besseres ansieht . Kommen wir ohne weitere Umschweife zur Sache.

Entwicklungsumgebung

Es wurde erst vor Kurzem aktualisiert, Freunde, die noch nicht aktualisiert haben, beeilen Sie sich

  1. Android Studio 2.2.2

  2. JDK1.7

  3. API 24

  4. Gradle 2.2 .2

Verwandte Wissenspunkte

  1. Verwendung der Videoaufzeichnungsschnittstelle SurfaceView

  2. Die Verwendung der Kamera

  3. Der Fokus und Zoom der Kamera

  4. Die Verwendung der Videoaufzeichnungssteuerung MediaRecorder

  5. Einfache Anpassung der Ansicht

  6. GestureDetector (Gestenerkennung) verwenden

Es gibt viele Dinge verwendet, aber keine Sorge, lass uns einen nach dem anderen gehen.

Entwicklung starten

Fallanalyse

Sie können das kurze Video öffnen Ihr WeChat und analysieren Sie kurz seine Funktionen: Welche?

  1. Grundlegende Videovorschaufunktion

  2. Drücken Sie lange auf „Zum Aufnehmen gedrückt halten“ um ein Video aufzunehmen

  3. Der Fortschrittsbalken während der Aufnahme wird von beiden Seiten zur Mitte hin kürzer

  4. Wenn Sie loslassen oder der Fortschrittsbalken das erreicht Am Ende stoppt die Videoaufzeichnung und wird gespeichert

  5. Wischen Sie von „Zum Aufnehmen gedrückt halten“ nach oben, um die Videoaufzeichnung abzubrechen

  6. Doppeltippen Sie auf den Bildschirm zum Vergrößern

Gemäß der obigen Analyse haben wir es Schritt für Schritt abgeschlossen

Aufbau des Layouts

Die Implementierung der Layout-Schnittstelle ist in Ordnung und nicht schwierig


<?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>

Implementierung der Videovorschau

Schritt 1: Holen Sie sich die SurfaceView-Steuerelement, grundlegende Eigenschaften und entsprechende Überwachung festlegen (die Erstellung des Steuerelements erfolgt asynchron, nur wenn es wirklich „vorbereitet“ ist – kann erst aufgerufen werden, nachdem es bereit ist)


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

Schritt 2: Implementieren Sie die Schnittstellenmethode, öffnen Sie die Vorschau des Videos in der surfaceCreated-Methode und zerstören Sie es in 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;
  }
}

Schritt 3: Zu implementierende Methode Videovorschau


/**
 * 开启预览
 *
 * @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();
    }
  }

}

Hinweis: Der Autofokus-Code wird oben hinzugefügt, einige Mobiltelefone können jedoch

Benutzerdefinierte Zwei-Wege-Funktion sein Das Verkleinern des Fortschrittsbalkens wird nicht unterstützt

Einige Anfänger wie ich fühlen sich großartig, wenn sie sehen, wie eine bestimmte Ansicht X angepasst wird. Tatsächlich hat Google bereits viel Code für uns geschrieben, sodass wir ihn einfach verwenden können . Und unser Fortschrittsbalken ist nichts, nur eine Zeile. Reden wir heute darüber

Schritt 1: Ansicht erben, Initialisierung abschließen


: OnProgressEndListener, wird hauptsächlich verwendet, wenn der Fortschrittsbalken die Mitte erreicht, um die Kamera zu benachrichtigen, die Aufnahme zu beenden. Die Schnittstelle lautet wie folgt:
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);
}


Schritt 2: Festlegen die Setter-Methode, um unseren Fortschritt über die Statusänderung zu benachrichtigen
public interface OnProgressEndListener{
  void onProgressEndListener();
}
/**
 * 当进度条结束后的 监听
 * @param onProgressEndListener
 */
public void setOnProgressEndListener(OnProgressEndListener onProgressEndListener) {
  mOnProgressEndListener = onProgressEndListener;
}


Schritt 3: Der wichtigste Schritt besteht darin, unseren Fortschrittsbalken mithilfe der onDraw-Methode (Canvas-Leinwand) zu zeichnen Ansicht
/**
 * 设置进度
 * @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();
}



Verarbeitung von Aufnahmeereignissen
@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));
  }
}

Zu den während der Aufnahme ausgelösten Ereignissen gehören vier:

    Zum Aufnehmen lange drücken
  1. Zum Speichern hochheben
  2. Zum Abbrechen nach oben wischen
  3. Zum Vergrößern (Zoom) doppelklicken
  4. Analysieren Sie nun diese 4 Ereignisse einzeln:
Ich habe die ersten drei Ereignisse in einer onTouch()-Callback-Methode platziert

Über die vierte werden wir später sprechen
Lassen Sie uns zunächst die lokalen Variablen in onTouch() auflisten:


@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;
  // ...
}
Zum Aufzeichnen lange drücken

Zum Aufzeichnen lange drücken, wir müssen das ACTION_DOWN-Ereignis abhören und den Thread verwenden, um das Senden des Handlers zu verzögern, um den Fortschrittsbalken zu aktualisieren



Hinweis: Lassen Sie uns nicht über die startRecord()-Methode sprechen. Wir müssen nur wissen, dass sie nach der Ausführung aufgezeichnet werden kann, aber das Handler-Ereignis muss nur für die Aktualisierung des Fortschritts erwähnt werden bar
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;
}


////////////////////////////////////////////////////
// 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;
    }
  }
}
Hochheben und speichern

Ebenso müssen wir uns hier das ACTION_UP-Ereignis anhören, aber bedenken Sie das, wenn Wenn der Benutzer zu schnell hochhebt (die Aufnahmezeit ist zu kurz), besteht keine Notwendigkeit zum Speichern. Darüber hinaus umfasst dieses Ereignis das Hochheben im Abbruchzustand. Erklären Sie: Dies ist der Moment, in dem der Benutzer hochhebt, um die Aufnahme abzubrechen Brechen Sie die Aufnahme ab. Bitte schauen Sie sich den Code an Es ist jetzt, wir werden es später vorstellen. An den Namen können Sie erkennen, dass Ersteres zum Stoppen der Aufnahme, aber nicht zum Speichern verwendet wird, Letzteres zum Stoppen der Aufnahme und Speichern.


Nach oben wischen stornieren


Koordinate Ein Teil davon besagt, dass Sie das Stornierungsereignis erhöhen und erkennen müssen, dass Sie nach oben schieben müssen, um zu stornieren
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;


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

Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in die Verwendung von Android zur Implementierung der WeChat-Funktion zur kurzen Videoaufzeichnung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn