2017-09-07 106 views
0

我有一个对角线形状的图像视图。当我点击图像视图时,它不尊重修剪区域,并在透明区域执行单击操作。自定义视图可点击区域重叠

我怎样才能让只有非透明区域或形状可点击?如果需要,您可以看到视图的源代码here

在此先感谢。

enter image description here

enter image description here

+0

你需要一些基本的数学检查,如果您的点击位置就是你的梯形形状内部或不 – pskink

+2

,但如果你正在使用'clipPath'然后看看'android.graphics.Region'文档,这使得这份工作对你 – pskink

+0

好主意。其实我一直在试图用Region来完成这个。如果你有任何工作示例或任何指南来帮助我对此,我真的很感激 – santalu

回答

2

为合并@Raghunandan和@的Oguzhan-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(); 
    } 
} 
2

更新您的库代码,并找到了解决办法。 使用您的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); 
    } 
} 
+0

谢谢你的回答。这使正确绘制的点击选择器位于剪辑路径区域内部。为此+1。但我仍然可以点击矩形的透明区域。 – santalu