Android5.0以上版本录屏实现代码(完整代码)

我录屏的方式是分别录制音频和视频,最后合并成mp4格式,比较麻烦,因为网上完整的教程比较少,所以我打算写一个完整版的,照着我的代码写完之后,至少是能够实现功能的,而不是简单的介绍下用法。

1既然是录制视频,我们应该有一个按钮控制开始和结束。

2在录制之前,需要先判断一下Android系统的版本是否大于5.0,并且动态申请一下权限(读写,录音,照相机),这一步可以在点开始按钮的时候执行  

  if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
  != PackageManager.PERMISSION_GRANTED) {
  ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 102);
 }
 if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO)
  != PackageManager.PERMISSION_GRANTED) {
  ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 103);
 }
 if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
  != PackageManager.PERMISSION_GRANTED) {
  ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 104);
 }
 Intent intent = null;
 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
  intent = mediaProjectionManager.createScreenCaptureIntent();
  startActivityForResult(intent, 101);//正常情况是要执行到这里的,作用是申请捕捉屏幕
 } else {
  ShowUtil.showToast(context, "Android版本太低,无法使用该功能");
 }

3定义MediaProjection和MediaProjectionManager等一些其他必要的变量

  boolean isrun = false;//用来标记录屏的状态private MediaProjectionManager mediaProjectionManager;
  private MediaProjection mediaProjection;//录制视频的工具private int width, height, dpi;//屏幕宽高和dpi,后面会用到
  private ScreenRecorder screenRecorder;//这个是自己写的录视频的工具类,下文会放完整的代码
  Thread thread;//录视频要放在线程里去执行

在onCreat里写好实例化

mediaProjectionManager = (MediaProjectionManager) context.getSystemService(MEDIA_PROJECTION_SERVICE);
WindowManager manager = this.getWindowManager();
DisplayMetrics outMetrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(outMetrics);
width = outMetrics.widthPixels;
height = outMetrics.heightPixels;
dpi = outMetrics.densityDpi;

4我们在onActivityResult回调方法中,来处理返回的事件

@Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 if (requestCode == 102) {
  Toast.makeText(context, "缺少读写权限", Toast.LENGTH_SHORT).show();
  return;
 }
 if (requestCode == 103) {
  Toast.makeText(context, "缺少录音权限", Toast.LENGTH_SHORT).show();
  return;
 }
 if (requestCode == 104) {
  Toast.makeText(context, "缺少相机权限", Toast.LENGTH_SHORT).show();
  return;
 }
 if (requestCode != 101) {
  Log.e("HandDrawActivity", "error requestCode =" + requestCode);
 }
 if (resultCode != RESULT_OK) {
  Toast.makeText(context, "捕捉屏幕被禁止", Toast.LENGTH_SHORT).show();
  return;
 }
 mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
 if (mediaProjection != null) {
  screenRecorder = new ScreenRecorder(width, height, mediaProjection, dpi);
 }
 thread = new Thread() {
  @Override
  public void run() {
  screenRecorder.startRecorder();//跟ScreenRecorder有关的下文再说,总之这句话的意思就是开始录屏的意思
  }
 };
 thread.start();
 binding.startPlayer.setText("停止");//开始和停止我用的同一个按钮,所以开始录屏之后把按钮文字改一下
 isrun = true;//录屏状态改成真
 }

5先放上ScreenRecorder代码,只想要结果的朋友呢,直接把类粘贴走,把报错的地方改一改(在我自己的项目里可是不报错的),就实现了录制屏幕的功能了,还想看看的,可以往下看看

