dynamically set height of view behind BottomSheet to fill space above sheet - android

I'm trying to correctly set the height of the view (mapfragment in below example) that normally sits behind the BottomSheet.
I want the bottom sheet to persist in a half-open state with the option to expand to full screen, but not fully collapse. Example of desired functionality on the two right screens of these uber examples. To do this I set the peek height to half the screen width and the mapfragment height to half and that kinda worked
// Get bottom nav height
var navBarHeight = 0
val navBarId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
if (navBarId > 0) {
navBarHeight = convertDpToPixel(resources.getDimensionPixelSize(navBarId))
}
// Set bottom sheet "persisted" open height
val metrics = resources.displayMetrics
bottomSheetBehavior.peekHeight = metrics.heightPixels / 2
// programmatically set map height
val params = mapFragment?.view?.layoutParams
params?.height = (metrics.heightPixels / 2) - navBarHeight
mapFragment?.view?.layoutParams = params
However, there's an issue as shown in this screenshot - fragment height mismatch - where the "Google" logo and "locate me" button on the map are slightly covered due to some insets not being accounted for.
I'm assuming I have to dynamically account for the insets and bottom nav to get the correct height. This just seems rather error prone though considering the varying sizes of those across devices and versions. I have to imagine there's a better way, no?
Due to limitations of the BottomSheet component, I can't wrap the FrameLayout in a LinearLayout and use a layout_weight solution as it needs to be a direct child of the CoordinatorLayout.
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- I want both of these fragments to fill 50% of this coordinator layout -->
<fragment
android:id="#+id/mapFragment"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="#+id/standardBottomSheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<!--... bottom screen layout ... -->
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Is there something I'm missing here?

Related

ConstraintLayout Guideline expanding together with view

I got this setup:
<ScrollView 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:fillViewport="true">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
app:layout_constraintBottom_toTopOf="#+id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.constraint.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.35" />
...
</android.support.constraint.ConstraintLayout>
</ScrollView>
Now when the view is not full and has no need for a scroll bar - everything works as expected - the image is 35% in relation to the screen size. But as more content appears under the image, a need for scroll bar appears and the guideline's constraintGuide of 0.35 percent seems to be calculated off whole length of the screen (not physical), so the ImageView also becomes bigger as the view becomes "longer".
Is there a way to avoid this and always have x percent of physical screen size?
The guideline you have specified is placed at a percentage distance from the top of the ConstraintLayout. Unfortunately, for your application, the guideline is tied to the overall height of the view and not a percentage of the screen. So, if the ConstraintLayout is taller than the screen size allocated to it, you will see the shift. See documentation for Guideline.
Positioning a Guideline is possible in three different ways:
specifying a fixed distance from the left or the top of a layout (layout_constraintGuide_begin)
specifying a fixed distance from the right or the bottom of a layout (layout_constraintGuide_end)
specifying a percentage of the width or the height of a layout (layout_constraintGuide_percent)
You can specify a static offset from the top of the layout in terms of dp, but this will not accommodate different screen sizes. I don't believe there is a solution just using XML.
You can, however, calculate the number of pixels in code and set the distance on a run-time basis. You would need to change the Guideline to one that is a fixed distance from the top of the layout, calculate the distance from the top, and call setGuidelineBegin to place the guideline.
setGuidelineBegin
void setGuidelineBegin (int guidelineID,
int margin)
Set the guideline's distance form the top or left edge.

Material Design: Nav Drawer Width

