Skip to content

Commit

Permalink
Temporary fix to fab position on screen (#5758)
Browse files Browse the repository at this point in the history
Fab has been broken since v3. This is a very temporary fix until we refactor it and upgrade to fab from AndroidX
  • Loading branch information
guyca authored Dec 12, 2019
1 parent e236cb9 commit 4714983
Show file tree
Hide file tree
Showing 13 changed files with 97 additions and 161 deletions.
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
package com.reactnativenavigation.presentation;


import androidx.annotation.NonNull;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

import com.reactnativenavigation.R;
import com.reactnativenavigation.parse.FabOptions;
import com.reactnativenavigation.viewcontrollers.ViewController;
import com.reactnativenavigation.views.Fab;
import com.reactnativenavigation.views.FabMenu;
import com.reactnativenavigation.views.ReactComponent;

import static android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM;
import static android.widget.RelativeLayout.ALIGN_PARENT_LEFT;
import static android.widget.RelativeLayout.ALIGN_PARENT_RIGHT;
import static android.widget.RelativeLayout.ALIGN_PARENT_TOP;
import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;

import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.github.clans.fab.FloatingActionButton.SIZE_MINI;
import static com.github.clans.fab.FloatingActionButton.SIZE_NORMAL;
import static com.reactnativenavigation.utils.ObjectUtils.perform;

public class FabPresenter {
private ViewGroup viewGroup;
private ReactComponent component;
private ViewController component;

private Fab fab;
private FabMenu fabMenu;

public void applyOptions(FabOptions options, @NonNull ReactComponent component, @NonNull ViewGroup viewGroup) {
public void applyOptions(FabOptions options, @NonNull ViewController component, @NonNull ViewGroup viewGroup) {
this.viewGroup = viewGroup;
this.component = component;

Expand All @@ -51,10 +49,9 @@ public void applyOptions(FabOptions options, @NonNull ReactComponent component,
}
}

public void mergeOptions(FabOptions options, @NonNull ReactComponent component, @NonNull ViewGroup viewGroup) {
public void mergeOptions(FabOptions options, @NonNull ViewController component, @NonNull ViewGroup viewGroup) {
this.viewGroup = viewGroup;
this.component = component;

if (options.id.hasValue()) {
if (fabMenu != null && fabMenu.getFabId().equals(options.id.get())) {
mergeParams(fabMenu, options);
Expand Down Expand Up @@ -103,139 +100,44 @@ private void removeFab() {
}

private void setParams(View fab, FabOptions options) {
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
if (viewGroup instanceof RelativeLayout) {
RelativeLayout.LayoutParams layoutParamsRelative = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParamsRelative.bottomMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
layoutParamsRelative.rightMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
layoutParamsRelative.leftMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
layoutParamsRelative.topMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
if (options.alignHorizontally.hasValue()) {
if ("right".equals(options.alignHorizontally.get())) {
layoutParamsRelative.removeRule(ALIGN_PARENT_LEFT);
layoutParamsRelative.addRule(ALIGN_PARENT_RIGHT);
}
if ("left".equals(options.alignHorizontally.get())) {
layoutParamsRelative.removeRule(ALIGN_PARENT_RIGHT);
layoutParamsRelative.addRule(ALIGN_PARENT_LEFT);
}
} else {
layoutParamsRelative.addRule(ALIGN_PARENT_RIGHT);
CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
lp.rightMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
lp.leftMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
lp.bottomMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
fab.setTag(R.id.fab_bottom_margin, lp.bottomMargin);
lp.gravity = Gravity.BOTTOM;
if (options.alignHorizontally.hasValue()) {
if ("right".equals(options.alignHorizontally.get())) {
lp.gravity |= Gravity.RIGHT;
}
if (options.alignVertically.hasValue()) {
if ("top".equals(options.alignVertically.get())) {
layoutParamsRelative.removeRule(ALIGN_PARENT_BOTTOM);
layoutParamsRelative.addRule(ALIGN_PARENT_TOP);
}
if ("bottom".equals(options.alignVertically.get())) {
layoutParamsRelative.removeRule(ALIGN_PARENT_TOP);
layoutParamsRelative.addRule(ALIGN_PARENT_BOTTOM);
}
} else {
layoutParamsRelative.addRule(ALIGN_PARENT_BOTTOM);
if ("left".equals(options.alignHorizontally.get())) {
lp.gravity |= Gravity.LEFT;
}
layoutParams = layoutParamsRelative;
}
if (viewGroup instanceof FrameLayout) {
FrameLayout.LayoutParams layoutParamsFrame = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParamsFrame.bottomMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
layoutParamsFrame.rightMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
layoutParamsFrame.leftMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
layoutParamsFrame.topMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
if (options.alignHorizontally.hasValue()) {
if ("right".equals(options.alignHorizontally.get())) {
removeGravityParam(layoutParamsFrame, Gravity.LEFT);
setGravityParam(layoutParamsFrame, Gravity.RIGHT);
}
if ("left".equals(options.alignHorizontally.get())) {
removeGravityParam(layoutParamsFrame, Gravity.RIGHT);
setGravityParam(layoutParamsFrame, Gravity.LEFT);
}
} else {
setGravityParam(layoutParamsFrame, Gravity.RIGHT);
}
if (options.alignVertically.hasValue()) {
if ("top".equals(options.alignVertically.get())) {
removeGravityParam(layoutParamsFrame, Gravity.BOTTOM);
setGravityParam(layoutParamsFrame, Gravity.TOP);
}
if ("bottom".equals(options.alignVertically.get())) {
removeGravityParam(layoutParamsFrame, Gravity.TOP);
setGravityParam(layoutParamsFrame, Gravity.BOTTOM);
}
} else {
setGravityParam(layoutParamsFrame, Gravity.BOTTOM);
}
layoutParams = layoutParamsFrame;
} else {
lp.gravity |= Gravity.RIGHT;
}
fab.setLayoutParams(layoutParams);
fab.setLayoutParams(lp);
}

private void mergeParams(View fab, FabOptions options) {
ViewGroup.LayoutParams layoutParams = fab.getLayoutParams();
if (viewGroup instanceof RelativeLayout) {
RelativeLayout.LayoutParams layoutParamsRelative = (RelativeLayout.LayoutParams) fab.getLayoutParams();
if (layoutParamsRelative == null) {
layoutParamsRelative = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParamsRelative.bottomMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
layoutParamsRelative.rightMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
layoutParamsRelative.leftMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
layoutParamsRelative.topMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) perform(
fab,
new CoordinatorLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT),
View::getLayoutParams
);
fab.setTag(R.id.fab_bottom_margin, lp.leftMargin);
lp.gravity = Gravity.BOTTOM;
if (options.alignHorizontally.hasValue()) {
if ("right".equals(options.alignHorizontally.get())) {
lp.gravity |= Gravity.RIGHT;
}
if (options.alignHorizontally.hasValue()) {
if ("right".equals(options.alignHorizontally.get())) {
layoutParamsRelative.removeRule(ALIGN_PARENT_LEFT);
layoutParamsRelative.addRule(ALIGN_PARENT_RIGHT);
}
if ("left".equals(options.alignHorizontally.get())) {
layoutParamsRelative.removeRule(ALIGN_PARENT_RIGHT);
layoutParamsRelative.addRule(ALIGN_PARENT_LEFT);
}
if ("left".equals(options.alignHorizontally.get())) {
lp.gravity |= Gravity.RIGHT;
}
if (options.alignVertically.hasValue()) {
if ("top".equals(options.alignVertically.get())) {
layoutParamsRelative.removeRule(ALIGN_PARENT_BOTTOM);
layoutParamsRelative.addRule(ALIGN_PARENT_TOP);
}
if ("bottom".equals(options.alignVertically.get())) {
layoutParamsRelative.removeRule(ALIGN_PARENT_TOP);
layoutParamsRelative.addRule(ALIGN_PARENT_BOTTOM);
}
}
layoutParams = layoutParamsRelative;
}
if (viewGroup instanceof FrameLayout) {
FrameLayout.LayoutParams layoutParamsFrame = (FrameLayout.LayoutParams) fab.getLayoutParams();
if (layoutParamsFrame == null) {
layoutParamsFrame = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParamsFrame.bottomMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
layoutParamsFrame.rightMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
layoutParamsFrame.leftMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
layoutParamsFrame.topMargin = (int) viewGroup.getContext().getResources().getDimension(R.dimen.margin);
}
if (options.alignHorizontally.hasValue()) {
if ("right".equals(options.alignHorizontally.get())) {
removeGravityParam(layoutParamsFrame, Gravity.LEFT);
setGravityParam(layoutParamsFrame, Gravity.RIGHT);
}
if ("left".equals(options.alignHorizontally.get())) {
removeGravityParam(layoutParamsFrame, Gravity.RIGHT);
setGravityParam(layoutParamsFrame, Gravity.LEFT);
}
}
if (options.alignVertically.hasValue()) {
if ("top".equals(options.alignVertically.get())) {
removeGravityParam(layoutParamsFrame, Gravity.BOTTOM);
setGravityParam(layoutParamsFrame, Gravity.TOP);
}
if ("bottom".equals(options.alignVertically.get())) {
removeGravityParam(layoutParamsFrame, Gravity.TOP);
setGravityParam(layoutParamsFrame, Gravity.BOTTOM);
}
}
layoutParams = layoutParamsFrame;
} else {
lp.gravity |= Gravity.RIGHT;
}
fab.setLayoutParams(layoutParams);
fab.setLayoutParams(lp);
}

private void applyFabOptions(Fab fab, FabOptions options) {
Expand Down Expand Up @@ -373,14 +275,4 @@ private void mergeFabMenuOptions(FabMenu fabMenu, FabOptions options) {
fabMenu.disableCollapse();
}
}

private void setGravityParam(FrameLayout.LayoutParams params, int gravityParam) {
params.gravity = params.gravity | gravityParam;
}

private void removeGravityParam(FrameLayout.LayoutParams params, int gravityParam) {
if ((params.gravity & gravityParam) == gravityParam) {
params.gravity = params.gravity & ~gravityParam;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.reactnativenavigation.utils;

import android.view.View;

import com.reactnativenavigation.R;

import androidx.coordinatorlayout.widget.CoordinatorLayout;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
Expand All @@ -14,4 +18,8 @@ public static CoordinatorLayout.LayoutParams matchParentWithBehaviour(Coordinato
lp.setBehavior(behavior);
return lp;
}

public static void updateBottomMargin(View view, int additionalMargin) {
((CoordinatorLayout.LayoutParams) view.getLayoutParams()).bottomMargin = additionalMargin + ViewTags.get(view, R.id.fab_bottom_margin, 0);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.reactnativenavigation.utils;

import androidx.annotation.Nullable;

import com.reactnativenavigation.utils.Functions.Func1;
import com.reactnativenavigation.utils.Functions.FuncR1;

import androidx.annotation.Nullable;

public class ObjectUtils {
public static <T> void perform(@Nullable T obj, Func1<T> action) {
if (obj != null) action.run(obj);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.reactnativenavigation.utils;

import android.view.View;

import androidx.annotation.NonNull;

public class ViewTags {
public static @NonNull <T> T get(View view, int key, @NonNull T defaultValue) {
return view.getTag(key) == null ? defaultValue : (T) view.getTag(key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.app.Activity;
import android.view.View;

import com.reactnativenavigation.interfaces.ScrollEventListener;
import com.reactnativenavigation.parse.Options;
import com.reactnativenavigation.presentation.ComponentPresenter;
import com.reactnativenavigation.presentation.Presenter;
Expand Down Expand Up @@ -45,6 +46,11 @@ public void setDefaultOptions(Options defaultOptions) {
presenter.setDefaultOptions(defaultOptions);
}

@Override
public ScrollEventListener getScrollEventListener() {
return perform(view, null, ComponentLayout::getScrollEventListener);
}

@Override
public void onViewAppeared() {
super.onViewAppeared();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import android.view.ViewManager;
import android.view.ViewTreeObserver;

import com.reactnativenavigation.interfaces.ScrollEventListener;
import com.reactnativenavigation.parse.Options;
import com.reactnativenavigation.parse.params.Bool;
import com.reactnativenavigation.parse.params.NullBool;
Expand Down Expand Up @@ -86,6 +87,10 @@ public void setWaitForRender(Bool waitForRender) {
this.waitForRender = waitForRender;
}

public ScrollEventListener getScrollEventListener() {
return null;
}

public void addOnAppearedListener(Runnable onAppearedListener) {
onAppearedListeners.add(onAppearedListener);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
import com.reactnativenavigation.viewcontrollers.ViewController;
import com.reactnativenavigation.viewcontrollers.topbar.TopBarController;
import com.reactnativenavigation.views.Component;
import com.reactnativenavigation.views.ReactComponent;
import com.reactnativenavigation.views.Fab;
import com.reactnativenavigation.views.FabMenu;
import com.reactnativenavigation.views.StackLayout;
import com.reactnativenavigation.views.stack.StackBehaviour;
import com.reactnativenavigation.views.topbar.TopBar;
Expand All @@ -35,6 +36,7 @@

import static com.reactnativenavigation.utils.CollectionUtils.*;
import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.matchParentWithBehaviour;
import static com.reactnativenavigation.utils.CoordinatorLayoutUtils.updateBottomMargin;
import static com.reactnativenavigation.utils.ObjectUtils.perform;

public class StackController extends ParentController<StackLayout> {
Expand Down Expand Up @@ -98,9 +100,7 @@ public void mergeOptions(Options options) {
public void applyChildOptions(Options options, ViewController child) {
super.applyChildOptions(options, child);
presenter.applyChildOptions(resolveCurrentOptions(), this, child);
if (child.getView() instanceof ReactComponent) {
fabOptionsPresenter.applyOptions(this.options.fabOptions, (ReactComponent) child.getView(), getView());
}
fabOptionsPresenter.applyOptions(this.options.fabOptions, child, getView());
performOnParentController(parent ->
parent.applyChildOptions(
this.options.copy()
Expand All @@ -119,8 +119,8 @@ public void mergeChildOptions(Options options, ViewController child) {
super.mergeChildOptions(options, child);
if (child.isViewShown() && peek() == child) {
presenter.mergeChildOptions(options, resolveCurrentOptions(), this, child);
if (options.fabOptions.hasValue() && child instanceof ReactComponent) {
fabOptionsPresenter.mergeOptions(options.fabOptions, (ReactComponent) child, getView());
if (options.fabOptions.hasValue()) {
fabOptionsPresenter.mergeOptions(options.fabOptions, child, getView());
}
}
performOnParentController(parent ->
Expand Down Expand Up @@ -395,7 +395,10 @@ public void clearTopTabs() {

@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, ViewGroup child, View dependency) {
perform(findController(child), controller -> presenter.applyTopInsets(this, controller));
perform(findController(child), controller -> {
if (dependency instanceof TopBar) presenter.applyTopInsets(this, controller);
if (dependency instanceof Fab || dependency instanceof FabMenu) updateBottomMargin(dependency, getBottomInset());
});
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import com.reactnativenavigation.views.BehaviourAdapter;
import com.reactnativenavigation.views.BehaviourDelegate;
import com.reactnativenavigation.views.Fab;
import com.reactnativenavigation.views.FabMenu;
import com.reactnativenavigation.views.topbar.TopBar;

import androidx.annotation.NonNull;
Expand All @@ -17,6 +19,8 @@ public StackBehaviour(BehaviourAdapter delegate) {

@Override
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull ViewGroup child, @NonNull View dependency) {
return dependency instanceof TopBar;
return dependency instanceof TopBar ||
dependency instanceof Fab ||
dependency instanceof FabMenu;
}
}
1 change: 1 addition & 0 deletions lib/android/app/src/main/res/values/ids.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
<item name="fragment_screen_content" type="id"/>
<item name="topBarBackgroundComponent" type="id"/>
<item name="bottomTabs" type="id"/>
<item name="fab_bottom_margin" type="id" />
</resources>
Loading

3 comments on commit 4714983

@itsam
Copy link
Contributor

@itsam itsam commented on 4714983 Feb 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! Any news or plans on this matter? thank you in advance

@guyca
Copy link
Collaborator Author

@guyca guyca commented on 4714983 Feb 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @itsam 👋
We plan to replace the current FAB implementation with the actual FAB from AndroidX next Q. Other than this, I don't have much to update.

@itsam
Copy link
Contributor

@itsam itsam commented on 4714983 Feb 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @guyca
Thanks for your prompt reply. Your work and effort are much appreciated :)

Please sign in to comment.