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.
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));
You should use rootView.getX()
and rootView.getY()
as the left and top bounderies... and (rootView.getX() + rootView.getWidth())
as the right and (rootView.getY() + rootView.getHeight())
for the bottom boundary.
You have to write your boundary logic in onTouch() in the ACTION_MOVE case.
Hope this helps.
In OnTouch you calculate where to move your view
case MotionEvent.ACTION_MOVE:
v.animate()
.x(event.getRawX() + dX)
.y(event.getRawY() + dY)
.setDuration(0)
.start();
You should check the boundaries for x and y before moving it.
case MotionEvent.ACTION_MOVE:
float x = event.getRawX() + dX; float y = event.getRawY() + dY;
if (x > boundaryRight) x = boundaryRight;
else if (x < boundaryLeft) x = boundaryLeft;
if (y < boundaryTop) y = boundaryTop;
else if (y > boundaryBottom) y = boundaryBottom;
v.animate()
.x(x)
.y(y)
.setDuration(0)
.start();
And to calculate boundaries of your RelativeLayout at run-time you should use Runnable or a Listener or similar Determining the size of an Android view at runtime