According to the Material Design specs the Nav Drawer's width on mobile devices must be
side nav width = screen width - app bar height
How do we implement this on android?
I have two partial solutions. First is the hacky way: in the containing activity I put this code:
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR1) {
final Display display = getWindowManager().getDefaultDisplay();
final Point size = new Point();
display.getSize(size);
final ViewGroup.LayoutParams params = mDrawerFragment.getView().getLayoutParams();
params.width = size.x - getResources().getDimensionPixelSize(
R.dimen.abc_action_bar_default_height_material
);
mFragmentUserList.getView().setLayoutParams(params);
}
This, however, causes a second layout cycle and doesn't work in gingerbread: it is not optimal.
The second solution involves adding a Space between the fragment and the drawerLayout. It however, displaces the shadow and the spot where the user can press to return to the main app. It also crashes when the "hamburguer" icon is pressed. Not optimal either.
Is there a better solution, preferably one that involves styles and xml?
I managed to make a solution using XML style declarations but it is a bit hacky as well. My approach was to use margins instead of applying a set width to avoid writing any code to calculate the layout manually. I've created a basic style rule to highlight how to get this working.
Unfortunately, DrawerLayout currently applies a minimum margin of 64dp. For this approach to work, we need to offset that value with negative margins so we can get the desired width for the navigation drawer. Hopefully this can be resolved in the future (Someone has filed an issue regarding it) so we can just reference the abc_action_bar_default_height_material dimension reference for the margin.
Follow these steps:
Add the following dimension and style definitions:
values/dimens.xml
<!-- Match 56dp default ActionBar height on portrait orientation -->
<dimen name="nav_drawer_margin_offset">-8dp</dimen>
values-land/dimens.xml
<!-- Match 48dp default ActionBar height on landscape orientation -->
<dimen name="nav_drawer_margin_offset">-16dp</dimen>
values/styles.xml
<!-- Nav drawer style to set width specified by Material Design specification -->
<style name="NavDrawer">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_marginRight">#dimen/nav_drawer_margin_offset</item>
</style>
values-sw600dp/styles.xml
<!-- Margin already matches ActionBar height on tablets, just modify width -->
<style name="NavDrawer">
<item name="android:layout_width">320dp</item>
<item name="android:layout_marginRight">0dp</item>
</style>
Once you have added the rules above in your project, you can reference the NavDrawer style in your navigation drawer view:
layout/navigation_drawer.xml (or other appropriate view being used for your navigation drawer)
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/navigation_drawer"
style="#style/NavDrawer"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:background="#FFF"
/>
With the Android Design Support Library it is now really simple to implement navigation drawer including correct sizing. Use the NavigationView and either use its ability to make drawer out of menu resource (example here) or you can just wrap it around the view which you currenty use for showing your drawer list (e.g. ListView, RecyclerView). NavigationView will then take care of the drawer sizing for you.
Here's an example how I use the NavigationView wrapped around ListView:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/navdrawer_layout"
android:fitsSystemWindows="true">
<!-- Layout where content is shown -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include android:id="#+id/toolbar"
layout="#layout/toolbar" />
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/toolbar" />
<!-- Toolbar shadow for pre-lollipop -->
<View style="#style/ToolbarDropshadow"
android:layout_width="match_parent"
android:layout_height="3dp"
android:layout_below="#id/toolbar" />
</RelativeLayout>
<android.support.design.widget.NavigationView
android:id="#+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start">
<ListView
android:id="#+id/navdrawer_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="singleChoice"
android:dividerHeight="0dp"
android:divider="#null"/>
</android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>
This way you can use NavigationViews sizing and still use your own drawer list. Though it is much easier to make the drawer list out of menu resource (example here) you can't use custom views for the list items.
After looking for a simpler solution, I found a very clarifying article: Material Navigation Drawer sizing.
Here:
The Nexus 5 screen is a nice 640 x 360 dp (xxhdpi), and an app bar on
it is 56 dp tall. So the nav drawer should be:
[width in dp] = 360dp — 56dp = 304dp
A Nexus 4 sports a 640 x 384 dp (xhdpi) screen
instead. Same 56dp app bar height. Its nav drawer?
[width in dp] = 384dp — 56dp = 328dp
So, how did the Google designers come up with
288dp and 304dp widths, respectively? I have no idea.
Google apps, on
the other hand, seem to agree with my maths. Oh, and you know what the
funniest thing is in all this? The iPhone (which has different screen
heights, but a constant 320 dp width) is marked correctly as having a
264dp nav drawer.
Basically, it shows that some guidelines about the navigation drawer contradict themselves and that you can use the following rule to avoid calculations:
You can basically always use 304dp on -sw360dp
and 320dp on -sw384dp for your navigation drawer, and you'll get it right.
They already updated specs, now navigation drawer width is:
Math.min(screenWidth — actionBarSize, 6 * actionBarSize);
Well I found this very difficult to understand and implement. Unfortunately, Matthew's solution made my nav drawer too wide for landscape, and it seemed contrary to Google's practices, i.e. the nav width is determined by the device's smallest width. In any event it wouldn't work in my case as I disabled the configuration in my manifest. So I decided to changed the nav width dynamically with the following code.
It should be noted my app is just for phones and I settled on 320dp being the maximum width. This is also why I've settled on 56dp toolbar height for both orientations.
Hope it helps someone, and that they can avoid the unnecessary stress it caused me.
navDrawLayout.post(new Runnable()
{
#Override
public void run() {
Resources r = getResources();
DisplayMetrics metrics = new DisplayMetrics();
if(r.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int height = metrics.heightPixels;
float screenWidth = height / r.getDisplayMetrics().density;
float navWidth = (screenWidth - 56);
navWidth = Math.min(navWidth, 320);
int newWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, navWidth, r.getDisplayMetrics());
DrawerLayout.LayoutParams params = (DrawerLayout.LayoutParams) navDrawLayout.getLayoutParams();
params.width = newWidth;
navDrawLayout.setLayoutParams(params);
}
if(r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int width = metrics.widthPixels;
float screenWidth = width / r.getDisplayMetrics().density;
float navWidth = screenWidth - 56;
navWidth = Math.min(navWidth, 320);
int newWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, navWidth, r.getDisplayMetrics());
DrawerLayout.LayoutParams params = (DrawerLayout.LayoutParams) navDrawLayout.getLayoutParams();
params.width = newWidth;
navDrawLayout.setLayoutParams(params);
}
}
}
);
The Navigation Drawer width depends on the device smallest width, orientation and Android version.
Check out this article about sizing Navigation Drawer.

