如何让对话框CardView内的StreetViewPanoramaView顶部显示圆角?
你猜的没错,StreetViewPanoramaView确实继承自SurfaceView,而SurfaceView的渲染机制比较特殊——它是在一个独立于应用窗口的Surface上绘制内容的,所以父布局(比如你的CardView)的裁剪规则(像clipChildren或者cardCornerRadius)对它根本起不了作用,这就是为什么顶部还是直角的原因。
下面给你几个实用的解决办法,按兼容性和易用性排序:
方法一:用遮罩层模拟圆角(兼容性最好)
这个方法的思路很简单:在StreetViewPanoramaView的顶部盖一个和CardView背景色一致的遮罩,遮罩的顶部设置成圆角,刚好把街景视图的直角顶部挡住。
步骤1:修改布局
在你的CardView内部,给StreetViewPanoramaView加一个同级的遮罩View:
<android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" android:clipChildren="true" app:cardBackgroundColor="@color/white" app:cardCornerRadius="16dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 街景视图 --> <com.google.android.gms.maps.StreetViewPanoramaView android:id="@+id/streetView" android:layout_width="match_parent" android:layout_height="200dp" /> <!-- 顶部圆角遮罩,颜色和CardView背景一致 --> <View android:layout_width="match_parent" android:layout_height="16dp" android:background="@drawable/top_corner_mask" android:layout_alignTop="@id/streetView" /> <!-- 你的其他布局内容 --> ... </RelativeLayout> </android.support.v7.widget.CardView>
步骤2:创建遮罩的shape文件
在res/drawable下新建top_corner_mask.xml,内容如下:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <!-- 颜色和CardView的背景色保持一致 --> <solid android:color="@color/white" /> <!-- 只设置顶部两个角的圆角,和CardView的radius一致 --> <corners android:topLeftRadius="16dp" android:topRightRadius="16dp" /> </shape>
这个方法不管是低版本还是高版本的Android系统都能完美运行,而且不需要写额外的Java/Kotlin代码,非常省心。
方法二:用ViewOutlineProvider裁剪容器(API 21+)
从Android 5.0(API 21)开始,系统提供了ViewOutlineProvider来实现视图的圆角裁剪。我们可以把StreetViewPanoramaView放在一个容器里,然后给容器设置圆角裁剪规则,这样就能间接让街景视图显示圆角了。
步骤1:修改布局
把StreetViewPanoramaView放到一个FrameLayout里:
<android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" android:clipChildren="true" app:cardBackgroundColor="@color/white" app:cardCornerRadius="16dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 自定义圆角容器 --> <FrameLayout android:id="@+id/street_view_container" android:layout_width="match_parent" android:layout_height="200dp"> <com.google.android.gms.maps.StreetViewPanoramaView android:id="@+id/streetView" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> <!-- 你的其他布局内容 --> ... </RelativeLayout> </android.support.v7.widget.CardView>
步骤2:在代码中设置裁剪规则
在Activity或者Fragment里,给容器设置OutlineProvider:
FrameLayout streetViewContainer = findViewById(R.id.street_view_container); // 开启裁剪 streetViewContainer.setClipToOutline(true); // 设置圆角轮廓 streetViewContainer.setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { // 把16dp转换成像素值,避免不同分辨率设备显示不一致 int cornerRadius = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics() ); // 设置圆角矩形的轮廓 outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), cornerRadius); } });
这个方法的视觉效果更自然,不会有遮罩层的“叠加感”,但只支持API 21及以上的系统,如果你的应用需要兼容更低版本,还是优先用方法一。
方法三:直接给StreetViewPanoramaView设置裁剪(API 28+)
从Android 9.0(API 28)开始,SurfaceView也支持setClipToOutline了,你可以试试直接给街景视图设置圆角:
StreetViewPanoramaView streetView = findViewById(R.id.streetView); streetView.setClipToOutline(true); streetView.setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { int cornerRadius = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics() ); outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), cornerRadius); } });
不过这个方法的兼容性最差,而且部分设备可能因为SurfaceView的底层实现问题,效果不如容器裁剪稳定,所以只推荐在高版本设备上尝试。
内容的提问来源于stack exchange,提问作者frapeti

