编辑
2021-06-22
客户端技术
00
请注意,本文编写于 824 天前,最后修改于 105 天前,其中某些信息可能已经过时。

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

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

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

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

java
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); } }
java
mView.setOutlineProvider(new TextureVideoViewOutlineProvider(radius)); mView.setClipToOutline(true);

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

下面是我自己试了一下:

xml
<?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>
java
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); } }

本文作者:Tim

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!