Search code examples
androidandroid-layouttouch-event

onTouchEvent on Android custom view doesn't work when 2 custom view are created


My goal is to draw 3 equals custom views in an Activity. The custom view overrides onTouchEvent. If I add just one custom view in the Activity, by XML or programmatically, all works fine. If I add 2 custom views (the same custom view) the touch event works only for one custom view, the other one is fixed in layout and doesn't intercept anything at all. I've seen other discussions but I can't find the problem. My custom view class:

 public class IconCropView extends View {

//contants strings
private static final String TAG = "IconCropView";
private static final int TEXT_COLOR = Color.RED;
private static final float TEXT_SIZE = 30.0f;
private static final float STROKE_WIDTH = 4.0f;
//drawing objects
private Paint paint;
//text
private Paint textPaint;

//point objects
private Point[] points;
private Point start;
private Point offset;

//variable ints
private int altezzaMinima;
private int altezza;
private int halfCorner;
private int cornerColor;
private int edgeColor;
private int outsideColor;
private int corner = 30;

//variable booleans
private boolean initialized = false;

//drawables
private Drawable moveDrawable;
private Drawable  resizeDrawable2;
//context
Context context;

//Rect del canvas
Rect cropBorder;
boolean clickInCropRect;

public IconCropView(Context context) {
    super(context);
    this.context = context;
    init(null,null);
}

public IconCropView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    this.context = context;
    init(attrs,null);
}

public IconCropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.context = context;
    init(attrs,null);
}
public IconCropView(Context context, Point[] punti) {
    super(context);
    this.context = context;
    init(null,punti);
}


private void init(@Nullable AttributeSet attrs,Point[] punti ){


    paint = new Paint();
    start = new Point();
    offset = new Point();

    //initial dimensions
    altezzaMinima = 400;
    altezza = altezzaMinima;
    halfCorner = 25;

    //colors
    cornerColor = Color.BLACK;
    edgeColor = Color.WHITE;
    outsideColor =Color.parseColor("#00000088");

    //initialize corners;
    points = new Point[4];

    points[0] = new Point();
    points[1] = new Point();
    points[2] = new Point();
    points[3] = new Point();

    if(punti == null) {
        //init corner locations;
        //top left
        points[0].x = 0;
        points[0].y = 0;

        //top right
        points[1].x = 600;
        points[1].y = 0;

        //bottom left
        points[2].x = 0;
        points[2].y = altezzaMinima;

        //bottom right
        points[3].x = 600;
        points[3].y = altezzaMinima;
    }
    if(punti != null){
        //init corner locations;
        //top left

        points[0].x = punti[0].x;
        points[0].y = punti[0].y;

        //top right
        points[1].x = punti[1].x;
        points[1].y = punti[1].y;

        //bottom left
        points[2].x = punti[2].x;
        points[2].y = punti[2].y;

        //bottom right
        points[3].x = punti[3].x;
        points[3].y = punti[3].y;
    }
    //init drawables

    resizeDrawable2 = getResources().getDrawable(R.drawable.circle);

    //set initialized to true
    initialized = true;

    //inizializzo testo
    textPaint = new Paint();
    textPaint.setColor(TEXT_COLOR);
    textPaint.setTextSize(TEXT_SIZE);

}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //set paint to draw edge, stroke
    if(initialized) {
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setColor(edgeColor);
        paint.setStrokeWidth(4);
        cropBorder = new Rect(points[0].x + halfCorner, points[0].y + halfCorner,points[0].x + halfCorner + 500,points[0].y + halfCorner + altezza);
        //crop rectangle
        canvas.drawRect(cropBorder, paint);
        //set paint to draw outside color, fill
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(outsideColor);
        resizeDrawable2.setBounds(points[2].x, points[2].y, points[2].x + halfCorner*2, points[2].y + halfCorner*2);
        //place corner drawable
        resizeDrawable2.draw(canvas);

    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    //return super.onTouchEvent(event);

    switch(event.getActionMasked()){
        case MotionEvent.ACTION_DOWN:{

            //get the coordinates
            start.x = (int)event.getX();
            start.y = (int)event.getY();

            //get the corner touched if any
            corner = getCorner(start.x, start.y);

            //get the offset of touch(x,y) from corner top-left point
            offset = getOffset(start.x, start.y, corner);

            //account for touch offset in starting point
            start.x = start.x - offset.x;
            start.y = start.y - offset.y;

            // dentro il rettangolo
            clickInCropRect = cropBorder.contains( start.x, start.y);
            break;

        }
        case MotionEvent.ACTION_MOVE:{
            if(corner == 0 || clickInCropRect ) {
                points[0].x = Math.max(points[0].x + (int) Math.min(Math.floor((event.getX() - start.x - offset.x)), Math.floor(getWidth() - points[0].x - 2*halfCorner - (cropBorder.width()))), 0);
                points[1].x = Math.max(points[1].x + (int) Math.min(Math.floor((event.getX() - start.x - offset.x)), Math.floor(getWidth() - points[1].x - 2*halfCorner)), altezza);
                points[2].x = Math.max(points[2].x + (int) Math.min(Math.floor((event.getX() - start.x - offset.x)), Math.floor(getWidth() - points[2].x - 2*halfCorner - (cropBorder.width()))), 0);
                points[3].x = Math.max(points[3].x + (int) Math.min(Math.floor((event.getX() - start.x - offset.x)), Math.floor(getWidth() - points[3].x - 2*halfCorner)), altezza);

                points[0].y = Math.max(points[0].y + (int) Math.min(Math.floor((event.getY() - start.y - offset.y)), Math.floor(getHeight() - points[0].y - 2*halfCorner - (cropBorder.height()) )), 0);
                points[1].y = Math.max(points[1].y + (int) Math.min(Math.floor((event.getY() - start.y - offset.y)), Math.floor(getHeight() - points[1].y - 2*halfCorner - (cropBorder.height()))), 0);
                points[2].y = Math.max(points[2].y + (int) Math.min(Math.floor((event.getY() - start.y - offset.y)), Math.floor(getHeight() - points[2].y - 2*halfCorner)), altezza);
                points[3].y = Math.max(points[3].y + (int) Math.min(Math.floor((event.getY() - start.y - offset.y)), Math.floor(getHeight() - points[3].y - 2*halfCorner)), altezza);

                start.x = points[0].x+(cropBorder.width()/2);
                start.y = points[0].y+(cropBorder.height()/2);
                Log.v("TOUCHED", points[0].x + " ");

                invalidate();

            }
            else if (corner == 2){
                altezza =  Math.min((Math.min((Math.max(altezzaMinima, (int)(altezza + Math.floor(event.getY()) - start.y - offset.y))), altezza + (getHeight() - points[2].y - 2* halfCorner ))), altezza + (getWidth() - points[1].x - 2* halfCorner ));
                points[2].y = points[0].y + altezza;
                points[3].y = points[0].y + altezza;
                points[3].x = points[0].x + altezza;
                points[1].x = points[0].x + altezza;
                start.y = points[2].y;
                invalidate();


            }
            break;

        }

    }
    return true;
}

