I'm trying to add a custom empty view to a ListView as shown in this Q&A. But the ListView seems to add the empty view to its parent, or maybe to the root ViewGroup, rather than to itself. Am I doing something wrong here?
I created a small layout file that demonstrates the problem. Notice how the empty view is placed in the top left corner of the activity, completely outside the ListView:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/top_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Some text above the ListView" />
<ListView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_alignParentLeft="true"
android:layout_below="#id/top_text" />
<TextView
android:id="#android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ListView empty text" />
</RelativeLayout>
Aren't the list items children of the ListView?
Yes, when the ListView creates them via the associated ListAdapter.
The empty view is not a list item. It is not a child of the ListView. It is simply another view. AdapterView toggles the visibility of itself and the empty view, depending upon whether or not the AdapterView is empty. If the AdapterView is empty, its own visibility is set to GONE and the empty view is set to VISIBLE. If the AdapterView is not empty, its own visibility is set to VISIBLE and that of the empty view is set to GONE. See updateEmptyStatus() in the source code for AdapterView.
Given the visibility toggle effect, the typical approach is to have the empty view roughly occupy the same space as does the ListView. However, that's not a requirement.
Related
Currently I have a recycler view with match parents width and grid layout inside a section. So I set click listener for the parent layout, and for the list item which both would gives different reaction when clicked. The problem is the list is not always full, and when it is not full, I can't click the white space of the recycler view because it was blocked by the recycler view.
I have tried set click listener to the recycler view itself, but no luck with that. I also can't change the layout params to become wrap content, because it has to be match parent to be put with grid layout.
Actually I have a dirty solution for this, on the getItemCount, I will hardcode the count to be max grid column size, and create an empty layout viewholder type. In the onBindViewholder, I can validate if the current column position bigger than the real list size, I simply lay out that empty layout viewholder which I can set click listener on. I'm just thinking maybe there is a cleaner solution for this.
Set onClickListener on the Layout inside, not on the RecyclerView itself.
Add the layout in your custom layout resource file, give it an id and set onClickListener in your adapter class.
Since the RecyclerView extends Scroll-layout it will override the click settings of the outer layer.
I would say use a constraintlayout and then below the recyclerview add a empty relativelayout covering your gird which can have this click listener;
<android.support.v7.widget.CardView
android:id="#+id/card_view_outer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/global_dimen_16_dp"
android:layout_marginLeft="#dimen/global_dimen_16_dp"
android:layout_marginTop="#dimen/global_dimen_24_dp"
android:layout_marginRight="#dimen/global_dimen_16_dp"
app:cardBackgroundColor="#color/black12"
app:cardCornerRadius="#dimen/global_dimen_4_dp"
app:cardElevation="3dp">
<android.support.v7.widget.CardView
android:id="#+id/card_view_inner"
android:layout_width="match_parent"
android:layout_height="240dp"
android:layout_gravity="center"
android:layout_margin="1dp"
android:gravity="center"
app:cardBackgroundColor="#color/white100"
app:cardCornerRadius="#dimen/global_dimen_4_dp"
app:cardElevation="0dp">
<android.support.constraint.ConstraintLayout
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"/>
<RelativeLayout
android:id="#+id/layover"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#id/recyclerView" />
</android.support.v7.widget.CardView>
</android.support.v7.widget.CardView>
I'm trying to make a layout similar to the people app, so I have a list with a fixed header at the top. I define the header on top of the listview inside a relative layout but the "glow effect" at the top of the list is only for the listview and not the whole layout.
This is what I have:
and this is what I want:
This is my layout file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:id="#+id/header_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:minHeight="20dp"
android:orientation="vertical" >
<TextView
android:id="#+id/status_header"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#string/last_updated"
android:textSize="12sp"
android:textStyle="bold"
android:textAllCaps="true"
android:gravity="center|left"
android:layout_marginLeft="20dp"
android:layout_marginTop="7dp"
android:layout_marginBottom="7dp"
android:textColor="#color/nice_blue"
/>
<View style="#style/HeaderDivider"/>
</LinearLayout>
<ListView
android:id="#+id/status_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:headerDividersEnabled="false"
android:footerDividersEnabled="false"
android:layout_below="#+id/header_layout" >
</ListView>
The glow effect in both examples are functioning the same.
What is happening within the People application is that it is also a ListView just like yours. The effect happens in the same place. The Me, #, A, etc headers are actually inside the ListView.
Option 1
To do what you are looking to do, put a header in the ListView, you'll also need to create a custom ListView. This means extending ListView.
It sounds more challenging than it is, but you should be able to find a number of examples online. Look for examples that extend a ListView rather than creating a custom ListView.
The general idea is that you'll add in a TextView into the the View of the ListView that will act as your header. This will put the header text Last updated at inside of the ListView view that you've created and since it is inside, it will be included underneath the glow.
Option 2
You may be able to use the addHeaderView(View v, Object data, boolean isSelectable) or addHeaderView(View v) methods on the ListView itself.
I believe this will add a header row inside the ListView, but I've never used this so I can't say for sure how it works. This may be easier, but will be less flexible.
I want to make multiple empty views for a list view and set them programmatically.
So I 've got a listview in an ListActivity. The way my client wants the app, I have a header bar in the app, so the layout looks like this
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/providerListLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<include
android:id="#+id/headerBar_ref"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
layout="#layout/header_with_dashboard_button" />
<include
android:id="#+id/loadingView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
layout="#layout/loading_view" />
<RelativeLayout
android:id="#+id/listViewWrapper"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="#id/headerBar_ref" >
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/headerBar_ref" >
</ListView>
</RelativeLayout>
So I have the 2 empty views in separate xml files. In the list Activity I try to set the empty view like so:
RelativeLayout rootLayout = (RelativeLayout) findViewById(R.id.listViewWrapper);
RelativeLayout noFavsLayout = (RelativeLayout) this
.getLayoutInflater().inflate(emptyViewLayoutId,
rootLayout);
getListView().setEmptyView(noFavsLayout);
But when I do this, the empty view is there ALL the time. I've also tried to to add the view using addContentView(), but that takes over the whole screen. I've not been able to find a solution on S/O yet
Based on reading http://wiresareobsolete.com/2010/08/adapting-to-empty-views/, the actual mechanism for showing the empty view is that the adapter checks to see if the list is empty, and then sets the visibility of either the ListView or empty view to View.GONE, then sets the other one to View.VISIBLE. For this to work properly, both views have to be in the same parent view. In your example, this would mean something like
<RelativeLayout
android:id="#+id/listViewWrapper"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="#id/headerBar_ref" >
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="#android:id/empty"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="This list is empty."
/>
</RelativeLayout>
(Note that I removed "layout_below" from your ListView, it was the only item in the relative layout, so it didn't need that reference. Also, the view has been added in the XML, you should not have to inflate it in the java.)
Now if you want to programmatically set a different empty view (for instance after performing a search), you could add another view to your relative layout, with another id (such as noResults)... and discover that it's always displayed.
<TextView
android:id="#+id/noResults"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="No results were returned."
/>
So in your onCreate() you'll need to find that view and set its visibility to gone as well.
ListView listView = (ListView)findViewById(android.R.id.list);
View empty = findViewById(android.R.id.empty);
listView.setEmptyView(empty);
View noResults = findViewById(R.id.no_results);
noResults.setVisibility(View.GONE);
Then whenever you change the empty view for your list, you'll want to set the visibility of the other view to GONE to make sure only one is getting displayed.
listView.setEmptyView(noResults);
empty.setVisibility(View.GONE);
I hope this helps!
I am currently trying to create a nested ListView.
An outer ListView contains items, of which each consists of a TextView, as caption, and another ListView with children.
Filling the ListViews works really nice using another nested ListView adapter, yet, when it comes to actually displaying the content on the device, the nested list items are not high enough to display all the children. To be exact, only the first nested list item is displayed.
My current layout approach is the following:
Outer list item:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5px"
>
<TextView android:id="#+id/day"
android:textSize="14sp"
android:textStyle="bold"
android:textColor="#ff5e00"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollHorizontally="false"/>
<ListView android:id="#+id/innerList"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fadingEdge="none"
/>
</LinearLayout>
and an inner list layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5px"
>
<TextView android:id="#+id/innertextview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollHorizontally="false"
android:singleLine="false"/>
</LinearLayout>
Is there any chance to adapt the size of the outer list item in a way that all the inner list items will be displayed?
I also looked at ExpandableListViews. This approach worked, yet, I do not want to have the list items collapsed.
Thank you very much,
Matthias
I did not really like this solution, so I looked into this issue some more and found another solution:
ExpandableListView inherits from ListView. What it does is overriding the getView method from BaseAdapter and make a distinction between child and parent items. It is not a real nested list, but it flattens the list by using different layout items.
The same can be done for creating a NestedListView. Inherit from ListView and expect a self created adapter, which makes a distinction between child and parent layout items.
For each one return the appropriately filled layout items.
Done.
You should be able to make the items in an ExpandableListView uncollaposable. This is probably you best option. For instructions on how disable collapsing, checkout this SO Post.
I have a ListView that is being populated with a custom adapter. I have a pretty basic layout for each row of the the ListView (not even sure if this is applicable to my question):
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/BaseStyle"
android:orientation="vertical"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:paddingTop="8dip"
android:paddingBottom="8dip"
>
<TextView
android:id="#+id/text"
style="#style/BaseStyle.Title"
/>
</RelativeLayout>
I'd like to overlay a single TextView at the bottom of the ListView, but not entirely sure how as working with Android layouts can be an exercise in futility. :)
Wrap your main layout with a FrameLayout and put the view that you want to be in overlay as last child.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/home_container"
android:layout_height="match_parent"
android:layout_width="match_parent">
...
your ListView
...
your text view (it is gonna be on top of your layout)
</FrameLayout>
From your description I am not sure that this is what you are looking for, you should give some more info in your question, let me know.
If instead you are just talking about the TextView inside each list item then you just need to specifiy the alignment in your text view:
android:layout_alignParentBottom="true"