当前位置: 移动技术网 > IT编程>移动开发>Android > Android实现Camera2预览和拍照效果

Android实现Camera2预览和拍照效果

2019年07月24日  | 移动技术网IT编程  | 我要评论

简介

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

学习要点:

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

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

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

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

实现效果

环境

sdk>21

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

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网