CardView Scroll behavior when keyboard is up - android

Our app's main content is a RecylerView of CardView's. For signup we require more than just a username/password to create an account so we decided to make the signup flow out of CardView's to match the user experience once they sign up.
To do that I have a single Activity that animates fragments in from the bottom and existing fragments out the top to emulate scrolling. This fake scrolling occurs when the user enters data and hits next to advance. This works pretty well except for one case. When we have a EditText for input the keyboard comes up and covers the 'next' button on the bottom of the screen.
In our user testing we've noticed a high percentage of users trying to scroll the card up to get to the next button instead of dismissing the keyboard.
I've spent a lot of time unsuccessfully trying to get the CardView to scroll up to reveal the button and I'm out of ideas and looking for new ones.
The signup Activity layout only contains a FrameLayout that I load Fragments into. Each fragment that gets loaded has a CardView for the root layout.
In the manifest I have set the activity's windowsSoftInputMode to adjustResize, adjustPan with little success.
activity_signup.xml
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/signUpContent"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
simplified fragment_enter_code.xml
<android.support.v7.widget.CardView
style="#style/CardViewStyle.SignUp"
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="wrap_content"
app:cardCornerRadius="25dp"
app:cardElevation="2dp"
app:cardUseCompatPadding="true"
app:contentPadding="8dp">
<EditText
android:id="#+id/codeEditText"
style="#style/HintedEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Code"
android:inputType="text"/>
<Button
style="#style/NextButton"
android:id="#+id/nextButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:text="#string/next"/>
</android.support.v7.widget.CardView>
When I tried putting the CardView in a ScrollView the cardview layout (with fillViewport true), I get a scrollbar but the card doesn't scroll and the cardview layout gets messed up.
Does anyone know the windowSoftInputMode's well enough to point me in the right direction? Or is the CardView just not going to scroll outside of a Container that is design to hold them?
It feels like the solution to this is in manipulating the activity's view not the fragments.

I ended up creating a new app to just play around with this issue and noticed that if I had a layout that didn't contain a CardView whose root layout was a ScrollView it didn't scroll unless the activities windowSoftInputMode is set to adjustResize and then it will scroll.
I then made a layout with a <ScrollView><CardView><content...></CardView></ScrollView> and the size of the CardView was always the default row size for a card and wouldn't match_parent. I solved that with fillViewPort="true" on the ScrollView but when the keyboard came up it wouldn't scroll.
Turns out the secret sauce was to put a FrameLayout (or anyother layout) in between the CardView and the ScrollView.
You still have to account for the resize of the Layout to prevent your view elements not stacking over each other but Once you do that you now get the view above the soft keyboard to scroll and the ability to reach the rest of the UI with a CardView.
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:fillViewport="true">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardCornerRadius="35dp"
app:cardElevation="2dp"
app:cardUseCompatPadding="true"
app:contentPadding="8dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="200dp"
android:src="#drawable/common_ic_googleplayservices"/>
<EditText
android:id="#+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_below="#id/image"
android:hint="Input"
android:inputType="text"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/input"
android:orientation="vertical"
android:gravity="bottom|center_horizontal">
<Button
android:id="#+id/nextButton"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:enabled="false"
android:text="Next"/>
</LinearLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
</FrameLayout>
</ScrollView>

Related

Android: Cover underlying elevated views, and making a flat button

Sorry for the elementary question but I can't find the solution:
When covering a button with a layout without elevation, it emerges over the layout because a button has a depth (negative depth, it points at the viewer).
So, if you have a
<FrameLayout... >
<Button.../>
<FrameLayout
id="#+id/fragmentsContainerOrAnything"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#C00F"
android:clickable="true"
android:focusable="true"
>
</FrameLayout>
</FrameLayout>
The button would show through the FrameLayout.... In this case, literally "out of the blue" because the button has depth and so it emerges over the layout, even though the layout is placed "upon" the button.
The question is:
How do I make the FrameLayout cover the underlying Button without fiddling with its elevation.
How do I make the button flat? Of course I can just set it back to the origins and call it "TextView". But is there a nicer way?
Thanks
Add your button inside FrameLayout
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#C00F"
android:clickable="true"
android:focusable="true" />
</FrameLayout>