import android.hardware.display.DisplayManager;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.os.Build;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.Surface;
import com.coremedia.iso.boxes.Container;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
import com.googlecode.mp4parser.authoring.tracks.AppendTrack;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class ScreenRecorder {
 private int mWidth, mHeight, mDensty;
 private MediaProjection mediaProjection;
 private MediaCodec.BufferInfo mBufferInfo;
 private MediaCodec mEncorder;
 private Surface mInputSurface;
 private MediaMuxer mMuxer;
 private boolean isQuit = false;
 private boolean mMuxerStarted = false;
 private int mTrackIndex;
 private String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/cache";
 private MediaRecorder mediaRecorder;
 public ScreenRecorder(int mWidth, int mHeight, MediaProjection mediaProjection, int mDensty) {
 this.mWidth = mWidth;
 this.mHeight = mHeight;
 this.mediaProjection = mediaProjection;
 this.mDensty = mDensty;
 File file = new File(path);
 if (!file.exists()) {
  file.mkdirs();
 }
 }
 public void startRecorder() {
 prepareRecorder();
 startLuYin();
 startRecording();
 }
 public void stop() {
 isQuit = true;
 releaseEncorders(1);
 List<String> filePath = new ArrayList<>();
 filePath.add(path + "/APlanyinpin.amr");
 filePath.add(path + "/APlanshipin.mp4");
 joinVideo(filePath, path);
 }
 public void destory() {
 releaseEncorders(0);
 }
 private void startLuYin() {
 File file = new File(path, "APlanyinpin.amr");
 mediaRecorder = new MediaRecorder();
 mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
 mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
 mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
 mediaRecorder.setOutputFile(file.getAbsolutePath());
 try {
  mediaRecorder.prepare();
  mediaRecorder.start();
  Log.e("HandDrawActivity", "已经开始录音");
 } catch (IOException e) {
  e.printStackTrace();
 }
 }
 private void prepareRecorder() {
 mBufferInfo = new MediaCodec.BufferInfo(); //元数据,描述bytebuffer的数据,尺寸,偏移
 //创建格式化对象 MIMI_TYPE 传入的 video/avc 是H264编码格式
 MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);
 int frameRate = 45;
 format.setInteger(MediaFormat.KEY_BIT_RATE, 3000000);
 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
 format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
 format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
 format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate);
 format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / frameRate);
 try {
  mEncorder = MediaCodec.createEncoderByType("video/avc");
  mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
  mInputSurface = mEncorder.createInputSurface();
  mEncorder.start();
 } catch (IOException e) {
  e.printStackTrace();
  releaseEncorders(0);
 }
 }
 private void startRecording() {
 File saveFile = new File(path, "APlanshipin.mp4");
 try {
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  mMuxer = new MediaMuxer(saveFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
  mediaProjection.createVirtualDisplay("SCREENRECORDER", mWidth, mHeight, mDensty, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
   mInputSurface, null, null);
  drainEncoder();
  }
 } catch (Exception e) {
  e.printStackTrace();
 }
 }
 private void drainEncoder() {
 while (!isQuit) {
  Log.e("TAG", "drain.....");
  int bufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
  if (bufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
  try {
   Thread.sleep(10);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  }
  if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
  mTrackIndex = mMuxer.addTrack(mEncorder.getOutputFormat());
  if (!mMuxerStarted && mTrackIndex >= 0) {
   mMuxer.start();
   mMuxerStarted = true;
   Log.e("HandDrawActivity", "已经开始录屏");
  }
  }
  if (bufferIndex >= 0) {
  Log.e("TAG", "drain...write..");
  ByteBuffer bufferData = mEncorder.getOutputBuffer(bufferIndex);
  if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
   mBufferInfo.size = 0;
  }
  if (mBufferInfo.size != 0) {
   if (mMuxerStarted) {
   bufferData.position(mBufferInfo.offset);
   bufferData.limit(mBufferInfo.offset + mBufferInfo.size);
   mMuxer.writeSampleData(mTrackIndex, bufferData, mBufferInfo);
   }
  }
  mEncorder.releaseOutputBuffer(bufferIndex, false);
  if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
   break;
  }
  }
 }
 Log.e("HandDrawActivity", "已经结束录屏");
 }
 private void releaseEncorders(int i) {
 if (mediaProjection != null) {
  mediaProjection.stop();
 }
 mBufferInfo = null;
 if (mEncorder != null) {
  mEncorder.stop();
 }
 mInputSurface = null;
 if (mMuxer != null && i == 1) {
  mMuxer.stop();
 }
 if (mediaRecorder != null) {
  mediaRecorder.stop();
  mediaRecorder.reset();
  mediaRecorder.release();
 }
 }
 private boolean joinVideo(List<String> filePaths, String resultPath) {
 Log.e("HandDrawActivity", "准备合成中");
 boolean result = false;
 if (filePaths == null || filePaths.size() <= 0 || TextUtils.isEmpty(resultPath)) {
  throw new IllegalArgumentException();
 }
 if (filePaths.size() == 1) { // 只有一个视频片段,不需要合并
  return true;
 }
 try {
  Movie[] inMovies = new Movie[filePaths.size()];
  for (int i = 0; i < filePaths.size(); i++) {
  Log.e("HandDrawActivity", "filePaths=" + filePaths.get(i));
  File f = new File(filePaths.get(i));
  if (f.exists()) {
   inMovies[i] = MovieCreator.build(filePaths.get(i));
  }
  }
  // 分别取出音轨和视频
  List<Track> videoTracks = new LinkedList<>();
  List<Track> audioTracks = new LinkedList<>();
  for (Movie m : inMovies) {
  for (Track t : m.getTracks()) {
   if (t.getHandler().equals("soun")) {
   audioTracks.add(t);
   }
   if (t.getHandler().equals("vide")) {
   videoTracks.add(t);
   }
  }
  }
  // 合并到最终的视频文件
  Movie outMovie = new Movie();
  if (audioTracks.size() > 0) {
  outMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
  }
  if (videoTracks.size() > 0) {
  outMovie.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
  }
  Container mp4file = new DefaultMp4Builder().build(outMovie);
  // 将文件输出
  File resultFile = new File(resultPath, "APlanTeacherAnswer.mp4");
  if (resultFile.exists() && resultFile.isFile()) {
  resultFile.delete();
  }
  FileChannel fc = new RandomAccessFile(resultFile, "rw").getChannel();
  mp4file.writeContainer(fc);
  fc.close();
  Log.e("HandDrawActivity", "合成完毕");
  // 合成完成后把原片段文件删除
  for (String filePath : filePaths) {
  File file = new File(filePath);
  file.delete();
  }
  result = true;
  HandDrawActivity.sendVideo();
 } catch (FileNotFoundException e) {
  e.printStackTrace();
 } catch (Exception e) {
  e.printStackTrace();
 }
 return result;
 }
}

