通过触摸屏绘画(不连续的线条)

时间:2012-03-26 17:23:44

标签: android android-canvas

我的活动类文件:

package com.drawing.test;

import android.app.Activity;
import android.os.Bundle;


import android.app.Activity;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.LinearLayout;

public class TstActivity extends Activity implements OnTouchListener
{
    float x1 = 0, y1 = 0, x2 = 0, y2 = 0;
    public static boolean action=false;
    private Bitmap mBitmap;
    private Canvas mCanvas;
    private Paint mBitmapPaint;

    Drawer mDrawer;

    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        LinearLayout mLinearLayout = (LinearLayout) findViewById(R.id.drawView);
        mLinearLayout.setOnTouchListener((OnTouchListener) this);
        mLinearLayout.addView(new Drawer(this));
    }

    public boolean onTouch(View v, MotionEvent event) 
    {
        switch (event.getAction()) 
        {
            case MotionEvent.ACTION_DOWN:
                x1 = event.getX();
                y1 = event.getY();
                action=false;
                //v.invalidate();
            return true;
            case MotionEvent.ACTION_MOVE:
                x1=x2;
                y1=y2;
                x2 = event.getX();
                y2 = event.getY();
                v.invalidate();
                action=true;
            return true;
            case MotionEvent.ACTION_UP:
                x2 = event.getX();
                y2 = event.getY();
                v.invalidate();
                action=true;
            return true;

        }
        return false;
    }

    public class Drawer extends View
    {

        public Drawer(Context context)
        {
            super(context);
            mBitmap = Bitmap.createBitmap(400, 800, Bitmap.Config.ARGB_8888);
            mCanvas = new Canvas(mBitmap);
            mBitmapPaint = new Paint(Paint.DITHER_FLAG);
            mBitmapPaint.setColor(Color.MAGENTA);
            invalidate();
        }

        protected void onDraw(Canvas canvas)
        {
            if(x1==x2 && y1==y2)return;
            Paint p = new Paint();
            // Canvas mCanvas1=new Canvas(mBitmap);
            p.setColor(Color.parseColor("#7CFC00"));
            canvas.drawBitmap(mBitmap, 0, 0, p);
            //  canvas.drawLine(x1, y1, x2 , y2, p);
            p.setColor(Color.RED);
            // mCanvas1.drawLine(x1, y1, x2, y2,p);
            if(action==true)mCanvas.drawLine(x1, y1, x2, y2, mBitmapPaint);

        }
    }
}

我的布局XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/drawView">

</LinearLayout>

当我在模拟器上尝试此操作时,线条在中间断开但继续绘制。我也尝试过设备,同样的问题发生但数量较少。此外,显示屏有时会闪烁,或者完全消失。当它完全变成黑色时,如果我触摸一次就会回来。

我做错了什么,我是否应该改变方法。

任何建议都表示赞赏。感谢

2 个答案:

答案 0 :(得分:2)

这是一个同步问题。基本上,invalidate() 阻塞调用:它只是告诉系统在将来的某个时刻重绘。那么会发生什么:

  • 您为(x1,y1)和(x2,y2)
  • 设置了一个值
  • invaldate()计划尽快重绘
  • 但是另一个触摸事件会在绘制线之前覆盖(x1,y1)和(x2,y2) :旧值永远丢失

您可以通过添加两个匹配计数器来证明它,一个用于invalidate(),另一个用于onDraw()。一段时间后,您会看到invalidate()的来电次数大于onDraw()的次数。我的建议是将点数与Queue中的触摸事件保持一致。另请注意,每次分配Bitmap时,都需要在完成后调用recycle以避免内存泄漏。事实上,像素存储在本机内存中,并且在视图被销毁时不会被垃圾收集。当我的活动停止时,我通常会拨打recycle。这是我的代码:

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    public static class Drawer extends View  {

        private Bitmap cache;
        private Queue<PointF> points;
        private PointF from;

        public Drawer(Context ctx, AttributeSet attrs) {
            super(ctx, attrs);
            points = new ConcurrentLinkedQueue<PointF>();
        }

        @Override
        public boolean onTouchEvent(MotionEvent evt) {
            switch (evt.getAction()) {
            case MotionEvent.ACTION_DOWN: from = new PointF(evt.getX(), evt.getY()); break;
            case MotionEvent.ACTION_MOVE: points.add(new PointF(evt.getX(), evt.getY())); invalidate(); break;
            case MotionEvent.ACTION_UP: from = null; break;
            default: from = null;
            }
            return true;
        }

        @Override
        public void onSizeChanged(int w, int h, int oldw, int oldh) {
            if (w == 0 || h == 0)
                return;
            cache = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        }

        @Override
        public void onDraw(Canvas systemCanvas) {
            int w = getWidth();
            int h = getHeight();
            if (w == 0 || h == 0)
                return;
            if (cache == null)
                cache = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            // Draw on the cache
            Canvas canvas = new Canvas(cache);
            Paint paint = new Paint();
            paint.setStrokeWidth(4);
            paint.setColor(Color.MAGENTA);
            paint.setFlags(Paint.ANTI_ALIAS_FLAG);

            drawPoints(points, canvas, paint);

            // Draw the cache with the system canvas
            systemCanvas.drawBitmap(cache, 0, 0, paint);
        }

        private void drawPoints(Queue<PointF> points, Canvas canvas, Paint paint) {
            if (from == null)
                return;
            PointF to;
            while ((to = points.poll()) != null) {
                canvas.drawLine(from.x, from.y, to.x, to.y, paint);
                from = to;
            }
        }

    }
}

这是布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:id="@+id/drawView">
    <view 
        class="com.zybnet.test.MainActivity$Drawer"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</LinearLayout>

您可以看到海关视图也可以在XML中使用:)

答案 1 :(得分:0)

对于motionEvent.ACTION_MOVE,他们可以并将进行批处理。我相信这就是为什么会出现中断的原因。

请参阅文档:

<强>配料

为了提高效率,使用ACTION_MOVE的运动事件可以将单个对象内的多个运动样本一起批处理。使用getX(int)和getY(int)可以获得最新的指针坐标。使用getHistoricalX(int,int)和getHistoricalY(int,int)访问批处理中的早期坐标。坐标只是“历史”,只要它们比批次中的当前坐标更旧;但是,它们仍然不同于先前运动事件中报告的任何其他坐标。要按时间顺序处理批次中的所有坐标,首先使用历史坐标,然后使用当前坐标。