Setting maximum height on RecyclerView

I have a dialog fragment that contains linear layout that involves a titleText above a RecyclerView, and at the very bottom, there's a button below the recyclerView.
Since a recyclerView expands or collapses based on the number of items the adapter sets, the button sometimes gets truncated and no longer appears to be on screen, since the recyclerView just covers the entire screen.
My question is, is there a way to set the maximum height of the recyclerView without ever hiding the button underneath. I also don't want to just give the view a random height just in case the recyclerView contains no items, and it would just be a blank section.
Please let me know if you've ever run into this issue, and how you resolved this. Thanks!
UPDATED
You can achieve this easily using layout weights. Here's an example:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Title"
android:textSize="21sp"/>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="30dp">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:text="Submit"/>
</FrameLayout>
The Title and RecyclerView will wrap content according to contents and button will always take up bottom place.
I suggest using RelativeLayout as it handles the positioning of views for cases like yours, so that you can actually focus on main design.
<?xml version="1.0" encoding="utf-8"?>
<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">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:text="Some title" />
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/title"
android:layout_above="#+id/button"/>
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:gravity="center"/>
</RelativeLayout>
Above XML code is the skeleton code for what you need. you can add margins and dimensions to control the spacing. But in any case (until you provide negative margins) your views will never overlap each other.
Main trick of using RelativeLayout is the ability to use XML tags like
android:layout_below or android:layout_above or android:layout_start
or android:layout_end which perfectly aligns your view the way you
want.

Android: ListView pushes out everything else below it

In my app, there is a tabbed activity with three fragments. The first fragment has a form to create a new task, the second fragment has the list of all the saved tasks, and the third fragment will show the comments on a task when selected from the list in the second fragment. The third fragment is also supposed to act like a chat activity which posts comments when you type them in and tap the send button. The XML layout of this third fragment is as follows:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:background="#android:color/darker_gray"
android:orientation="vertical"
tools:context="com.example.ishita.assigntasks.CommentsFragment">
<TextView
android:id="#+id/frag_task_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#dd55ff"
android:padding="10dp" />
<ListView
android:id="#+id/frag_comment_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:transcriptMode="normal" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#android:color/white">
<EditText
android:id="#+id/frag_msg_edit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:layout_weight="1" />
<ImageButton
android:id="#+id/frag_send_btn"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:background="#android:color/background_light"
android:contentDescription="#string/send_btn"
android:src="#android:drawable/ic_menu_send" />
</LinearLayout>
</LinearLayout>
As you can see, there is a TextView and a ListView and below that is another LinearLayout. Here is how it should look (the purple bar is the TextView):
And here is how it actually looks:
The TextView shows up above the ListView, but the LinearLayout does not. It is there, though. If I do android:layout_marginBottom="20dp" on the outermost LinearLayout, it does show up, but the ActionBar scrolls up and overlaps with the notification bar so that the title on the ActionBar and the notifications on the notification bar are both visible simultaneously and neither is legible.
I searched a lot and this seemed a common issue, but none of the solutions helped me. Believe me, I tried everything I could find. I tried wrapping the whole thing in a FrameLayout, using a RelativeLayout in place of the outermost LinearLayout, using two LinearLayouts--one to wrap the TextView and the ListView and the other to wrap the EditText and the ImageButton, etc., but nothing was able to show the bottom LinearLayout below the ListView. I even tried to set focus on the EditText when the fragment launches so that the keyboard would show and I can type, but even that doesn't help.
Note: On the other hand, if I use the exact same layout on an activity, the bottom LinearLayout displays exactly as it should.
I am unable to find the bug. Please help!
It looks like your toolbar pushes fragment layout down without decreasing its height. I have no idea why this happens (there are no root layout code here).
As a workaround you can set fragments layout bottom margin to ?attr/actionBarSize
As you have given layout_weight 1 in listview layout, so it occupies the whole space available. In order to get rid of you have to give some static height or use layout_weight in right manner
<ListView
android:id="#+id/frag_comment_list"
android:layout_width="match_parent"
android:layout_height="500dp"
android:transcriptMode="normal" />
or try like this
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:background="#android:color/darker_gray"
android:orientation="vertical"
tools:context="com.example.ishita.assigntasks.CommentsFragment">
<TextView
android:id="#+id/frag_task_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#dd55ff"
android:padding="10dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical"
android:background="#android:color/white">
<ListView
android:id="#+id/frag_comment_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transcriptMode="normal" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal"
android:background="#android:color/white">
<EditText
android:id="#+id/frag_msg_edit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="textMultiLine"
android:layout_weight="1" />
<ImageButton
android:id="#+id/frag_send_btn"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:background="#android:color/background_light"
android:contentDescription="#string/send_btn"
android:src="#android:drawable/ic_menu_send" />
</LinearLayout>
</LinearLayout>
All,
Thank you for all your responses. I found a workaround by trial and error now and thought I should post the answer for others who face the same issue. I set android:layout_marginBottom="50dp" on the inner LinearLayout (the one wrapping the comments bar--EditText and ImageButton). Somehow, this sets the layout correctly and the fragment functions properly in both Lollipop and Jellybean OS's. I haven't tested on other OS versions.
First of all you should read this, what is layout_weight and how it works.
You assigned layout_weight to ListView as 1, it means it covers whole height. just change it to 0.7 or any proportion you want (less than 1) will solve the problem.
<ListView
android:id="#+id/frag_comment_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.7"
android:transcriptMode="normal" />

