This question already exists:
Closed 10 years ago.
Possible Duplicate:
Dynamic ListView with dynamic GUI
I have to fetch the data from sqlite database. Database will contain countryname , card name and card id and status, I have to display countryname then list of cards dynamically for example if USA has four card then in list view it will display USA then all then four cards then UK then cards of UK and so on it should be implemented with check box for each item and if user clicks on suppose a card which is displaying in USA category then I have to update its status in database for example if a card in USA is checked then in database we have to update "yes" similar function for other cards .. So how to achieve this?
I think you are asking about dynamic list view with check box in its list's cell.
First of all you need an adapter for filling your list...Like This..
ListView listView=(ListView)findViewById(R.id.listView);
listView.setAdapter(new MyListAdapter (<Your ArrayList>));
Now when ever we use check box or editbox in list.Then when we scroll list it call its getview method every time. So we need to manage value or status of components. Here to manage status of checkbox I had used Arraylist of boolean type.
Make an XMl file for your list's cell. Put listeners for list's cell components inside getview method.
public class MyListAdapter extends BaseAdapter
{
private ArrayList<HashMap<String, String>> data;
private ArrayList<Boolean> checks=new ArrayList<Boolean>();
public MyListAdapter ( ArrayList<HashMap<String, String>> d)
{
data=d;
inflater = (LayoutInflater)baseActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (int i = 0; i < d.size(); i++)
{
checks.add(i, false);//as first no check box is checked. So fill it with false value
}
}
public int getCount()
{
return data.size();
}
public Object getItem(int position)
{
return position;
}
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
View vi=convertView;
if(convertView==null)
vi = inflater.inflate(R.layout.list_row, null);
name = (TextView)vi.findViewById(R.id.name);// name
checkBox=(CheckBox)vi.findViewById(R.id.check_box);
email_id = (TextView)vi.findViewById(R.id.e_mail_id); // email ID
mobile_no = (TextView)vi.findViewById(R.id.mobile_no); // mobile
checkBox.setTag(Integer.valueOf(position));
checkBox.setOnClickListener(this);
checkBox.setChecked(checks.get(position));
name.setText(<set name form any data source>);
email_id.setText(<set emailid form any data source>);
mobile_no.setText(<set mobile form any data source>);
return vi;
}
}
Hope this should help you.
Cheers...
So in your case you have to implement Multiselect Expandable listview..
Okay, so what you need first of all is to create an xml file for each row in your ListView to be inflated.
So you have some textview for your country name etc. But now to the tricky part to adding dynamically those "cards" depending on the amount:
In your xml for each row, add a LinearLayout where you want the specified cards to appear, and then in the getView() method inside the ArrayAdapter (the custom one you've created) you need to do something like this:
cardsLayout = (LinearLayout) v.findViewById(R.id.cards_layout);
cardsLayout.removeAllViews(); // rows are reused in listview, so this prevent them to be duplicated
ImageView image;
for(int i = 0; i < country.getCards(); i++){ // I assume you're using array for cards of each country
image = new ImageView(ActivityName.this);
layoutParams = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 1.0f);
image.setLayoutParams(layoutParams);
image.setImageBitmap(someBitmapImage);
imageLayout.addView(image);
{
I assume you know how to create your own adapter. And this snippet of code just dynamically creates ImageViews and sets there weight to 1, so they will be equal in size through the row. If you need checkboxes or whatever, you can use the same approach by changing the ImageView to something else.
Related
I have searched these forums for nearly 3 hours and seen several similar questions but none of the answers works for me.
I have a single Activity, with several card views. One of the card views has a Spinner with string values and a very simple ListView. The user selects a value from the Spinner, between 1 and 12. The ListView should then display a number of strings equal to the value selected, based on the position in the spinner list. For example, user selects 2nd item in spinner list and the ListView displays 2 strings. I have a custom adapter on the listview. The ListView itself initially displays a single row, which is correct. However, after the user selects a value from the spinner, the listview is not displaying the extra rows, it still only displays one row. The data for the ListView comes from an ArrayList. I have checked the data model of the adapter after the user selects a value and it has the correct number of entries, as does the ArrayList itself, yet no matter what I try the ListView itself still only display the first row. I have tried NotifyDataSetChanged and every variation of Invalidate without success.
The various code samples:
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (parent == spDoseFrequency){
Toast.makeText(this,String.valueOf(position),Toast.LENGTH_SHORT).show();
rebuildReminderTimesList(position + 1);
}
}
private void rebuildReminderTimesList(int numberOfTimes){
Toast.makeText(this,"yup",Toast.LENGTH_SHORT).show();
//reset selected item to position 1
myApp.iSelectedReminderTimeIndex = 0;
//clear array and list, then rebuild with hourly timeslots
iarrTimes = new int[numberOfTimes][2];
liReminderTimes.clear();
int startTime = 8;
for (int i = 0; i < numberOfTimes; i++){
iarrTimes[i][0] = startTime + i;
iarrTimes[i][1] = 0;
liReminderTimes.add(pad(startTime + i) + ":00");
}
//refresh the listview
myAdapter.notifyDataSetChanged();
}
public class ReminderListAdapter extends ArrayAdapter {
List<String> liTimes;
Context ctx;
LayoutInflater inf;
public ReminderListAdapter(Context ctx, List<String> liTimes) {
super(ctx, R.layout.reminder_time_listview, liTimes);
this.liTimes = liTimes;
this.ctx = ctx;
inf = LayoutInflater.from(ctx);
}
public void setLiTimes(List<String> liTimes){
this.liTimes = liTimes;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder viewHolder;
if (view == null){
view = inf.inflate(R.layout.reminder_time_listview,parent,false);
viewHolder = new ViewHolder();
viewHolder.sTime = (TextView) view.findViewById(R.id.tvTime);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.sTime.setText(liTimes.get(position));
return view;
}
private static class ViewHolder{
TextView sTime;
}
}
Any help would be appreciated as this is driving me crazy.
Quick update to this question: I have just tested supplying the initial list more than one value but even then it only displays the first item. Is there perhaps a problem with using ListView inside a CardView object? All my other cards work fine, only the ListView one fails to display properly.
Also, I have tried amending the code so that instead of changing the number of elements in the list, it just changes the text in the string of the first element and this works fine. So the notifyDataSetChanged appears to be working, but it just won't display more than one item. A quick check of the Adapter.getCount() method also gives the correct number of elements back, but won't display them.
A lot of folks forget to do the notifyDataSetChanged() call, but you've got that. Are you using a custom adapter? If so, that makes this sound like an issue with one or more of the adapter's methods. In particular, it sounds like getCount or getView might not be returning what they should be. That could either be because of a flawed logic issue, the underlying data source isn't being updated correctly, or the underlying data source isn't the same object you think it is. Without seeing your adapter though, it's hard to diagnose.
I found the problem. I had several CardView objects inside a LinearLayout, which itself was inside a ScrollView. As soon as I removed the ScrollView, the List inside the Card displayed properly. This has unfortunately created another problem in that I can no longer scroll the page to see later cards, which I have not yet found a solution for, but that is a different topic.
I am previously working on PHP and js , and recently I am working on android listview
However, I encountered a problem in creating a custom adapter for listview
public View getView(int arg0, View arg1, ViewGroup arg2) {
// TODO
if (arg1 == null) {
arg1 = myInflater.inflate(R.layout.grid, arg2, false);
}
TextView name = (TextView) arg1.findViewById(R.id.text1);
TextView desc = (TextView) arg1.findViewById(R.id.text2);
ImageView image = (ImageView) arg1.findViewById(R.id.image1);
if (arg0 < images.length) {
image.setImageResource(images[arg0]);
}
name.setText(names[arg0]);
desc.setText(description[arg0]);
return arg1;
}
The problem is I have 3 array of content to pass to the listview grid, for the first two array, there are 10 element and the last one have 5 only. So , it is out of boundries for the last one. I added a condition to check whether it exceed 5 , but args0 seems not increased according to the row?
if (arg0 < images.length) {
image.setImageResource(images[arg0]);
}
The first five row and some other rows also has image setted, why is that and how to fix this? thanks
In General
since you want to display Data to your list, plx create an Object that represents Data.
like you named in your comment above:
public class ListEntry {
String name = "";
String gender = "";//use enum here perhaps -.-
String photoUrl = null; //or use byte[] photo or whatever you've stored in your array before
// write getters/setters for your members
}
then you can use one array ListEntry[] (or List<ListEntry>) to access all data. this way you get around your indexOutOfBoundsException.
lookup any listadapter tutorials online, e.g. the one from Vogella
Why do more than the first five entries have an image?
Androids Adapters for Listviews implement a caching mechanism to reduce the inflating (performance/memory cost intensive) of new list-items (e.g. rows) to a minimum. therefore there are only as many rows (or little more) created as displayed by the list. since you only set images if there are any, but never remove already set images from rows, you result in some rows that replay images they shouldn't. these rows are cached from previously outscrolling rows.
therefore add something like
if (listItem.photo != null) {
image.setImageResource(images[arg0]);
} else {
image.setVisibility(View.GONE);
}
as reference for listviews and their caching mechanism see Romain Guy on ListViews
Edit Regarding usage of Listadapter
The getView(..) you posted above is inside your ListAdapter implementation, prefarrably you've extended an ArrayAdapter<T>. if so, your T should now state ListEntry and you have any line of code that states
MyArrayAdapter myAdapter = new MyArrayAdapter() or something like that.
now you have an array or List of ListEntry like List<ListEntry> myCollection = new ArrayList<ListEntry>() or ListEntry[] listEntries = new ListEntry[10] and use
myAdapter.addAll(listEntries);
to get an item of your list inside your getView(..) you can use:
ListEntry currentEntry = getItem(arg0);
and refer the single members of currentEntry to set them ;-)
What about
if (images[arg0] != null) image.setImageResource(images[arg0]);
?
I''m struggling a little with the hierarchy here. I'd like to get references to every ImageButton view with the id delete_img in my listView. The imagebutton is added via the XML in the row layout xml.
Essentially i want to be able to set the visibility of a certain element within every row but i cant figure out how to get that sort of reference. Is there an alternative way of doing this? The method deleteShow() is my attempt to get at it so far but its obviously wrong as i am getting a Null Pointer when trying to set the Visibility.
NotesFragment
public class NotesFragment extends ListFragment {
private CommentsDataSource datasource;
private View v = null;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Cursor theNotes = (Cursor) returnNotes();
String[] projection = { MySQLiteHelper.COLUMN_ID,
MySQLiteHelper.COLUMN_COMMENT,
MySQLiteHelper.COLUMN_COMMENTNAME,
MySQLiteHelper.COLUMN_FOLDERFK };
int[] to = new int[] { R.id.id_txt, R.id.content_txt, R.id.title_text };
#SuppressWarnings("deprecation")
SimpleCursorAdapter sca = new SimpleCursorAdapter(getActivity(),
R.layout.notes_list_layout, theNotes, projection, to);
setListAdapter(sca);
View v = inflater.inflate(R.layout.notesfragment, container, false);
deleteShow();
return v;
}
#Override
public void onListItemClick(ListView parent, View v, int position, long id) {
Intent intentView = new Intent(getActivity().getApplicationContext(),
ViewNote.class);
intentView.putExtra("id", id);
startActivity(intentView);
}
public Cursor returnNotes() {
Cursor theNotesCursor = null;
datasource = new CommentsDataSource(getActivity());
datasource.open();
theNotesCursor = datasource.getAllCommentsAsCursor();
return theNotesCursor;
}
public void deleteShow() {
ImageButton b = (ImageButton) getActivity().findViewById(R.id.delete_img);
b.setVisibility(View.INVISIBLE);
}
public void onPause() {
super.onPause();
datasource.close();
}
}
The hierarchy for dealing with ListView is not that complicated once you understand what's going on. Think of the ListView as the framwork that holds a bunch of child views or Items. Those Items each have child views that consist of the individual elements that make up a row in the ListView. To modify a list Item you either need to (1) change the data backing that item and update your ArrayAdapter or (2) find the individual Item you are trying to modify from within the ListView and then act on the child views for that individual item.
The easiest way to do this is to modify the data in the adapter backing the list and then call notifyDataSetChanged() on your ArrayAdapter to update the ListView. I don't know how your adapter is set up so to give you direct advice is difficult but the general idea is that you want to change the data backing the Item you want to modify, change that data, and then call notifyDataSetChanged() on the ArrayAdapter so that the ListView reflects the changes.
To modify an individual item directly is much more complicated. You cannot do it in one step as your code proposes - finding the individual view by id and then changing its visibility - will not operate accross the entire list as you suspect. findViewById is likley returning null because it is not looking within an indvidual list element but within the whole list - i.e. the outer list structure - for a view that is not there.
To do what you want programatically you need to (1) get a reference to the ListView itself; (2) find the first displayed view within the list by calling getFirstVisiblePosition(); (3) figure out how far down from that first visibile item is the item you want to modify; (4) get that item; (5) modify it
This ends up just being a pain in the ass. Its much easier to modify the data backing the list and update than to find single view.
I have what I consider to be a strange dilemma, although YMMV.
I'm using a layout file that describes each line/row in a ListView (nothing too exotic about that). I have an id assigned to each one, such as:
android:id="#+id/checkBox1"
android:id="#+id/checkBox2"
android:id="#+id/checkBox3"
android:id="#+id/contactLabel" // a TextView
Now this doesn't seem to make sense, as these ids should be unique, so what is the id of the second
row? That is, if "row 1" honors the specified ids of checkbox1, checkbox2, checkbox3, and contactLabel, what would the "row 2" ids be?
I'm curious, but also I need to know because I want to save the values of the checkboxes to a SharedPreferences object.
Who has a clue about how to get around this?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Update
The first thing I need to solve is how to respond to a click on the ListView. This is my current conundrum related to all of this:
ListView doesn't know it's been clicked, or won't tell me
I've added this event handler to my ListActivity:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
String item = (String) getListAdapter().getItem(position);
Toast.makeText(this, item + " selected", Toast.LENGTH_LONG).show();
}
...but it's not getting called. I click on the Contacts that display, but no go.
I also tried it this way:
lv.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(getApplicationContext(),
"Click ListItem Number " + position, Toast.LENGTH_LONG)
.show();
}
});
...still no joy...I put breakpoints on both "Toast" lines, and they never get reached.
I read here:
http://www.vogella.de/articles/AndroidListView/article.html#listsactivity_layout
...that, "In case you need more the just a ListView in your Activity, you can use you own layout for
ListActivity."
...which I do, because I add a header and a footer in addition to the listview.
It goes on to say, "In this case your layout must have an ListView element with the android:id
attribute set to #android:id/list."
So I added this to my layout:
...but it makes no difference one way or the other.
The ID's for the items within the ListView widget are referenced through their parent view when you inflate it in your getView() method.
To elaborate, you would have something like this is you ListView adapter.
public View getView(int position, View convertView, ViewGroup viewGroup) {
if (convertView == null) {
convertView = LayoutInflater.from(ctx).inflate(R.layout.list_view_item, null);
}
Nown, a new view instance exists as the convertView. You can access your widgets using covertView.findViewById(R.id.checkBox1), convertView.findViewById(R.id.checkBox2), etc.
Each of these views is a child of your ListView. You can reference each individual view from your ListView using the getChildCount() and getChildAt() methods from the ListView object.
However, since it is recommended to use the convertView view to recycle views, in that case you will only have reference to the views on screen at a time.
Also, with regards to the SharedPreferences, all the views in your ListView are populated by an Adapter subclass which would be the actual object that puts the values in the Checkbox and TextView widgets. This Adapter has a dataset that you provide it. Why not reference the values from the dataset directly, instead of trying to find them from the list items which are populated from the dataset in any case ? You can write to the dataset from the ListView when someone clicks a CheckBox so you have an easy ordered reference to all the items in the ListView.
UPDATE: Added dummy source code
OK. Let's start with a hypothical list. We want to display say five items on the list. For simplicity, I'll assume each has a TextView and a Checkbox. So my container class is:
class Item {
String textView;
boolean checked;
}
Now in my Activity where I want to display this list, I put an ArrayList of items (you can use just about any datastructure) as a class variable. Then I get the ListView reference and assign it an adapter.
class MyActivity extends Activity {
ArrayList<Item> listItems;
.....
onCreate(Bundle icicle) {
.....
ListView listView = (ListView) findViewById(R.id.listView1); // this will be you list view
MyAdapter listAdapter = new MyAdapter();
listView.setAdapter(listAdapter);
....
// Rest of your Activity
....
MyAdapter extends BaseAdapter {
int getItemCount() {
return listItems.size();
}
Item getItem(int position) {
return listItems.get(position);
}
View getView(int position, View convertView, ViewGroup parent) {
// Here's the important part
Item currentItem = listItems.getItem(position); // Since the array is a class variable, you can do either get or getItem
..... // do the standard individual item inflating ....
checkbox = convertView.findViewById(R.id.checkbox);
checkbox.OnItemSelectedListener( new OnItemSelectedListener() { // or whatever listener there should be... I didn't check
... // do whatever...
currentItem.setChecked(true);
}
When you want to retrieve what items were clicked, just iterate through the Item class and find which ones are true or you perform whatever action you want within the Listener since you have a reference identifying individual members of the ListView dataset (here listItems ArrayList).
Apologies for any errors. Didn't do any checking.
I have a ListView in a ListActivity populated by a database table. Each row of the ListView is a RelativeLayout with three TextViews named rowid, date, and name in that order. I am able to select individual rows programmatically using the .setSelection(int position) method of the ListView.
Here is what I'm trying to do: When I push a button on the interface, get the rowid from the currently selected list row, and perform a db query using the rowid. I can't figure out how to get the rowid from the list itself. rowid may not be the same as the ID or position on the list as it is the rowid in the database.
I suspect this will require working with an adapter, but I've been trying/searching the web for a week and haven't been able to figure this out. Thanks for the help anyone can provide.
You know the list position of the currently selected item, you have a button outside the ListView that should trigger some action on that item, and you're not just making the ListView rows (or some child view within each row) clickable. Right?
You can get information from the list's adapter. getItem(int position) returns the object that is represented by the list item at position, so you can retrieve the information you need directly if it's stored in the object. getView(int position) returns the view for the row, allowing you to use findViewById(int id) to retrieve your TextView.
If you don't already have the adapter, you can get it from the ListView using getAdapter().
// ListView myListView = the ListView in question
// int selectedRow = the currently selected row in the ListView
// Each row in the ListView is backed by an object of type MyCustomDataClass
int dbRowId;
Adapter adapter = myListView.getAdapter();
MyCustomDataClass data = (MyCustomDataClass) adapter.getItem(selectedRow);
dbRowId = data.getDatabaseRowId();
// OR
dbRowId = data.rowId;
// OR whatever method the object has for getting the ID.
// OR
View listViewRow = adapter.getView(selectedRow);
TextView dbRowView = (TextView) listViewRow.findViewById(R.id.rowid);
String dbRowAsString = dbRowView.getText().toString();
dbRowId = Integer.parseInt(dbRowAsString);
You might also consider whether it would be more natural for the user to just tap the ListView row, rather than selecting the row and then pressing another button. Reno's answer might work better.
I ended up using the last method with the following code.
int dbRowId;
Adapter adapter = myListView.getAdapter();
View listViewRow = adapter.getView(selectedRow);
TextView dbRowView = (TextView) listViewRow.findViewById(R.id.rowid, null, null);
String dbRowAsString = dbRowView.getText().toString();
dbRowId = Integer.parseInt(dbRowAsString);
The only change I had to make was adding null, null to the parameters in .findViewByID
TextView dbRowView = (TextView) listViewRow.findViewById(R.id.rowid, null, null);