Scrollable list trailed by fixed views - android

This might be a very beginner question, but I'm yet unable to find myself around the android jungle.
I've already got a RecyclerView working to show a list of items (with data binding and Room database and DiffUtil.ItemCallback and all).
I'd like to put 2 links after the list: "missing something?" and "add new entry" that will lead to other fragments.
What I have:
When I put 2 buttons (I don't know yet how to put links, but this is not the point of this question) after the RecyclerView, all in a LinearLayout, they stay fixed near the screen bottom. I mean, the RecyclerView is scrollable by itself, scrolling "beneath" the two buttons, the entire LinearLayout expanding to fill the screen (match_parent).
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="top"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Missing something?"
android:onClick="#{...}" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Add new item"
android:onClick="#{...}" />
</LinearLayout>
What I want
I'd like the 2 buttons to scroll along with the list, so that they are always positioned after the last item (think as if they were items themselves, albeit an heterogeneous list with different types/RecyclerView.ViewHolder).
For a big enough list the buttons will be initially off screen; to be scrolled in if the user happen to scroll to the bottom of the list.
What I tried
I tried with ScrollView around the LinearLayout, and it works, but everywhere everybody say that one should never put a RecyclerView inside a ScrollView (maybe because it is scrollable itself).
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/routines_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<!-- buttons -->
</LinearLayout>
</ScrollView>
Being really a beginner in android programming, I'd like to know how usually this kind of layout should be done. Only main directions will be enough for me.
NB. I don't know if I really need a RecyclerView because I don't expect this list to be lengthy. Maybe usually something around 4 to 8 items, possibly 10. But I really don't expect it to be much bigger than that. For many users the two links will even be visible all the time (i.e. no scroll at all).

RecyclerView is always the most efficient to show a list especially if you are getting the data from a database or an API. Don't put your recyclerview in a scrollview. You can add two items to the bottom of the list as your links and program your recyclerview to exhibit different properties for last two items. That is the best way I can think of. Good Luck!
Also, Recyclerview is very difficult to work with when you are working with complex data. With small lists such as in your case, it can seem inconvenient to create a whole adapter class and do everything you are supposed to do. When you have grasped the concepts on xml android and have plenty experience with that. You can move to jetpack compose and lazy column will make your life easy.

Related

Two RecyclerViews on one screen

I have been given the task to implement a feature on Mobile application. Previously I had no experience with Android nor Java.
What I need is one screen (one fragment) to display two lists of events with uneven number of members one after another, let's call them ListX and ListY.
I've made layout containing two RecyclerViews and two labels (one label to act as header for each of recycler views).
It is working as it is now and I have events displayed, but ListX is scrollable even though its layout_height is set to wrap_content (only 6 items displayed at the time) while ListY is not scrollable and is displaying all events in collection.
What I need is all items from ListX displayed in first RecyclerView (without scroll), and after that ListY displayed in second RecyclerView without scroll too.
Layout:
<FrameLayout
android:id="#+id/resultsView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipeContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/label_daily" />
<android.support.v7.widget.RecyclerView
android:id="#+id/list_daily"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/label_daily"
tools:listitem="#layout/fragment_timetracking_event_item" />
<TextView
android:id="#+id/label_unfinished" />
<android.support.v7.widget.RecyclerView
android:id="#+id/list_unfinished"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/label_unfinished"
tools:listitem="#layout/fragment_timetracking_event_item" />
</LinearLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
As you can see from XML both RecyclerViews have layout_height set to wrap_content yet first RecyclerView is not obeying.
Here is sketch of how it should look like
Is there any way to put both lists in one RecyclerView and make custom separators? Or any other way to anchor beginning of one list to the end of the other and make them show all items without scroll?
I hope to get help here.
You don't need 2 recyclerViews, you can achieve using single recylcerView.
You should read about getItemViewType & using same in bindViewHolder & createViewHolder
You can specify these three types
Header
DailyItem
FinishItem
Create your list like this
add Daily HEADER
add Daily ITEMS
add Unfinished HEADER
add Unfinished ITEMS
Now you can render both lists in single recyclerview
Your problem
...other way to anchor beginning of one list to the end of the other and make them show all items without scroll?
has a simple solution without rewriting your code to the one RecycleView - just put android.support.v4.widget.NestedScrollView instead of ScrollView in your xml layout. It seems to be a bug in the ScrollView widget, which is fixed in android.support.v4.widget.NestedScrollView.
P.S. After solving this problem you will face with other problem - Android RecycleView scrolls with no momentum, which is resolved in "RecyclerView within NestedScrollView Scrolling Issue". It is fine only for short item list in RecycleView because as it is mentioned in comments to above article
This is not a good solution since disabling nested scrolling will disable cell reuse as well, therefore loading all cells at once.
which will have impact on the memory consumption of your app in the case of long item list.

Form with list, RecyclerView in ScrollView or multiple ViewHolder in RecyclerView?

I'm making a form where user can see uploaded files and add new ones, besides other general inputs.
As of now, I'm nesting a LinearLayout (for general part of the form) and a RecyclerView (for the uploaded list) inside NestedScrollingView, with nestedScrollingEnabled=false and layout_height=wrap_content in RecyclerView.
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="15dp">
<EditText/> and a bunch of other inputs....
<android.support.v7.widget.RecyclerView
android:id="#+id/attachment_preview_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
However, I've read that doing that is not good because it RecyclerView will not recycle its view and renders everything instead. And from a test I tried, (set a log on onCreateViewHolder), it does renders everything. And I'm worried it will cause OOM because the users range from low-end devices (512MB RAM) to high-end (2MB+ RAM)
But, making the general part as another view in RecyclerView will be tricky, because when the view is recycled I probably will lose whatever data changed in the input, unless I set up on change listener and update the actual data with the changes.
I'm trying to get the best of both worlds, recycling views for the upload list (because it can easily go to tens/hundreds) while keeping it easy to maintain the general form data especially when navigating away from/return to the app.
So, is there a way to make this work with the two options? Or is there a better solution to this case?

Implementing a sticky section in the middle of the activity in android

I'm now facing quite interesting problem in develpoing android app.
So, I have 3 sections, and the order is following.
[VIEW_ON_TOP]
[TO_BE_STICKED]
[SCROLLVIEW]
Of course, scroll view has many children views, anyway it might not seem to be the problem.
What I'm trying to do is that when I scroll the [VIEW_ON_TOP] until the top of the [TO_BE_STICKED] is on the top of the screen, the [TO_BE_STICKED] section should be sticked on the top literally, then I have to move on the focus on [SCROLLVIEW] for scrolling, which means that there are exactly two sections for scrolling.
Thus, what I'm thinking is that let these 3 sections be included in the one listview, so each section is one of the listitems, then it would be nothing but the problem for 2 listitems and one sticky header problem of listview. How do you think? Is it resonable idea? Or any other suggestions, or comments?
Please note that [VIEW_ON_TOP] and [SCROLLVIEW] is independent xml file, so it is needed to make these into object for listviewitem.
I found a simple library on GitHub.
https://github.com/emilsjolander/StickyScrollViewItems
the procedure is following.
<com.emilsjolander.components.StickyScrollViewItems.StickyScrollView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/sticky_scroll">
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:weightSum="5">
<include
layout="ON_TOP"
/>
<View
THIS IS FOR SEPARATE LINE
/>
<include
layout="ON_BOTTOM"
/>
</LinearLayout>
</com.emilsjolander.components.StickyScrollViewItems.StickyScrollView>

Can my layout be improved for efficiency?

I have a layout as shown below. It is inflated by code and added to a HorizontalScrollView, sometimes a few hundred times, and causing getting memory issues.
I'm wondering if there's anything that can be done to make it more efficient? Originally I used LinearLayouts, and replacing that with RelativeLayout made a huge difference to the scrolling. Now I'm wondering if it can be further improved?
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="156dp"
android:layout_height="254dp"
android:paddingLeft="7dip"
android:paddingRight="7dip">
<FrameLayout android:id="#+id/button_frame"
android:layout_width="156dp"
android:layout_height="218dp">
<ImageView android:id="#+id/button_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/image_bg"
/>
<ImageView android:id="#+id/button_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom|right"
/>
<TextView android:id="#+id/button_select"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="2dip"
android:layout_marginRight="2dip"
android:background="#drawable/btn_selector_bg_selected"
/>
<TextView android:id="#+id/button_selected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginLeft="2dip"
android:layout_marginRight="2dip"
android:background="#drawable/title_bg_selected"/>
</FrameLayout>
<TextView
android:id="#+id/button_title"
android:layout_width="156dp"
android:layout_height="36dp"
android:textColor="#ffffff"
android:singleLine="true"
android:textSize="13sp"
android:textStyle="bold"
android:gravity="center_vertical|left"
android:ellipsize="end"
android:paddingLeft="30dip"
android:paddingRight="5dip"
android:layout_below="#id/button_frame"
android:background="#drawable/title_bg"/>
</RelativeLayout>
The ImageView button_image is populated using AQuery image caching, so I'm not sure if there's much more I can do to improve the way the image is handled. But any tips on improvements greatly appreciated.
In general, a good way to optimize layouts in Android is to minimize the amount of nesting of containers within each other. The deeper you nest layout containers, the more work the framework must do to measure, layout, and process events for the view hierarchy.
But I think you may be asking the wrong question.
The HorizontalScrollView and VerticalScollView are not intended for the use you're putting them to. They're meant to hold a mostly static layout, and allow it to scroll if necessary depending upon the size of the screen it happens to be running on.
You want a repetitive list of mostly identical Views, that the user can scroll. The correct Android view container to use is ListView, or one of the other descendants of AdapterView.
ListView is careful only to create/inflate the necessary child views to fill the space on screen, and reuse them as the user scrolls. This solves the memory problems you're experiencing. It does that by requiring you to pair it up with an Adapter - an object that wraps the actual data being displayed, and creates on-demand the correct view for a given data item.
Since you're trying to do horizontal scrolling, you might also look at Gallery (now deprecated in Android) or the newer ViewPager class, both of which support horizontal movement through a large list of data.
If you can use a ListView instead of a HorizontalScrollView, you could create an Array Adapter that uses the viewHolder pattern which essentially allows you to re-use views per item in your list view. Take a look at this for more details: http://www.jmanzano.es/blog/?p=166

How can I make horizontally scrollable items in a vertical listview?

I would like to use horizontall scrolling items in a vertically scrolling Listview.
My naive take on this was to put the contents of the listview items inside a scrollView. The items are wider horizontally than the scrollview, but not higher than the scrollview. Since the listview is a normal vertically scrolling listview, I figured that dragging vertically would scroll in the list, while dragging horizontally would scroll in the items.
However that didn't work. The list scrolls fine vertically and shows the items correctly, but scrolling horizontally does not work (nothing happens). Unfortunately I am really not sure where to go from here.
Note that the items should scroll horizontally independently of the other items, i.e the whole list should not scroll sideways when dragging sideways.
As a reference, I would like the list to behave similar to what it does in the app 'Pulse', in case you have seen it.
Make ordinary ListView with any adapter you like but design the item Layout something like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<HorizontalScrollView
android:id="#+id/hor_scroll"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/lin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6.0dip"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:focusable="false" />
<TextView
android:textAppearance="?android:textAppearanceMedium"
android:gravity="center_vertical"
android:id="#+id/text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:singleLine="true"
android:layout_toRightOf="#id/icon"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true" />
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>
You'll have Vertically Scrollable ListView with Horizontally Scrollable items. And the items are scrolled independantly from other items.
You need to use a HorizontalScrollView. Droidstack is an open source app (I wrote) that does just this (in the questions lists you can scroll the tags on each question), if you want a concrete example.
if you follow the good practices of android development, you should never put a ScrollView inside a ListView, is unrecomended bu Romain Guy and other people from android development, to read the arguments read here: Android ScrollView layout problem
from the android docs:
"You should never use a HorizontalScrollView with a ListView, since ListView takes care of its own scrolling. Most importantly, doing this defeats all of the important optimizations in ListView for dealing with large lists, since it effectively forces the ListView to display its entire list of items to fill up the infinite container supplied by HorizontalScrollView."
EDIT: it seems that the warning posted above is an error from the android documentation, talking with some colleagues they told me its possible. The issue in the documentation is here, http://code.google.com/p/android/issues/detail?id=2781.
my appologies
You can use "ViewPager" each element in the list can be a ViewPager

Categories

Resources