private int getCorner(float x, float y){
    int corner = 5;
    for (int i = 0; i < points.length; i++){
        float dx = x - points[i].x;
        float dy = y - points[i].y;
        int max = halfCorner * 2;
        if(dx <= max && dx >= 0 && dy <= max && dy >= 0){
            return i;
        }
    }
    return corner;
}

private Point getOffset(int left, int top, int corner){
    Point offset = new Point();
    if(corner == 5){
        offset.x = 0;
        offset.y = 0;
    }else{
        offset.x = left - points[corner].x;
        offset.y = top - points[corner].y;
    }
    return offset;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

  }

In onCreate Activity :

    IconCropView iconCrop1 = new IconCropView(context);
    IconCropView iconCrop2 = new IconCropView(context,punti);
    RelativeLayout myLayout = findViewById(R.id.myLayout);
    myLayout .addView(iconCrop1);
    myLayout .addView(iconCrop2);

I've put them also in the XML file but the result is the same.

I'll appreciate your helps.

Thank you all.


Solution

  • I solved the problem. The issue wasn't the onTouchEvent but the container where IconCropView was defined in. Before I simply defined an element and added it to the layout. Now in my opinion the correct way is:

    1. Define layout where IconCropView is created in. It should be greater than IconCropview otherwise it can't move with touch:

    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(mImageViewWidth, mImageViewHeight / 4 );

    1. Define IconCropView margins related to its container.

    params.setMargins(marginLeft, marginTop, marginRight, marginBottom);

    3)Create new IconCropView and add It in the parent layout activity. Mlayout is the main layout of activity:

    IconCropView cropView = new IconCropView(context, width, height);
    
    cropView .setLayoutParams(params);
    
    mLayout.addView(cropView);
    

    I hope this can be useful for someone.