Fix header rendering – layout and transparency (#184)

* Let UINavController control subcontroller view's frames.

This PR changes the way we've been handling yoga <> NavController layout interactions. Now we ignore frame updates coming from react for the main Screen view to allow NavController take the controll. In order to keep yoga working we now use `setSize` to pass the dimensions of the view back to yoga such that it can properly calculate layout of the views under Screen component.

* Header resizing fixes for Android.

In this change we use CoordinatorLayout as a stack screen container to handle rendering of toolbar and screen content. Thanks to this approach we can support collapsable bars in the future. Instead of relying on RN to layout screen container when renered under ScreenStack we rely on Android native layout to measure and position screen content and then use UIManager.setNodeSize to communicate that back to react.
This commit is contained in:
Krzysztof Magiera
2019-10-18 15:08:39 +02:00
committed by GitHub
parent c590283359
commit 4a9a3a877a
10 changed files with 309 additions and 217 deletions

View File

@@ -1,10 +1,8 @@
package com.swmansion.rnscreens;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
@@ -13,11 +11,12 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.facebook.react.bridge.GuardedRunnable;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.MeasureSpecAssertions;
import com.facebook.react.uimanager.PointerEvents;
import com.facebook.react.uimanager.ReactPointerEventsView;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.events.EventDispatcher;
public class Screen extends ViewGroup implements ReactPointerEventsView {
@@ -33,34 +32,6 @@ public class Screen extends ViewGroup implements ReactPointerEventsView {
FADE
}
public static class ScreenFragment extends Fragment {
private Screen mScreenView;
public ScreenFragment() {
throw new IllegalStateException("Screen fragments should never be restored");
}
@SuppressLint("ValidFragment")
public ScreenFragment(Screen screenView) {
super();
mScreenView = screenView;
}
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return mScreenView;
}
@Override
public void onDestroy() {
super.onDestroy();
mScreenView.mEventDispatcher.dispatchEvent(new ScreenDismissedEvent(mScreenView.getId()));
}
}
private static OnAttachStateChangeListener sShowSoftKeyboardOnAttach = new OnAttachStateChangeListener() {
@Override
@@ -77,8 +48,7 @@ public class Screen extends ViewGroup implements ReactPointerEventsView {
}
};
private final Fragment mFragment;
private final EventDispatcher mEventDispatcher;
private @Nullable Fragment mFragment;
private @Nullable ScreenContainer mContainer;
private boolean mActive;
private boolean mTransitioning;
@@ -87,13 +57,23 @@ public class Screen extends ViewGroup implements ReactPointerEventsView {
public Screen(ReactContext context) {
super(context);
mFragment = new ScreenFragment(this);
mEventDispatcher = context.getNativeModule(UIManagerModule.class).getEventDispatcher();
}
@Override
protected void onLayout(boolean changed, int l, int t, int b, int r) {
// no-op
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
final int width = r - l;
final int height = b - t;
final ReactContext reactContext = (ReactContext) getContext();
reactContext.runOnNativeModulesQueueThread(
new GuardedRunnable(reactContext) {
@Override
public void runGuarded() {
reactContext.getNativeModule(UIManagerModule.class)
.updateNodeSize(getId(), width, height);
}
});
}
}
@Override
@@ -166,14 +146,18 @@ public class Screen extends ViewGroup implements ReactPointerEventsView {
mContainer = container;
}
protected @Nullable ScreenContainer getContainer() {
return mContainer;
protected void setFragment(Fragment fragment) {
mFragment = fragment;
}
protected Fragment getFragment() {
protected @Nullable Fragment getFragment() {
return mFragment;
}
protected @Nullable ScreenContainer getContainer() {
return mContainer;
}
public void setActive(boolean active) {
if (active == mActive) {
return;

View File

@@ -6,7 +6,6 @@ import android.view.ViewGroup;
import android.view.ViewParent;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentTransaction;
@@ -16,13 +15,12 @@ import com.facebook.react.modules.core.ReactChoreographer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ScreenContainer extends ViewGroup {
public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
protected final ArrayList<Screen> mScreens = new ArrayList<>();
private final Set<Screen> mActiveScreens = new HashSet<>();
protected final ArrayList<T> mScreenFragments = new ArrayList<>();
private final Set<ScreenFragment> mActiveScreenFragments = new HashSet<>();
private @Nullable FragmentTransaction mCurrentTransaction;
private boolean mNeedUpdate;
@@ -59,24 +57,30 @@ public class ScreenContainer extends ViewGroup {
markUpdated();
}
protected T adapt(Screen screen) {
return (T) new ScreenFragment(screen);
}
protected void addScreen(Screen screen, int index) {
mScreens.add(index, screen);
T fragment = adapt(screen);
screen.setFragment(fragment);
mScreenFragments.add(index, fragment);
screen.setContainer(this);
markUpdated();
}
protected void removeScreenAt(int index) {
mScreens.get(index).setContainer(null);
mScreens.remove(index);
mScreenFragments.get(index).getScreen().setContainer(null);
mScreenFragments.remove(index);
markUpdated();
}
protected int getScreenCount() {
return mScreens.size();
return mScreenFragments.size();
}
protected Screen getScreenAt(int index) {
return mScreens.get(index);
return mScreenFragments.get(index).getScreen();
}
protected final FragmentActivity findRootFragmentActivity() {
@@ -120,25 +124,24 @@ public class ScreenContainer extends ViewGroup {
}
}
private void attachScreen(Screen screen) {
getOrCreateTransaction().add(getId(), screen.getFragment());
mActiveScreens.add(screen);
private void attachScreen(ScreenFragment screenFragment) {
getOrCreateTransaction().add(getId(), screenFragment);
mActiveScreenFragments.add(screenFragment);
}
private void moveToFront(Screen screen) {
private void moveToFront(ScreenFragment screenFragment) {
FragmentTransaction transaction = getOrCreateTransaction();
Fragment fragment = screen.getFragment();
transaction.remove(fragment);
transaction.add(getId(), fragment);
transaction.remove(screenFragment);
transaction.add(getId(), screenFragment);
}
private void detachScreen(Screen screen) {
getOrCreateTransaction().remove(screen.getFragment());
mActiveScreens.remove(screen);
private void detachScreen(ScreenFragment screenFragment) {
getOrCreateTransaction().remove(screenFragment);
mActiveScreenFragments.remove(screenFragment);
}
protected boolean isScreenActive(Screen screen, List<Screen> allScreens) {
return screen.isActive();
protected boolean isScreenActive(ScreenFragment screenFragment) {
return screenFragment.getScreen().isActive();
}
@Override
@@ -164,26 +167,26 @@ public class ScreenContainer extends ViewGroup {
protected void onUpdate() {
// detach screens that are no longer active
Set<Screen> orphaned = new HashSet<>(mActiveScreens);
for (int i = 0, size = mScreens.size(); i < size; i++) {
Screen screen = mScreens.get(i);
boolean isActive = isScreenActive(screen, mScreens);
if (!isActive && mActiveScreens.contains(screen)) {
detachScreen(screen);
Set<ScreenFragment> orphaned = new HashSet<>(mActiveScreenFragments);
for (int i = 0, size = mScreenFragments.size(); i < size; i++) {
ScreenFragment screenFragment = mScreenFragments.get(i);
boolean isActive = isScreenActive(screenFragment);
if (!isActive && mActiveScreenFragments.contains(screenFragment)) {
detachScreen(screenFragment);
}
orphaned.remove(screen);
orphaned.remove(screenFragment);
}
if (!orphaned.isEmpty()) {
Object[] orphanedAry = orphaned.toArray();
for (int i = 0; i < orphanedAry.length; i++) {
detachScreen((Screen) orphanedAry[i]);
detachScreen((ScreenFragment) orphanedAry[i]);
}
}
// detect if we are "transitioning" based on the number of active screens
int activeScreens = 0;
for (int i = 0, size = mScreens.size(); i < size; i++) {
if (isScreenActive(mScreens.get(i), mScreens)) {
for (int i = 0, size = mScreenFragments.size(); i < size; i++) {
if (isScreenActive(mScreenFragments.get(i))) {
activeScreens += 1;
}
}
@@ -191,16 +194,16 @@ public class ScreenContainer extends ViewGroup {
// attach newly activated screens
boolean addedBefore = false;
for (int i = 0, size = mScreens.size(); i < size; i++) {
Screen screen = mScreens.get(i);
boolean isActive = isScreenActive(screen, mScreens);
if (isActive && !mActiveScreens.contains(screen)) {
for (int i = 0, size = mScreenFragments.size(); i < size; i++) {
ScreenFragment screenFragment = mScreenFragments.get(i);
boolean isActive = isScreenActive(screenFragment);
if (isActive && !mActiveScreenFragments.contains(screenFragment)) {
addedBefore = true;
attachScreen(screen);
attachScreen(screenFragment);
} else if (isActive && addedBefore) {
moveToFront(screen);
moveToFront(screenFragment);
}
screen.setTransitioning(transitioning);
screenFragment.getScreen().setTransitioning(transitioning);
}
tryCommitTransaction();
}

View File

@@ -0,0 +1,50 @@
package com.swmansion.rnscreens;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.events.EventDispatcher;
public class ScreenFragment extends Fragment {
protected Screen mScreenView;
public ScreenFragment() {
throw new IllegalStateException("Screen fragments should never be restored");
}
@SuppressLint("ValidFragment")
public ScreenFragment(Screen screenView) {
super();
mScreenView = screenView;
}
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return mScreenView;
}
public Screen getScreen() {
return mScreenView;
}
@Override
public void onDestroy() {
super.onDestroy();
((ReactContext) mScreenView.getContext())
.getNativeModule(UIManagerModule.class)
.getEventDispatcher()
.dispatchEvent(new ScreenDismissedEvent(mScreenView.getId()));
}
}

View File

@@ -8,42 +8,76 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class ScreenStack extends ScreenContainer {
public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
private final ArrayList<Screen> mStack = new ArrayList<>();
private final Set<Screen> mDismissed = new HashSet<>();
private final ArrayList<ScreenStackFragment> mStack = new ArrayList<>();
private final Set<ScreenStackFragment> mDismissed = new HashSet<>();
private Screen mTopScreen = null;
private ScreenStackFragment mTopScreen = null;
private boolean mLayoutEnqueued = false;
public ScreenStack(Context context) {
super(context);
}
public void dismiss(Screen screen) {
mDismissed.add(screen);
public void dismiss(ScreenStackFragment screenFragment) {
mDismissed.add(screenFragment);
onUpdate();
}
public Screen getTopScreen() {
for (int i = getScreenCount() - 1; i >= 0; i--) {
Screen screen = getScreenAt(i);
if (!mDismissed.contains(screen)) {
return screen;
}
}
throw new IllegalStateException("Stack is empty");
}
public Screen getRootScreen() {
for (int i = 0, size = getScreenCount(); i < size; i++) {
Screen screen = getScreenAt(i);
if (!mDismissed.contains(screen)) {
if (!mDismissed.contains(screen.getFragment())) {
return screen;
}
}
throw new IllegalStateException("Stack has no root screen set");
}
@Override
protected ScreenStackFragment adapt(Screen screen) {
return new ScreenStackFragment(screen);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0, size = getChildCount(); i < size; i++) {
getChildAt(i).layout(0, 0, getWidth(), getHeight());
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
for (int i = 0, size = getChildCount(); i < size; i++) {
getChildAt(i).measure(
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
}
}
private final Runnable mLayoutRunnable = new Runnable() {
@Override
public void run() {
mLayoutEnqueued = false;
measure(
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
}
};
@Override
public void requestLayout() {
super.requestLayout();
if (!mLayoutEnqueued) {
mLayoutEnqueued = true;
post(mLayoutRunnable);
}
}
@Override
protected void removeScreenAt(int index) {
Screen toBeRemoved = getScreenAt(index);
@@ -54,20 +88,20 @@ public class ScreenStack extends ScreenContainer {
@Override
protected void onUpdate() {
// remove all screens previously on stack
for (Screen screen : mStack) {
if (!mScreens.contains(screen) || mDismissed.contains(screen)) {
getOrCreateTransaction().remove(screen.getFragment());
for (ScreenStackFragment screen : mStack) {
if (!mScreenFragments.contains(screen) || mDismissed.contains(screen)) {
getOrCreateTransaction().remove(screen);
}
}
Screen newTop = null;
Screen belowTop = null; // this is only set if newTop has TRANSPARENT_MODAL presentation mode
ScreenStackFragment newTop = null;
ScreenStackFragment belowTop = null; // this is only set if newTop has TRANSPARENT_MODAL presentation mode
for (int i = mScreens.size() - 1; i >= 0; i--) {
Screen screen = mScreens.get(i);
for (int i = mScreenFragments.size() - 1; i >= 0; i--) {
ScreenStackFragment screen = mScreenFragments.get(i);
if (!mDismissed.contains(screen)) {
if (newTop == null) {
newTop = screen;
if (newTop.getStackPresentation() != Screen.StackPresentation.TRANSPARENT_MODAL) {
if (newTop.getScreen().getStackPresentation() != Screen.StackPresentation.TRANSPARENT_MODAL) {
break;
}
} else {
@@ -78,34 +112,34 @@ public class ScreenStack extends ScreenContainer {
}
for (Screen screen : mScreens) {
for (ScreenStackFragment screen : mScreenFragments) {
// add all new views that weren't on stack before
if (!mStack.contains(screen) && !mDismissed.contains(screen)) {
getOrCreateTransaction().add(getId(), screen.getFragment());
getOrCreateTransaction().add(getId(), screen);
}
// detach all screens that should not be visible
if (screen != newTop && screen != belowTop && !mDismissed.contains(screen)) {
getOrCreateTransaction().hide(screen.getFragment());
getOrCreateTransaction().hide(screen);
}
}
// attach "below top" screen if set
if (belowTop != null) {
final Screen top = newTop;
getOrCreateTransaction().show(belowTop.getFragment()).runOnCommit(new Runnable() {
final ScreenStackFragment top = newTop;
getOrCreateTransaction().show(belowTop).runOnCommit(new Runnable() {
@Override
public void run() {
top.bringToFront();
top.getScreen().bringToFront();
}
});
}
getOrCreateTransaction().show(newTop.getFragment());
getOrCreateTransaction().show(newTop);
if (!mStack.contains(newTop)) {
// if new top screen wasn't on stack we do "open animation" so long it is not the very first screen on stack
if (mTopScreen != null) {
// there was some other screen attached before
int transition = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
switch (mTopScreen.getStackAnimation()) {
switch (mTopScreen.getScreen().getStackAnimation()) {
case NONE:
transition = FragmentTransaction.TRANSIT_NONE;
break;
@@ -118,7 +152,7 @@ public class ScreenStack extends ScreenContainer {
} else if (mTopScreen != null && !mTopScreen.equals(newTop)) {
// otherwise if we are performing top screen change we do "back animation"
int transition = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
switch (mTopScreen.getStackAnimation()) {
switch (mTopScreen.getScreen().getStackAnimation()) {
case NONE:
transition = FragmentTransaction.TRANSIT_NONE;
break;
@@ -132,7 +166,7 @@ public class ScreenStack extends ScreenContainer {
mTopScreen = newTop;
mStack.clear();
mStack.addAll(mScreens);
mStack.addAll(mScreenFragments);
tryCommitTransaction();
}

View File

@@ -0,0 +1,82 @@
package com.swmansion.rnscreens;
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import com.facebook.react.uimanager.PixelUtil;
import com.google.android.material.appbar.AppBarLayout;
public class ScreenStackFragment extends ScreenFragment {
private static final float TOOLBAR_ELEVATION = PixelUtil.toPixelFromDIP(4);
private AppBarLayout mAppBarLayout;
private Toolbar mToolbar;
private boolean mShadowHidden;
@SuppressLint("ValidFragment")
public ScreenStackFragment(Screen screenView) {
super(screenView);
}
public void removeToolbar() {
if (mAppBarLayout != null) {
((CoordinatorLayout) getView()).removeView(mAppBarLayout);
}
}
public void setToolbar(Toolbar toolbar) {
if (mAppBarLayout != null) {
mAppBarLayout.addView(toolbar);
}
mToolbar = toolbar;
AppBarLayout.LayoutParams params = new AppBarLayout.LayoutParams(
AppBarLayout.LayoutParams.MATCH_PARENT, AppBarLayout.LayoutParams.WRAP_CONTENT);
params.setScrollFlags(0);
mToolbar.setLayoutParams(params);
}
public void setToolbarShadowHidden(boolean hidden) {
if (mShadowHidden != hidden) {
mAppBarLayout.setTargetElevation(hidden ? 0 : TOOLBAR_ELEVATION);
mShadowHidden = hidden;
}
}
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
CoordinatorLayout view = new CoordinatorLayout(getContext());
CoordinatorLayout.LayoutParams params = new CoordinatorLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
params.setBehavior(new AppBarLayout.ScrollingViewBehavior());
mScreenView.setLayoutParams(params);
view.addView(mScreenView);
mAppBarLayout = new AppBarLayout(getContext());
// By default AppBarLayout will have a background color set but since we cover the whole layout
// with toolbar (that can be semi-transparent) the bar layout background color does not pay a
// role. On top of that it breaks screens animations when alfa offscreen compositing is off
// (which is the default)
mAppBarLayout.setBackgroundColor(Color.TRANSPARENT);
mAppBarLayout.setLayoutParams(new AppBarLayout.LayoutParams(
AppBarLayout.LayoutParams.MATCH_PARENT, AppBarLayout.LayoutParams.WRAP_CONTENT));
view.addView(mAppBarLayout);
if (mToolbar != null) {
mAppBarLayout.addView(mToolbar);
}
return view;
}
}

View File

@@ -1,6 +1,7 @@
package com.swmansion.rnscreens;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
@@ -16,42 +17,10 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.views.text.ReactFontManager;
public class ScreenStackHeaderConfig extends ViewGroup {
private static final float TOOLBAR_ELEVATION = PixelUtil.toPixelFromDIP(4);
private static final class ToolbarWithLayoutLoop extends Toolbar {
private final Runnable mLayoutRunnable = new Runnable() {
@Override
public void run() {
mLayoutEnqueued = false;
measure(
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
}
};
private boolean mLayoutEnqueued = false;
public ToolbarWithLayoutLoop(Context context) {
super(context);
}
@Override
public void requestLayout() {
super.requestLayout();
if (!mLayoutEnqueued) {
mLayoutEnqueued = true;
post(mLayoutRunnable);
}
}
}
private final ScreenStackHeaderSubview mConfigSubviews[] = new ScreenStackHeaderSubview[3];
private int mSubviewsCount = 0;
private String mTitle;
@@ -63,20 +32,18 @@ public class ScreenStackHeaderConfig extends ViewGroup {
private boolean mIsBackButtonHidden;
private boolean mIsShadowHidden;
private int mTintColor;
private int mWidth;
private int mHeight;
private final Toolbar mToolbar;
private OnBackPressedCallback mBackCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
getScreenStack().dismiss(getScreen());
getScreenStack().dismiss(getScreenFragment());
}
};
private OnClickListener mBackClickListener = new OnClickListener() {
@Override
public void onClick(View view) {
getScreenStack().dismiss(getScreen());
getScreenStack().dismiss(getScreenFragment());
}
};
@@ -84,36 +51,18 @@ public class ScreenStackHeaderConfig extends ViewGroup {
super(context);
setVisibility(View.GONE);
mToolbar = new ToolbarWithLayoutLoop(context);
mToolbar = new Toolbar(context);
// set primary color as background by default
TypedValue tv = new TypedValue();
if (context.getTheme().resolveAttribute(android.R.attr.colorPrimary, tv, true)) {
mToolbar.setBackgroundColor(tv.data);
}
mWidth = 0;
mHeight = 0;
if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
mHeight = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
}
}
private void updateToolbarLayout() {
mToolbar.measure(
View.MeasureSpec.makeMeasureSpec(mWidth, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(mHeight, View.MeasureSpec.EXACTLY));
mToolbar.layout(0, 0, mWidth, mHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mWidth != (r - l)) {
mWidth = (r - l);
updateToolbarLayout();
}
// no-op
}
@Override
@@ -141,10 +90,13 @@ public class ScreenStackHeaderConfig extends ViewGroup {
return null;
}
private Fragment getScreenFragment() {
private ScreenStackFragment getScreenFragment() {
ViewParent screen = getParent();
if (screen instanceof Screen) {
return ((Screen) screen).getFragment();
Fragment fragment = ((Screen) screen).getFragment();
if (fragment instanceof ScreenStackFragment) {
return (ScreenStackFragment) fragment;
}
}
return null;
}
@@ -159,16 +111,16 @@ public class ScreenStackHeaderConfig extends ViewGroup {
Screen parent = (Screen) getParent();
if (mIsHidden) {
if (mToolbar.getParent() != null) {
parent.removeView(mToolbar);
getScreenFragment().removeToolbar();
}
return;
}
if (mToolbar.getParent() == null) {
parent.addView(mToolbar);
getScreenFragment().setToolbar(mToolbar);
}
AppCompatActivity activity = (AppCompatActivity) parent.getFragment().getActivity();
AppCompatActivity activity = (AppCompatActivity) getScreenFragment().getActivity();
activity.setSupportActionBar(mToolbar);
ActionBar actionBar = activity.getSupportActionBar();
@@ -188,7 +140,7 @@ public class ScreenStackHeaderConfig extends ViewGroup {
// shadow
actionBar.setElevation(mIsShadowHidden ? 0 : TOOLBAR_ELEVATION);
getScreenFragment().setToolbarShadowHidden(mIsShadowHidden);
// title
actionBar.setTitle(mTitle);

View File

@@ -59,4 +59,9 @@ public class ScreenStackViewManager extends ViewGroupManager<ScreenStack> {
public View getChildAt(ScreenStack parent, int index) {
return parent.getScreenAt(index);
}
@Override
public boolean needsCustomLayoutForChildren() {
return true;
}
}