Search code examples
androidandroid-custom-viewontouchlistenerandroid-relativelayout

Bound a View to drag inside RelativeLayout


I have created a draggable view inside RelativeLayout. But it seems to go beyond the RelativeLayout.

I simply want to make a View draggable inside ViewGroup

And this view is draggable according to Screen. And it is draggable beyond the boundaries of RelativeLayout. How could I restrict it to stay draggable within RelativeLayout.

CustomImageButton

public class ImageButtonCustom extends ImageButton implements View.OnTouchListener{

    float dX, dY;

    private RelativeLayout rootView;
    private ImageButtonCustom imageButtonCustom;
    private OnMoveListener onMoveListener;

    public ImageButtonCustom(Context context,RelativeLayout rootView){
        super(context);
        this.rootView = rootView;
        init();

    }
    public ImageButtonCustom(Context context) {
        super(context);
        init();
    }

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

    public ImageButtonCustom(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(){
        imageButtonCustom = this;
        setImageResource(R.drawable.upper_left);
        setBackgroundColor(Color.TRANSPARENT);
        setOnTouchListener(this);

        /*RelativeLayout.LayoutParams rl = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        rl.addRule(RelativeLayout.ALIGN_BOTTOM);*/

        rootView.addView(this);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                dX = v.getX() - event.getRawX();
                dY = v.getY() - event.getRawY();
                break;
            case MotionEvent.ACTION_UP:
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                break;
            case MotionEvent.ACTION_POINTER_UP:
                break;
            case MotionEvent.ACTION_MOVE:
                v.animate()
                        .x(event.getRawX() + dX)
                        .y(event.getRawY() + dY)
                        .setDuration(0)
                        .start();
                //no use of ViewPositionUtil
                onMoveListener.onMove(new Position());//positionXY);
                break;
        }
        rootView.invalidate();
        return true;
    }

    public void setOnMoveListener(OnMoveListener onMoveListener){
        this.onMoveListener = onMoveListener;
    }

    public float getCenterX(){
        return getX() + getWidth()  / 2;

    }
    public float getCenterY(){
        return getY() + getHeight() / 2;

    }

    public interface OnMoveListener{
        void onMove(Position positionXY);
    }
}

EDIT:

The ImageButton is Draggable but it goes outside of parent .Restrict it to drag within it's parent Layout.


Solution

  • Here's an extract from my old diaries. Hope it works for you.

    public class OnDragTouchListener implements View.OnTouchListener {
    
        /**
         * Callback used to indicate when the drag is finished
         */
        public interface OnDragActionListener {
            /**
             * Called when drag event is started
             *
             * @param view The view dragged
             */
            void onDragStart(View view);
    
            /**
             * Called when drag event is completed
             *
             * @param view The view dragged
             */
            void onDragEnd(View view);
        }
    
        private View mView;
        private View mParent;
        private boolean isDragging;
        private boolean isInitialized = false;
    
        private int width;
        private float xWhenAttached;
        private float maxLeft;
        private float maxRight;
        private float dX;
    
        private int height;
        private float yWhenAttached;
        private float maxTop;
        private float maxBottom;
        private float dY;
    
        private OnDragActionListener mOnDragActionListener;
    
        public OnDragTouchListener(View view) {
            this(view, (View) view.getParent(), null);
        }
    
        public OnDragTouchListener(View view, View parent) {
            this(view, parent, null);
        }
    
        public OnDragTouchListener(View view, OnDragActionListener onDragActionListener) {
            this(view, (View) view.getParent(), onDragActionListener);
        }
    
        public OnDragTouchListener(View view, View parent, OnDragActionListener onDragActionListener) {
            initListener(view, parent);
            setOnDragActionListener(onDragActionListener);
        }
    
        public void setOnDragActionListener(OnDragActionListener onDragActionListener) {
            mOnDragActionListener = onDragActionListener;
        }
    
        public void initListener(View view, View parent) {
            mView = view;
            mParent = parent;
            isDragging = false;
            isInitialized = false;
        }
    
        public void updateBounds() {
            updateViewBounds();
            updateParentBounds();
            isInitialized = true;
        }
    
        public void updateViewBounds() {
            width = mView.getWidth();
            xWhenAttached = mView.getX();
            dX = 0;
    
            height = mView.getHeight();
            yWhenAttached = mView.getY();
            dY = 0;
        }
    
        public void updateParentBounds() {
            maxLeft = 0;
            maxRight = maxLeft + mParent.getWidth();
    
            maxTop = 0;
            maxBottom = maxTop + mParent.getHeight();
        }
    
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (isDragging) {
                float[] bounds = new float[4];
                // LEFT
                bounds[0] = event.getRawX() + dX;
                if (bounds[0] < maxLeft) {
                    bounds[0] = maxLeft;
                }
                // RIGHT
                bounds[2] = bounds[0] + width;
                if (bounds[2] > maxRight) {
                    bounds[2] = maxRight;
                    bounds[0] = bounds[2] - width;
                }
                // TOP
                bounds[1] = event.getRawY() + dY;
                if (bounds[1] < maxTop) {
                    bounds[1] = maxTop;
                }
                // BOTTOM
                bounds[3] = bounds[1] + height;
                if (bounds[3] > maxBottom) {
                    bounds[3] = maxBottom;
                    bounds[1] = bounds[3] - height;
                }
    
                switch (event.getAction()) {
                    case MotionEvent.ACTION_CANCEL:
                    case MotionEvent.ACTION_UP:
                        onDragFinish();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        mView.animate().x(bounds[0]).y(bounds[1]).setDuration(0).start();
                        break;
                }
                return true;
            } else {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        isDragging = true;
                        if (!isInitialized) {
                            updateBounds();
                        }
                        dX = v.getX() - event.getRawX();
                        dY = v.getY() - event.getRawY();
                        if (mOnDragActionListener != null) {
                            mOnDragActionListener.onDragStart(mView);
                        }
                        return true;
                }
            }
            return false;
        }
    
        private void onDragFinish() {
            if (mOnDragActionListener != null) {
                mOnDragActionListener.onDragEnd(mView);
            }
    
            dX = 0;
            dY = 0;
            isDragging = false;
        }
    }
    

    And you can set it using:

    myView.setOnTouchListener(new OnDragTouchListener(myView));
    

    Or by adding this directly in init method of your Custom View:

    setOnTouchListener(new OnDragTouchListener(this));