简体   繁体   English

为什么没有为我设置OnTouchListener?

[英]Why OnTouchListener is not set for me?

I am working on a custom view called CanvasView . 我正在研究一个名为CanvasView的自定义视图。 This is a view that allows me to draw stuff on it outside of the onDraw method. 此视图使我可以在onDraw方法之外在其上绘制内容。 It is something like this: 它是这样的:

public class CanvasView extends View {
    private ArrayList<Shape> shapes;
    private Paint paint;

    public CanvasView (Context c) {
        super(c);
        init ();
    }

    public CanvasView(Context context, AttributeSet attrs) {
        super (context, attrs);
        init ();
    }

    public CanvasView(Context context, AttributeSet attrs, int defStyleAttr) {
        super (context, attrs, defStyleAttr);
        init ();
    }
    private void init () {
        shapes = new ArrayList<> ();
        paint = new Paint ();
        paint.setStrokeWidth (5);
        paint.setColor (Color.BLACK);
    }

    //focus on this method, I think the others are irrelevant
    @Override
    public void setOnTouchListener (final OnTouchListener listener) {
        final OnTouchListener baseListener = new OnTouchListener () {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                float x = event.getX ();
                float y = event.getY ();
                if (x < 18 || x > getWidth () - 18 || y < 18 ||
                        y > getHeight () - 18)
                    return false;

                return true;
            }
        };

        super.setOnTouchListener (new OnTouchListener () {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (baseListener.onTouch (v, event)) {
                    if (listener != null) {
                        return listener.onTouch (v, event);
                    } else {
                        return true;
                    }
                }
                return false;
            }
        });
    }

    @Override
    protected void onDraw (Canvas c) {
        super.onDraw (c);
        for (Shape s : shapes) {
            s.draw (c);
        }
        //draw the border. irrelevant
        c.drawLine (3, 3, getWidth () - 3, 3, paint);
        c.drawLine (3, getHeight () - 3, getWidth () - 3, getHeight () - 3, paint);
        c.drawLine (3, 3, 3, getHeight () - 3, paint);
        c.drawLine (getWidth () - 3, 3, getWidth () - 3, getHeight () - 3, paint);

        //draw the inner border
        c.drawLine (18, 18, getWidth () - 18, 18, paint);
        c.drawLine (18, getHeight () - 18, getWidth () - 18, getHeight () - 18, paint);
        c.drawLine (18, 18, 18, getHeight () - 18, paint);
        c.drawLine (getWidth () - 18, 18, getWidth () - 18, getHeight () - 18, paint);
    }

    public void addShape (Shape s) {
        shapes.add (s);
    }

    public void clear () {
        shapes.clear ();
    }
}

Explanation: 说明:

Shape is an interface with a method: Shape是具有方法的接口:

public void draw (Canvas c);

Focus on the setOnTouchListener override. 专注于setOnTouchListener覆盖。 I overrode this method because I want to limit the user not to touch the borders of the CanvasView . 我覆盖了此方法,因为我想限制用户不要触摸CanvasView的边界。 As you can see, I first invoke the baseListener to check whether the touch is in bounds. 如您所见,我首先调用baseListener来检查触摸是否在范围之内。 And then I invoke the listener passed in the constructor. 然后,我调用在构造函数中传递的侦听器。 Is this a good practice? 这是一个好习惯吗?

Anyway, I set the OnTouchListener of a CanvasView in the onCreate method: 无论如何,我在onCreate方法中设置了CanvasViewOnTouchListener

canvas.setOnTouchListener (new View.OnTouchListener () {
        @Override
        public boolean onTouch(View v, final MotionEvent event) {
            if (point1 != null && point2 != null) {
                throw new IllegalStateException ("Both point1 and point2 are not null");
            }

            if (point1 == null) {
                point1 = new PointF (event.getX (), event.getY ());
            } else { //point2 is null
                point2 = new PointF (event.getX (), event.getY ());
                canvas.addShape (new Shape () {
                    @Override
                    public void draw(Canvas c) {
                        c.drawLine (point1.x, point1.y, point2.x, point2.y, paint);
                    }
                });
                canvas.setOnTouchListener (null);
            }

            canvas.addShape (new Shape () {
                @Override
                public void draw(Canvas c) {
                    c.drawCircle (event.getX (), event.getY (), 13, paint);
                }
            });

            return true;
        }
    });

Explanation: 说明:

point1 and point2 are fields declared in the activity class. point1point2是在活动类中声明的字段。 When the user touches the screen, one of them will be instantiated and a little circle is drawn at the point. 当用户触摸屏幕时,将实例化其中之一,并在该点绘制一个小圆圈。 When the user touches the second time, point2 is instantiated and a line is drawn between the two points. 当用户第二次触摸时,将实例化point2并在两点之间绘制一条线。

When I run my app and touch the screen, nothing is drawn! 当我运行我的应用程序并触摸屏幕时,什么也没画! I think the setOnClickListener override is not written correctly. 我认为setOnClickListener覆盖未正确编写。 Can anyone tell me why? 谁能告诉我为什么?

The onDraw method of a View is only called when the View is first created. 仅在首次创建View时才调用View的onDraw方法。 To cause it to be called again, you need to mark the View as “dirty” (changes have been made) using the invalidate() function on the View, every time the contents of the View change. 为了使它再次被调用,您需要在View的内容每次更改时,使用View上的invalidate()函数将View标记为“脏”(已进行更改)。

Thus, a convenient place to call this function would be just after adding a shape to the View in your addShape function. 因此,在addShape函数中向View添加形状之后,就可以方便地调用此函数。

 public void addShape (Shape s) {
        shapes.add (s);
        this.invalidate();
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM