安卓:我需要一个圆形的相机预览。如何实现?

5

我需要帮助。

我正在尝试开发一个简单的应用程序,它可以拍照(哇,这真是太原创了!)。一切都很好。我唯一需要的是一个圆形相机预览。

我的相机预览类(继承自surfaceview)放置在一个框架布局中,这基本上就是我的相机预览。如您所知,它呈矩形形状。由于我对该应用有更大的计划,我需要相机预览为圆形(例如,某人可以拍摄某人的面部照片,我可以在周围画一些图画......)。

现在,我不知道该怎么做。我尝试了不同的方法,创建了一个带有xml的形状,并将其设置为我的框架布局的背景,但那根本没用。

在花费了数小时在谷歌上寻找解决方案后,我决定放弃并来到这里。

因此,请如果有人知道任何内容,请告诉我们 :) 如果需要澄清,请不要犹豫问询。

3个回答

3

简单方法:

1)不设置预览表面

2)捕捉原始数据

3)转换为位图并将其制作成圆形

4)显示(例如在ImageView上)

仅供参考:

public class CameraRoundPriview extends ImageView {
 private Camera camera;
 private Bitmap bitmap = null;
 private int mPreviewWidth, mPreviewHeight; 
 boolean mCameraOn = false;  

 public CameraRoundPriview(Context context, AttributeSet attrs) {
        super(context, attrs); 
 }   

 private Bitmap getclip() { 
     //clip
     Bitmap output = Bitmap.createBitmap(bitmap.getHeight(),
                                         bitmap.getHeight(), 
                                         Config.ARGB_8888);  
     Canvas canvas = new Canvas(output);     
     final int color = 0xff424242;
     final Paint paint = new Paint();
     final Rect rect = new Rect(0, 0, bitmap.getHeight(),
                                     bitmap.getHeight()); 

     paint.setAntiAlias(true);
     canvas.drawARGB(0, 0, 0, 0);
     canvas.drawCircle(bitmap.getHeight() / 2,
                       bitmap.getHeight() / 2, 
                       bitmap.getHeight() / 2, paint);
     paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
     canvas.drawBitmap(bitmap, rect, rect, paint);

     //rotate
      android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
      android.hardware.Camera.getCameraInfo(getCameraID(), info);
      int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
      int degrees = 0;
      switch (rotation) {
          case Surface.ROTATION_0:
              degrees = 0;
              break;
          case Surface.ROTATION_90:
              degrees = 90;
              break;
          case Surface.ROTATION_180: 
              degrees = 180;
              break;
          case Surface.ROTATION_270:
              degrees = 270;
              break;
      }

     int result = degrees; 
     if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP){
          if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
              result = (info.orientation + degrees) % 360;
              result = (360 - result) % 360; // compensate the mirror
          } else { // back-facing
              result = (info.orientation - degrees + 360) % 360;
          }
     }
     Matrix matrix = new Matrix();
     matrix.postRotate(result);
     Bitmap scaledBitmap  = Bitmap.createScaledBitmap(output,output.getWidth(),output.getHeight(),true);
     Bitmap rotatedBitmap = Bitmap.createBitmap(scaledBitmap , 0, 0, scaledBitmap.getWidth(), scaledBitmap .getHeight(), matrix, true);

     return rotatedBitmap;
 }

private void showImage(){   
     if ((bitmap != null)){
         this.setImageBitmap(getclip());   
     }
 }

public boolean onClickCamera(){
    if (mCameraOn){
        mCameraOn = false;
        cameraStop();
    }else{
        mCameraOn = true;
        cameraStart();
    }
    return mCameraOn;
}

private void cameraStop(){
    if (camera == null) return;
    camera.setPreviewCallback(null);
    camera.release();
    camera = null; 
}

private int getCameraID(){
    // specify camera id
    return 0;
}

