user1618201
user1618201

Reputation:

Change Custom View Width And Height In Android

In my android app, I have a crop image. So, I programming a custom view as my crop box. I can move the crop box. But my problem is how can I drag border of the crop box and change width and height of it. How can I do it?

Attr Class:

public class Attr {

    public static final float CROP_BOX_START_X = 5;
    public static final float CROP_BOX_START_Y = 5;
    public static final float CROP_BOX_END_X = 305;
    public static final float CROP_BOX_END_Y = 105;

}

CropBox Class:

public class CropBox extends View {

    private Paint paint = new Paint();


    public CropBox(Context context) {
        super(context);
    }

    public CropBox(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
    }


    @Override
    public void onDraw(Canvas canvas) {
        float[][] circleXY = {
            {Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y},
            {(Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2, Attr.CROP_BOX_START_Y},
            {Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y},
            {Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y},
            {(Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2, Attr.CROP_BOX_END_Y},
            {Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y},
            {Attr.CROP_BOX_START_X, (Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2},
            {Attr.CROP_BOX_END_X, (Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2}
        };
        float[][] lineXY = {
            {Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y},
            {Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y},
            {Attr.CROP_BOX_START_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_START_X, Attr.CROP_BOX_END_Y},
            {Attr.CROP_BOX_END_X, Attr.CROP_BOX_START_Y, Attr.CROP_BOX_END_X, Attr.CROP_BOX_END_Y}
        };

        paint.setColor(Color.CYAN);
        paint.setStrokeWidth(1);

        for(int i = 0 ; i < circleXY.length ; i++)
            canvas.drawCircle(circleXY[i][0], circleXY[i][1], 5, paint);

        paint.setStrokeWidth(2);

        for(int i = 0 ; i < lineXY.length ; i++)
            canvas.drawLine(lineXY[i][0], lineXY[i][2], lineXY[i][2], lineXY[i][3], paint);
    }

}

CropTestActivity Class:

public class CropTestActivity extends Activity {

    private ImageView imageView;
    private CropBox cropBox;
    private RelativeLayout relativeLayout;
    private RelativeLayout.LayoutParams layoutParams;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.crop_test_layout);
        imageView = (ImageView)findViewById(R.id.android_image);
        cropBox = new CropBox(this);
        relativeLayout = (RelativeLayout)findViewById(R.id.crop_test_layout);
        layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
        imageView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                layoutParams.leftMargin = imageView.getWidth() / 2 - (int)((Attr.CROP_BOX_START_X + Attr.CROP_BOX_END_X) / 2) + imageView.getLeft();
                layoutParams.topMargin = imageView.getHeight() / 2 - (int)((Attr.CROP_BOX_START_Y + Attr.CROP_BOX_END_Y) / 2) + imageView.getTop();
            }
        });
        relativeLayout.addView(cropBox, layoutParams);
        cropBox.setOnTouchListener(new Crop(imageView));
    }

}

Crop Class:

public class Crop implements OnTouchListener {

    private static final int NONE = 0;
    private static final int BOX_DRAG = 1;
    private static final int BORDER_DRAG = 2;

    private int mode = NONE;

    private float cropBoxStartX = Attr.CROP_BOX_START_X;
    private float cropBoxStartY = Attr.CROP_BOX_START_Y;
    private float cropBoxEndX = Attr.CROP_BOX_END_X;
    private float cropBoxEndY = Attr.CROP_BOX_END_Y;

    private ImageView imageView;

    private PointF start = new PointF();


    public Crop(ImageView imageView) {
        this.imageView = imageView;
    }


    public boolean onTouch(View view, MotionEvent event) {
        RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)view.getLayoutParams();

        switch(event.getAction() & MotionEvent.ACTION_MASK) {

            case MotionEvent.ACTION_DOWN:
                start.set(event.getX(), event.getY());
                if(event.getX() > cropBoxStartX && event.getX() < cropBoxEndX && event.getY() > cropBoxStartY && event.getY() < cropBoxEndY)
                    mode = BOX_DRAG;
                else if(event.getX() == cropBoxStartX || event.getX() == cropBoxEndX || event.getY() == cropBoxStartY || event.getY() == cropBoxEndY)
                    mode = BORDER_DRAG;
                else
                    mode = NONE;
                break;

            case MotionEvent.ACTION_UP:
                mode = NONE;
                break;

            case MotionEvent.ACTION_MOVE:
                if(mode == BOX_DRAG) {
                    layoutParams.leftMargin = (int)event.getX() - (int)start.x + view.getLeft();
                    layoutParams.topMargin = (int)event.getY() - (int)start.y + view.getTop();
                    while(layoutParams.topMargin + 5 < imageView.getTop())
                        layoutParams.topMargin++;
                    while(layoutParams.leftMargin + (cropBoxEndX - cropBoxStartX + 5) > imageView.getRight())
                        layoutParams.leftMargin--;
                    while(layoutParams.topMargin + (cropBoxEndY - cropBoxStartY + 5) > imageView.getBottom())
                        layoutParams.topMargin--;
                    while(layoutParams.leftMargin + 5 < imageView.getLeft())
                        layoutParams.leftMargin++;
                }
                else if(mode == BORDER_DRAG) {
                }
                break;
        }
        view.setLayoutParams(layoutParams);
        return true;
    }

}

