欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android实现Camera2预览和拍照效果

程序员文章站 2023-11-28 21:40:28
简介 网上对于 camera2 的介绍有很多,在 github 上也有很多关于 camera2 的封装库,但是对于那些库,封装性太强,有时候我们仅仅是需要个简简单单的拍照...

简介

网上对于 camera2 的介绍有很多,在 github 上也有很多关于 camera2 的封装库,但是对于那些库,封装性太强,有时候我们仅仅是需要个简简单单的拍照功能而已,因此,自定义一个 camera 使之变得轻量级那是非常重要的了。(本文并非重复造*, 而是在于学习 camera2api 的基本功能, 笔记之。)

学习要点:

使用 android camera2 api 的基本功能。
迭代连接到设备的所有相机的特征。
显示相机预览和拍摄照片。

camera2 api 为连接到 android 设备的各个相机设备提供了一个界面。 它替代了已弃用的 camera 类。

  • 使用 getcameraidlist 获取所有可用摄像机的列表。 然后,您可以使用 getcameracharacteristics,并找到适合您需要的最佳相机(前 / 后面,分辨率等)。
  • 创建一个 cameradevice.statecallback 的实例并打开相机。 当相机打开时,准备开始相机预览。
  • 使用 textureview 显示相机预览。 创建一个 cameracapturesession 并设置一个重复的 capturerequest。
  • 静像拍摄需要几个步骤。 首先,需要通过更新相机预览的 capturerequest 来锁定相机的焦点。
  • 然后,以类似的方式,需要运行一个预捕获序列。之后,它准备拍摄一张照片。 创建一个新的 capturerequest 并调用 [capture] 。

完成后,别忘了解锁焦点。

实现效果

Android实现Camera2预览和拍照效果环境

sdk>21

camera2 类图

Android实现Camera2预览和拍照效果

Android实现Camera2预览和拍照效果

代码实现

camerapreview.java

/**
 * created by shenhua on 2017-10-20-0020.
 * email shenhuanet@126.com
 */
public class camerapreview extends textureview {

  private static final string tag = "camerapreview";
  private static final sparseintarray orientations = new sparseintarray();//从屏幕旋转转换为jpeg方向
  private static final int max_preview_width = 1920;//camera2 api 保证的最大预览宽高
  private static final int max_preview_height = 1080;
  private static final int state_preview = 0;//显示相机预览
  private static final int state_waiting_lock = 1;//焦点锁定中
  private static final int state_waiting_pre_capture = 2;//拍照中
  private static final int state_waiting_non_pre_capture = 3;//其它状态
  private static final int state_picture_taken = 4;//拍照完毕
  private int mstate = state_preview;
  private int mratiowidth = 0, mratioheight = 0;
  private int msensororientation;
  private boolean mflashsupported;

  private semaphore mcameraopencloselock = new semaphore(1);//使用信号量 semaphore 进行多线程任务调度
  private activity activity;
  private file mfile;
  private handlerthread mbackgroundthread;
  private handler mbackgroundhandler;
  private size mpreviewsize;
  private string mcameraid;
  private cameradevice mcameradevice;
  private capturerequest.builder mpreviewrequestbuilder;
  private capturerequest mpreviewrequest;
  private cameracapturesession mcapturesession;
  private imagereader mimagereader;

  static {
    orientations.append(surface.rotation_0, 90);
    orientations.append(surface.rotation_90, 0);
    orientations.append(surface.rotation_180, 270);
    orientations.append(surface.rotation_270, 180);
  }

  public camerapreview(context context) {
    this(context, null);
  }

  public camerapreview(context context, attributeset attrs) {
    this(context, attrs, 0);
  }

  public camerapreview(context context, attributeset attrs, int defstyleattr) {
    super(context, attrs, defstyleattr);
    mfile = new file(getcontext().getexternalfilesdir(null), "pic.jpg");
  }