private void cameraStart(){
      Camera camera = Camera.open(getCameraID());
      final Camera.Size previewSize   = camera.getParameters().getPreviewSize();
      mPreviewWidth                   = previewSize.width; 
      mPreviewHeight                  = previewSize.height; 

      try {  
       camera.setPreviewCallback(new PreviewCallback() {
        public void onPreviewFrame(byte[] data, Camera camera_call) {
         ByteArrayOutputStream outstr = new ByteArrayOutputStream();
         Rect rect = new Rect(0, 0, mPreviewWidth, mPreviewHeight);  
         YuvImage yuvimage=new YuvImage(data,ImageFormat.NV21,mPreviewWidth,mPreviewHeight,null);
         yuvimage.compressToJpeg(rect, 50, outstr);
         bitmap = BitmapFactory.decodeByteArray(outstr.toByteArray(), 0, outstr.size());
         showImage();
         camera_call.addCallbackBuffer(data);
        } 
       }); 
      } catch (Exception e) {}
      camera.startPreview();

      this.camera=camera;   
}
}

1
您可以在相机预览上叠加一个ImageView。将SurfaceViewImageView都放在一个FrameLayout中,两者都设置为match_parent,并且图像必须位于顶部。 将其设置为带有透明圆形的黑色图像。

那确实有效,但我还需要做更多的工作并寻找其他解决方案。由于活动的背景颜色会根据用户所做的某些选择而改变,因此图像的矩形将保持为白色,因此对我的目的来说不起作用。也许我应该在第一次解释时就说明这一点,然而,你的解决方案完全可以胜任。 - user3494305
1
如果需要更改颜色或形状,您可以编写自定义“View”并实现“onDraw”方法以绘制所需的形状。 - Kirill Kulakov
是的,我创建了一个实现SurfaceView的Drawing类,并尝试重写onDraw(Canvas c)方法。我只是在相机上方绘制了一段文本,但是我得到了黑色背景,实际上应该看到我的相机预览。在我的Activity中:frame = //获取我的帧布局引用draw = new Draw(this) //我的绘图类,在相机预览的顶部进行绘制camerapreview = new Camerapreview(this,camera) //我的相机预览类frame.addView(camerapreview); frame.addView(draw);这样做会使文本出现在黑色背景上。 - user3494305
好的,我已经解决了它,我添加了draw.setZOrderOnTop(true);和透明度,现在它很好。 - user3494305

0

这很简单,如下所示:

这适用于CameraX或Camera2 API。

注意:您必须具有透明背景。

您必须拥有以下类似的XML文件。

 <com.RoundedCameraPreview
            android:id="@+id/viewFinder"
            android:background="#00000000"
            app:scaleType="fillCenter"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

还要别忘了在style.xml中声明

<declare-styleable name="PreviewView">
        <attr name="isRound" format="boolean" />
    </declare-styleable>

代码是这样编写的

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.view.PreviewView;

public class RoundedCameraPreview extends PreviewView {


    Path clipPath;
    boolean isRound;

    public RoundedCameraPreview(@NonNull Context context) {
        super(context);
    }

    public RoundedCameraPreview(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedCameraPreview(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.getTheme().obtainStyledAttributes(
                attrs,
                R.styleable.PreviewView,
                0, 0);

        try {
            isRound = a.getBoolean(R.styleable.PreviewView_isRound, true);
        } finally {
            a.recycle();
        }
    }

    public boolean isRound() {
        return isRound;
    }

    public void setIsRound(boolean isRound) {
        this.isRound = isRound;
        invalidate();
        requestLayout();
    }

    public RoundedCameraPreview(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        if (isRound) {
            clipPath = new Path();
            //TODO: define the circle you actually want
            clipPath.addCircle(canvas.getWidth() / 2, canvas.getWidth() / 2, canvas.getWidth() / 2, Path.Direction.CW);

            Paint paint = new Paint();
            paint.setAntiAlias(true);
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

            canvas.clipPath(clipPath);
            canvas.drawPath(clipPath, paint);
        }
        super.dispatchDraw(canvas);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接