2 * Copyright © 2021 Damyan Ivanov.
3 * This file is part of MoLe.
4 * MoLe is free software: you can distribute it and/or modify it
5 * under the term of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your opinion), any later version.
9 * MoLe is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License terms for details.
14 * You should have received a copy of the GNU General Public License
15 * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
18 package net.ktnx.mobileledger.ui;
20 import android.animation.Animator;
21 import android.animation.AnimatorListenerAdapter;
22 import android.animation.TimeInterpolator;
23 import android.annotation.SuppressLint;
24 import android.content.Context;
25 import android.view.MotionEvent;
26 import android.view.View;
27 import android.view.ViewParent;
28 import android.view.ViewPropertyAnimator;
30 import androidx.annotation.NonNull;
31 import androidx.recyclerview.widget.RecyclerView;
33 import com.google.android.material.floatingactionbutton.FloatingActionButton;
35 import net.ktnx.mobileledger.utils.DimensionUtils;
36 import net.ktnx.mobileledger.utils.Logger;
38 public class FabManager {
39 private static final boolean FAB_SHOWN = true;
40 private static final boolean FAB_HIDDEN = false;
41 private final FloatingActionButton fab;
42 private boolean wantedFabState = FAB_SHOWN;
43 private ViewPropertyAnimator fabSlideAnimator;
44 private int fabVerticalOffset;
45 public FabManager(FloatingActionButton fab) {
48 @SuppressLint("ClickableViewAccessibility")
49 public static void handle(FabHandler activity, RecyclerView recyclerView) {
50 final float triggerAbsolutePixels = DimensionUtils.dp2px(activity.getContext(), 20f);
51 final float triggerRelativePixels = triggerAbsolutePixels / 4f;
52 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
54 public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
55 Logger.debug("touch", "Scrolled " + dy);
57 activity.showManagedFab();
59 activity.hideManagedFab();
61 super.onScrolled(recyclerView, dx, dy);
64 recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
65 private float absoluteAnchor = -1;
67 public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
68 switch (e.getActionMasked()) {
69 case MotionEvent.ACTION_DOWN:
70 absoluteAnchor = e.getRawY();
71 // Logger.debug("touch",
72 // String.format(Locale.US, "Touch down at %4.2f", absoluteAnchor));
74 case MotionEvent.ACTION_MOVE:
75 if (absoluteAnchor < 0)
78 final float absoluteY = e.getRawY();
79 // Logger.debug("touch", String.format(Locale.US, "Move to %4.2f", absoluteY));
81 if (absoluteY > absoluteAnchor + triggerAbsolutePixels) {
83 // Logger.debug("touch", "SHOW");
84 activity.showManagedFab();
85 absoluteAnchor = absoluteY;
87 else if (absoluteY < absoluteAnchor - triggerAbsolutePixels) {
89 // Logger.debug("touch", "HIDE");
90 activity.hideManagedFab();
91 absoluteAnchor = absoluteY;
100 private void slideFabTo(int target, long duration, TimeInterpolator interpolator) {
101 fabSlideAnimator = fab.animate()
102 .translationY((float) target)
103 .setInterpolator(interpolator)
104 .setDuration(duration)
105 .setListener(new AnimatorListenerAdapter() {
106 public void onAnimationEnd(Animator animation) {
107 fabSlideAnimator = null;
111 public void showFab() {
112 if (wantedFabState == FAB_SHOWN)
115 // b.btnAddTransaction.show();
116 if (this.fabSlideAnimator != null) {
117 this.fabSlideAnimator.cancel();
118 fab.clearAnimation();
121 wantedFabState = FAB_SHOWN;
123 com.google.android.material.animation.AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
125 public void hideFab() {
126 if (wantedFabState == FAB_HIDDEN)
129 calcVerticalFabOffset();
131 // b.btnAddTransaction.hide();
132 if (this.fabSlideAnimator != null) {
133 this.fabSlideAnimator.cancel();
134 fab.clearAnimation();
137 wantedFabState = FAB_HIDDEN;
138 slideFabTo(fabVerticalOffset, 150L,
139 com.google.android.material.animation.AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR);
141 private void calcVerticalFabOffset() {
142 if (fabVerticalOffset > 0)
143 return;// already calculated
144 int top = fab.getTop();
145 ViewParent parent = fab.getParent();
146 while (parent != null && !(parent instanceof View))
147 parent = parent.getParent();
149 if (parent != null) {
150 View parentView = (View) parent;
151 int parentHeight = parentView.getHeight();
152 fabVerticalOffset = parentHeight - top;
155 public interface FabHandler {
156 Context getContext();
158 void showManagedFab();
160 void hideManagedFab();