Android - Movable/Draggable Floating Action Button (FAB)

后端 未结 9 1659
温柔的废话
温柔的废话 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:23

    Based on @ban-geoengineering answer I updated as perform ripple effect and left and right gravity like faceebook chat bubble. I created custom click listener cuz if consume touch event inside this code block, ripple effect doesnt work clearly.

        
    
    package com.sample;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.animation.OvershootInterpolator;
    
    import com.google.android.material.floatingactionbutton.FloatingActionButton;
    
    public class DraggableFloatingActionButton extends FloatingActionButton implements View.OnTouchListener {
        CustomClickListener customClickListener;
    
        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;
    
        int viewWidth;
        int viewHeight;
    
        int parentWidth;
        int parentHeight;
    
        float newX;
        float newY;
    
        public DraggableFloatingActionButton(Context context) {
            super(context);
            init();
        }
    
        public DraggableFloatingActionButton(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public DraggableFloatingActionButton(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 false; // not Consumed for ripple effect
    
            } else if (action == MotionEvent.ACTION_MOVE) {
    
                viewWidth = view.getWidth();
                viewHeight = view.getHeight();
    
                View viewParent = (View) view.getParent();
                parentWidth = viewParent.getWidth();
                parentHeight = viewParent.getHeight();
    
                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
    
                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 (newX > ((parentWidth - viewWidth - layoutParams.rightMargin) / 2)) {
                    newX = parentWidth - viewWidth - layoutParams.rightMargin;
                } else {
                    newX = layoutParams.leftMargin;
                }
    
                view.animate()
                        .x(newX)
                        .y(newY)
                        .setInterpolator(new OvershootInterpolator())
                        .setDuration(300)
                        .start();
    
                if (Math.abs(upDX) < CLICK_DRAG_TOLERANCE && Math.abs(upDY) < CLICK_DRAG_TOLERANCE) { // A click
                    if (customClickListener != null) {
                        customClickListener.onClick(view);
                    }
                    return false;// not Consumed for ripple effect
                } else { // A drag
                    return false; // not Consumed for ripple effect
                }
    
            } else {
                return super.onTouchEvent(motionEvent);
            }
    
        }
    
        public void setCustomClickListener(CustomClickListener customClickListener) {
            this.customClickListener = customClickListener;
        }
    
        public interface CustomClickListener {
            void onClick(View view);
        }
    
    }
    

提交回复
热议问题