在学习不规则区域点击中看到了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);
}
}
}