]> git.ktnx.net Git - mobile-ledger.git/blobdiff - app/src/main/java/net/ktnx/mobileledger/ui/FabManager.java
separate FAB management in a helper class
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / ui / FabManager.java
diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/FabManager.java b/app/src/main/java/net/ktnx/mobileledger/ui/FabManager.java
new file mode 100644 (file)
index 0000000..b916534
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright © 2021 Damyan Ivanov.
+ * This file is part of MoLe.
+ * MoLe is free software: you can distribute it and/or modify it
+ * under the term of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your opinion), any later version.
+ *
+ * MoLe is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License terms for details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeInterpolator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewParent;
+import android.view.ViewPropertyAnimator;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
+import net.ktnx.mobileledger.utils.DimensionUtils;
+import net.ktnx.mobileledger.utils.Logger;
+
+public class FabManager {
+    private static final boolean FAB_SHOWN = true;
+    private static final boolean FAB_HIDDEN = false;
+    private final FloatingActionButton fab;
+    private boolean wantedFabState = FAB_SHOWN;
+    private ViewPropertyAnimator fabSlideAnimator;
+    private int fabVerticalOffset;
+    public FabManager(FloatingActionButton fab) {
+        this.fab = fab;
+    }
+    @SuppressLint("ClickableViewAccessibility")
+    public static void handle(FabHandler activity, RecyclerView recyclerView) {
+        final float triggerAbsolutePixels = DimensionUtils.dp2px(activity.getContext(), 20f);
+        final float triggerRelativePixels = triggerAbsolutePixels / 4f;
+        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+            @Override
+            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+                Logger.debug("touch", "Scrolled " + dy);
+                if (dy <= 0)
+                    activity.showManagedFab();
+                else
+                    activity.hideManagedFab();
+
+                super.onScrolled(recyclerView, dx, dy);
+            }
+        });
+        recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
+            private float absoluteAnchor = -1;
+            @Override
+            public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+                switch (e.getActionMasked()) {
+                    case MotionEvent.ACTION_DOWN:
+                        absoluteAnchor = e.getRawY();
+//                        Logger.debug("touch",
+//                                String.format(Locale.US, "Touch down at %4.2f", absoluteAnchor));
+                        break;
+                    case MotionEvent.ACTION_MOVE:
+                        if (absoluteAnchor < 0)
+                            break;
+
+                        final float absoluteY = e.getRawY();
+//                        Logger.debug("touch", String.format(Locale.US, "Move to %4.2f", absoluteY));
+
+                        if (absoluteY > absoluteAnchor + triggerAbsolutePixels) {
+                            // swipe down
+//                            Logger.debug("touch", "SHOW");
+                            activity.showManagedFab();
+                            absoluteAnchor = absoluteY;
+                        }
+                        else if (absoluteY < absoluteAnchor - triggerAbsolutePixels) {
+                            // swipe up
+//                            Logger.debug("touch", "HIDE");
+                            activity.hideManagedFab();
+                            absoluteAnchor = absoluteY;
+                        }
+
+                        break;
+                }
+                return false;
+            }
+        });
+    }
+    private void slideFabTo(int target, long duration, TimeInterpolator interpolator) {
+        fabSlideAnimator = fab.animate()
+                              .translationY((float) target)
+                              .setInterpolator(interpolator)
+                              .setDuration(duration)
+                              .setListener(new AnimatorListenerAdapter() {
+                                  public void onAnimationEnd(Animator animation) {
+                                      fabSlideAnimator = null;
+                                  }
+                              });
+    }
+    public void showFab() {
+        if (wantedFabState == FAB_SHOWN)
+            return;
+
+//        b.btnAddTransaction.show();
+        if (this.fabSlideAnimator != null) {
+            this.fabSlideAnimator.cancel();
+            fab.clearAnimation();
+        }
+
+        wantedFabState = FAB_SHOWN;
+        slideFabTo(0, 200L,
+                com.google.android.material.animation.AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
+    }
+    public void hideFab() {
+        if (wantedFabState == FAB_HIDDEN)
+            return;
+
+        calcVerticalFabOffset();
+
+//        b.btnAddTransaction.hide();
+        if (this.fabSlideAnimator != null) {
+            this.fabSlideAnimator.cancel();
+            fab.clearAnimation();
+        }
+
+        wantedFabState = FAB_HIDDEN;
+        slideFabTo(fabVerticalOffset, 150L,
+                com.google.android.material.animation.AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR);
+    }
+    private void calcVerticalFabOffset() {
+        if (fabVerticalOffset > 0)
+            return;// already calculated
+        int top = fab.getTop();
+        ViewParent parent = fab.getParent();
+        while (parent != null && !(parent instanceof View))
+            parent = parent.getParent();
+
+        if (parent instanceof View) {
+            View parentView = (View) parent;
+            int parentHeight = parentView.getHeight();
+            fabVerticalOffset = parentHeight - top;
+        }
+    }
+    public interface FabHandler {
+        Context getContext();
+
+        void showManagedFab();
+
+        void hideManagedFab();
+    }
+}