自定义视图可点击区域重叠
Custom view clickable area overlap
我有一个对角线形状的图像视图。当我单击 imageview 时,它不考虑裁剪区域并执行单击透明区域。
我怎样才能只点击非透明区域或形状?如果需要,您可以查看视图的源代码here
提前致谢。
更新了您的库代码并找到了解决方案。
在绘制布局之前使用库计算的 mClipPath
。在单一布局中尝试过。
首先重写 dispatchDraw()
方法,如下所示:
@Override
protected void dispatchDraw(Canvas canvas) {
final Path path = mClipPath;
canvas.clipPath(path);
super.dispatchDraw(canvas);
}
然后添加你的xml这两行:
android:clickable="true"
android:foreground="?selectableItemBackground"
完整小部件 xml:
<com.santalu.diagonalimageview.DiagonalImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="@dimen/collapsing_image_height"
android:layout_marginTop="?actionBarSize"
android:background="?android:windowBackground"
android:scaleType="centerCrop"
android:src="@drawable/demo"
android:clickable="true"
android:foreground="?selectableItemBackground"
app:di_borderEnabled="false"
app:di_direction="left"
app:di_overlap="@dimen/collapsing_overlap_size"
app:di_position="bottom"/>
完整代码:
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.support.annotation.IntDef;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Created by santalu on 7/4/17.
*
* Note: if position set NONE mask won't be applied
*
* POSITION DIRECTION
*
* TOP LEFT | RIGHT
* BOTTOM LEFT | RIGHT
* LEFT TOP | BOTTOM
* RIGHT TOP | BOTTOM
*/
public class DiagonalImageView extends AppCompatImageView {
public static final String TAG = DiagonalImageView.class.getSimpleName();
@Retention(RetentionPolicy.SOURCE)
@IntDef({ NONE, LEFT, RIGHT, TOP, BOTTOM })
public @interface Position {
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({ LEFT, RIGHT, TOP, BOTTOM })
public @interface Direction {
}
public static final int NONE = 0;
public static final int TOP = 1;
public static final int RIGHT = 2;
public static final int BOTTOM = 4;
public static final int LEFT = 8;
private final Path mClipPath = new Path();
private final Path mBorderPath = new Path();
private final Paint mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int mPosition;
private int mDirection;
private int mOverlap;
private int mBorderColor;
private int mBorderSize;
private boolean mBorderEnabled;
public DiagonalImageView(Context context) {
super(context);
init(context, null);
}
public DiagonalImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
setLayerType(LAYER_TYPE_HARDWARE, null);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiagonalImageView);
try {
mPosition = a.getInteger(R.styleable.DiagonalImageView_di_position, NONE);
mDirection = a.getInteger(R.styleable.DiagonalImageView_di_direction, RIGHT);
mOverlap = a.getDimensionPixelSize(R.styleable.DiagonalImageView_di_overlap, 0);
mBorderSize = a.getDimensionPixelSize(R.styleable.DiagonalImageView_di_borderSize, 0);
mBorderColor = a.getColor(R.styleable.DiagonalImageView_di_borderColor, Color.BLACK);
mBorderEnabled = a.getBoolean(R.styleable.DiagonalImageView_di_borderEnabled, false);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStyle(Style.STROKE);
mBorderPaint.setStrokeWidth(mBorderSize);
} finally {
a.recycle();
}
}
public void set(@Position int position, @Direction int direction) {
if (mPosition != position || mDirection != direction) {
mClipPath.reset();
mBorderPath.reset();
}
mPosition = position;
mDirection = direction;
postInvalidate();
}
public void setPosition(@Position int position) {
if (mPosition != position) {
mClipPath.reset();
mBorderPath.reset();
}
mPosition = position;
postInvalidate();
}
public void setDirection(@Direction int direction) {
if (mDirection != direction) {
mClipPath.reset();
mBorderPath.reset();
}
mDirection = direction;
postInvalidate();
}
public void setBorderEnabled(boolean enabled) {
mBorderEnabled = enabled;
postInvalidate();
}
public @Position int getPosition() {
return mPosition;
}
public @Direction int getDirection() {
return mDirection;
}
public boolean isBorderEnabled() {
return mBorderEnabled;
}
@Override protected void onDraw(Canvas canvas) {
if (mClipPath.isEmpty()) {
super.onDraw(canvas);
return;
}
int saveCount = canvas.save();
canvas.clipPath(mClipPath);
super.onDraw(canvas);
if (!mBorderPath.isEmpty()) {
canvas.drawPath(mBorderPath, mBorderPaint);
}
canvas.restoreToCount(saveCount);
}
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (!changed) {
return;
}
if (mClipPath.isEmpty()) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (width <= 0 || height <= 0) {
return;
}
setClipPath(width, height);
}
}
private void setClipPath(final int width, final int height) {
mClipPath.reset();
mBorderPath.reset();
switch (mPosition) {
case TOP:
if (mDirection == LEFT) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, mOverlap);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, 0);
mBorderPath.lineTo(width, mOverlap);
}
} else {
mClipPath.moveTo(0, mOverlap);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, mOverlap);
mBorderPath.lineTo(width, 0);
}
}
break;
case RIGHT:
if (mDirection == TOP) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width - mOverlap, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(width, 0);
mBorderPath.lineTo(width - mOverlap, height);
}
} else {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width - mOverlap, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(width - mOverlap, 0);
mBorderPath.lineTo(width, height);
}
}
break;
case BOTTOM:
if (mDirection == LEFT) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height - mOverlap);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, height);
mBorderPath.lineTo(width, height - mOverlap);
}
} else {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height - mOverlap);
if (mBorderEnabled) {
mBorderPath.moveTo(0, height - mOverlap);
mBorderPath.lineTo(width, height);
}
}
break;
case LEFT:
if (mDirection == TOP) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(mOverlap, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, 0);
mBorderPath.lineTo(mOverlap, height);
}
} else {
mClipPath.moveTo(mOverlap, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(mOverlap, 0);
mBorderPath.lineTo(0, height);
}
}
break;
}
mClipPath.close();
mBorderPath.close();
}
@Override
protected void dispatchDraw(Canvas canvas) {
final Path path = mClipPath;
canvas.clipPath(path);
super.dispatchDraw(canvas);
}
}
通过合并@Raghunandan 和@oğuzhan-döngül 的解决方案,我终于设法解决了这个问题。可以为未来的读者找到最终代码 here。感谢大家的支持。
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
import android.support.annotation.IntDef;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Created by santalu on 7/4/17.
*
* Note: if position set NONE mask won't be applied
*
* POSITION DIRECTION
*
* TOP LEFT | RIGHT
* BOTTOM LEFT | RIGHT
* LEFT TOP | BOTTOM
* RIGHT TOP | BOTTOM
*/
public class DiagonalImageView extends AppCompatImageView {
public static final String TAG = DiagonalImageView.class.getSimpleName();
@Retention(RetentionPolicy.SOURCE)
@IntDef({ NONE, LEFT, RIGHT, TOP, BOTTOM })
public @interface Position {
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({ LEFT, RIGHT, TOP, BOTTOM })
public @interface Direction {
}
public static final int NONE = 0;
public static final int TOP = 1;
public static final int RIGHT = 2;
public static final int BOTTOM = 4;
public static final int LEFT = 8;
private final Path mClipPath = new Path();
private final Path mBorderPath = new Path();
private final Paint mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Region mClickRegion = new Region();
private RectF mClickRect = new RectF();
private int mPosition;
private int mDirection;
private int mOverlap;
private int mBorderColor;
private int mBorderSize;
private boolean mBorderEnabled;
public DiagonalImageView(Context context) {
super(context);
init(context, null);
}
public DiagonalImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
setLayerType(LAYER_TYPE_HARDWARE, null);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiagonalImageView);
try {
mPosition = a.getInteger(R.styleable.DiagonalImageView_di_position, NONE);
mDirection = a.getInteger(R.styleable.DiagonalImageView_di_direction, RIGHT);
mOverlap = a.getDimensionPixelSize(R.styleable.DiagonalImageView_di_overlap, 0);
mBorderSize = a.getDimensionPixelSize(R.styleable.DiagonalImageView_di_borderSize, 0);
mBorderColor = a.getColor(R.styleable.DiagonalImageView_di_borderColor, Color.BLACK);
mBorderEnabled = a.getBoolean(R.styleable.DiagonalImageView_di_borderEnabled, false);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStyle(Style.STROKE);
mBorderPaint.setStrokeWidth(mBorderSize);
} finally {
a.recycle();
}
}
public void set(@Position int position, @Direction int direction) {
if (mPosition != position || mDirection != direction) {
mClipPath.reset();
mBorderPath.reset();
}
mPosition = position;
mDirection = direction;
postInvalidate();
}
public void setPosition(@Position int position) {
if (mPosition != position) {
mClipPath.reset();
mBorderPath.reset();
}
mPosition = position;
postInvalidate();
}
public void setDirection(@Direction int direction) {
if (mDirection != direction) {
mClipPath.reset();
mBorderPath.reset();
}
mDirection = direction;
postInvalidate();
}
public void setBorderEnabled(boolean enabled) {
mBorderEnabled = enabled;
postInvalidate();
}
public @Position int getPosition() {
return mPosition;
}
public @Direction int getDirection() {
return mDirection;
}
public boolean isBorderEnabled() {
return mBorderEnabled;
}
@Override protected void onDraw(Canvas canvas) {
if (mClipPath.isEmpty()) {
super.onDraw(canvas);
return;
}
int saveCount = canvas.save();
canvas.clipPath(mClipPath);
super.onDraw(canvas);
if (!mBorderPath.isEmpty()) {
canvas.drawPath(mBorderPath, mBorderPaint);
}
canvas.restoreToCount(saveCount);
}
@Override protected void dispatchDraw(Canvas canvas) {
if (!mClipPath.isEmpty()) {
canvas.clipPath(mClipPath);
}
super.dispatchDraw(canvas);
}
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (!changed) {
return;
}
if (mClipPath.isEmpty()) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (width <= 0 || height <= 0) {
return;
}
setClipPath(width, height);
}
}
@Override public boolean onTouchEvent(MotionEvent event) {
if (!mClickRegion.isEmpty()) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Point point = new Point();
point.x = (int) event.getX();
point.y = (int) event.getY();
//Log.d(TAG, "point: " + point);
if (!mClickRegion.contains(point.x, point.y)) {
//Log.d(TAG, "clicked outside");
return false;
}
}
}
return super.onTouchEvent(event);
}
private void setClipPath(final int width, final int height) {
mClipPath.reset();
mBorderPath.reset();
switch (mPosition) {
case TOP:
if (mDirection == LEFT) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, mOverlap);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, 0);
mBorderPath.lineTo(width, mOverlap);
}
} else {
mClipPath.moveTo(0, mOverlap);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, mOverlap);
mBorderPath.lineTo(width, 0);
}
}
break;
case RIGHT:
if (mDirection == TOP) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width - mOverlap, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(width, 0);
mBorderPath.lineTo(width - mOverlap, height);
}
} else {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width - mOverlap, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(width - mOverlap, 0);
mBorderPath.lineTo(width, height);
}
}
break;
case BOTTOM:
if (mDirection == LEFT) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height - mOverlap);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, height);
mBorderPath.lineTo(width, height - mOverlap);
}
} else {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height - mOverlap);
if (mBorderEnabled) {
mBorderPath.moveTo(0, height - mOverlap);
mBorderPath.lineTo(width, height);
}
}
break;
case LEFT:
if (mDirection == TOP) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(mOverlap, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, 0);
mBorderPath.lineTo(mOverlap, height);
}
} else {
mClipPath.moveTo(mOverlap, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(mOverlap, 0);
mBorderPath.lineTo(0, height);
}
}
break;
}
mClipPath.close();
mClipPath.computeBounds(mClickRect, true);
mClickRegion.setPath(mClipPath, new Region((int) mClickRect.left, (int) mClickRect.top, (int) mClickRect.right, (int) mClickRect.bottom));
mBorderPath.close();
}
}
我有一个对角线形状的图像视图。当我单击 imageview 时,它不考虑裁剪区域并执行单击透明区域。
我怎样才能只点击非透明区域或形状?如果需要,您可以查看视图的源代码here
提前致谢。
更新了您的库代码并找到了解决方案。
在绘制布局之前使用库计算的 mClipPath
。在单一布局中尝试过。
首先重写 dispatchDraw()
方法,如下所示:
@Override
protected void dispatchDraw(Canvas canvas) {
final Path path = mClipPath;
canvas.clipPath(path);
super.dispatchDraw(canvas);
}
然后添加你的xml这两行:
android:clickable="true"
android:foreground="?selectableItemBackground"
完整小部件 xml:
<com.santalu.diagonalimageview.DiagonalImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="@dimen/collapsing_image_height"
android:layout_marginTop="?actionBarSize"
android:background="?android:windowBackground"
android:scaleType="centerCrop"
android:src="@drawable/demo"
android:clickable="true"
android:foreground="?selectableItemBackground"
app:di_borderEnabled="false"
app:di_direction="left"
app:di_overlap="@dimen/collapsing_overlap_size"
app:di_position="bottom"/>
完整代码:
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.support.annotation.IntDef;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Created by santalu on 7/4/17.
*
* Note: if position set NONE mask won't be applied
*
* POSITION DIRECTION
*
* TOP LEFT | RIGHT
* BOTTOM LEFT | RIGHT
* LEFT TOP | BOTTOM
* RIGHT TOP | BOTTOM
*/
public class DiagonalImageView extends AppCompatImageView {
public static final String TAG = DiagonalImageView.class.getSimpleName();
@Retention(RetentionPolicy.SOURCE)
@IntDef({ NONE, LEFT, RIGHT, TOP, BOTTOM })
public @interface Position {
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({ LEFT, RIGHT, TOP, BOTTOM })
public @interface Direction {
}
public static final int NONE = 0;
public static final int TOP = 1;
public static final int RIGHT = 2;
public static final int BOTTOM = 4;
public static final int LEFT = 8;
private final Path mClipPath = new Path();
private final Path mBorderPath = new Path();
private final Paint mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int mPosition;
private int mDirection;
private int mOverlap;
private int mBorderColor;
private int mBorderSize;
private boolean mBorderEnabled;
public DiagonalImageView(Context context) {
super(context);
init(context, null);
}
public DiagonalImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
setLayerType(LAYER_TYPE_HARDWARE, null);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiagonalImageView);
try {
mPosition = a.getInteger(R.styleable.DiagonalImageView_di_position, NONE);
mDirection = a.getInteger(R.styleable.DiagonalImageView_di_direction, RIGHT);
mOverlap = a.getDimensionPixelSize(R.styleable.DiagonalImageView_di_overlap, 0);
mBorderSize = a.getDimensionPixelSize(R.styleable.DiagonalImageView_di_borderSize, 0);
mBorderColor = a.getColor(R.styleable.DiagonalImageView_di_borderColor, Color.BLACK);
mBorderEnabled = a.getBoolean(R.styleable.DiagonalImageView_di_borderEnabled, false);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStyle(Style.STROKE);
mBorderPaint.setStrokeWidth(mBorderSize);
} finally {
a.recycle();
}
}
public void set(@Position int position, @Direction int direction) {
if (mPosition != position || mDirection != direction) {
mClipPath.reset();
mBorderPath.reset();
}
mPosition = position;
mDirection = direction;
postInvalidate();
}
public void setPosition(@Position int position) {
if (mPosition != position) {
mClipPath.reset();
mBorderPath.reset();
}
mPosition = position;
postInvalidate();
}
public void setDirection(@Direction int direction) {
if (mDirection != direction) {
mClipPath.reset();
mBorderPath.reset();
}
mDirection = direction;
postInvalidate();
}
public void setBorderEnabled(boolean enabled) {
mBorderEnabled = enabled;
postInvalidate();
}
public @Position int getPosition() {
return mPosition;
}
public @Direction int getDirection() {
return mDirection;
}
public boolean isBorderEnabled() {
return mBorderEnabled;
}
@Override protected void onDraw(Canvas canvas) {
if (mClipPath.isEmpty()) {
super.onDraw(canvas);
return;
}
int saveCount = canvas.save();
canvas.clipPath(mClipPath);
super.onDraw(canvas);
if (!mBorderPath.isEmpty()) {
canvas.drawPath(mBorderPath, mBorderPaint);
}
canvas.restoreToCount(saveCount);
}
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (!changed) {
return;
}
if (mClipPath.isEmpty()) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (width <= 0 || height <= 0) {
return;
}
setClipPath(width, height);
}
}
private void setClipPath(final int width, final int height) {
mClipPath.reset();
mBorderPath.reset();
switch (mPosition) {
case TOP:
if (mDirection == LEFT) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, mOverlap);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, 0);
mBorderPath.lineTo(width, mOverlap);
}
} else {
mClipPath.moveTo(0, mOverlap);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, mOverlap);
mBorderPath.lineTo(width, 0);
}
}
break;
case RIGHT:
if (mDirection == TOP) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width - mOverlap, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(width, 0);
mBorderPath.lineTo(width - mOverlap, height);
}
} else {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width - mOverlap, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(width - mOverlap, 0);
mBorderPath.lineTo(width, height);
}
}
break;
case BOTTOM:
if (mDirection == LEFT) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height - mOverlap);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, height);
mBorderPath.lineTo(width, height - mOverlap);
}
} else {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height - mOverlap);
if (mBorderEnabled) {
mBorderPath.moveTo(0, height - mOverlap);
mBorderPath.lineTo(width, height);
}
}
break;
case LEFT:
if (mDirection == TOP) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(mOverlap, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, 0);
mBorderPath.lineTo(mOverlap, height);
}
} else {
mClipPath.moveTo(mOverlap, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(mOverlap, 0);
mBorderPath.lineTo(0, height);
}
}
break;
}
mClipPath.close();
mBorderPath.close();
}
@Override
protected void dispatchDraw(Canvas canvas) {
final Path path = mClipPath;
canvas.clipPath(path);
super.dispatchDraw(canvas);
}
}
通过合并@Raghunandan 和@oğuzhan-döngül 的解决方案,我终于设法解决了这个问题。可以为未来的读者找到最终代码 here。感谢大家的支持。
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
import android.support.annotation.IntDef;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Created by santalu on 7/4/17.
*
* Note: if position set NONE mask won't be applied
*
* POSITION DIRECTION
*
* TOP LEFT | RIGHT
* BOTTOM LEFT | RIGHT
* LEFT TOP | BOTTOM
* RIGHT TOP | BOTTOM
*/
public class DiagonalImageView extends AppCompatImageView {
public static final String TAG = DiagonalImageView.class.getSimpleName();
@Retention(RetentionPolicy.SOURCE)
@IntDef({ NONE, LEFT, RIGHT, TOP, BOTTOM })
public @interface Position {
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({ LEFT, RIGHT, TOP, BOTTOM })
public @interface Direction {
}
public static final int NONE = 0;
public static final int TOP = 1;
public static final int RIGHT = 2;
public static final int BOTTOM = 4;
public static final int LEFT = 8;
private final Path mClipPath = new Path();
private final Path mBorderPath = new Path();
private final Paint mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Region mClickRegion = new Region();
private RectF mClickRect = new RectF();
private int mPosition;
private int mDirection;
private int mOverlap;
private int mBorderColor;
private int mBorderSize;
private boolean mBorderEnabled;
public DiagonalImageView(Context context) {
super(context);
init(context, null);
}
public DiagonalImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
setLayerType(LAYER_TYPE_HARDWARE, null);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiagonalImageView);
try {
mPosition = a.getInteger(R.styleable.DiagonalImageView_di_position, NONE);
mDirection = a.getInteger(R.styleable.DiagonalImageView_di_direction, RIGHT);
mOverlap = a.getDimensionPixelSize(R.styleable.DiagonalImageView_di_overlap, 0);
mBorderSize = a.getDimensionPixelSize(R.styleable.DiagonalImageView_di_borderSize, 0);
mBorderColor = a.getColor(R.styleable.DiagonalImageView_di_borderColor, Color.BLACK);
mBorderEnabled = a.getBoolean(R.styleable.DiagonalImageView_di_borderEnabled, false);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStyle(Style.STROKE);
mBorderPaint.setStrokeWidth(mBorderSize);
} finally {
a.recycle();
}
}
public void set(@Position int position, @Direction int direction) {
if (mPosition != position || mDirection != direction) {
mClipPath.reset();
mBorderPath.reset();
}
mPosition = position;
mDirection = direction;
postInvalidate();
}
public void setPosition(@Position int position) {
if (mPosition != position) {
mClipPath.reset();
mBorderPath.reset();
}
mPosition = position;
postInvalidate();
}
public void setDirection(@Direction int direction) {
if (mDirection != direction) {
mClipPath.reset();
mBorderPath.reset();
}
mDirection = direction;
postInvalidate();
}
public void setBorderEnabled(boolean enabled) {
mBorderEnabled = enabled;
postInvalidate();
}
public @Position int getPosition() {
return mPosition;
}
public @Direction int getDirection() {
return mDirection;
}
public boolean isBorderEnabled() {
return mBorderEnabled;
}
@Override protected void onDraw(Canvas canvas) {
if (mClipPath.isEmpty()) {
super.onDraw(canvas);
return;
}
int saveCount = canvas.save();
canvas.clipPath(mClipPath);
super.onDraw(canvas);
if (!mBorderPath.isEmpty()) {
canvas.drawPath(mBorderPath, mBorderPaint);
}
canvas.restoreToCount(saveCount);
}
@Override protected void dispatchDraw(Canvas canvas) {
if (!mClipPath.isEmpty()) {
canvas.clipPath(mClipPath);
}
super.dispatchDraw(canvas);
}
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (!changed) {
return;
}
if (mClipPath.isEmpty()) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (width <= 0 || height <= 0) {
return;
}
setClipPath(width, height);
}
}
@Override public boolean onTouchEvent(MotionEvent event) {
if (!mClickRegion.isEmpty()) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Point point = new Point();
point.x = (int) event.getX();
point.y = (int) event.getY();
//Log.d(TAG, "point: " + point);
if (!mClickRegion.contains(point.x, point.y)) {
//Log.d(TAG, "clicked outside");
return false;
}
}
}
return super.onTouchEvent(event);
}
private void setClipPath(final int width, final int height) {
mClipPath.reset();
mBorderPath.reset();
switch (mPosition) {
case TOP:
if (mDirection == LEFT) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, mOverlap);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, 0);
mBorderPath.lineTo(width, mOverlap);
}
} else {
mClipPath.moveTo(0, mOverlap);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, mOverlap);
mBorderPath.lineTo(width, 0);
}
}
break;
case RIGHT:
if (mDirection == TOP) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width - mOverlap, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(width, 0);
mBorderPath.lineTo(width - mOverlap, height);
}
} else {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width - mOverlap, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(width - mOverlap, 0);
mBorderPath.lineTo(width, height);
}
}
break;
case BOTTOM:
if (mDirection == LEFT) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height - mOverlap);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, height);
mBorderPath.lineTo(width, height - mOverlap);
}
} else {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height - mOverlap);
if (mBorderEnabled) {
mBorderPath.moveTo(0, height - mOverlap);
mBorderPath.lineTo(width, height);
}
}
break;
case LEFT:
if (mDirection == TOP) {
mClipPath.moveTo(0, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(mOverlap, height);
if (mBorderEnabled) {
mBorderPath.moveTo(0, 0);
mBorderPath.lineTo(mOverlap, height);
}
} else {
mClipPath.moveTo(mOverlap, 0);
mClipPath.lineTo(width, 0);
mClipPath.lineTo(width, height);
mClipPath.lineTo(0, height);
if (mBorderEnabled) {
mBorderPath.moveTo(mOverlap, 0);
mBorderPath.lineTo(0, height);
}
}
break;
}
mClipPath.close();
mClipPath.computeBounds(mClickRect, true);
mClickRegion.setPath(mClipPath, new Region((int) mClickRect.left, (int) mClickRect.top, (int) mClickRect.right, (int) mClickRect.bottom));
mBorderPath.close();
}
}