Maintaining "fitsSystemWindows" window insets through Scene Transition - android

I have a layout that transitions between 2 Scenes.
layout.xml
<FrameLayout
android:id="#+id/framelayout_1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
// Some background views.
<FrameLayout
android:id="#+id/scene_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/scene_a" />
</FrameLayout>
</FrameLayout>
res/layouts/scene_a.xml
<android.support.constraint.ConstraintLayout
android:id="#+id/constraintlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
// Child views removed for brevity
</android.support.constraint.ConstraintLayout>
Scene B layout is identical.
In the activity, ViewCompat.setOnApplyWindowInsetsListener(frameLayout, (v, insets) -> insets); is called to use fitsSystemWindows with FrameLayout.
The views are correct on initial view inflation i.e views are drawn under the status bar, but content is pushed down by the window insets to avoid the status bar.
However, when I transition to Scene B, that padding provided by fitsSystemWindows is lost and the content jumps up. Scene A loses the padding too, when returning.
Any assistance greatly appreciated with how to keep this padding through the transitions.

OK, so had nothing to do with Scene Transitions and everything to do with android:fitsSystemWindows.
The default behaviour for any view that uses android:fitsSystemWindows is to consume the window insets it gets passed. The insets get passed down depth first - see this blog.
So in my example above, the first scene - which has set android:fitsSystemWindows consumes the insets, so scene B does not get a chance. Similarly when I transition back to scene A (they have already been consumed).
The fix in my example was to remove the android:fitsSystemWindows from both scenes and place it in the scene root instead.

Related

Prevent a child in ScrollView from being scrolled out of the screen

Somehow I came across Reddit app, and wondered how their Scrolling mechanism works. As you can see from the gif, there is a Menubar (Up/Downvote-comment-share bar) that always locates within the screen and can’t be scrolled out of the screen when scrolled up/down. When scrolling up, it will be located underneath the Toolbar (the grey bar at the top). When scrolling down, it will be located above the EditTextView (the Add-a-comment bar at the bottom).
Relative layout
|-->Toolbar (android:id="#+id/toolbar")
|-->ScrollView (android:layout_below="#id/toolbar")
|-->Child (This child is located underneath the Toolbar when scrolling up)
|-->Child
|-->Child
If I wanted to write this page, what dependencies, widgets or concepts
would I need to use or look into?
Note: You can give me snippets of codes if you prefer :)
you can use the CoordinatorLayout layout to realize that function. the layout like that. for details, you can see this blog in the medium.
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/behavior_demo_coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap"
android:background="?attr/colorPrimary" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/behavior_demo_swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="#+id/behavior_demo_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.design.widget.CoordinatorLayout>
The toolbar (containing the menu) is outside of the ScrollView in the XML.
Or it may not even be a ScrollView at all.
It may just be view recycling- but even so the toolbar is outside of that XML layout used for displaying the content.
For hiding the TopBar and bottomBar (comment box) I recommend you use Animation
Simply call animation like
//on scrollUp
topLayout.animate().translationY(-300).alpha(0.0f).setDuration(300);
// this will make the layout go up and fade to invisible
//on scrollDown
topLayout.animate().translationY(0).alpha(1.0f).setDuration(300);
// this will make the layout come down and fade to be visible
Implement this will both Views using addOnScrollListener() or I suggest you to have a look at this answer for implementing scroll Up/Down using CoordinateLayout
Hope this will help!

SlidingUpPanelLayout problem with visibility

I have a problem with SlidingUpPanelLayout. My view is build like that:
<com.sothree.slidinguppanel.SlidingUpPanelLayout xmlns:sothree="http://schemas.android.com/apk/res-auto"
android:id="#+id/list_sliding_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="top"
sothree:umanoDragView="#+id/dragView"
sothree:umanoOverlay="true"
sothree:umanoPanelHeight="#dimen/filtering_list_closed_height"
sothree:umanoShadowHeight="#dimen/app_bar_elevation">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
// some views
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<FrameLayout
android:id="#+id/list_filtering_fragment_container"
android:name="com.example.test.scenes.list.filtering.FilteringFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.sothree.slidinguppanel.SlidingUpPanelLayout>
and it was working until I've added new feature where I have to set list_filtering_fragment_container visibility to GONE. Everything works fine when I switch visibility status but it's not working when I move to another fragment and come back to previous one.
EDIT
It looks like:
Normal state that I want to achieve after set visibility to VISIBLE
State that I have after set visibility to VISIBLE (after changing fragments)
also I can see in layout inspector that location on Screen and height of this element is different for both cases.
I tried to use slidingUpPanel.setPanelState(PanelState.HIDDEN) but it for some reason doesn't work in 100% cases. It look like view goes outside screen and doesn't come back to it's proper position. And question is why it behaves like that?

android:background killing Activity enter/return transitions

