I'm having some problems when trying to remove the header from a listView. At first I use addHeaderView() to add it, but when I change to another layout I want it to disappear but removeHeaderView() doesn't work...
I also tried setting visibility to GONE and it doesn't disappear...
What can I do?
Thanks in advance
Try the approach mentioned below..
Android ListView#addHeaderView and ListView#addFooterView methods are strange: you have to add the header and footer Views before you set the ListView's adapter so the ListView can take the headers and footers into consideration -- you get an exception otherwise. Here we add a ProgressBar (spinner) as the headerView:
// spinner is a ProgressBar
listView.addHeaderView(spinner);
We'd like to be able to show and hide that spinner at will, but removing it outright is dangerous because we'd never be able to add it again without destroying the ListView -- remember, we can't addHeaderView after we've it's adapter:
listView.removeHeaderView(spinner); //dangerous!
So let's hide it! Turns out that's hard, too. Just hiding the spinner view itself results in an empty, but still visible, header area.
Now try to hide the spinner:
spinner.setVisibility(View.GONE);
Result: header area still visible with an ugly space:
The solution is to put the progress bar in a LinearLayout that wraps it's content, and hiding the content. That way the wrapping LinearLayout will collapse when its content is hidden, resulting in a headerView that is technically still present, but 0dip high:
<LinearLayout
xmlns:a="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<!-- simplified -->
<ProgressBar
android:id="#+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Then, set the layout as the header:
spinnerLayout = getLayoutInflater().inflate(R.layout.header_view_spinner, null);
listView.addHeaderView(spinnerLayout);
And when we need to hide it, hide the layout's content, not the layout:
spinnerLayout.findViewById(R.id.spinner).setVisibility(View.GONE);
Now the header disappears from view. No more ugly space at the top!
Most people don't like to use AddHeaderView, however I sometimes found it very convenient, to avoid modifying complex adapters, or if the headers are not very related to them.
With this easy trick you will be able to seamlessly remove/add headers:
I add an empty LinearLayout with orientation vertical, and height wrap_content, as the only Header View (let mListView be the target listView):
LinearLayout mCustomHeaders=new LinearLayout(context);
mCustomHeaders.setOrientation(LinearLayout.VERTICAL);
mListView.addHeaderView(mCustomHeaders);
mListView.setAdapter (.......)
Thenafter, I can add random stuff, anywhere, to the list as header, even when the list is full:
mCustomHeaders.add(myHeaderView1);
mCustomHeaders.add(myHeaderView2);
mCustomHeaders.add(myHeaderView3);
You can also delete all headers, anytime:
mCustomHeaders.removeAllViews(); // will erase all headers
You get the idea .... Hope it helps !
The problem is that you are always creating a new object when you do:
View headerView = someView
So the new view is not the same as the view already added as listView header, try this:
View headerView = inflater.inflate(R.layout.adapter_datatable_saleitem_header, null, false);
headerView.setTag(this.getClass().getSimpleName() + "header");
if (listView.getHeaderViewsCount() > 0) {
View oldView = listView.findViewWithTag(this.getClass().getSimpleName() + "header");
if (oldView != null) {
listView.removeHeaderView(oldView);
}
}
You can check if header count > 0 then remove the header and add it again.
its works fine for me.
View _headerView;
private void function HandleHeaderView(){
if(listView.getHeaderViewsCount() > 0)
{
listView.removeHeaderView(_headerView);
}
/* Add list view header */
_headerView = GetHeaderView();
listView.addHeaderView(_headerView);
}
private View GetHeaderView()
{
View header = getLayoutInflater().inflate(R.layout.header_layout, null);
// TODO: ...
return header
}
Where drawerLogoView is my headerview, here's what I do:
drawerLogoView = mInflater.inflate(R.layout.navigation_drawer_custom_layout, null);
mDrawerList.addHeaderView(drawerLogoView,null,false);
LinearLayout layOut = ((LinearLayout)drawerLogoView.findViewById(R.id.NavProfilePreviewLayout));
layOut.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 0));
That way, it becomes invisible :D
To show it back, you can use this:
LinearLayout layOut = ((LinearLayout)drawerLogoView.findViewById(R.id.NavProfilePreviewLayout));
layOut.setLayoutParams(newRelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
If you are using addHeaderView(), you can't delete your header after that.
So, don't use addHeaderView(). Rather, create your own adapter that
blends your main roster of Views with your header. While my
MergeAdapter will not handle your specific case, you can use it to see
the concept of blending multiple row sources:
https://github.com/commonsguy/cwac-merge
I encountered this problem in a slightly disguised scenario: the ListView I was dealing with came from PreferenceFragment, and the header represents a PreferenceCategory. So, my freedom of setting up the ListView was severely limited. But there were two approaches (partly inspired by other answers on this page). One was to add a custom layout to my PreferenceCategory (using a class that extends android.preference.PreferenceCategory, see Custom PreferenceCategory Headings). But I found an easier workaround: for the first Preference in this PreferenceCategory, I override onCreateView():
#Override public View onCreateView(ViewGroup parent) {
parent.setTop(-parent.getChildAt(0).getTop());
return super.onCreateView(parent);
}
Related
I have a custom XML file. I want to repeat this in a layout (say Relative) n number of times, dynamically (obviously).
I have seen many posts, but none helped. I am not looking for a ListView or Adapters or so. It's as simple as - A RelativeLayout. Inside it, adding the custom XML one above another. Any number of times.
With a static LinearLayout (Vertical orientation), adding the view dynamically results in rendering it once, not one below another. Don't know why. Although a TextView or so do repeat one below the other in a loop inside a LinearLayout (Vertical).
Then I dynamically created the layout (Relative), and inflated the custom XML. Displayed one. When I tried for another below the first it told me to remove child's parent first (Exception). If I do that and add again, its as good as removing the first rendered view and adding it again.
So how can I get multiple views in same layout?
A rough presentation of what I've attempted:
mainLayout = (RelativeLayout)findViewById(R.id.mainlay); //Mainlayout containing some views already
params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.BELOW,R.id.sideLayout); //sideLayout is an existing LinearLayout within the main layout.
View child = getLayoutInflater().inflate(R.layout.dynamiccustomlayout,null);
RelativeLayout r1 = new RelativeLayout(this);
r1.setLayoutParams(params);
r1.addView(child);
mainLayout.addView(r1);
mainLayout.setLayoutParams(params);
mainLayout.addView( child);
/* r2 = new RelativeLayout(this);
r2.setLayoutParams(params);
r2.addView(contentLayout); [Gives exception] */
This is how it worked out for me...
Before that, the issue with android is:
If you add dynamic views inside a LinearLayout (Horizontal), they will appear horizontally with new created instances, added to the view.
However, shockingly, it's not the same in case of LinearLayout (Vertical orientation). Hence the whole mess.
Solution:
The RelativeLayout layout file was binded with the variable, somewhat like this:
customLay = (RelativeLayout) mainLay.findViewById(R.id.dynamicCustomLayout);
Then, a Dynamic RelativeLayout was created within which the former variable is added/wrapped.
customLayout = new RelativeLayout(this);
customLayout.addView(customLay);
Every layout is assigned an id:
customLayout.setId(i);
And then a loop is run (2 if conditions for i=0 and i>0)
for i>0 (indicates the 2nd dynamic layout, to be added below the first), LayoutParameter is created:
params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
And then for i>0, using the ids of dynamic views, they are added one below the other:
//Following code below used id to place these views below each other to attain list type arrangement of views//
// i==0 for first view on top//
if (i == 0) {
params.addRule(RelativeLayout.BELOW, R.id.sideLayout);
customLayout.setLayoutParams(params);
}
// i>0 for views that will follow the first top//
else {
params.addRule(RelativeLayout.BELOW, i - 1);
customLayout.setLayoutParams(params);
}
Then added to main root layout, where all these views or cards need to be displayed:
includeLayout.addView(customLayout);
Ofcourse, the code is not just this. I have written the essential points that helped me achieve the target and that may help others in future.
So the main essence was ---
using a Dynamic RelativeLayout, to
bind the static RelativeLayout, and
assigning ids to the Dynamic RelativeLayout wrappers, and
on basis of ids use RelativeLayoutParameters to place the following
ids below the previous ones.
You have to instanciate every child by itself
View child = getLayoutInflater().inflate(R.layout.dynamiccustomlayout,null);
r1.addView(child);
View child2 = getLayoutInflater().inflate(R.layout.dynamiccustomlayout,null);
r1.addView(child2);
//ok, i do a analog thing in obne of my apps. here is the code:
public class FlxForm extends LinearLayout {
public FlxForm(Context context) {
super(context);
inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.flxform, this);
this.setPadding(0, 0, 0, 0);
container = (LinearLayout) this.findViewById(R.id.flxform);
this.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));
//here is my funtion to calculate the items i want to add, its a little bit too complicated, but in the end it works like:
for(int i=0;i<10;i++){
View x = inflater.inflate(R.layout.dynamiccustomlayout,null);
container.addview(x);
}
}
}
XML for the Form
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/flxform"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:background="#android:color/transparent"
android:orientation="vertical" >
</LinearLayout>
Then you can instantiate a "Form" Objekt and add it into a ScrollView
For doing this You would have to nest your RelativeLayout inside a ScrollView and Manage all the Scrolling, items adding, memory management, etc manually.
So the simple solution for adding n Number of Custom Views is to use a RecyclerView, ListView, GridView, etc with a neat CustomAdapter and Your Custom View.
Here is a nice example of using RecyclerView with custom Adapter :
http://code.tutsplus.com/tutorials/getting-started-with-recyclerview-and-cardview-on-android--cms-23465
I hope this Helps.
I have a ListFragment and in the ListFragment.onViewCreated I have this code:
super.onViewCreated(view, savedInstanceState);
View header = getActivity().getLayoutInflater().inflate(R.layout.rowheader, null);
this.getListView().addHeaderView(header);
My layout simply have a LinearLayout (horizontal) with four TextView's inside.
Now what I do not understand is that the header receives click events and it also displays the blue overlay/color/thing on the header when it is clicked. How can that be, and can I disable this somehow?
Thank you
Søren
use this
this.getListView().addHeaderView(header, null, false)
instead of
this.getListView().addHeaderView(header);
What kind of Object is the header view?
You might want to see if the view has a setEnabled() method, setClickable() or setSelectable(). You would be able to set that before you add the header view to the list view
If footer view added in ListView, then divider disappears from last item of ListView.
Even I have set android:footerDividersEnabled="true" with ListView and my footer view is just TextTiew.
Setting isSelectable to true didn't work for me, maybe because I was also calling removeFooterView when my list was done loading.
What finally fixed it for me was setting android:layout_height to "fill_parent" instead of "wrap_content" on the ListView.
The ListView implementation in Android never draws dividers between items that are disabled, which if you are just calling the addFooterView(View v) method then by default your footer will be.
Instead you need to call the addFooterView(View v, Object data, boolean isSelectable) method with isSelectable set to true. You can just pass null for the data object if you don't need it.
This almost worked for me. I was after a divider after the last list item, but not after the footer as my footer was empty space. I ended up adding two footers, one selectable of zero height and one not selectable containing the padding.
TextView view = new TextView(this);
view.setLines(0);
TextView view1 = new TextView(this);
view1.setLines(4);
mListView.addFooterView(view, null, true);
mListView.addFooterView(view1, null, false);
mListView.setFooterDividersEnabled(true);
Try setting the layout_height of the ListView to match_parent:
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#333333"
android:dividerHeight="1px"
When the layout_height is set to wrap_content it might skip the bottom divider:
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="#333333"
android:dividerHeight="1px"
Head through the wall approach, but reliable, is to manually add divider as a footer view.
ListView myListView = (ListView) view.findViewById(R.id.my_list_view);
myListView.addFooterView(getInflater().inflate(R.layout.horizontal_divider, myListView, false), null, false);
myListView.addFooterView(getInflater().inflate(R.layout.the_original_footer_view, myListView, false), null, false);
Where the layout file would look like this:
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="?android:attr/dividerVertical" />
This approach can be used to add the divider easily even after the last footer, regardless it is selectable, enabled or anything - it just stays there.
Note the height is 1px rather than 1dp. Though against all recommendations, at least at the device I tested this gives the same divider height as ListView, while 1dp does not.
I've been grappling with this problem throughout the life of my project. I have many lists in my project and most of them have headers. I have been making a separate layout file and adding it to the list using addHeaderView(). The problem is that when the data (ArrayList, in my case) is empty, the header doesn't appear. After hours of searching for a way to reuse the header layout as an empty view, I gave up and decided to replicate the layout in code and assign it as an empty view using setEmptyView(). The problem with this is that a lot of the headers contain clickable views and so I have to double all of my clickable logic for every view that is duplicated. I tried to include the header before but failed, mostly because I still don't quite understand how layouts are inflated, etc.
Finally, I have come up with a solution that I would like to share with the community, in case others are having a similar problem. I don't know if this is the best way to solve this problem and any feedback or suggestions would definitely be welcomed.
here is the code for the layout that contains the list list view:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
>
<ListView
android:id="#+id/lv_my_list"
style="#style/ListStyle"
android:headerDividersEnabled="false"
/>
<include
layout="#layout/my_list_header"
android:id="#+id/my_list_empty"
/>
</LinearLayout>
the style defines the layout width and height among other things.
now I have the layout file that contains the header view
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/my_list_header"
>
<Button
android:id="#+id/my_list_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Click Me"
/>
</LinearLayout>
I know LinearLayout is not very efficient and I am experimenting with using merge or other efficiency measures, but this is the first version that works so I'm going with it for now. Finally the code:
// set up the header
myListView = (ListView)findViewById(R.id.lv_my_list);
View header = View.inflate(this, R.layout.my_list_header, null);
Button b = (Button)header.findViewById(R.id.my_list_button);
b.setOnClickListener(this);
myListView.addHeaderView(header);
// set up the empty view
LinearLayout ll = (LinearLayout)findViewById(R.id.my_list_empty);
b = (Button)ll.findViewById(R.id.my_list_button);
b.setOnClickListener(this);
meLocalListView.setEmptyView(ll);
The reason I wrapped the button in a layout is because I can set each instance of the button to use this as an OnClickListener and then refer to both of them with a single id: R.id.my_list_button in my onClick method. I need to test this a lot more but it seems to work for now. I haven't implemented it on all my lists yet, just the one so it might not work in all situations. If you have any suggestions please let me know because this has been a problem for me for a long time now. One problem might be that if you want to instantiate the button from the ID you would probably have to go through this entire process again to access the correct IDs?
If you want to show the header of a ListView when the list is empty, what you really want to do is to show the list itself rather than a separate empty view. So, the simplest way is to check whether your list is empty and set the list visible if it is not already:
getListView().setVisibility(View.VISIBLE);
You can test this very easily with the following code:
public class TestListActivity extends ListActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_listview);
// create the empty grid item mapping
String[] from = new String[] {};
int[] to = new int[] {};
// prepare the empty list
List<HashMap<String, String>> fillMaps = new ArrayList<HashMap<String, String>>();
// fill in the grid_item layout
SimpleAdapter adapter = new SimpleAdapter(this, fillMaps, R.layout.test_listitem, from, to);
View listHeader =
((LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.inflate(R.layout.test_listheader, null, false);
getListView().addHeaderView(listHeader);
getListView().setAdapter(adapter);
}
}
The list is empty, but the header is visible.
My solution is to create a view with the layout for the header, but set the background to be the same as the list items.
android:background="#android:drawable/list_selector_background"
But my solution does not work if I use 'include' to embed the header layout from another layout file. Do not know why.
I have a ListView. The data behind it is fetched from the Internet, in sets of 10-30 items whenever the user scrolls all the way to the bottom. In order to indicate that it is loading more items, I used addFooterView() to add a simple view that displays a "Loading..." message and a spinner. Now, when I'm out of data (no more data to fetch), I want to hide that message. I tried to do:
loadingView.setVisibility(View.GONE);
Unfortunately, while that does hide the view, it leaves space for it. I.e. I end up with a big blank space where the "Loading" message used to be. How can I go about properly hiding this view?
I can't use removeFooterView() because I may need to show it again, in which case I can't call addFooterView() again because an adapter has already been set on the ListView, and you can't call addHeaderView() / addFooterView() after setting an adapter.
It seems that you are allowed to call addHeaderView() / addFooterView() after setAdapter() as long as you call one of those methods at least once before. That is a rather poor design decision from Google, so I filed an issue. Combine this with removeFooterView() and you have my solution.
+1 for the other two answers I got, they're valid (and arguably more correct) solutions. Mine, however, is the simplest, and I like simplicity, so I'll mark my own answer as accepted.
Try setting the footer's height to 0px or 1px before hiding it. Alternatively, wrap the footer view in a wrap_content height FrameLayout and hide/show the inner view, leaving the FrameLayout visible; the height should wrap properly then.
in my case addFooterView / removeFooterView() cause some artefacts.
And I found other solution. I used FrameLayout as FooterView. And when I want to add Footer I called mFrameFooter.addView(myFooter); and mFrameFooter.removeAllViews(); for remove.
FrameLayout frameLayout = new FrameLayout(this);
listView.addFooterView(frameLayout);
......
......
//For adding footerView
frameLayout.removeAllViews();
frameLayout.addView(mFooterView);
//For hide FooterView
frameLayout.removeAllViews();
The Droid-Fu library has a class designed for having a loading footer show and hide: ListAdapterWithProgress.
Works well in my project:
1.Add footer view first
mListView.addFooterView(mFooterView);
mListView.setAdapter(mAdapter);
2.Set visibility
mFooterView.setVisibility(View.GONE);
mFooterView.setPadding(0, 0, 0, 0);
3.Set invisibility
mFooterView.setVisibility(View.GONE);
mFooterView.setPadding(0, -1*mFooterView.getHeight(), 0, 0);
As #YoniSamlan pointed out, it can be achieved in a simple way. You have to specify
android:layout_height="wrap_content"
in the ViewGroup that contains the "Load More" button. Doesn't have to be FrameLayout, see below for a simple -working- example that uses a LinearLayout.
Both images show a screen that is scrolled all the way to the bottom. First one has a visible footer that wraps around the "load more" button. Second images shows that the footer collapses if you set button's visibility to GONE.
You can show again the footer (inside some callback) by changing the visibility:
loadMore.setVisibility(View.VISIBLE); // set to View.GONE to hide it again
Perform listView initialization as usual
// Find View, set empty View if needed
mListView = (ListView) root.findViewById(R.id.reservations_search_results);
mListView.setEmptyView(root.findViewById(R.id.search_reservations_list_empty));
// Instantiate footerView using a LayoutInflater and add to listView
footerView = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE))
.inflate(R.layout.load_more_footer_view, null, false);
// additionally, find the "load more button" inside the footer view
loadMore = footerView.findViewById(R.id.load_more);
loadMore.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
fetchData();
}
});
// add footer view to the list
mListView.addFooterView(footerView);
// after we're done setting the footerView, we can setAdapter
adapter = new ReservationsArrayAdapter(getActivity(), R.layout.list_item_reservations_search, reservationsList);
mListView.setAdapter(adapter);
load_more_footer_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="#+id/load_more"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="9dp"
android:gravity="center"
android:layout_gravity="center"
android:background="#drawable/transparent_white_border"
android:textColor="#android:color/white"
android:text="#string/LOAD_MORE"/>
It should be a bug of Android.
You don't need to remove or add footer view dynamically. You just need to create an unspecified height parent Layout (either inflate it from an xml file or create it programatically) and then add your view which you want to hide or show into it.
And you can set the view, but NOT the parent Layout, to VISIBLE or GONE or something else now. It works for me.
Used
footer.removeAllViews();
This does not remove footer but flushes children.
You again have to repopulate children. Can check by
footer.getChildCount()<2
I also found that is possible call onContentChanged() (if you use ListActivity) to force recreate ListView if I need add HeaderView to them after setAdapter() call, but it is very ugly hack.
I have created a ListView that handles this. It also has an option to use the EndlessScrollListener I've created to handle endless listviews, that loads data until there's no more data to load.
You can see these classes here:
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/helper/ListViewWithLoadingIndicatorHelper.java
- Helper to make it possible to use the features without extending from SimpleListViewWithLoadingIndicator.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/listener/EndlessScrollListener.java
- Listener that starts loading data when the user is about to reach the bottom of the ListView.
https://github.com/CyberEagle/OpenProjects/blob/master/android-projects/widgets/src/main/java/br/com/cybereagle/androidwidgets/view/SimpleListViewWithLoadingIndicator.java
- The EndlessListView. You can use this class directly or extend from it.
I have small hack way to resolve this problem for everywhere.
Put listview and footer view (just sub layout) in parent layout like LinnearLayout, remember that footerview below listview.
Controller this footer view gone and visibility like nomal view. And done!
first I am adding my footer to the listview,like this
listView.addFooterView(Utils.b);
Then on button click , I remove the view,
listView.removeFooterView(Utils.b);
I am adding the footer everytime when I am hitting the async,and theus the're no duplicate entry.I could aslo check for the count and so it like this,
if(listView.getFooterViewsCount() > 0){//if footer is added already do something}
When you want to remove the footer in ListView just call
listView.addFooterView(new View(yourContext));
It will add a dummy empty view which will not reserve any space