Layout XML:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/crop_test_layout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <ImageView
        android:id="@+id/android_image"
        android:src="@drawable/android"
        android:layout_width="fill_parent"
        android:layout_height="300dp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="10dp"
        android:layout_marginLeft="10dp"
        android:layout_gravity="center"
        android:scaleType="fitXY"
        android:contentDescription="@string/android_image_description" >
    </ImageView>

</RelativeLayout>

Before Resize:

enter image description here

After Resize:

enter image description here

Thanks for your help.

Upvotes: 8

Views: 3941

Answers (1)

Vijay C
Vijay C

Reputation: 4869

Following is the solution,

Modified onCreate from Activity

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.crop_test_layout);
    imageView = (ImageView)findViewById(R.id.android_image);
    cropBox = new CropBox(this, imageView);
    relativeLayout = (RelativeLayout)findViewById(R.id.crop_test_layout);
    relativeLayout.addView(cropBox);
}

Modified CropBox class:

public class CropBox extends View {

    private static final int CROP_BOX_START_X = 5;
    private static final int CROP_BOX_START_Y = 5;
    private static final int CROP_BOX_END_X = 305;
    private static final int CROP_BOX_END_Y = 105;

    private static final int DRAG_SQUARE = 75;

    public ImageView mImageView;
    boolean mIsFirstClick = false;

    private Paint paint = new Paint();
    private Rect mRect;

    public CropBox(Context context, ImageView aBaseView) {
        super(context);
        mImageView = aBaseView;
        mRect = new Rect(CROP_BOX_START_X, CROP_BOX_START_Y, CROP_BOX_END_X, CROP_BOX_END_Y);
        setOnTouchListener(new Crop());
    }

    public CropBox(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
    }

    @Override
    public void onDraw(Canvas canvas) {

        paint.setStrokeWidth(2);

        paint.setColor(Color.CYAN);
        paint.setStyle(Paint.Style.STROKE);

        canvas.drawRect(mRect, paint);

        canvas.drawLine(mRect.right-DRAG_SQUARE, mRect.bottom-DRAG_SQUARE, 
                mRect.right, mRect.bottom-DRAG_SQUARE, paint);

        canvas.drawLine(mRect.right-DRAG_SQUARE, mRect.bottom-DRAG_SQUARE, 
                mRect.right-DRAG_SQUARE, mRect.bottom, paint);
    }

    class Crop implements OnTouchListener {

        private static final int NONE = 0;
        private static final int BOX_DRAG = 1;
        private static final int BORDER_DRAG = 2;

        private int mode = NONE;

        private PointF start = new PointF();

        public boolean onTouch(View view, MotionEvent event) {
            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)view.getLayoutParams();

            switch(event.getAction() & MotionEvent.ACTION_MASK) {

                case MotionEvent.ACTION_DOWN:
                    start.set(event.getX(), event.getY());

                    if((event.getX() <= mRect.right && event.getX() >=(mRect.right - DRAG_SQUARE)) 
                            && (event.getY() >= mRect.top && event.getY() >=(mRect.bottom - DRAG_SQUARE))){
                        mode = BORDER_DRAG;
                        mIsFirstClick = false;
                    }
                    else if(mRect.contains((int)event.getX(), (int)event.getY())) {
                        mode = BOX_DRAG;
                        if (mIsFirstClick){
                            mRect = new Rect(CROP_BOX_START_X, CROP_BOX_START_Y, 
                                    CROP_BOX_END_X, CROP_BOX_END_Y);
                            mIsFirstClick = false;  
                        } else {
                            mIsFirstClick = true;   
                        }
                    }
                    else{
                        mode = NONE;
                        mIsFirstClick = true;
                    }
                    break;

                case MotionEvent.ACTION_UP:
                    mode = NONE;
                    break;

                case MotionEvent.ACTION_MOVE:
                    mIsFirstClick = false;
                    if(mode == BOX_DRAG) {
                        layoutParams.leftMargin = (int)event.getX() - (int)start.x + view.getLeft();
                        layoutParams.topMargin = (int)event.getY() - (int)start.y + view.getTop();
                    }
                    else if(mode == BORDER_DRAG) {
                        if (event.getX() > view.getLeft() && event.getY() > view.getTop()){
                            mRect.right = (int) event.getX();
                            mRect.bottom = (int) event.getY();
                        }
                    }
                    while(layoutParams.topMargin + 5 < mImageView.getTop())
                        layoutParams.topMargin++;
                    while(layoutParams.leftMargin + mRect.right > mImageView.getRight())
                        layoutParams.leftMargin--;
                    while(layoutParams.topMargin + mRect.bottom > mImageView.getBottom())
                        layoutParams.topMargin--;
                    while(layoutParams.leftMargin + 5 < mImageView.getLeft())
                        layoutParams.leftMargin++;
                    break;
            }
            view.setLayoutParams(layoutParams);
            invalidate();
            return true;
        }
    }
}

Some points I would like to mention.

  • Merged Attr and Crop in CropBox
  • No need of creating a rectangle from lines. You can use Rect.
  • Never initialize an array/object in Draw method
  • Added a feature: if double touched on rectangle it returns to original position
  • There might be some hitches about the restricting the rect in imageview. I am sure you can fix those... :)

Other than this there is another interesting way using scaling of canvas Image in Canvas with touch events Use that class instead of Cropbox and try it.

Hope it helps..

Upvotes: 3

Related Questions