Android - SlidingPaneLayout - set width on the Pane

I'm using a SlidingPaneLayout in my activity:
<android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/myslidingpanelayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- menu left -->
<LinearLayout
android:id="#+id/menu"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#8d305f"
android:orientation="vertical" >
...
</LineareLayout>
<!-- main page right-->
<LinearLayout
android:id="#+id/right_main"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#fff"
android:orientation="vertical" >
...
</LineareLayout>
</android.support.v4.widget.SlidingPaneLayout>
I want the menu to cover 3/4 of the page I want it to work on all the phones so I can't put for example
android:layout_width="300dp"
I want to calculate the screen width and set it to the left pane
Thank for your help
Thanks for you all I found this answer and it works with me:
int width;
int height;
if (android.os.Build.VERSION.SDK_INT >= 13){
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
width = size.x;
height = size.y;
}else {
Display display = getWindowManager().getDefaultDisplay();
width = display.getWidth(); // deprecated
height = display.getHeight(); // deprecated
}
if(width>0&&height>0){
LinearLayout layout = (LinearLayout)findViewById(R.id.menu);
// Gets the layout params that will allow you to resize the layout
LayoutParams params = layout.getLayoutParams();
// Changes the height and width to the specified *pixels*
params.height = height;
params.width = width*3/4;
}
Just looking up the doc for sliding pane, looks like it functions like a linear layout, and can use the
layout_weight
parameter to set a percentage based width since the parent viewgroup is match_parent
In the case of 3/4 = 75% you can
android:layout_weight="0.75"
From the android docs http://developer.android.com/reference/android/support/v4/widget/SlidingPaneLayout.html:
Like LinearLayout, SlidingPaneLayout supports the use of the layout parameter layout_weight on child views to determine how to divide leftover space after measurement is complete. It is only relevant for width. When views do not overlap weight behaves as it does in a LinearLayout.
When views do overlap, weight on a slideable pane indicates that the pane should be sized to fill all available space in the closed state. Weight on a pane that becomes covered indicates that the pane should be sized to fill all available space except a small minimum strip that the user may use to grab the slideable view and pull it back over into a closed state.
And from the LinearLayout docs http://developer.android.com/guide/topics/ui/layout/linear.html#Weight
Note: You will end up setting the layout_width parameter to 0dp since the view group will actually use the weight to lay the children out
Apart from Selecsosi's answer, which is correct, there is also this view I wrote to always display the second item as a pane (ignoring the default show-side-by-side-if-the-fit behaviour). It can, as the name shows, wrap around the sliding view.
You can implement the behaviour you're after by either using a lot of #dimen resources and switching them based on swXXXdp-(port|land) or just setting the sliding view's width at runtime (something I'm reasonably certain you can do with the default layout as well).

How can I design a layout bigger than phone's screen?

I'm developing an Android application and I want to design, in eclipse, a layout bigger than screen height.
I have a layout for a fragment and this fragment will be inside a ScrollView on FragmentActivity.
This is my fragment's layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/user_pro_main_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/text_state"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/layout_state"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
Do I have to change android:layout_height="match_parent" to make it bigger on eclipse's designer?
What do I have to do if I want to see the layout bigger on eclipse designer?
Answer is pretty simple: you can't view layout which is biggern then screen on Eclipse Editor.
Possible workarounds:
1. Comment part of top views (visible) to see bottom (which are invisible), then uncomment when ready to launch.
2. Change Device Preview to bigger resolution (Nexus 10), this will give you some extra space.
You can always explicitly set the exact dp value in layout_height, but of course most of the time I don't think you want a fixed value, so do it programatically.
LinearLayout yourLayout; // Get it by findViewById()
yourLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, your_calculated_height));
You would set android:layout_height="wrap_content" and as you add child elements beyond the physical screen it will continue to stretch the layout.
As for viewing this on Eclipse, I'm not sure. I personally would just run it on a device to view it.
just calculate device height and width and add int value to calculated height and width at runtime at layouts height and width.
public void deviceDisplay(){
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
}

How to create a view that is bigger than the screen?