6从startRecorder方法说起

public void startRecorder() {
 prepareRecorder();//录视频前的准备
 startLuYin();//直接录音频(不用准备)
 startRecording();//录视频
 }

录音的方法

private void startLuYin() {
 File file = new File(path, "APlanyinpin.amr");
 mediaRecorder = new MediaRecorder();
 mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//声音来源,麦克
 mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);//音频格式,默认,其实就是上面定义好的amr了,除此之外还有mp4
 mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//编码格式,问题是我不知道编码格式对什么有影响,是音质高低还是文件大小还是解析快慢,等我有时间去专门研究一下
 mediaRecorder.setOutputFile(file.getAbsolutePath());
 try {
  mediaRecorder.prepare();
  mediaRecorder.start();
  Log.e("HandDrawActivity", "已经开始录音");
 } catch (IOException e) {
  e.printStackTrace();
 }
 }
//录视频前的准备工作
private void prepareRecorder() {
 mBufferInfo = new MediaCodec.BufferInfo(); //元数据,描述bytebuffer的数据,尺寸,偏移
 //创建格式化对象 MIMI_TYPE 传入的 video/avc 是H264编码格式
 MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);
 int frameRate = 45;
 format.setInteger(MediaFormat.KEY_BIT_RATE, 3000000);
 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
 format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
 format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
 format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
 format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate);
 format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / frameRate);//编码器的设置,具体是设置的啥我也不太清楚,但是网上查一查都是这么写的!!!
 try {
  mEncorder = MediaCodec.createEncoderByType("video/avc");
  mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
  mInputSurface = mEncorder.createInputSurface();
  mEncorder.start();//让编码器先跑起来
 } catch (IOException e) {
  e.printStackTrace();
  releaseEncorders(0);
 }
 }

