Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - Movable/Draggable Floating Action Button (FAB)

I am using a FloatingActionButton in my app. Occasionally, it overlaps essential content, so I would like to make it so the user can drag the FAB out of the way.

No drag and drop functionality, per se, is required. It just needs to be movable. The docs do not mention this, but I'm sure I've seen such functionality in other apps.

Can you anyone advise / provide a code snippet on how to do it (preferably in XML).

like image 787
ban-geoengineering Avatar asked Sep 22 '17 18:09

ban-geoengineering


People also ask

How do you make a floating action button movable in flutter?

By default, the shape of the floating action button (FAB) in the flutter is circular and the location is bottom right floated. You can change the location and shape of the floating action button using properties in Scaffold() widget class and FloatingActionButton() widget class.

What is floating action button in Android?

A floating action button (FAB) is a circular button that triggers the primary action in your app's UI. This page shows you how to add the FAB to your layout, customize some of its appearance, and respond to button taps.

How do I import a floating action button?

Add the floating action button to your layoutThe size of the FAB, using the app:fabSize attribute or the setSize() method. The ripple color of the FAB, using the app:rippleColor attribute or the setRippleColor() method. The FAB icon, using the android:src attribute or the setImageDrawable() method.


2 Answers

Based on this answer for another SO question this is the code I have created. It seems to work nicely (with working click functionality) and isn't dependent on the FAB's parent layout or positioning...

package com.example;  import android.content.Context; import android.support.design.widget.FloatingActionButton; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup;  public class MovableFloatingActionButton extends FloatingActionButton implements View.OnTouchListener {      private final static float CLICK_DRAG_TOLERANCE = 10; // Often, there will be a slight, unintentional, drag when the user taps the FAB, so we need to account for this.      private float downRawX, downRawY;     private float dX, dY;      public MovableFloatingActionButton(Context context) {         super(context);         init();     }      public MovableFloatingActionButton(Context context, AttributeSet attrs) {         super(context, attrs);         init();     }      public MovableFloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {         super(context, attrs, defStyleAttr);         init();     }      private void init() {         setOnTouchListener(this);     }      @Override     public boolean onTouch(View view, MotionEvent motionEvent){          ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams)view.getLayoutParams();          int action = motionEvent.getAction();         if (action == MotionEvent.ACTION_DOWN) {              downRawX = motionEvent.getRawX();             downRawY = motionEvent.getRawY();             dX = view.getX() - downRawX;             dY = view.getY() - downRawY;              return true; // Consumed          }         else if (action == MotionEvent.ACTION_MOVE) {              int viewWidth = view.getWidth();             int viewHeight = view.getHeight();              View viewParent = (View)view.getParent();             int parentWidth = viewParent.getWidth();             int parentHeight = viewParent.getHeight();              float newX = motionEvent.getRawX() + dX;             newX = Math.max(layoutParams.leftMargin, newX); // Don't allow the FAB past the left hand side of the parent             newX = Math.min(parentWidth - viewWidth - layoutParams.rightMargin, newX); // Don't allow the FAB past the right hand side of the parent              float newY = motionEvent.getRawY() + dY;             newY = Math.max(layoutParams.topMargin, newY); // Don't allow the FAB past the top of the parent             newY = Math.min(parentHeight - viewHeight - layoutParams.bottomMargin, newY); // Don't allow the FAB past the bottom of the parent              view.animate()                     .x(newX)                     .y(newY)                     .setDuration(0)                     .start();              return true; // Consumed          }         else if (action == MotionEvent.ACTION_UP) {              float upRawX = motionEvent.getRawX();             float upRawY = motionEvent.getRawY();              float upDX = upRawX - downRawX;             float upDY = upRawY - downRawY;              if (Math.abs(upDX) < CLICK_DRAG_TOLERANCE && Math.abs(upDY) < CLICK_DRAG_TOLERANCE) { // A click                 return performClick();             }             else { // A drag                 return true; // Consumed             }          }         else {             return super.onTouchEvent(motionEvent);         }      }  } 

And here is the XML...

    <com.example.MovableFloatingActionButton         android:id="@+id/fab"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_gravity="bottom|end"         android:layout_margin="@dimen/fab_margin"         android:src="@drawable/ic_navigate_next_white_24dp"/> 

Basically, you just need to replace android.support.design.widget.FloatingActionButton with com.example.MovableFloatingActionButton in your XML.

like image 125
ban-geoengineering Avatar answered Oct 08 '22 13:10

ban-geoengineering


Try this:

public class MainActivity extends AppCompatActivity implements View.OnTouchListener {   float dX;   float dY;   int lastAction;    @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);      final View dragView = findViewById(R.id.draggable_view);     dragView.setOnTouchListener(this);   }    @Override   public boolean onTouch(View view, MotionEvent event) {     switch (event.getActionMasked()) {       case MotionEvent.ACTION_DOWN:         dX = view.getX() - event.getRawX();         dY = view.getY() - event.getRawY();         lastAction = MotionEvent.ACTION_DOWN;         break;        case MotionEvent.ACTION_MOVE:         view.setY(event.getRawY() + dY);         view.setX(event.getRawX() + dX);         lastAction = MotionEvent.ACTION_MOVE;         break;        case MotionEvent.ACTION_UP:         if (lastAction == MotionEvent.ACTION_DOWN)           Toast.makeText(DraggableView.this, "Clicked!", Toast.LENGTH_SHORT).show();         break;        default:         return false;     }     return true;   } } 

And the XML:

<ImageButton         android:id="@+id/draggable_view"         android:background="@mipmap/ic_launcher"         android:layout_gravity="bottom|right"         android:layout_marginBottom="20dp"         android:layout_marginEnd="20dp"         android:layout_width="wrap_content"         android:layout_height="wrap_content"/> 

You can make any View Draggable and Clickable.

like image 21
Akanshi Srivastava Avatar answered Oct 08 '22 12:10

Akanshi Srivastava