I would like to remove paddingTop/marginTop from the navigation view in Multi-Window mode of Android N. Like Gmail already does.
If you see the image below, I'm talking about the normal padding with size equals to the status bar at the beginning of the navigation view.
So basically in Multi-Window mode (see the image below) I have to remove that padding when my app is in the second part of the screen.
Unfortunately from the new api 24 you have isInMultiWindowMode() but it's not possible to know in which part of the screen is your app.
Instead of trying to figure out if you're in multi-window mode and on which part of the screen, you need to make your navigation view header respect system windows insets.
Normally you care about just one window - the one your app is drawn in. Usually you don't even think there are any windows. Isn't your app drawn fullscreen? Well, actually no. Usually there is some space reserved for system bars, like status bar at the top and navigation bar at the bottom. They are drawn in separate windows - system windows. (Oh, and now we've got multi-window mode in N. More like multi-app-window mode, because if you count system windows then multi-window has been around for a while.)
You can make your navigation view header adjust its insets depending on whether it is under a system window (in this case: status bar) or not with just a few simple tweaks.
Assuming the navigation view is defined like that:
<android.support.design.widget.NavigationView
...
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main"
... />
and there is a simple header layout in nav_header_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="#dimen/nav_header_height"
android:background="#drawable/nav_header_background"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="32dp">
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#android:drawable/default_profile_picture" />
...
</LinearLayout>
you just need change it like that:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="#dimen/nav_header_height"
android:background="#drawable/nav_header_background"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#android:drawable/sym_def_app_icon"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"/>
...
</LinearLayout>
First you need to add android:fitsSystemWindows="true" to the layout.
Now you need to make top padding smaller, as fitsSystemWindows will automatically add padding the size of status bar. So previously your top padding was from the top of your header, now it is only from the bottom of the status bar.
And you have to move all your paddings from the layout somewhere else (for example I moved them to margins on child views), because fitsSystemWindows will overwrite those paddings.
After this if your app is in the bottom part of multi-window split then the padding for status bar will not be added. It will also make your navigation view look properly in any other cases where it's not under the status bar or if the status bar changes size in any future version of Android or some crazy custom ROM.
For me nothing was working so I ended up going this route and it got the job done:
<android.support.design.widget.NavigationView
...
app:insetForeground="#null"/>
Technically, the insets are still present but since the insetForeground resource used to draw on them is now null, that logic is skipped in ScrimInsetsFrameLayout's onDraw method (which is the parent class of NavigationView).
So when all else fails, this is a fairly efficient route.
Related
I'm struggled with this for hours looking for solution on google and stackoverflow. Thought that this is some trivial bug in my app but finally made empty project and can reproduce this too. Just run new project and select "Tabbed Activity" with navigation style "Action Bar Tabs (with ViewPager)
Then try to put any widget at the bottom of the fragment's layout. I did this by modify fragment_main.xml and adding:
android:layout_height="match_parent"
android:gravity="bottom"
android:textAlignment="center"
So the whole layout is:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity$PlaceholderFragment">
<TextView
android:id="#+id/section_label"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/activity_vertical_margin"
android:gravity="bottom"
android:text="aaaaaaa"
android:textAlignment="center"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="#+id/constraintLayout"
tools:layout_constraintLeft_creator="1"
tools:layout_constraintTop_creator="1" />
</android.support.constraint.ConstraintLayout>
In design mode everything looks fine:
But when you run app:
You will see text only when you swipe action bar to hide it:
So it is impossible to set widget at the bottom of the tab's fragment or even match some list/picture to the height of the parent because bottom edge will be always under navigation bar.
Workaround which I found is here:
ViewPager with Toolbar and TabLayout has wrong height
First one is to put AppBarLayout and ViewPager between LinearLayout but then I lose hidding action bar functionality when scrolling ViewPager's content. Second one is add android:paddingBottom="?attr/actionBarSize" in ViewPager but then there is a gap when I hide action bar. Seriously there is no solution for this?
I think this is an expected behavior since the ActionBar gets hidden when scrolling up. In the design mode the text can be shown because it doesn't display the TabLayout. However, when you launch the app, it will inflate the TabLayout and the fragment will go below it. So it's not like the fragment is getting expanded or giving you wrong height.
Imagine putting an ImageView that has a matching height of the visible field (from below the TabLayout to right above the navigation menu). When you hide action bar from there, it will have a gap on the bottom since there's no content to fill up the space of hidden action bar, unless you stretch the ImageView as you scroll up, which will result in wired stretched image :/
One possible solution I can think of is, if you want to add a view on the bottom of the fragment, I will set the actionbar padding to the view and when I scroll the screen, I will adjust the padding depends on the scroll offset so that I can always be on the bottom.
I have tried to implement the material components bottom app bar, following these guidelines and doing a refactor to AndroidX + updating my AppTheme.
Material components - bottom app bar
So far so good, all working, but the button is cut off in my fragment.
The xml preview however shows this, which seems like everything is fine:
Here is my xml code:
<androidx.coordinatorlayout.widget.CoordinatorLayout
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:fitsSystemWindows="true">
<!-- Other components and views -->
<com.google.android.material.bottomappbar.BottomAppBar
android:id="#+id/bottombar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:elevation="2dp"
app:fabAlignmentMode="center"
app:fabCradleVerticalOffset="10dp"
app:fabCradleMargin="10dp" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
android:adjustViewBounds="true"
app:layout_anchor="#id/bottombar"
app:layout_anchorGravity="top|center"
app:srcCompat="#drawable/marker" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
I even increased the fabCradleMargin and the fabCradleOffset - otherwise the button is completely in the bottom and not at all floating in that half circle as it is supposed to...
anyone got any clues for this? Thanks a lot!
So I noticed it has to do with the height of the bottom app bar. If i manually set it to 80dp, the Fab shows as it is supposed to.
I tried around a bit more and noticed this manual height setting is only necessary in fragments. I only placed the bottom app bar in a fragment to test it anyways.
So now I implemented it the same way as in the guideline in my MainActivityand then set up a function showFab(Boolean enable) that can then be called in different fragments based on the need.
Works like a charm, if anyone faces the same issues. I guess this might be as it is not supposed to be implemented in a single fragment only.
I am using the new Android 4.4 FLAG_TRANSLUCENT_NAVIGATION flag to turn the navigation bar (back, home button etc) at the bottom of the screen translucent. This works fine but a side effect of this is the layout of my Activity now displays beneath the status bar at the top of the screen (even though I have not set the status bar as being translucent). I want to avoid a hacky fix I.e. applying padding to the top of the layout.
Is there a way to set the navigation as translucent whilst ensuring the status bar appears normally and does NOT allow the layout to display beneath it?
The code I am using is as follows:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window w = getWindow();
w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
Thanks
ianhanniballake's answer actually works, but you shouldn't use android:fitsSystemWindows="true" on the toplevel view. Use this property as following :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#color/colorPrimaryDark">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#ffffff"
android:fitsSystemWindows="true">
<!-- Your other views -->
</LinearLayout>
</LinearLayout>
As you can see, you have to set the color on the top-level view, and put the property on another container.
Add android:fitsSystemWindows="true" to your top level view - android:fitsSystemWindows will automatically resize the view to take into account the system windows such as the status bar.
I have created a sliding layout using the Umano code:
https://github.com/umano/AndroidSlidingUpPanel
It works perfectly, but I have one problem. My panel should be partly transparent, so the original view is still visible when it's up.
The view background is set properly to #64000000, but it is still completely opaque and nothing can be seen behind it.
Here's the code:
<com.sothree.slidinguppanel.SlidingUpPanelLayout
android:id="#+id/sliding_up_panel"
android:layout_height="match_parent"
android:layout_width="match_parent">
<ImageView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:scaleType="fitCenter"
android:id="#+id/images_gallery_image" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:background="#64000000">
<!-- MY STUFF (transparent stuff) -->
</RelativeLayout>
</com.sothree.slidinguppanel.SlidingUpPanelLayout>
I thought maybe the issue is caused by hiding of UI elements underneath the expanded panel, so I tried to play around in the library code, but it doesn't seem to be related.
While debugging, I have noticed that hasOpaqueBackground(View v) in line 355 of the library does return false, as expected - but the view still is opaque.
The support for this has been added in the library. Just set overlay attribute to true, and set the color of the panel to transparent.
I had the same problem, It seem's as if this feature was added before but it isnt in the code. I used the other version of SlidingUpPanelLayout.java (here) and changed mIsTransparent to true false
I have a rather trivial question, but I thought I'd give it a shot. I'm writing an android app and I have a viewgroup form that is loaded from a web server. While the internet access is running, I used to pop up a dialog box with a progress bar. I decided it would look less clunky if I swtiched to the nice small indeterminate progress bar in the title bar.
The title bar option does look less busy, except that the form items (textviews, buttons etc) are all enabled during the retrieval. I then used a recursive routine to disable all the views in the viewgroup, but that again looks ugly - the greyed out textviews (2.3.3) look gross, especially the one with the focus with the orange bar around it. If I pop up a progress bar, the underlying view looks nicely disabled - the window behind is simply slowly dimmed down. From a visual point of view, it much more obvious that we're waiting for something to occur when the entire window is dimmed rather than being faced with a bunch of disabled controls.
Is there a call I can make to disable a view in a similar way the OS does when a progress bar or other window is overlapped on top? This would give me the best of both worlds.
I guess the other option is to set the view to invisible during the access, but I got curious because I can see the OS doing exactly what I want when I use the popup.
I figured out how to do this using a relative layout with a progress dialog wrapped in a frame layout overlapping the main view. When I do my network get, I set the visibility of the frame layout to visible with the background set to translucent, and disable the controls in the underlying view. Works pretty well.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<ScrollView
android:id="#+id/itemView"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</ScrollView>
<FrameLayout
android:id="#+id/itemProgress"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#55000000"
android:visibility="gone"
>
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center|center_vertical"
android:indeterminateOnly="true"
/>
</FrameLayout>
</RelativeLayout>