You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何防止PhotoView绘制超出带顶部圆角的父LinearLayout边界?

解决PhotoView超出LinearLayout顶部圆角边界的问题

我之前处理过类似的布局裁剪问题,核心原因其实很清晰:你给LinearLayout设置了outlineProvider="background"来复用背景的圆角作为裁剪轮廓,但Android默认不会自动用这个轮廓去裁剪子View——clipChildrenclipPadding只能处理布局边界的裁剪,管不到自定义outline的情况。

下面给你几个按优先级排序的靠谱解决方案:

方案一:给LinearLayout添加clipToOutline="true"

这是最直接的修复方式,只需要在你的LinearLayout标签里补上这个属性,就能让它根据背景的圆角轮廓裁剪所有子View:

<LinearLayout 
    android:outlineProvider="background"
    android:clipToOutline="true"  <!-- 新增这个关键属性 -->
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:background="@drawable/sheet_bg" 
    android:orientation="vertical">

    <include 
        android:id="@+id/preview_layout_frame" 
        layout="@layout/preview_snaped_helfie" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:layout_weight="1" />

    <android.support.v7.widget.RecyclerView 
        android:id="@+id/helfie_list" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:layout_gravity="bottom" 
        android:padding="20dp" />
</LinearLayout>

注意:clipToOutline只对支持outline的View生效,LinearLayout完全符合要求,而且你的背景是shape圆角,系统能正确识别这个轮廓。

方案二:给PhotoView单独套一层带裁剪的FrameLayout

如果方案一因为include布局的层级嵌套问题没生效,可以给PhotoView单独加一个父容器,专门负责裁剪逻辑:

  1. 修改你的preview_snaped_helfie布局,把PhotoView放在FrameLayout里:
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:outlineProvider="background"
    android:clipToOutline="true"
    android:background="@drawable/sheet_top_corners"> <!-- 只保留顶部圆角的shape -->

    <com.github.chrisbanes.photoview.PhotoView 
        android:id="@+id/helfie_preview" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:adjustViewBounds="true" 
        android:scaleType="center" />
</FrameLayout>
  1. 对应的sheet_top_corners shape和你原来的sheet_bg一致,只保留顶部圆角:
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <solid android:color="@color/dark" />
    <corners android:topLeftRadius="35dp" android:topRightRadius="35dp" />
</shape>

这样PhotoView的缩放区域会被FrameLayout的圆角完全约束,不受外层LinearLayout的布局属性影响。

方案三:自定义裁剪容器(终极兜底方案)

如果上面的系统自带方案都因为设备兼容性问题失效,你可以写一个简单的自定义ViewGroup,手动控制画布裁剪:

public class RoundedTopContainer extends FrameLayout {
    private Path mClipPath;
    private float mTopRadius;

    public RoundedTopContainer(Context context) {
        super(context);
        init(context, null);
    }

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

    private void init(Context context, AttributeSet attrs) {
        // 可以通过自定义属性传入圆角半径,这里默认35dp
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RoundedTopContainer);
        mTopRadius = ta.getDimension(R.styleable.RoundedTopContainer_topRadius, dp2px(35));
        ta.recycle();

        mClipPath = new Path();
        setWillNotDraw(false); // 开启自定义绘制
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 绘制顶部圆角的裁剪路径
        mClipPath.reset();
        mClipPath.addRoundRect(new RectF(0, 0, w, h), 
            new float[]{mTopRadius, mTopRadius, mTopRadius, mTopRadius, 0, 0, 0, 0}, 
            Path.Direction.CW);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        int saveCount = canvas.save();
        canvas.clipPath(mClipPath);
        super.dispatchDraw(canvas);
        canvas.restoreToCount(saveCount);
    }

    private float dp2px(float dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
    }
}

然后在布局里用这个容器包裹PhotoView,就能确保任何设备上都能正确裁剪顶部圆角。


内容的提问来源于stack exchange,提问作者Belvi Nosakhare

火山引擎 最新活动