Auto-hide FAB on scroll (#435)

* feat(composeButton): hide fab on scroll
* feat(composeButton): hide when scrolling in profile fragment
* refactor(compose-fab): show fab after small scroll distance
* refactor(compose-fab): code cleanup
* feat(composeButton): hide when scrolling in profile
* fix: duplicate fab var
* feat(fab): show when scrolled to top
* add option to turn it off

---------

Co-authored-by: FineFindus <63370021+FineFindus@users.noreply.github.com>
This commit is contained in:
sk22
2023-02-17 13:20:22 +01:00
committed by GitHub
parent 1567e5aba4
commit d20f8669e8
6 changed files with 90 additions and 3 deletions

View File

@@ -43,6 +43,7 @@ public class GlobalUserPreferences{
public static boolean bottomEncoding; public static boolean bottomEncoding;
public static boolean collapseLongPosts; public static boolean collapseLongPosts;
public static boolean spectatorMode; public static boolean spectatorMode;
public static boolean autoHideFab;
public static String publishButtonText; public static String publishButtonText;
public static ThemePreference theme; public static ThemePreference theme;
public static ColorPreference color; public static ColorPreference color;
@@ -91,6 +92,7 @@ public class GlobalUserPreferences{
bottomEncoding=prefs.getBoolean("bottomEncoding", false); bottomEncoding=prefs.getBoolean("bottomEncoding", false);
collapseLongPosts=prefs.getBoolean("collapseLongPosts", true); collapseLongPosts=prefs.getBoolean("collapseLongPosts", true);
spectatorMode=prefs.getBoolean("spectatorMode", false); spectatorMode=prefs.getBoolean("spectatorMode", false);
autoHideFab=prefs.getBoolean("autoHideFab", true);
publishButtonText=prefs.getString("publishButtonText", ""); publishButtonText=prefs.getString("publishButtonText", "");
theme=ThemePreference.values()[prefs.getInt("theme", 0)]; theme=ThemePreference.values()[prefs.getInt("theme", 0)];
recentLanguages=fromJson(prefs.getString("recentLanguages", null), recentLanguagesType, new HashMap<>()); recentLanguages=fromJson(prefs.getString("recentLanguages", null), recentLanguagesType, new HashMap<>());
@@ -131,6 +133,7 @@ public class GlobalUserPreferences{
.putBoolean("prefixRepliesWithRe", prefixRepliesWithRe) .putBoolean("prefixRepliesWithRe", prefixRepliesWithRe)
.putBoolean("collapseLongPosts", collapseLongPosts) .putBoolean("collapseLongPosts", collapseLongPosts)
.putBoolean("spectatorMode", spectatorMode) .putBoolean("spectatorMode", spectatorMode)
.putBoolean("autoHideFab", autoHideFab)
.putString("publishButtonText", publishButtonText) .putString("publishButtonText", publishButtonText)
.putBoolean("bottomEncoding", bottomEncoding) .putBoolean("bottomEncoding", bottomEncoding)
.putInt("theme", theme.ordinal()) .putInt("theme", theme.ordinal())

View File

@@ -3,6 +3,10 @@ package org.joinmastodon.android.fragments;
import android.app.Activity; import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import android.view.animation.TranslateAnimation;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.R; import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses; import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;

View File

@@ -13,9 +13,12 @@ import android.text.Layout;
import android.text.StaticLayout; import android.text.StaticLayout;
import android.text.TextPaint; import android.text.TextPaint;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowInsets; import android.view.WindowInsets;
import android.view.animation.TranslateAnimation;
import android.widget.ImageButton;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.Toolbar; import android.widget.Toolbar;
@@ -75,10 +78,11 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
protected DisplayItemsAdapter adapter; protected DisplayItemsAdapter adapter;
protected String accountID; protected String accountID;
protected PhotoViewer currentPhotoViewer; protected PhotoViewer currentPhotoViewer;
protected ImageButton fab;
protected int scrollDiff = 0;
protected HashMap<String, Account> knownAccounts=new HashMap<>(); protected HashMap<String, Account> knownAccounts=new HashMap<>();
protected HashMap<String, Relationship> relationships=new HashMap<>(); protected HashMap<String, Relationship> relationships=new HashMap<>();
protected Rect tmpRect=new Rect(); protected Rect tmpRect=new Rect();
protected ImageButton fab;
public BaseStatusListFragment(){ public BaseStatusListFragment(){
super(20); super(20);
@@ -285,11 +289,42 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
@Override @Override
public void onViewCreated(View view, Bundle savedInstanceState){ public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
fab=view.findViewById(R.id.fab);
list.addOnScrollListener(new RecyclerView.OnScrollListener(){ list.addOnScrollListener(new RecyclerView.OnScrollListener(){
@Override @Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
if(currentPhotoViewer!=null) if(currentPhotoViewer!=null)
currentPhotoViewer.offsetView(-dx, -dy); currentPhotoViewer.offsetView(-dx, -dy);
if (fab!=null && GlobalUserPreferences.autoHideFab) {
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
TranslateAnimation animate = new TranslateAnimation(
0,
0,
0,
fab.getHeight() * 2);
animate.setDuration(300);
animate.setFillAfter(true);
fab.startAnimation(animate);
fab.setVisibility(View.INVISIBLE);
scrollDiff = 0;
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
if (list.getChildLayoutPosition(list.getChildAt(0)) == 0 || scrollDiff > 400) {
fab.setVisibility(View.VISIBLE);
TranslateAnimation animate = new TranslateAnimation(
0,
0,
fab.getHeight() * 2,
0);
animate.setDuration(300);
animate.setFillAfter(true);
fab.startAnimation(animate);
scrollDiff = 0;
} else {
scrollDiff += Math.abs(dy);
}
}
}
} }
}); });
list.addItemDecoration(new StatusListItemDecoration()); list.addItemDecoration(new StatusListItemDecoration());
@@ -327,7 +362,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
updateToolbar(); updateToolbar();
if (withComposeButton()) { if (withComposeButton()) {
fab = view.findViewById(R.id.fab);
fab.setVisibility(View.VISIBLE); fab.setVisibility(View.VISIBLE);
fab.setOnClickListener(this::onFabClick); fab.setOnClickListener(this::onFabClick);
fab.setOnLongClickListener(this::onFabLongClick); fab.setOnLongClickListener(this::onFabLongClick);

View File

@@ -19,11 +19,13 @@ import android.os.Bundle;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.style.ImageSpan; import android.text.style.ImageSpan;
import android.util.Log;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SubMenu; import android.view.SubMenu;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -31,9 +33,11 @@ import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver; import android.view.ViewTreeObserver;
import android.view.WindowInsets; import android.view.WindowInsets;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.view.animation.TranslateAnimation;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
@@ -144,10 +148,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
private Uri editNewAvatar, editNewCover; private Uri editNewAvatar, editNewCover;
private String profileAccountID; private String profileAccountID;
private boolean refreshing; private boolean refreshing;
private View fab; private ImageButton fab;
private WindowInsets childInsets; private WindowInsets childInsets;
private PhotoViewer currentPhotoViewer; private PhotoViewer currentPhotoViewer;
private boolean editModeLoading; private boolean editModeLoading;
protected int scrollDiff = 0;
private static final int MAX_FIELDS=4; private static final int MAX_FIELDS=4;
@@ -749,6 +754,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username)); notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
} }
public ImageButton getFab() {
return fab;
}
private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){ private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
int topBarsH=getToolbar().getHeight()+statusBarHeight; int topBarsH=getToolbar().getHeight()+statusBarHeight;
if(scrollY>avatarBorder.getTop()-topBarsH){ if(scrollY>avatarBorder.getTop()-topBarsH){
@@ -779,6 +788,37 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
if(currentPhotoViewer!=null){ if(currentPhotoViewer!=null){
currentPhotoViewer.offsetView(0, oldScrollY-scrollY); currentPhotoViewer.offsetView(0, oldScrollY-scrollY);
} }
if (GlobalUserPreferences.autoHideFab) {
int dy = scrollY - oldScrollY;
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
TranslateAnimation animate = new TranslateAnimation(
0,
0,
0,
fab.getHeight() * 2);
animate.setDuration(300);
animate.setFillAfter(true);
fab.startAnimation(animate);
fab.setVisibility(View.INVISIBLE);
scrollDiff = 0;
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
if (v.getScrollY() == 0 || scrollDiff > 400) {
fab.setVisibility(View.VISIBLE);
TranslateAnimation animate = new TranslateAnimation(
0,
0,
fab.getHeight() * 2,
0);
animate.setDuration(300);
animate.setFillAfter(true);
fab.startAnimation(animate);
scrollDiff = 0;
} else {
scrollDiff += Math.abs(dy);
}
}
}
} }
private Fragment getFragmentForPage(int page){ private Fragment getFragmentForPage(int page){

View File

@@ -248,6 +248,11 @@ public class SettingsFragment extends MastodonToolbarFragment{
GlobalUserPreferences.save(); GlobalUserPreferences.save();
needAppRestart=true; needAppRestart=true;
})); }));
items.add(new SwitchItem(R.string.sk_settings_hide_fab, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.autoHideFab, i->{
GlobalUserPreferences.autoHideFab=i.checked;
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{ items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
GlobalUserPreferences.translateButtonOpenedOnly=i.checked; GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
GlobalUserPreferences.save(); GlobalUserPreferences.save();

View File

@@ -261,4 +261,5 @@
<string name="sk_settings_hide_interaction">Hide interaction buttons</string> <string name="sk_settings_hide_interaction">Hide interaction buttons</string>
<string name="sk_follow_as">Follow from other account</string> <string name="sk_follow_as">Follow from other account</string>
<string name="sk_followed_as">Followed from %s</string> <string name="sk_followed_as">Followed from %s</string>
<string name="sk_settings_hide_fab">Auto-hide Compose button</string>
</resources> </resources>