Is it possible to create a view that is bigger than the screen?
I need a view that has a bigger width then the screen of the device. I use this view in a rotation animation. During the rotation the parts that were not on the screen before animating the view will become visible.
Is there a way to achieve this effect with the android framework?
Update
I tried to set my parent layout much bigger then the screen and it is working. This will make somethings a little bit uncomfortable but it could work. The next problem now is that my layout still starts at the left side of the screen. I can't think of a method to make the layout to expand itself to the left and the right of the screen.
Ok I got an answer. It is not very nice because it uses a deprecated View class but it works at least on my current testing screen resolution other resolutions are tested tomorrow.
I wrapped the view that I wanted to expand beyond the screen in an absolute layout like this:
<AbsoluteLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true">
<ImageView
android:id="#+id/content"
android:layout_width="600dip"
android:layout_height="420dip"
android:scaleType="centerCrop"
android:layout_x="-200dip"
android:layout_y="60dip"
android:src="#color/testcolor" />
</AbsoluteLayout>
The -200 x coordinate makes the view stick 200dip out of the left side of the screen. If I'm animating the view those parts that are outside the screen will gradually become visible.
E.g. setting negative bottom margin together with setting extra large layout_height (large enough for you) solved the similar issue as for me.
Works fine at least using API 11+ animations/rotations.
Could look like:
android:layout_marginBottom="-1000dp"
android:layout_height="1000dp"
In case anyone still comes up on this page. The key is your root layout, it will only work with a FrameLayout (or the deprecated absolutelayout). Then you have two options to make your child view bigger.
through xml, this is quick and easy but you don't know the actual screen width & height in advance so your off with setting a ridiculously high value for layout_width & layout_height to cover all screens.
Calculate the screen size programatically and make the view's width/height proportional bigger to this..
Also be aware that your bigger view still starts in the top left corner of the screen so to account this you will have to give a negative top & left margin that's half of what you are adding to the view's width/height
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) viewToMakeBigger.getLayoutParams();
int marginLeft = (int) (viewToMakeBigger.getWidth()*0.1);
int marginTop = (int) (viewToMakeBigger.getHeight()*0.1);
params.width = (int) (viewToMakeBigger.getWidth()*1.2);
params.height = (int) (viewToMakeBigger.getHeight()*1.2);
params.leftMargin = -marginLeft;
params.topMargin = -marginTop;
viewToMakeBigger.setLayoutParams(params);
HorizontalScrollView:
http://developer.android.com/reference/android/widget/HorizontalScrollView.html
Layout container for a view hierarchy that can be scrolled by the user, allowing it to be larger than the physical display.
The simple axml below creates an ImageView that is 400dp wider than the screen (even though the layout_width is set to equal the parent's width) using a negative left and right margin of 200dp.
The ImageView is situated 250dp above the top of the screen using a negative top margin, with 450dp of 700dp vertical pixels visible on the screen.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:background="#FFFF0000"
android:layout_height="700dp"
android:layout_marginLeft="-200dp"
android:layout_marginRight="-200dp"
android:layout_marginTop="-250dp"
android:layout_width="match_parent" />
</RelativeLayout>
You can override the views in the onMeasure method. This will set your View dimensions to 1000x1000 px.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(1000, 1000);
}
Is it possible to create a view that is bigger than the screen?
Why not, you can define the layout_width and layout_height in px(or dip) as you want:
android:layout_width="10000px"
android:layout_height="20000px"
You need to change the size of the window, by getWindow().setLayout. This will increase the size for your window. Since the root layout can be as big as its parent you can then increase the size of the view you want to be bigger than the screen size. It works for me let me know
You can use ViewSwitcher to handle that. Used with Animation and a OnGestureListener looks pretty good.
You can do it programmatically:
FrameLayout.LayoutParams rootViewParams = (FrameLayout.LayoutParams) rootView.getLayoutParams();
rootViewParams.height=displayMetrics.heightPixels+(int)dpToPixels(60);
rootViewParams.width=displayMetrics.widthPixels+(int)dpToPixels(60);
rootView.setLayoutParams(rootViewParams);
rootView.setX(rootView.getX() - dpToPixels(30));
rootView.setY(rootView.getY() - dpToPixels(30));
MUST BE ONLY IN
"public void onWindowFocusChanged(boolean hasFocus)" method.
and
rootView = (RelativeLayout) findViewById(R.id.rootLayout);
Inside "protected void onCreate(Bundle savedInstanceState)" method.
Where yout .xml file is like this:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/rootLayout"
tools:context="com.example.Activity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_centerHorizontal="true"
android:layout_margin="30dp"
android:layout_centerVertical="true"
android:layout_height="match_parent">
// Bla bla bla
</RelativeLayout>
and:
public float dpToPixels(float dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}

Categories

Resources