这里也是准备工作

private void startRecording() {
 File saveFile = new File(path, "APlanshipin.mp4");
 try {
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  mMuxer = new MediaMuxer(saveFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);//百度一下MediaMuxer,讲的很详细的
  mediaProjection.createVirtualDisplay("SCREENRECORDER", mWidth, mHeight, mDensty, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
   mInputSurface, null, null);
  drainEncoder();
  }
 } catch (Exception e) {
  e.printStackTrace();
 }
 }

这个就是开始写视频文件了

private void drainEncoder() {
 while (!isQuit) {
  Log.e("TAG", "drain.....");
  int bufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
  if (bufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
  try {
   Thread.sleep(10);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  }
  if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
  mTrackIndex = mMuxer.addTrack(mEncorder.getOutputFormat());
  if (!mMuxerStarted && mTrackIndex >= 0) {
   mMuxer.start();
   mMuxerStarted = true;
   Log.e("HandDrawActivity", "已经开始录屏");
  }
  }
  if (bufferIndex >= 0) {
  Log.e("TAG", "drain...write..");
  ByteBuffer bufferData = mEncorder.getOutputBuffer(bufferIndex);
  if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
   mBufferInfo.size = 0;
  }
  if (mBufferInfo.size != 0) {
   if (mMuxerStarted) {
   bufferData.position(mBufferInfo.offset);
   bufferData.limit(mBufferInfo.offset + mBufferInfo.size);
   mMuxer.writeSampleData(mTrackIndex, bufferData, mBufferInfo);
   }
  }
  mEncorder.releaseOutputBuffer(bufferIndex, false);
  if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
   break;
  }
  }
 }
 Log.e("HandDrawActivity", "已经结束录屏");
 }

这个就是把录好的音频和视频合并成mp4的方法了,也是点击停止录屏的时候用到的

private boolean joinVideo(List<String> filePaths, String resultPath) {
 Log.e("HandDrawActivity", "准备合成中");
 boolean result = false;
 if (filePaths == null || filePaths.size() <= 0 || TextUtils.isEmpty(resultPath)) {
  throw new IllegalArgumentException();
 }
 if (filePaths.size() == 1) { // 只有一个视频片段,不需要合并
  return true;
 }
 try {
  Movie[] inMovies = new Movie[filePaths.size()];
  for (int i = 0; i < filePaths.size(); i++) {
  Log.e("HandDrawActivity", "filePaths=" + filePaths.get(i));
  File f = new File(filePaths.get(i));
  if (f.exists()) {
   inMovies[i] = MovieCreator.build(filePaths.get(i));
  }
  }
  // 分别取出音轨和视频
  List<Track> videoTracks = new LinkedList<>();
  List<Track> audioTracks = new LinkedList<>();
  for (Movie m : inMovies) {
  for (Track t : m.getTracks()) {
   if (t.getHandler().equals("soun")) {
   audioTracks.add(t);
   }
   if (t.getHandler().equals("vide")) {
   videoTracks.add(t);
   }
  }
  }
  // 合并到最终的视频文件
  Movie outMovie = new Movie();
  if (audioTracks.size() > 0) {
  outMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
  }
  if (videoTracks.size() > 0) {
  outMovie.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
  }
  Container mp4file = new DefaultMp4Builder().build(outMovie);
  // 将文件输出
  File resultFile = new File(resultPath, "APlanTeacherAnswer.mp4");
  if (resultFile.exists() && resultFile.isFile()) {
  resultFile.delete();
  }
  FileChannel fc = new RandomAccessFile(resultFile, "rw").getChannel();
  mp4file.writeContainer(fc);
  fc.close();
  Log.e("HandDrawActivity", "合成完毕");
  // 合成完成后把原片段文件删除
  for (String filePath : filePaths) {
  File file = new File(filePath);
  file.delete();
  }
  result = true;
  HandDrawActivity.sendVideo();
 } catch (FileNotFoundException e) {
  e.printStackTrace();
 } catch (Exception e) {
  e.printStackTrace();
 }
 return result;
 }

