How to use multiple ListView/GridView in same UI? - android

I want to use multiple ListView/GridView within same User Interface; I don't want them to be expanded to their full length and placed under ScrollView.

If you want to learn about ListView, here is a nice tutorial. About your question, here is the similar one! Your question might even be duplicate of this.

I don't think that putting multiple ListView/GridView objects inside ScrollView is a good idea.
The biggest advantage of ListView/GridView is that they reuse View's. When you scroll a ListView what the system really does is to use fixed number of views, and swaps the view settings (text, image source etc.). This is done by requesting the getView(int,View,ViewGroup) method from the list Adapter.
What you are trying to do is to force the ListView/GridView to render all it's raws, which pretty much beats the whole purpose of using ListView/GridView in the first place.
Use LinearLayout inside the ScrollView instead, and then add Views dynamically from your Activity/Fragment

Just specify dimensions to your ListView/GridView that are not all match_parent.
Also, if you want to proportionally allocate say 50% and 50% of available height to your list and grid, put them in a LinearLayout and use the layout_weight mechanism:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
... />
<GridView
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
... />
</LinearLayout>

you can set the layout to include how many listViews and gridViews as you wish. just choose a layout and set their sizes yourself. if you put them in a linearLayout, you can set a weight for each of them, to make their width/height proportional to the layout width/height.
However, do note that the more you put, the more cluttered the UI is.
Also note that Google suggests to never put listViews and gridViews inside ScrollViews (this was being talked about on the "the world of listView" lecture) .

Related

Having RecyclerView inside a NestedScrollView calls onBindView for all the items

I have two RecyclerViews placed vertically in a LinearLayout. I need to make both of them scrollable and that is why I have put the LinearLayout inside NestedScrollView
This is the my layout file.
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
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.support.v7.widget.RecyclerView
android:id="#+id/featured_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v7.widget.RecyclerView
android:id="#+id/all_topic_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Also, I am disabling nested scrolling in Java code.
disableNestedScrolling(findViewById(R.id.all_topic_list));
disableNestedScrolling(findViewById(R.id.featured_list));
My RecylerView library version is 26.1.0
This works fine perfectly, but then onBindViewHolder method is getting called for all the items in the list. Ideally it should only be called for the visible items in the list.
I think the issue is happening because I am giving wrap_content to the RecyclerView. A lot of answers on this question suggest that the issue is solved in v23.2.1, but I am already using v26.1.0. How to solve this issue?
I had exactly the same problem. RecyclerViews are not meant to be placed inside scroll containers with the same scroll direction. The view recycling only works when the height is set to MATCH_PARENT.
Depending on the complexity of the content inside of the NestedScrollView and the anticipated amount of RecyclerView items:
Ignore the problem. If there are only a few simple items, you may
not need view recycling at all.
When I hit the problem, I analysed the layouts of other popular apps: For example, WhatsApp only uses RecyclerViews (or ListViews with view recycling) in some parts of their app.
Particularly, this group settings screen with hundreds of possible items is made of multiple ListViews wrapped by a ScrollView, without any view recycling.
Replace the NestedScrollView with a single
ReyclerView with multiple item types and put all of your scrollable content inside of it. This is the way to go if you need view recycling.
Beware that you also have to convert all the other content in the NestedScrollView (headers and footers, spacing) to RecyclerView items with their own ViewHolders.
If the setup is rather simple, I would recommend you to implement it without additional libraries, following the link above.
There are a few different libraries available to solve your problem (all of them follow the second approach with a single RecyclerView), but most come with a lot of extra features which you may not need:
RendererRecyclerViewAdapter
It comes with a ViewRenderer/ViewModel interface, which works like a
"partial" RecyclerView for a single item type. You would create one
for every item type and then register them in a single adapter.
Epoxy
A library/framework create by airbnb and used heavily in their app.
They have a lot of scrollable content (similar to a web page) with a
lot of different item types. Epoxy also helps with the composition of
the different items on a page, and it handles animations when the
content or its order changes. Too much if you only need it for a single screen.
Litho
A complete UI framework created by Facebook which comes with it's own rendering engine, a replacement for xml layouts and much more. As far as I understand, it allows you to do to handle large amounts of items (like the Facebook timeline) and the view recycling is handled automatically. Like Epoxy, you would only use this if your app includes things like endless scrolling with a lot of different item types and you really need the performance.
I tried Epoxy and RendererRecyclerViewAdapter, but after all I created my own multiple item type adapter. It can be created in less than 100 lines of code.
Starting from RecyclerView:1.2.0-alpha04 we can use ConcatAdapter to solve this problem
https://developer.android.com/reference/androidx/recyclerview/widget/ConcatAdapter
I tried your problem by adding 20 items in each recyclerview, with NestedScrollView application called onBindViewHolder method 40 times. As you disabling nested scrolling in Java code i suggest to use Scrollview. By using ScrollView application called onBindViewHolder 33 times.
If you fix your recyclerView's height to specific size instead of "match-parent" it will reduce call to onBindViewHolder greatly.
<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="false">
<android.support.v7.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.vishal.my2.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/featured_list"
android:layout_width="match_parent"
android:layout_height="300dp" />
<android.support.v7.widget.RecyclerView
android:id="#+id/all_topic_list"
android:layout_width="match_parent"
android:layout_height="300dp" />
</android.support.v7.widget.LinearLayoutCompat>
</ScrollView>
If Specifying hardcoded value to recyclerView's height does not meet your application requirement then you can try using ListView instead of recyclerView. pardon me if i am wrong, This was my first time answering any question.
Add this to nested scroll view android:fillViewport="false"