  @override
  protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
    super.onmeasure(widthmeasurespec, heightmeasurespec);
    int width = measurespec.getsize(widthmeasurespec);
    int height = measurespec.getsize(heightmeasurespec);
    if (0 == mratiowidth || 0 == mratioheight) {
      setmeasureddimension(width, height);
    } else {
      if (width < height * mratiowidth / mratioheight) {
        setmeasureddimension(width, width * mratioheight / mratiowidth);
      } else {
        setmeasureddimension(height * mratiowidth / mratioheight, height);
      }
    }
  }

  public void onresume(activity activity) {
    this.activity = activity;
    startbackgroundthread();
    //当activity或fragment onresume()时,可以冲洗打开一个相机并开始预览,否则,这个surface已经准备就绪
    if (this.isavailable()) {
      opencamera(this.getwidth(), this.getheight());
    } else {
      this.setsurfacetexturelistener(msurfacetexturelistener);
    }
  }

  public void onpause() {
    closecamera();
    stopbackgroundthread();
  }

  public void setoutputdir(file file) {
    this.mfile = file;
  }

  public void setaspectratio(int width, int height) {
    if (width < 0 || height < 0) {
      throw new illegalargumentexception("size can't be negative");
    }
    mratiowidth = width;
    mratioheight = height;
    requestlayout();
  }

  public void setautoflash(capturerequest.builder requestbuilder) {
    if (mflashsupported) {
      requestbuilder.set(capturerequest.control_ae_mode,
          capturerequest.control_ae_mode_on_auto_flash);
    }
  }

  public void takepicture() {
    lockfocus();
  }

  private void startbackgroundthread() {
    mbackgroundthread = new handlerthread("camerabackground");
    mbackgroundthread.start();
    mbackgroundhandler = new handler(mbackgroundthread.getlooper());
  }

  private void stopbackgroundthread() {
    mbackgroundthread.quitsafely();
    try {
      mbackgroundthread.join();
      mbackgroundthread = null;
      mbackgroundhandler = null;
    } catch (interruptedexception e) {
      e.printstacktrace();
    }
  }

  /**
   * 处理生命周期内的回调事件
   */
  private final textureview.surfacetexturelistener msurfacetexturelistener = new textureview.surfacetexturelistener() {

    @override
    public void onsurfacetextureavailable(surfacetexture texture, int width, int height) {
      opencamera(width, height);
    }

    @override
    public void onsurfacetexturesizechanged(surfacetexture texture, int width, int height) {
      configuretransform(width, height);
    }

    @override
    public boolean onsurfacetexturedestroyed(surfacetexture texture) {
      return true;
    }

    @override
    public void onsurfacetextureupdated(surfacetexture texture) {
    }
  };

  /**
   * 相机状态改变回调
   */
  private final cameradevice.statecallback mstatecallback = new cameradevice.statecallback() {

    @override
    public void onopened(@nonnull cameradevice cameradevice) {
      mcameraopencloselock.release();
      log.d(tag, "相机已打开");
      mcameradevice = cameradevice;
      createcamerapreviewsession();
    }

    @override
    public void ondisconnected(@nonnull cameradevice cameradevice) {
      mcameraopencloselock.release();
      cameradevice.close();
      mcameradevice = null;
    }

    @override
    public void onerror(@nonnull cameradevice cameradevice, int error) {
      mcameraopencloselock.release();
      cameradevice.close();
      mcameradevice = null;
      if (null != activity) {
        activity.finish();
      }
    }
  };

  /**
   * 处理与照片捕获相关的事件
   */
  private cameracapturesession.capturecallback mcapturecallback = new cameracapturesession.capturecallback() {

    private void process(captureresult result) {
      switch (mstate) {
        case state_preview: {
          break;
        }
        case state_waiting_lock: {
          integer afstate = result.get(captureresult.control_af_state);
          if (afstate == null) {
            capturestillpicture();
          } else if (captureresult.control_af_state_focused_locked == afstate ||
              captureresult.control_af_state_not_focused_locked == afstate) {
            integer aestate = result.get(captureresult.control_ae_state);
            if (aestate == null || aestate == captureresult.control_ae_state_converged) {
              mstate = state_picture_taken;
              capturestillpicture();
            } else {
              runprecapturesequence();
            }
          }
          break;
        }
        case state_waiting_pre_capture: {
          integer aestate = result.get(captureresult.control_ae_state);
          if (aestate == null ||
              aestate == captureresult.control_ae_state_precapture ||
              aestate == capturerequest.control_ae_state_flash_required) {
            mstate = state_waiting_non_pre_capture;
          }
          break;
        }
        case state_waiting_non_pre_capture: {
          integer aestate = result.get(captureresult.control_ae_state);
          if (aestate == null || aestate != captureresult.control_ae_state_precapture) {
            mstate = state_picture_taken;
            capturestillpicture();
          }
          break;
        }
      }
    }

    @override
    public void oncaptureprogressed(@nonnull cameracapturesession session,
                    @nonnull capturerequest request,
                    @nonnull captureresult partialresult) {
      process(partialresult);
    }

    @override
    public void oncapturecompleted(@nonnull cameracapturesession session,
                    @nonnull capturerequest request,
                    @nonnull totalcaptureresult result) {
      process(result);
    }

  };

  /**
   * 在确定相机预览大小后应调用此方法
   *
   * @param viewwidth 宽
   * @param viewheight 高
   */
  private void configuretransform(int viewwidth, int viewheight) {
    if (null == mpreviewsize || null == activity) {
      return;
    }
    int rotation = activity.getwindowmanager().getdefaultdisplay().getrotation();
    matrix matrix = new matrix();
    rectf viewrect = new rectf(0, 0, viewwidth, viewheight);
    rectf bufferrect = new rectf(0, 0, mpreviewsize.getheight(), mpreviewsize.getwidth());
    float centerx = viewrect.centerx();
    float centery = viewrect.centery();
    if (surface.rotation_90 == rotation || surface.rotation_270 == rotation) {
      bufferrect.offset(centerx - bufferrect.centerx(), centery - bufferrect.centery());
      matrix.setrecttorect(viewrect, bufferrect, matrix.scaletofit.fill);
      float scale = math.max(
          (float) viewheight / mpreviewsize.getheight(),
          (float) viewwidth / mpreviewsize.getwidth());
      matrix.postscale(scale, scale, centerx, centery);
      matrix.postrotate(90 * (rotation - 2), centerx, centery);
    } else if (surface.rotation_180 == rotation) {
      matrix.postrotate(180, centerx, centery);
    }
    this.settransform(matrix);
  }

  /**
   * 根据mcameraid打开相机
   */
  private void opencamera(int width, int height) {
    setupcameraoutputs(width, height);
    configuretransform(width, height);
    cameramanager manager = (cameramanager) getcontext().getsystemservice(context.camera_service);
    try {
      if (!mcameraopencloselock.tryacquire(2500, timeunit.milliseconds)) {
        throw new runtimeexception("time out waiting to lock camera opening.");
      }
      if (activitycompat.checkselfpermission(activity, manifest.permission.camera) != packagemanager.permission_granted) {
        // todo: consider calling
        //  activitycompat#requestpermissions
        // here to request the missing permissions, and then overriding
        //  public void onrequestpermissionsresult(int requestcode, string[] permissions,
        //                     int[] grantresults)
        // to handle the case where the user grants the permission. see the documentation
        // for activitycompat#requestpermissions for more details.
        return;
      }
      manager.opencamera(mcameraid, mstatecallback, mbackgroundhandler);
    } catch (cameraaccessexception e) {
      e.printstacktrace();
    } catch (interruptedexception e) {
      throw new runtimeexception("interrupted while trying to lock camera opening.", e);
    }
  }

  /**
   * 关闭相机
   */
  private void closecamera() {
    try {
      mcameraopencloselock.acquire();
      if (null != mcapturesession) {
        mcapturesession.close();
        mcapturesession = null;
      }
      if (null != mcameradevice) {
        mcameradevice.close();
        mcameradevice = null;
      }
      if (null != mimagereader) {
        mimagereader.close();
        mimagereader = null;
      }
    } catch (interruptedexception e) {
      throw new runtimeexception("interrupted while trying to lock camera closing.", e);
    } finally {
      mcameraopencloselock.release();
    }
  }

  /**
   * 设置相机相关的属性或变量
   *
   * @param width 相机预览的可用尺寸的宽度
   * @param height 相机预览的可用尺寸的高度
   */
  @suppresswarnings("suspiciousnamecombination")
  private void setupcameraoutputs(int width, int height) {
    cameramanager manager = (cameramanager) getcontext().getsystemservice(context.camera_service);
    try {
      for (string cameraid : manager.getcameraidlist()) {
        cameracharacteristics characteristics = manager.getcameracharacteristics(cameraid);
        // 在这个例子中不使用前置摄像头
        integer facing = characteristics.get(cameracharacteristics.lens_facing);
        if (facing != null && facing == cameracharacteristics.lens_facing_front) {
          continue;
        }
        streamconfigurationmap map = characteristics.get(cameracharacteristics.scaler_stream_configuration_map);
        if (map == null) {
          continue;
        }

        size largest = collections.max(arrays.aslist(map.getoutputsizes(imageformat.jpeg)),
            new comparesizesbyarea());
        mimagereader = imagereader.newinstance(largest.getwidth(), largest.getheight(),
            imageformat.jpeg, /*maximages*/2);
        mimagereader.setonimageavailablelistener(
            monimageavailablelistener, mbackgroundhandler);

        int displayrotation = activity.getwindowmanager().getdefaultdisplay().getrotation();
        // noinspection constantconditions
        msensororientation = characteristics.get(cameracharacteristics.sensor_orientation);
        boolean swappeddimensions = false;
        switch (displayrotation) {
          case surface.rotation_0:
          case surface.rotation_180:
            if (msensororientation == 90 || msensororientation == 270) {
              swappeddimensions = true;
            }
            break;
          case surface.rotation_90:
          case surface.rotation_270:
            if (msensororientation == 0 || msensororientation == 180) {
              swappeddimensions = true;
            }
            break;
          default:
            log.e(tag, "display rotation is invalid: " + displayrotation);
        }

        point displaysize = new point();
        activity.getwindowmanager().getdefaultdisplay().getsize(displaysize);
        int rotatedpreviewwidth = width;
        int rotatedpreviewheight = height;
        int maxpreviewwidth = displaysize.x;
        int maxpreviewheight = displaysize.y;

        if (swappeddimensions) {
          rotatedpreviewwidth = height;
          rotatedpreviewheight = width;
          maxpreviewwidth = displaysize.y;
          maxpreviewheight = displaysize.x;
        }

        if (maxpreviewwidth > max_preview_width) {
          maxpreviewwidth = max_preview_width;
        }

        if (maxpreviewheight > max_preview_height) {
          maxpreviewheight = max_preview_height;
        }

        mpreviewsize = chooseoptimalsize(map.getoutputsizes(surfacetexture.class),
            rotatedpreviewwidth, rotatedpreviewheight, maxpreviewwidth,
            maxpreviewheight, largest);

        int orientation = getresources().getconfiguration().orientation;
        if (orientation == configuration.orientation_landscape) {
          setaspectratio(mpreviewsize.getwidth(), mpreviewsize.getheight());
        } else {
          setaspectratio(mpreviewsize.getheight(), mpreviewsize.getwidth());
        }
        boolean available = characteristics.get(cameracharacteristics.flash_info_available);
        mflashsupported = available == null ? false : available;

        mcameraid = cameraid;
        return;
      }
    } catch (cameraaccessexception e) {
      e.printstacktrace();
    } catch (nullpointerexception e) {
      log.e(tag, "设备不支持camera2");
    }
  }

  /**
   * 获取一个合适的相机预览尺寸
   *
   * @param choices      支持的预览尺寸列表
   * @param textureviewwidth 相对宽度
   * @param textureviewheight 相对高度
   * @param maxwidth     可以选择的最大宽度
   * @param maxheight     可以选择的最大高度
   * @param aspectratio    宽高比
   * @return 最佳预览尺寸
   */
  private static size chooseoptimalsize(size[] choices, int textureviewwidth, int textureviewheight,
                     int maxwidth, int maxheight, size aspectratio) {
    list<size> bigenough = new arraylist<>();
    list<size> notbigenough = new arraylist<>();
    int w = aspectratio.getwidth();
    int h = aspectratio.getheight();
    for (size option : choices) {
      if (option.getwidth() <= maxwidth && option.getheight() <= maxheight &&
          option.getheight() == option.getwidth() * h / w) {
        if (option.getwidth() >= textureviewwidth &&
            option.getheight() >= textureviewheight) {
          bigenough.add(option);
        } else {
          notbigenough.add(option);
        }
      }
    }
    if (bigenough.size() > 0) {
      return collections.min(bigenough, new comparesizesbyarea());
    } else if (notbigenough.size() > 0) {
      return collections.max(notbigenough, new comparesizesbyarea());
    } else {
      log.e(tag, "couldn't find any suitable preview size");
      return choices[0];
    }
  }

  /**
   * 为相机预览创建新的cameracapturesession
   */
  private void createcamerapreviewsession() {
    try {
      surfacetexture texture = this.getsurfacetexture();
      assert texture != null;
      // 将默认缓冲区的大小配置为想要的相机预览的大小
      texture.setdefaultbuffersize(mpreviewsize.getwidth(), mpreviewsize.getheight());
      surface surface = new surface(texture);
      mpreviewrequestbuilder = mcameradevice.createcapturerequest(cameradevice.template_preview);
      mpreviewrequestbuilder.addtarget(surface);
      // 我们创建一个 cameracapturesession 来进行相机预览
      mcameradevice.createcapturesession(arrays.aslist(surface, mimagereader.getsurface()),
          new cameracapturesession.statecallback() {

            @override
            public void onconfigured(@nonnull cameracapturesession cameracapturesession) {
              if (null == mcameradevice) {
                return;
              }
              // 会话准备好后,我们开始显示预览
              mcapturesession = cameracapturesession;
              try {
                mpreviewrequestbuilder.set(capturerequest.control_af_mode,
                    capturerequest.control_af_mode_continuous_picture);
                setautoflash(mpreviewrequestbuilder);
                mpreviewrequest = mpreviewrequestbuilder.build();
                mcapturesession.setrepeatingrequest(mpreviewrequest, mcapturecallback, mbackgroundhandler);
              } catch (cameraaccessexception e) {
                e.printstacktrace();
              }
            }

            @override
            public void onconfigurefailed(@nonnull cameracapturesession cameracapturesession) {
            }
          }, null);
    } catch (cameraaccessexception e) {
      e.printstacktrace();
    }
  }

  /**
   * 从指定的屏幕旋转中检索照片方向
   *
   * @param rotation 屏幕方向
   * @return 照片方向(0,90,270,360)
   */
  private int getorientation(int rotation) {
    return (orientations.get(rotation) + msensororientation + 270) % 360;
  }

  /**
   * 锁定焦点
   */
  private void lockfocus() {
    try {
      // 如何通知相机锁定焦点
      mpreviewrequestbuilder.set(capturerequest.control_af_trigger, camerametadata.control_af_trigger_start);
      // 通知mcapturecallback等待锁定
      mstate = state_waiting_lock;
      mcapturesession.capture(mpreviewrequestbuilder.build(), mcapturecallback, mbackgroundhandler);
    } catch (cameraaccessexception e) {
      e.printstacktrace();
    }
  }

  /**
   * 解锁焦点
   */
  private void unlockfocus() {
    try {
      mpreviewrequestbuilder.set(capturerequest.control_af_trigger,
          camerametadata.control_af_trigger_cancel);
      setautoflash(mpreviewrequestbuilder);
      mcapturesession.capture(mpreviewrequestbuilder.build(), mcapturecallback,
          mbackgroundhandler);
      mstate = state_preview;
      mcapturesession.setrepeatingrequest(mpreviewrequest, mcapturecallback,
          mbackgroundhandler);
    } catch (cameraaccessexception e) {
      e.printstacktrace();
    }
  }

  /**
   * 拍摄静态图片
   */
  private void capturestillpicture() {
    try {
      if (null == activity || null == mcameradevice) {
        return;
      }
      final capturerequest.builder capturebuilder =
          mcameradevice.createcapturerequest(cameradevice.template_still_capture);
      capturebuilder.addtarget(mimagereader.getsurface());
      capturebuilder.set(capturerequest.control_af_mode,
          capturerequest.control_af_mode_continuous_picture);
      setautoflash(capturebuilder);
      // 方向
      int rotation = activity.getwindowmanager().getdefaultdisplay().getrotation();
      capturebuilder.set(capturerequest.jpeg_orientation, getorientation(rotation));
      cameracapturesession.capturecallback capturecallback
          = new cameracapturesession.capturecallback() {

        @override
        public void oncapturecompleted(@nonnull cameracapturesession session,
                        @nonnull capturerequest request,
                        @nonnull totalcaptureresult result) {
          toast.maketext(getcontext(), "saved: " + mfile, toast.length_short).show();
          log.d(tag, mfile.tostring());
          unlockfocus();
        }
      };
      mcapturesession.stoprepeating();
      mcapturesession.abortcaptures();
      mcapturesession.capture(capturebuilder.build(), capturecallback, null);
    } catch (cameraaccessexception e) {
      e.printstacktrace();
    }
  }

  /**
   * 运行precapture序列来捕获静止图像
   */
  private void runprecapturesequence() {
    try {
      // 设置拍照参数请求
      mpreviewrequestbuilder.set(capturerequest.control_ae_precapture_trigger,
          capturerequest.control_ae_precapture_trigger_start);
      mstate = state_waiting_pre_capture;
      mcapturesession.capture(mpreviewrequestbuilder.build(), mcapturecallback, mbackgroundhandler);
    } catch (cameraaccessexception e) {
      e.printstacktrace();
    }
  }

  /**
   * 比较两者大小
   */
  private static class comparesizesbyarea implements comparator<size> {

    @override
    public int compare(size lhs, size rhs) {
      return long.signum((long) lhs.getwidth() * lhs.getheight() -
          (long) rhs.getwidth() * rhs.getheight());
    }
  }

  /**
   * imagereader的回调对象
   */
  private final imagereader.onimageavailablelistener monimageavailablelistener
      = new imagereader.onimageavailablelistener() {

    @override
    public void onimageavailable(imagereader reader) {
      mbackgroundhandler.post(new imagesaver(reader.acquirenextimage(), mfile));
    }
  };

  /**
   * 将捕获到的图像保存到指定的文件中
   */
  private static class imagesaver implements runnable {

    private final image mimage;
    private final file mfile;

    imagesaver(image image, file file) {
      mimage = image;
      mfile = file;
    }

    @override
    public void run() {
      bytebuffer buffer = mimage.getplanes()[0].getbuffer();
      byte[] bytes = new byte[buffer.remaining()];
      buffer.get(bytes);
      fileoutputstream output = null;
      try {
        output = new fileoutputstream(mfile);
        output.write(bytes);
      } catch (ioexception e) {
        e.printstacktrace();
      } finally {
        mimage.close();
        if (null != output) {
          try {
            output.close();
          } catch (ioexception e) {
            e.printstacktrace();
          }
        }
      }
    }
  }

}