这个就是结束的时候了,该清空的清空,该注销的注销, i是用来判断录没录的,有可能刚进入这个页面都没录过,直接就返回到别的页面了,那就有可能空指针异常,因为有些变量都没初始化,所以用i判断一下,也可以自己写别的方法判端

private void releaseEncorders(int i) {
 if (mediaProjection != null) {
  mediaProjection.stop();
 }
 mBufferInfo = null;
 if (mEncorder != null) {
  mEncorder.stop();
 }
 mInputSurface = null;
 if (mMuxer != null && i == 1) {
  mMuxer.stop();
 }
 if (mediaRecorder != null) {
  mediaRecorder.stop();
  mediaRecorder.reset();
  mediaRecorder.release();
 }
 }

7部分代码也是我从网上扒的,但是网上的代码就没怎么见过比较完整的版本的,我上面写的都是经过我自己测试绝对没问题的而且代码也没什么遗漏的,要是发现有遗漏的代码我后续再补上。

(0)

相关推荐

  • Android中手机录屏并转换GIF的两种方式

    之前在博文中为了更好的给大家演示APP的实现效果,本人了解学习了几种给手机录屏的方法,今天就给大家介绍两种我个人用的比较舒服的两种方法: (1)配置adb环境后,使用cmd命令将手机界面操作演示存为视频文件 (2)使用Google浏览器(Google Chrome)提供的扩展程序Vysor将手机界面演示在电脑上(几乎没有延时羡慕) 下面我们具体介绍两种方法的使用步骤: 一.使用cmd命令录屏 (1)SDK下载 网上有各种SDK下载的方法,个人认为安装AndroidStudio后连接自己的手机,根

  • android视频截屏&手机录屏实现代码

    本文介绍了android视频截屏&手机录屏实现代码,分享给大家,希望对大家有帮助 问题 在android中有时候我们需要对屏幕进行截屏操作,单一的截屏操作好解决可以通过activity的顶层view DecorView获取一个bitmap,得到就是当前activity上面的全部视图. View view = activity.getWindow().getDecorView(); view.setDrawingCacheEnabled(true); view.buildDrawingCache(

  • 详解有关Android截图与录屏功能的学习

    简单的截屏和录屏功能. 因为MediaProjection是5.0以上才出现的,所以今天所讲述功能实现,只在5.0以上的系统有效. 截屏: 步骤如下: 1:获取MediaProjectionManager 2:通过MediaProjectionManager.createScreenCaptureIntent()获取Intent 3:通过startActivityForResult传入Intent然后在onActivityResult中通过MediaProjectionManager.getMe

  • Android5.0以上版本录屏实现代码(完整代码)

    我录屏的方式是分别录制音频和视频,最后合并成mp4格式,比较麻烦,因为网上完整的教程比较少,所以我打算写一个完整版的,照着我的代码写完之后,至少是能够实现功能的,而不是简单的介绍下用法. 1既然是录制视频,我们应该有一个按钮控制开始和结束. 2在录制之前,需要先判断一下Android系统的版本是否大于5.0,并且动态申请一下权限(读写,录音,照相机),这一步可以在点开始按钮的时候执行 if (ContextCompat.checkSelfPermission(context, Manifest.

  • Android5.0中多种水波纹效果的实现代码

    水波纹效果已经不是什么稀罕的东西了,用过5.0新控件的小伙伴都知道这个效果,可是如果使用一个TextView或者Button或者其它普通控件的话,你是否知道如何给它设置水波纹效果呢?OK,我们今天就来看看这个水波纹效果的实现.水波纹效果的实现有系统自带属性可以实现,我们也可以自定义实现效果. 1.系统自带水波纹实现方式 有界水波纹 水波纹效果大致上可以分为两种,一种是有界的,一种无界,我们先来看看有界水波纹效果: 效果: 代码: <TextView android:layout_width=&quo

  • Python绘制的爱心树与表白代码(完整代码)

    Python给女朋友带来的快乐 用的的开发工具为pycham,pycham也是广泛用于做Python开发的工具.运用的turtle库,当然了如果是安装了anaconda3这个库那更好,以为这里面有我们会做Python程序设计时用到的大部分的库,turtle它是python中一个绘制图像的函数库,可以用它来绘制很多的东西,比如简单的小黄人.玫瑰花等,这个库也可以生说是一只马良的神笔的吧.比如以下是为女朋友准备的小小的惊喜吧. 1.爱心树 import turtle import random de

  • OpenCV实现简单录屏功能

    本文实例为大家分享了OpenCV实现简单录屏功能的具体代码,供大家参考,具体内容如下 OpenCV中VideoCapture和VideoWriter用于读写视频文件,这里的录屏功能用到VideoWriter,用于将捕获的屏幕的每一帧数据保存到视频文件. VideoWriter写视频文件的步骤 1.bool open(const String& filename, int fourcc, double fps,Size frameSize, bool isColor = true);2.void

  • android实现录屏功能

    本文实例为大家分享了android实现录屏功能的具体代码,供大家参考,具体内容如下 1.mian.activity package com.fpt.screenvideo; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.media.projection.MediaProjectionManager; import androi

  • Python统计词频并绘制图片(附完整代码)

    效果 1 实现代码 读取txt文件: def readText(text_file_path): with open(text_file_path, encoding='gbk') as f: # content = f.read() return content 得到文章的词频: def getRecommondArticleKeyword(text_content, key_word_need_num = 10, custom_words = [], stop_words =[], quer

  • 用python写一个福字(附完整代码)

    目录 前言: 一,扫五福活动如此火爆,为何不自己利用编程来生成福字! 二,完整代码 三,总结 前言: 支付宝 2022 集五福活动正式开启 数据显示,过去六年累计参与支付宝集五福的人数已经超过了 7 亿,每 2 个中国人里就有 1 个曾扫福.集福.送福. 一,扫五福活动如此火爆,为何不自己利用编程来生成福字! 首先作品奉上: ①,导入python库 import io from PIL import Image import requests ②,利用爬虫,获取单个汉字 def get_word

  • 用python写一个福字(附完整代码)

    目录 前言: 一,扫五福活动如此火爆,为何不自己利用编程来生成福字! 二,完整代码 三,总结 前言: 支付宝 2022 集五福活动正式开启 数据显示,过去六年累计参与支付宝集五福的人数已经超过了 7 亿,每 2 个中国人里就有 1 个曾扫福.集福.送福. 一,扫五福活动如此火爆,为何不自己利用编程来生成福字! 首先作品奉上: ①,导入python库 import io from PIL import Image import requests ②,利用爬虫,获取单个汉字 def get_word

  • C# wpf使用ffmpeg命令行实现录屏的示例代码

    目录 前言 一.主要步骤 1.使用 AllowsTransparency实现穿透框 2.获取音频设备名称 3.命令行启动ffmpeg 4.使用JobObject管理子进程 二.完整代码 三.效果预览 1.录制中 2.录制动态流程 总结 前言 上一章我们实现了截屏界面与功能,接下来可以在此基础上实现录屏功能,录屏采用ffmpeg命令行实现会方便一些,效果也是不错的,当然前提是要对Windows子进程的控制比较熟悉,做出来之后完全可以满足项目使用. 一.主要步骤 1.使用 AllowsTranspa

随机推荐