May changing the visibility of too many layouts a performance issue?

Suppose you have a FrameLayout containing 10 LinearLayouts, where only one is visible per time.
Each LinearLayout is a complex view, containing Button, EditText, TextView, etc.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/alice
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible">
<!-- complex stuff -->
</LinearLayout>
<!-- many more linear layouts... -->
<LinearLayout
android:id="#+id/juliett
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<!-- last complex stuff -->
</LinearLayout>
</FrameLayout>
Thus:
Changing the LinearLayout visibility, in order to show another item, would be a huge performance issue?
Given it is an issue, why using ViewFlipper does not slow down the app performance?
It's bad practice because the code easily become a mess. Ignoring that and focusing only on performance, when you set the visibility to GONE, the view isn't measured (it's different from INVISIBLE). The view occupies a little bit of memory, though. Depending on what you're doing, consider using ViewGroup.removeView().
It's hard to say without a benchmark, but theoretically it shouldn't have performance issues.
This is not a good way to implement because each time you need to show another view, other views must be gone. So that, you are going to write duplicated lines of codes for it. Viewswitcher is better choice. So what about performance then? View switcher is going to measure all children views which make only draw inside of itself. This trick makes view switcher faster because it does not need to recalculate dimensions for itself unless you disable it to use heterogeneous children views.
İf your views are homogeneous, the best way is implement a custom view and giving a class to changing state. For example, you set Alice object to your custom view to show Alice's properties and changing it programmaticly up to your business logic.
Good luck
Emre
Seriously, you need to consider fragment for above situation.
why to inflate un-necessary views.

How to align view (like in RelativeLayout) with a nephew view?

Related question. Answer: RelativeLayout can't do it. I'm asking how to do it anyway, with not just RL, or with something else.
General story: you have a complex layout that would be difficult to adjust, and along comes a request for something to be added, aligning with a nested view.
What is the best approach? A popup with a custom style? (not familiar with those yet)? Spending days changing the whole hierarchy to a single RelativeLayout? A custom Layout class as wrapper?
AbsoluteLayout (deprecated) or FrameLayout with programmatically changed LayoutParams or margins? (this I'd rather avoid, I prefer not to touch onMeasure, etc)
Simplified example (no relation to pic above):
LinearLayout defines relative heights of the elements. I don't know to do it with RelativeLayout.
anExpandableView is something to be animated as sliding from under someBar (here; full-width, but perhaps it may need to align its width, as well as vertical position).
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="#+id/topStuff"
layout="#layout/incl_topstuff"
android:layout_width="match_parent"
android:layout_weight="7"
android:layout_height="0dip" />
<include
android:id="#+id/someBar"
layout="#layout/incl_filters_and_stuff"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include
android:id="#+id/bottomStuff"
layout="#layout/incl_bottomstuff"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="10" />
</LinearLayout>
<include
android:id="#+id/anExpandableView"
layout="#layout/incl_filters"
android:visibility="gone"
android:layout_below="#id/someBar"/>
</RelativeLayout>
I know SO has an aversion to general questions, but I don't want an ad-hoc solution. I am asking what to do in cases which would be solved if only a wrapping RelativeLayout would allow alignment to a view that is not a direct sibling.
Putting it simply, RelativeLayout can only measure and layout it's direct children based on each other, but I guess you already knew that.
The only general solution would be to implement your own custom Layout class, which I wouldn't recommend. If I had to guess why RelativeLayout does not traverse the entire layout hierarchy at it's level and below, it's probably for performance reasons.
Unfortunately if you're using RelativeLayouts and LinearLayouts and you want views to be dependent on each other you have to pick one approach and stick to it, either the flat hierarchy of RelativeLayout, or the nested one of LinearLayout.
Based on your example, as far as I know, there is no way to implement weighted views with a RelativeLayout, so you're stuck with using a LinearLayout.
The easiest way to do what you want is to inflate your expandableView in code, align it with the bottom of the RelativeLayout, set it's height and position based on bottomStuff, and animate from there.
If you really want to do it in xml, I can think of one somewhat hacky, ad-hoc approach, but which can can be generalized to mirroring the measurement and layout of any hierarchy with a bit of work.
Create a parallel but invisible LinearLayout that is a sibling of the first one. Give it an empty view with weight 7 on top, an invisible copy of someBar in the middle, then your expandable view under that with weight 10. To have it slide up, either animate the height of the invisible someBar and the weight of the empty view on top towards 0, or remove them/set them to gone and set animateLayoutChanges on your LinearLayout.

How to set an imageView to not be fixed at a position

the xml file:
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="#+id/singleProp"
android:scrollbarAlwaysDrawVerticalTrack="true"
android:visibility="gone">
<ImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:id="#+id/propertyImg"/>
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/propertyData"
android:layout_below="#id/propertyImg">
</ListView>
</RelativeLayout>
With the above codes, the image will be fixed at top-center position. With CSS terms in html, the style of that image view is style="position:fixed" but what I want is style="position:relative".
For example,
I hope the picture can illustrate the idea.
May I know how I should modify my codes?
Let me elaborate a bit more on mark.zhai`s answer, since it's the only one that I find to be the proper approach.
First of all I wanna point out that you should think about implementing your list with a RecyclerView instead of ListView (right now it's generally favoured to use a RecyclerView; ListView is getting kinda deprecated)...
If you want to be sure that your ImageView works nicely (scroll-wise) with your list (without too much "side-work" on scroll integration), you should implement it as a first item of your list. If you stick with your ListView you can use the header function of it and add your ImageView with ListView's method addHeaderView. If you decide to move to a RecyclerView (which I think you should), you can accomplish that in a bit more difficult manner (more on that for example here).
why don't u make the imageview an item of the listview
For this you need to create a custom listview which having the first item as a image view and later having all your list items. Probably you can control this in your adapter getview method by the use of position(int)
For example if Position is equal to '0' show only image view and if position is not equal to '0' show your rest elements by using Visibility
Note: here their might be a chance of performance issues as it is loading the unnecessary views every time
To achieve this, you need to scrolling the complete layout. In general case, when you fling on a list view, only the item within the list view moves.
Check this link.
I'm not entirely sure what you're asking, as I don't know a lot about CSS, but you can check out this link which describes aligning elements within a Relative Layout.
You might want to try adding alignParentLeft or alignParentRight.
Remember margins are external to the object and padding is internal. For example to move the image 20dp from the left you would:
<ImageView
android:layout_alignParentLeft="true"
android:layout_marginLeft="20dp"
android:layout_width="200dp"
android:layout_height="200dp"
android:id="#+id/propertyImg"/>
Here are some useful links about mastering Relative Layouts:
Relative Layout Params
Moving Elements Around in a Relative Layout Tutorial
Another RelativeLayout Example
Margins and Padding
Why don't you use layout_below to take your layout below whatever you want, and use layout_gravity to set it's gravity. You can check these Link to get you better understand, it will help you in these problem.

Dynamic adapter view with uneven column size or Horizontal StaggeredGridView

I am trying to achieve the layout represented by the following image, using an adapter to compose the internal views:
All rows scroll together and recycling is in place.
I have not seen many approaches on SO or any of the blogs. FlowLayout doesn't allow scrolling, StaggeredGridView (Etsy's or Google's) doesn't allow for rotation, and synchronized multi ListView seems too involved.
What is SO approach to this custom component?
My approach would be to tie together n horizontal listviews in a single viewgroup. Listen to touch events on the viewgroup, and scroll all the 3 horizontal listviews together.
Layout would be like this -
LinearLayout(vertical)
--HorizontalListView1
--HorizontalListView2
--HorizontalListView3
--HorizontalListView-n
Now forward all the touch events to the parent viewgroup, and handle them in a single toucheventlistener. In the touchevent listener, calculate a scroll for all horizontal listviews, and call the corresponding scroll.
As all the scroll calls are done in a single call on UI thread, they should be processed in the next drawing pass. This should make it feel synchronized. If this doesn't work, you need to explicitly synchronize the views by extending them.
With this you will have view reuse and recycling as far as possible with horizontal listview. All views should scroll together if synchronization is in place.
Just an idea - use simple HorizontalScrollView with a single LinearLayout inside (the HSV has a requirement for only one child). Inside that LinearLayout place vertically another 3 LinearLayouts - one for each row. Inside each of the 3 layouts place (horizontally) the items that belong to one row. I haven't tried this but it would be the first thing I'd try if I was in your situation.
Something like:
<HorizontalScrollView>
<LinearLayout android:orientation="vertical">
<LinearLayout android:orientation="horizontal" />
<LinearLayout android:orientation="horizontal" />
<LinearLayout android:orientation="horizontal" />
</LinearLayout>
</HorizontalScrollView>
I think the best way for you is to extend ViewGroup and implement this component by yourself. Also extend BaseAdapter and create methods like gerFirstRowView, getSecondRowView and so on. You will be able to implement effective view recycling and avoid heavy nesting of views so your component will have good performance.
Use GridLayout with orientation as vertical, with your height the constant value for the 3 rows of ImageViews. Scale each ImageView to match the constant height in your adpater. Set the image to your android:src in XML or setImageResource in Java. This will scale the images accordingly without stretching.
Then add all your horizontal scrolling code.

Categories

Resources