Android - Movable/Draggable Floating Action Button (FAB)

后端 未结 9 1665
温柔的废话
温柔的废话 2020-12-28 14:47

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

9条回答
  •  爱一瞬间的悲伤
    2020-12-28 15:33

    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...

        
    

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

提交回复
热议问题