mainactivity.java

public class mainactivity extends appcompatactivity {

  camerapreview cameraview;

  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_main);
    cameraview = (camerapreview) findviewbyid(r.id.cameraview);
  }

  @override
  protected void onresume() {
    super.onresume();
    cameraview.onresume(this);
  }

  @override
  protected void onpause() {
    cameraview.onpause();
    super.onpause();
  }

  public void takepic(view view) {
    cameraview.takepicture();
  }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.constraintlayout 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:id="@+id/constraintlayout"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="@android:color/black"
  tools:context="com.shenhua.ocr.activity.main2activity">

  <com.shenhua.ocr.widget.camerapreview
    android:id="@+id/cameraview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

  <button
    android:layout_width="70dp"
    android:layout_height="70dp"
    android:layout_gravity="center"
    android:background="@drawable/ic_capture_200px"
    android:onclick="takepic"
    android:text="take"
    app:layout_constraintbottom_tobottomof="@id/constraintlayout"
    app:layout_constraintend_toendof="@id/constraintlayout"
    app:layout_constraintstart_tostartof="@id/constraintlayout"
    app:layout_constrainttop_totopof="@id/cameraview"
    app:layout_constraintvertical_bias="0.97" />

</android.support.constraint.constraintlayout>

资源文件 ic_capture_200px.xml

<vector android:height="24dp" android:viewportheight="1024.0"
  android:viewportwidth="1024.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
  <path android:fillcolor="#03a9f4" android:pathdata="m512,512m-393.8,0a393.8,393.8 0,1 0,787.7 0,393.8 393.8,0 1,0 -787.7,0z"/>
  <path android:fillcolor="#03a9f4" android:pathdata="m512,1024c229.2,1024 0,794.8 0,512s229.2,0 512,0s512,229.2 512,512 -229.2,512 -512,512zm512,984.6c261,0 472.6,-211.6 472.6,-472.6s773,39.4 512,39.4 39.4,251 39.4,512s211.6,472.6 472.6,472.6z"/>
</vector>

其它

manifest 权限:

<uses-permission android:name="android.permission.camera" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

android6.0 运行时权限未贴出。(注意:为了方便读者手机端阅读,本文代码部分的成员变量使用了行尾注释,在正常编程习惯中,请使用 /* / 注释。)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。