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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewOutlineProvider;

public class TextureVideoViewOutlineProvider extends ViewOutlineProvider {
private float mRadius;

public TextureVideoViewOutlineProvider(float radius) {
this.mRadius = radius;
}

@Override
public void getOutline(View view, Outline outline) {
Rect rect = new Rect();
view.getGlobalVisibleRect (rect);
int leftMargin = 0;
int topMargin = 0;
Rect selfRect = new Rect(leftMargin, topMargin,
rect.right - rect.left - leftMargin, rect.bottom - rect.top - topMargin);
outline.setRoundRect (selfRect, mRadius);
}
}
1
2
mView.setOutlineProvider (new TextureVideoViewOutlineProvider(radius));
mView.setClipToOutline (true);

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

下面是我自己试了一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<SurfaceView
android:id="@+id/preview_surface"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerInParent="true"
/>
</RelativeLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.graphics.Outline;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewOutlineProvider;

import java.io.IOException;

public class MainActivity extends AppCompatActivity {
private Camera mCamera; // 相机
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_main);
mCamera = android.hardware.Camera.open ();
SurfaceView mPreview = findViewById (R.id.preview_surface);
mPreview.setOutlineProvider (new TextureVideoViewOutlineProvider(
DensityUtil.dip2px (this, 150)));
mPreview.setClipToOutline (true);

mPreview.getHolder ().addCallback (new SurfaceHolder.Callback () {
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay (mPreview.getHolder ());
} catch (IOException e) {
e.printStackTrace ();
}
mCamera.startPreview ();
}

@Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height) {
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {

}
});
}

@Override
protected void onResume() {
super.onResume ();
if(mCamera != null) {
mCamera.startPreview ();
}
}

@Override
protected void onPause() {
super.onPause ();
if(mCamera != null) {
mCamera.stopPreview ();
}
}

@Override
protected void onDestroy() {
super.onDestroy ();
if(mCamera != null) {
mCamera.release ();
}
}
}

class TextureVideoViewOutlineProvider extends ViewOutlineProvider {
private final float mRadius;

public TextureVideoViewOutlineProvider(float radius) {
this.mRadius = radius;
}

@Override
public void getOutline(View view, Outline outline) {
Rect rect = new Rect();
view.getGlobalVisibleRect (rect);
int leftMargin = 0;
int topMargin = 0;
Rect selfRect = new Rect(leftMargin, topMargin,
rect.right - rect.left - leftMargin,
rect.bottom - rect.top - topMargin);
outline.setRoundRect (selfRect, mRadius);
}
}

class DensityUtil {
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources ().getDisplayMetrics ().density;
return (int) (dpValue * scale + 0.5f);
}

public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources ().getDisplayMetrics ().density;
return (int) (pxValue /scale + 0.5f);
}
}