I always used Activity enter/return transitions (using xml transitions like slide and fade to animate views) and always worked because it's very straightforward. Now in my last project I was having problems even with dummy activites (without any real processing), the transitions simply don't work, so after hours of changing layout code, I figured out that the responsible is the android:background in root FrameLayout of Activity Layout where I set some background color. When I removed this attribute, the transitions came back to work. Also, if I apply this attribute only in activity theme, the transitions stop again.
Currently my project is only animating the second activity that only has this layout structure:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="#color/colorPrimary"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView>
<RecyclerView>
</FrameLayout>
And the transition does slide in ImageView from top and slide in RecyclerView from bottom. But like I've said. It only works if the root FrameLayout don't have a background color.
Do you have any idea if it's a bug or anything else?
EDIT: If I use an ugly dummy view just to provide background color, the transitions work:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimary" />
<ImageView>
<RecyclerView>
</FrameLayout>
EDIT 2: Same problem happens if I use another root ViewGroup like LinearLayout instead FrameLayout.
I've found a solution (or workaround) using a friend tip:
if I use this attribute in root FrameLayout, the transition works:
android:transitionGroup="false"

How to let only specific fragments scroll inside a ViewPager of CoordinatorLayout?

Background
I have a viewPager, with 3 fragments and tabs for them. Each fragment has an intro phase (of its own) that doesn't have any scrollable content.
After leaving the intro phase, there is a recyclerView that the user can scroll in.
The problem
I need to use the new design library, so that when scrolling (only via recyclerView), it will hide the actionBar and let the tabs still be shown.
When the user goes to a fragment that doesn't have a scrollable content yet, the actionBar should re-appear, similar to what "Google Play Newsstand" has. In fact, I would even be happy to have what they have: as soon as you start swiping left/right, re-show the action bar.
Thing is, if I follow the guidelines and samples, I have 2 issues:
The non-scrollable phase for fragments gets truncated at the bottom, as if it can get scrolled.
I can't find how to re-show the actionBar, and make it stuck there till I switch to a scrollable content (either by switching to another fragment, or when the content of the current fragment changes to a scrollable content).
What I've tried
Here's a short snippet of the current layout XML file of the activity:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.CoordinatorLayout
android:id="#+id/activity_main__coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/activity_main__appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="?attr/actionBarTheme">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:layoutDirection="ltr"
android:theme="?attr/actionBarTheme"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
tools:ignore="UnusedAttribute"/>
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
app:tabGravity="fill"
app:tabIndicatorColor="#FFffffff"
app:tabIndicatorHeight="3dp"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<include layout="#layout/fabs"/>
</android.support.design.widget.CoordinatorLayout>
<include
layout="#layout/sliding_menu"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="left"/>
</android.support.v4.widget.DrawerLayout>
The fragments have a layout of a ViewAnimator that just switches between phases, while one of them is the non-scrollable content, and the other is the RecyclerView.
I've tried to add a NestedScrollView/ScrollView the non-scrollable content , and force it to fill itself, using android:fillViewport="true" , but it didn't work. For ScrollView it didn't even allow to scroll.
EDIT: Another thing I've tried is to use addOnPageChangeListener on the viewPager, so that in onPageSelected I could set the flags for the toolbar :
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
params.setScrollFlags(!needScrolling? 0 : LayoutParams.SCROLL_FLAG_SNAP | LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | LayoutParams.SCROLL_FLAG_SCROLL);
It works, but it has a issues too:
while scrolling horizontally, I can see the content of the non-scrollable fragment being truncated, and when going to the new fragment (stop touching the screen, to let it snap to the fragment), only then it shrinks its size to fit the correct space.
The toolbar doesn't get re-shown.
If the toolbar is hidden due to scrolling on another fragment, and I'm now on the non-scrollable fragment, it actually gets less space to fill than it's supposed to, so it has empty space at the bottom.
EDIT: one solution is to add an empty view of the same height of actionbar (layout_height="?actionBarSize") at the bottom of the non-scrollable fragments's content. However, when the action bar is hidden, I can see the view, so there is empty space. I still need to know how to re-show the actionbar on this case.
The question
How do I set a different behavior for the toolbar, so that it will re-show and stuck on certain states, yet be scrollable only when there is a RecyclerView shown on the current fragment?

How to position a view off-screen in Android layout

I'm using the Scene/Transition system introduced in 4.4 to simply move an image view from the left to the center of the screen. So the layout for my first scene requires the image to be off screen. I tried android:layout_marginRight but that no effect. What is the correct way to do this?
Use the property translationX or translationY with a negative value. This property sets the horizontal/vertical location of this view relative to its left/top position. You will not see the view off screen in the preview. Run your app and the view must be hidden.
Example:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:layout_margin="16dp"
android:translationX="-72dp"/>
</FrameLayout>
And then in the activity or fragment simply invoke the animate() function.
FloatingActionButton button = (FloatingActionButton)findViewById(R.id.button);
button.animate().translationX(0);
Was thinking one could make the image be to the right of something that is already at the right side, that should keep it off screen. Also, potentially just make it "gone" and then on transtion make it appear.

Categories

Resources