How to adjustPan only one view?

I have a scrolling background that doesnt work correctly when the software keyboard is on the screen,
so I've put android:windowSoftInputMode="adjustPan" in the manifest.
This prevents the scrolling background from breaking down, but it also prevents the rest of the screen from resizing so filling out the input fields becomes anoying.
Is there a way to apply android:windowSoftInputMode="adjustPan" only on the horizontalscrollview?
<HorizontalScrollView
android:id="#+id/scrollingbackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent">
<ImageView
android:id="#+id/bg_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="#drawable/background_loop"
android:adjustViewBounds="true"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="#drawable/background_loop"
android:adjustViewBounds="true"/>
</LinearLayout>
</HorizontalScrollView>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
//the input fields
I've just put an extra linearlayout at the bottom of the scrollview and sized it programmaticly 50% of the height of the screen. So when I use android:windowSoftInputMode="adjustPan" now I can still scroll down enough to see all the elements in the scrollview.
Not the cleanest solution IMO, but it'll do for now.

Android: Hidden buttonbar like in Hotmail app

Consider the Hotmail app for Android. When you check an e-mail item, three buttons appear at the bottom: [Mark Read] [Mark Unread] [Delete]
When you uncheck it, the buttons go away again.
What's the layout for this? I've tried this, but it yields scrolling problems at the bottom (can't see last item):
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/black" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#android:color/darker_gray"
android:orientation="horizontal"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:paddingTop="5dip" >
<Button
android:id="#+id/bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:text="#string/mark_read" />
</LinearLayout>
Then, I also need to show/hide this stuff?
Changing the visibility of bottom linearlayout will show/hide it. You'll need to give it an id and then
LinearLayout bottomLayout = (LinearLayout)findViewById(R.id.someId);
bottomLayout.setVisibility(View.GONE)// or View.VISIBLE
As for the scrolling problem, that occurs because the RelativeLayout overlays view components, so you can either show/hide the button overlaying the bottom of the ListView or change the Relativelayout to a LinearLayout so that the ListView ends before the button and change the visibility.
Though I'm not sure this will look very good when you suddenly show the button and the ListView has to resize itself.
Note on visibility
setVisibility(View.GONE);
will remove the view from the layout and other component may resize due to this. However using
setVisibility(View.INVISIBLE);
keeps the space the view took up in the layout and simply makes the view invisible and no resizing will occur.

Categories

Resources