I have a ListView. My point is, after selecting one of the elements on that view, I want to show a menubar Right below that element. What Im tryin to do is pretty much something like the app Any.Do.
Heres a picture of what I intent to do.
How can I do it ? Id rather not use GreenDroid.
To fill the array I use this code snniped:
TaskBoard.class
setListAdapter(new SimpleAdapter(this, array_tasks, R.layout.dash_tasks,
new String[] { "task", "project" , "date"}, new int[] { R.id.dashtask,
R.id.taskproject, R.id.final_date}));
R.layout.dash_tasks is the view im showing to the user.
This is a code snipped to call the menubar I want to show:
dash_tasks.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/menu_bar"
style="#style/page"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:baselineAligned="true"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone">
<include layout="#layout/menubar"></include>
</RelativeLayout>
EDITED:
Ive figured out how can I find the position Im clicking. The problem is, its a relative position. I mean, if I click in the 2nd item on the array, the menubar will apear in the 2nd position every 7 items (thts the number of items that fill a page). Im guessin this position is acording to the screen and not the ListView..
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
View x= parent.getChildAt(position);
View a= x.findViewById(R.id.mini_menu_bar);
a.setVisibility(View.VISIBLE);
I hope this helps you understand the problem
What you do is that in your list items layout xml you also have the menu (wrapped in a vertical linearlayout) but set it to android:visibility="gone". When the item is pressed you just show that hidden menu view.
It depends on how many view you wanna display at the same time. If there is only one, and you want it to sorta stand out without much of programming I would go for QuickAction. If you have multiple views at the same time expanded, my suggestion would be to use RelativeLayout and BeLow. What you need to do is fairly simple. Make sure there is a way for your to find the item below the tapped on, add the new view below the tapped item and add the change the item bellow tapped item to be below the new item. Something like the code below
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.BELOW, view.getId());
Another solution would be to use LinearLayout but requires a loop to find the index of the item tapped on and adding the new item below it:
parent.addView(subItem, index+1);
Related
I have a dialog containing a list of notes, and I want to make the user scroll to the end of the list to find the "OK" button to close the dialog.
The dialog contains a ListView and I add the button to the list using the footer row.
View footerView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).
inflate(R.layout.list_footer_button, null, false);
listView.addFooterView(footerView);
When i turn on talk back and open this dialog, it reads the wrong count for the ListView as it is including the footer row. For example if my list has 3 items, talkback reads "Item 1 of 4".
ISSUE
How can i get talkBack to read the correct number of items ignoring the footer row?
OR
If i can't change this, how else do i create a dialog where the user has to scroll to the end of the list before seeing the button to dismiss the dialog.
I have resolved this issue by adding the button to the View for the last row rather than adding a footer.
My layout for each row contains a textView and a button, and the button is set to be "invisible".
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout android:id="#+id/notes_details"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- other views here but removed for this post -->
<TextView
android:id="#+id/notes_text"
style="#style/notes_text_style"/>
</LinearLayout>
<Button
android:id="#+id/notes_button"
style="#style/notes_button"
android:layout_below="#id/notes_details"
android:layout_centerHorizontal="true"
android:visibility="invisible" />
</RelativeLayout>
In the adapter for the list, I show/hide the button based on whether the row is the last row
boolean isLastRow = iPosition == getCount()-1;
okButton.setVisibility(isLastRow ? View.VISIBLE : View.GONE);
Accessibility changes needed
There was additional work to make this all work for Accessibility purposes. If no changes were made to support accessibility, only the rows are navigatable. Therefore the last row will select the notes text and OK button as one selectable item and you would not be able to navigate to the button using a keyboard/trackerball.
Firstly you have to set the list so that items in a row are focusable
listView.setItemsCanFocus(true);
Secondly, in code in the adapter, i set the focusable/nextFocusXXX on the notes textView and button accordingly
// when in accessibility mode, we want to set the navigation order for
// the list item's text and the OK button
boolean isLastRow = iPosition == getCount()-1;
if (isAccessibilityEnabled) {
notesTextView.setFocusable(true);
okButton.setFocusable(true);
rowView.setNextFocusForwardId(isLastRow ? R.id.notes_button: R.id.notes_text);
rowView.setNextFocusDownId(isLastRow ? R.id.notes_button: R.id.notes_text);
okButton.setNextFocusUpId(R.id.notes_text);
}
I had another usecase with this issue.
Requirements
I have a screen containing a list of items and I have a Floating Action Button (FAB) on the bottom right of the screen allowing the user to add items to the list. On the right hand side of each row is a button presenting options for that row item.
I need the FAB to not overlay the last row in the list, otherwise I am never able to click on the right hand button of the last row as it will be under the FAB.
Solutions and their issues
I tried several solutions. One was adding an extra count to the list items and when getting the view for this last row I return an empty view, the height of the FAB. I also tried adding this empty padding row as the list footer. In both cases TalkBack reads one too many for the number of items in the list.
I can not use the solution I have posted for this question for my list of notes. I need the FAB to always be in the bottom right hand side of the screen (not just after the last row) even if there are only 1 or 2 items in the list or over a screen-full of items.
Working Solution
Adding the last 4 attributes in the code below to my list definition in the xml layout seems to solve the issue. There are no additional items added to the list so TalkBack reads the list count correctly and the following ensures the FAB in the bottom left of the screen never overlaps the last row.
<!-- Instead of adding a footer row to stay clear of the FAB,
add a padding to the listView and set clipToPadding to false
android:overScrollFooter is set to transparent to avoid having a divider
for the last item so there is just white space below the last item -->
<ListView
style="#style/my_list_style"
android:layout_alignParentTop="true"
android:paddingBottom="82dp"
android:clipToPadding="false"
android:footerDividersEnabled="false"
android:overScrollFooter="#android:color/transparent"/>
I have a messaging application. I can get messages and list from mysql database via json. When i get messages (for example last 10 messages and newer is at the bottom) first record is shown at the top, so user have to scroll down to see last message. I want to focus to last message and when user scrolls to top, I want to put there a Load Previous Messages button. I found Load More buttoni but its at the bottom of page. How can I do that?
My codes are these:
// Hashmap for ListView
categoryList = new ArrayList<HashMap<String, String>>();
// get listview
ListView lv = getListView();
Button btnLoadMore = new Button(this);
btnLoadMore.setText("Load older messages");
FrameLayout footerLayout = (FrameLayout) getLayoutInflater().inflate(R.layout.listfooter,null);
lv.addFooterView(btnLoadMore);
As I said in comments, simply change addFooterView() to addHeaderView() method which give you the Load More at the top of the list. This method use three parameters (at least one: View from reference) which are:
View v: the Load More view to add at the top
Object data: data associated to the view
boolean isSelectable: value to make the view selectable or not
Using these three params instead of only the view may allow you to prevent the color state on it by using android:listSelector attribute. Indeed, sometimes you want to prevent a background state on a header/footer view. That being says, the method might be:
lv.addHeaderView(headerLayout, null, false); // this isn't clickable now
Note: now, you can call the view variable headerLayout instead of footerLayout ;)
As I understand your requirements, HeaderView should have the Load More Button into it, to avoid to create it dynamically, as follows:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Load More"
android:onClick="loadMoreDatas" />
</LinearLayout>
Now, you have the right Button at the top of your list and you can add a method to handle the click event into the Activity (refer to android:onClick attribute) as:
public void loadMoreDatas(View v) {
// load more messages
}
Finally, to focus to a specific item, in your case the last item in the list (at the bottom), you should use setSelection(int position) which go to the index selected in its parameter. Then, after setting the Adapter, call this on the ListView as:
// set the adapter
setListAdapter(adapter);
// go to selection (last item)
lv.setSelection(adapter.getCount() - 1);
The getCount() method use (normally) your ArrayList size. Then, you have to prevent an IndexOutBoundsException because your array begins with position 0 and not 1. So, the last position is "All Items less First Position (0)".
However, the perfect method to begin at the bottom of the list is setStackFromBottom():
When stack from bottom is set to true, the list fills its content starting from the bottom of the view.
Then, it might be better to have:
// start from bottom
lv.setStackFromBottom(true);
This should do the trick and I hope this will be usefull.
I have set up a listview with one edit text widget. The listview is populated with a string file and I wanted to make changes on each item by clicking on the item. code builds and the listview shows as planned. When the item is clicked it gets focus but then looses focus after the keyboard pops up without any entries.
els_edit_tiles.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="match_parent"
android:orientation="vertical" >
<EditText
android:id="#+id/listText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10" >
</EditText>
This is the onclicklistener, I assume the changes will need to be here and did not post the adapter code.
private class ListClickhandler implements OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> Adapter, View view, int position, long arg3) {
TextView listText = (TextView) view.findViewById(R.id.listText);
String text = listText.getText().toString();
// Toast.makeText(context, text + " " + position, Toast.LENGTH_LONG).show();
}
}
I assume you mean the item loses its highlight, rather than focus. Focus goes wherever you tap and is there whether the highlight shows or not. So, I'll explain some options for the disappearing highlight.
For better or worse, they've designed it this way. You have a few choices. One is to subclass ListView and try to figure out how to add a sticky highlight. (I tried that and got it to work, but abandoned it for another reason - I wanted to put the list in a ScrollView, which doesn't work with a ListView).
Another choice: use a spinner (aka a drop down list). This doesn't exactly do what you're asking but it does show you which item was selected -- it is the one that shows when the spinner collapses.
Yet a third choice (I implemented this) - Use a LinearLayout and add each item as a child of the layout. You have to implement your own selection and highlighting, but that takes relatively little code (I can help if you need). This effectively gives you a list that shows all items - it doesn't scroll. You can include it in a ScrollView along with other elements to scroll the whole collection together.
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