【转】如何实现TextureView或者SurfaceView预览框为圆角

如何让 SurfaceView 或者 TextureView 实现圆角呢?这篇文章正好解决此问题,转载一手。其实主要是基于 ViewOutlineProvider 这个组件专门用于裁剪 View 的边界,注意使用 ViewOutlineProvider 时必须API >= 21。不光可以实现圆角裁剪,还可以实现轮廓、阴影等很多效果,这个如果后面用到了再补充。

项目中,视频预览界面框为圆角;但发现是使用的 Renderer 渲染的方法进行的,的确可以有圆角效果。

但有个问题是:我的视频源与要显示视频的预览框的长宽比率不同,预览框需要满屏看到视频源,如果不做处理,视频源只能通过压缩来显示。造成了视频的变形,如果不拉伸处理,那么就进行裁剪处理,需要对视频进行Matrix操作,如移动,伸缩处理。进行这些处理后,渲染的圆角效果就不再出现了。所以通过Renderer来进行圆角效果,不但复杂而且并不可靠。

通过网上的搜索可以找到 Android5.0后,一个类 ViewOutlineProvider;基于 View 自身的setClipToOutline(boolean clipToOutline) 和 setOutlineProvider(ViewOutlineProvider provider) 方法实现了该需求。

 1import android.graphics.Outline;
 2import android.graphics.Rect;
 3import android.view.View;
 4import android.view.ViewOutlineProvider;
 5
 6public class TextureVideoViewOutlineProvider extends ViewOutlineProvider {
 7    private float mRadius;
 8
 9    public TextureVideoViewOutlineProvider(float radius) {
10        this.mRadius = radius;
11    }
12
13    @Override
14    public void getOutline(View view, Outline outline) {
15        Rect rect = new Rect();
16        view.getGlobalVisibleRect(rect);
17        int leftMargin = 0;
18        int topMargin = 0;
19        Rect selfRect = new Rect(leftMargin, topMargin,
20                rect.right - rect.left - leftMargin, rect.bottom - rect.top - topMargin);
21        outline.setRoundRect(selfRect, mRadius);
22    }
23}
1mView.setOutlineProvider(new TextureVideoViewOutlineProvider(radius));
2mView.setClipToOutline(true);

任何 View 都可通过此方法达到圆角效果。

下面是我自己试了一下:

 1<?xml version="1.0" encoding="utf-8"?>
 2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3    xmlns:app="http://schemas.android.com/apk/res-auto"
 4    xmlns:tools="http://schemas.android.com/tools"
 5    android:layout_width="match_parent"
 6    android:layout_height="match_parent"
 7    tools:context=".MainActivity">
 8
 9    <SurfaceView
10        android:id="@+id/preview_surface"
11        android:layout_width="300dp"
12        android:layout_height="300dp"
13        android:layout_centerInParent="true"
14        />
15</RelativeLayout>
  1import androidx.appcompat.app.AppCompatActivity;
  2
  3import android.content.Context;
  4import android.graphics.Outline;
  5import android.graphics.Rect;
  6import android.hardware.Camera;
  7import android.os.Bundle;
  8import android.view.SurfaceHolder;
  9import android.view.SurfaceView;
 10import android.view.View;
 11import android.view.ViewOutlineProvider;
 12
 13import java.io.IOException;
 14
 15public class MainActivity extends AppCompatActivity {
 16    private Camera mCamera; // 相机
 17    @Override
 18    protected void onCreate(Bundle savedInstanceState) {
 19        super.onCreate(savedInstanceState);
 20        setContentView(R.layout.activity_main);
 21        mCamera = android.hardware.Camera.open();
 22        SurfaceView mPreview = findViewById(R.id.preview_surface);
 23        mPreview.setOutlineProvider(new TextureVideoViewOutlineProvider(
 24                DensityUtil.dip2px(this, 150)));
 25        mPreview.setClipToOutline(true);
 26
 27        mPreview.getHolder().addCallback(new SurfaceHolder.Callback() {
 28            @Override
 29            public void surfaceCreated(SurfaceHolder holder) {
 30                try {
 31                    mCamera.setPreviewDisplay(mPreview.getHolder());
 32                } catch (IOException e) {
 33                    e.printStackTrace();
 34                }
 35                mCamera.startPreview();
 36            }
 37
 38            @Override
 39            public void surfaceChanged(SurfaceHolder holder, 
 40                                       int format, int width, int height) {
 41            }
 42
 43            @Override
 44            public void surfaceDestroyed(SurfaceHolder holder) {
 45
 46            }
 47        });
 48    }
 49
 50    @Override
 51    protected void onResume() {
 52        super.onResume();
 53        if(mCamera != null) {
 54            mCamera.startPreview();
 55        }
 56    }
 57
 58    @Override
 59    protected void onPause() {
 60        super.onPause();
 61        if(mCamera != null) {
 62            mCamera.stopPreview();
 63        }
 64    }
 65
 66    @Override
 67    protected void onDestroy() {
 68        super.onDestroy();
 69        if(mCamera != null) {
 70            mCamera.release();
 71        }
 72    }
 73}
 74
 75class TextureVideoViewOutlineProvider extends ViewOutlineProvider {
 76    private final float mRadius;
 77
 78    public TextureVideoViewOutlineProvider(float radius) {
 79        this.mRadius = radius;
 80    }
 81
 82    @Override
 83    public void getOutline(View view, Outline outline) {
 84        Rect rect = new Rect();
 85        view.getGlobalVisibleRect(rect);
 86        int leftMargin = 0;
 87        int topMargin = 0;
 88        Rect selfRect = new Rect(leftMargin, topMargin,
 89                rect.right - rect.left - leftMargin, 
 90                rect.bottom - rect.top - topMargin);
 91        outline.setRoundRect(selfRect, mRadius);
 92    }
 93}
 94
 95class DensityUtil {
 96    public static int dip2px(Context context, float dpValue) {
 97        final float scale = context.getResources().getDisplayMetrics().density;
 98        return (int) (dpValue * scale + 0.5f);
 99    }
100
101    public static int px2dip(Context context, float pxValue) {
102        final float scale = context.getResources().getDisplayMetrics().density;
103        return (int) (pxValue / scale + 0.5f);
104    }
105}