android 布局实现子View不规则区域点击

在学习不规则区域点击中看到了Android中不规则形状View的布局实现
以下是自己的学习总结:

我们知道android中View的不规则区域点击可以通过Path和Region实现,而在布局中依然可以用此方法实现,下面就是实现步骤:

1.android布局中都有drawChild方法,这是绘制每个子View的方法,而canvas可以通过clipPath来裁切不规则区域,我们通过拦截这个方法做操作:

@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    beforeChild(canvas,child,drawingTime);
    return super.drawChild(canvas, child, drawingTime);
}

Path path = new Path();
RectF bounds = new RectF();

private void beforeChild(Canvas canvas, View child, long drawingTime) {
    path.addRoundRect(new RectF(child.getLeft(),
                    child.getTop(),
                    child.getRight(),
                    child.getBottom()),
            80,
            80,
            Path.Direction.CW);
    path.computeBounds(bounds, true);
    canvas.clipPath(path);
}

2.android中dispatchTouchEvent()里的isTransformedTouchPointInView()虽然是hide但是protected方法,可以通过重写这个方法来实现View的点击区域判断:

public boolean isTransformedTouchPointInView(float x, float y, View child, PointF outLocalPoint) {
    final float[] point = getTempPoint();
    point[0] = x;
    point[1] = y;
    transformPointToViewLocal(point, child);
    boolean isInView = pointInView(child, point[0], point[1]);
    if (isInView && outLocalPoint != null) {
        outLocalPoint.set(point[0], point[1]);
    }
    return isInView;
}

getTempPoint()复制出来

private float[] mTempPoint;

    private float[] getTempPoint() {
        if (mTempPoint == null) {
            mTempPoint = new float[2];
        }
        return mTempPoint;
    }

child.pointInView这个方法不是public,我们需要去把View中的pointInView()复制出来

public boolean pointInView(View child, float localX, float localY) {
    return localX >= 0 && localY >= 0 && localX < (child.getRight() - child.getLeft()) &&
            localY < (child.getBottom() - child.getTop());
}

transformPointToViewLocal()是hide,同样复制出来

public void transformPointToViewLocal(float[] point, View child) {
    point[0] += getScrollX() - child.getLeft();
    point[1] += getScrollY() - child.getTop();
    //child.hasIdentityMatrix()和child.getInverseMatrix()用下面方法替换
    Matrix matrix = child.getMatrix();
    Matrix inverse = new Matrix();
    if (!matrix.isIdentity()) {
        matrix.invert(inverse);
        inverse.mapPoints(point);
    }
}

最后我们还需要判断是否在不规则的点击区域内

public boolean isTransformedTouchPointInView(float x, float y, View child, PointF outLocalPoint) {
    final float[] point = getTempPoint();
    point[0] = x;
    point[1] = y;
    transformPointToViewLocal(point, child);
    boolean isInView = pointInView(child, point[0], point[1]);
    if (isInView && outLocalPoint != null) {
        outLocalPoint.set(point[0], point[1]);
    }
    if (isInView) {
       //在这里判断是否在规则区域内
        Region region = new Region();
        region.setPath(path, new Region(
                (int) bounds.left,
                (int) bounds.top,
                (int) bounds.right,
                (int) bounds.bottom));
        if (!region.contains((int) x, (int) y)) {
            isInView = false;
        }
    }

    return isInView;
}

完整代码:

public class ClipFrameLayout extends FrameLayout {
    public ClipFrameLayout(@NonNull Context context) {
        super(context);
    }

    public ClipFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        beforeChild(canvas, child, drawingTime);
        return super.drawChild(canvas, child, drawingTime);
    }

    Path path = new Path();
    RectF bounds = new RectF();

    private void beforeChild(Canvas canvas, View child, long drawingTime) {
        path.addRoundRect(new RectF(child.getLeft(),
                        child.getTop(),
                        child.getRight(),
                        child.getBottom()),
                80,
                80,
                Path.Direction.CW);
        path.computeBounds(bounds, true);
        canvas.clipPath(path);
    }

    private float[] mTempPoint;

    private float[] getTempPoint() {
        if (mTempPoint == null) {
            mTempPoint = new float[2];
        }
        return mTempPoint;
    }

    public boolean isTransformedTouchPointInView(float x, float y, View child, PointF outLocalPoint) {
        final float[] point = getTempPoint();
        point[0] = x;
        point[1] = y;
        transformPointToViewLocal(point, child);
        boolean isInView = pointInView(child, point[0], point[1]);
        if (isInView && outLocalPoint != null) {
            outLocalPoint.set(point[0], point[1]);
        }
        if (isInView) {
            Region region = new Region();
            region.setPath(path, new Region(
                    (int) bounds.left,
                    (int) bounds.top,
                    (int) bounds.right,
                    (int) bounds.bottom));
            if (!region.contains((int) x, (int) y)) {
                isInView = false;
            }

        }

        return isInView;
    }

    public boolean pointInView(View child, float localX, float localY) {
        return localX >= 0 && localY >= 0 && localX < (child.getRight() - child.getLeft()) &&
                localY < (child.getBottom() - child.getTop());
    }

    public void transformPointToViewLocal(float[] point, View child) {
        point[0] += getScrollX() - child.getLeft();
        point[1] += getScrollY() - child.getTop();

        Matrix matrix = child.getMatrix();
        Matrix inverse = new Matrix();
        if (!matrix.isIdentity()) {
            matrix.invert(inverse);
            inverse.mapPoints(point);
        }
    }

}

你可能感兴趣的:(android 布局实现子View不规则区域点击)