public class RectView extends View { private Paint mPaint; private RectF mRect; private float mX; private float mY; public RectView(Context context) { super(context); init(); } public RectView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public RectView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); }
private void init() { mPaint = new Paint(); mPaint.setColor(Color.RED); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); mPaint.setStrokeWidth(5); mRect = new RectF(100, 10, 300, 100); } @Override public boolean onTouchEvent(MotionEvent event) { mX = event.getX(); mY = event.getY(); Log.d("onTouchEvent", "onTouchEvent:--mX== " + mX + "---mY==" + mY); if (event.getAction() == MotionEvent.ACTION_DOWN) { invalidate(); return true; } else if (event.getAction() == MotionEvent.ACTION_UP) { mX = -1; mY = -1; } postInvalidate(); return super.onTouchEvent(event); }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //case1:圆角矩形 //Paint paint = new Paint(); //paint.setColor(Color.RED); //paint.setStyle(Paint.Style.FILL_AND_STROKE); //paint.setStrokeWidth(5); //RectF rect = new RectF(100, 10, 300, 100); // RectF rect:要绘制的矩形。 // float rx:生成圆角的椭圆的 X 轴半径。 // float ry:生成圆角的椭圆的 Y 轴半径。 //canvas.drawRoundRect(rect, 50, 50, paint); //case2:圆 半径=StrokeWidth/2+剩下的空白 //canvas.drawCircle(100,100,50,paint); //case3:椭圆是由矩形生成的;一个矩形,决定一个椭圆 //canvas.drawRect(rect,paint); //paint.setColor(Color.GREEN); //canvas.drawOval(rect,paint); //case4 弧是椭圆的一部分,而椭圆是根据矩形来生成的,所以弧也是根据矩形来生成的。 //带两边 连接圆心 //canvas.drawArc(rect,0,90, true,paint); //不带两边 ,只有圆弧 //canvas.drawArc(rect,0,90, false,paint); //canvas.drawArc(rect,0,90, false,paint); //case5 判断是否包含某个点 //该函数用于判断某个点是否在当前矩形中。 // 如果在,则返回 true;如果不在,则返回 false。 参数(x,y)就是当前要判断的点的坐标 if (mRect.contains(mX,mY)){ mPaint.setColor(Color.RED); }else { mPaint.setColor(Color.GREEN); } canvas.drawRect(mRect,mPaint); } }
值得注意的是,在 MotionEvent.ACTION_DOWN 中返回 true,因为当 MotionEvent. ACTION_DOWN 消息到来时,系统会判断返回值,当返回 true 时,表示当前控件已经在拦截 (消费)这个消息了,所以后续的 ACTION_MOVE、ACTION_UP 消息仍然继续传过来。如 果返回 false(系统默认返回 false),就表示当前控件不需要这个消息,那么后续的
ACTION_MOVE、ACTION_UP 消息就不会再传到这个控件。 当用户手指弹起时,我们需要还原矩形的颜色(绿色),所以将 mX,mY 全部设置为负值。
由于我们构造的矩形不包含坐标为负的点,所以也就还原了矩形的颜色。
最后,调用 postInvalidate()函数刷新控件屏幕,让控件重绘。
细心的读者会发现,在 ACTION_DOWN 消息到来时,我们调用了 invalidate()函数重绘控 件。其实,postInvalidate()和 invalidate()函数都是用来重绘控件的,区别是 invalidate()函数一 定要在主线程中执行,否则就会报错;而 postInvalidate()函数则没有那么多讲究,它可以在任何线程中执行,而不必一定是主线程。因为在 postInvalidate()函数中就是利用 handler 给主线 程发送刷新界面的消息来实现的,所以它可以在任何线程中执行而不会出错。而正因为它是 通过发送消息来实现的,所以它的界面刷新速度可能没有直接调用 invalidate()函数那么快。 因此,在确定当前线程是主线程的情况下,还是以 invalidate()函数为主。当我们不确定当前 要刷新界面的位置所处的线程是不是主线程的时候,还是调用 postInvalidate()函数为好;这里 笔者故意调用的是 postInvalidate()函数,因为 onTouchEvent()函数本来就是在主线程中的,所 以使用 invalidate()函数更合适。