I populate a ListView with a SimpleAdapter. Immediately after doing so, I have a function that tries to loop over the child views to set their background color programmatically. The problem is that the ListView may not have any children immediately after listView.setAdapter() is called. I don't know which callback to stick my function on.
// A HashMap to store the values for the ListView rows
List<HashMap<String, String>> aList = new ArrayList<HashMap<String, String>>();
for (int i = 0; i < steps.length; i++) {
HashMap<String, String> hm = new HashMap<String, String>();
hm.put("txt", steps[i]);
hm.put("icon", Integer.toString(step_images[i]));
aList.add(hm);
}
// Keys used in Hashmap
String[] from = {"icon", "txt"};
// Ids of views in layout
int[] to = {R.id.icon, R.id.txt};
// Instantiating an adapter to store each items
// R.layout.listview_steps defines the layout of each item
SimpleAdapter adapter = new SimpleAdapter(getBaseContext(), aList, R.layout.listview_steps, from, to);
// Setting the adapter to the listView
listView.setAdapter(adapter);
//fixme after the listView adapter is set, the listView may not have any children immediately.
//I'm not sure which callback to use to properly call refreshStepsListView().. So I'm hacking it and I just wait 250ms
(new Handler())
.postDelayed(
new Runnable() {
public void run() {
refreshStepsListView();
}
}, 250);
Don't need a callback. Do it live
public class ColoredAdapter extends SimpleAdapter {
ColoredAdapter(...) {
super(context, list, reslayout, from, to);
}
getView(...) {
// set colors here
}
Or you could create a list of a Step class rather than a Hashmap, plus use an ArrayAdapter<Step>
From what I understand. You just want to change the background of each item inside the listview.
First, define you class item for Adapter.
public class Item {
private String mTitle;
private String mBackgroundColor;
public void setTittle(String title){
this.mTitle = title;
}
public void setBackgroundColor(int color){
this.mBackgroundColor= color;
}
public void getTitle (){
return this.mTitle;
}
public void getBackgroundColor(){
return this.mBackgroundColor;
}
}
Then use ArrayAdapter instead of SimpleAdapter
See this sample Here
Make sure inside getView() of your ArrayAdapter, you have set item background color from item.getBackgroundColor()
itemView.setBackgroundResource(item.getBackgroundColor());
When you want to change the background color, you can just set new color to which item item.setBackgroundColor(newColor) and call adapter.notifyDataSetChanged() to refresh the adapter.
Do your background coloring work inside your adapter. You can simply extend SimpleAdapter and get access to the views as they are created. In the adapter is where you are expected to do any view manipulation logic.
Iterating over the children is not advised and will result in bugs, unmaintainable code, and bad performance.
public class ColorAdapter extends SimpleAdapter {
public ColorAdapter(Context context,
List<? extends Map<String, ?>> data,
int resource,
String[] from,
int[] to) {
super(context, data, resource, from, to);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
view.setBackgroundColor(Color.RED);
return view;
}
}
Related
I use SimpleAdapter with custom row layout for Listview
SimpleAdapter adapter = new SimpleAdapter(ListOrder.this, oslist,
R.layout.list_v,
new String[] { "name","time","status" },
new int[] { R.id.tablename, R.id.timeorder, R.id.status});
list.setAdapter(adapter);
After , i reload data of Listview and i want remove old data.
I don't know how i can't remove old data before add new data to Listview.
I used
adapter.remove(adapter.getItem(i));
but have error
The method remove(Object) is undefined for the type SimpleAdapter
Please help me !
Thank you!
You can simply set
listview.setAdapter(null);
Just modify the underlying List and inform the adapter about it. Like that:
oslist.clear();
adapter.notifyDataSetChanged();
if you only want to remove one or two:
oslist.remove(index);
adapter.notifyDataSetChanged();
SimpleAdapter does not have remove method so you should extend it to your own adapter and add remove method. E.g.:
private class Adapter extends SimpleAdapter {
private List<? extends Map<String, ?>> data;
public Adapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) {
super(context, data, resource, from, to);
this.data = data;
//... place some initializing code here
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//place your code for getView here.
return super.getView(position, convertView, parent);
}
public void remove(int position) {
if (position >= 0 && data.size() < position && data.get(position) != null) {
data.remove(position);
this.notifyDataSetChanged();
}
}
}
But if you have array of data that is updated (removed || added) regularly, consider using ArrayAdapter (even without your own extension) that has remove() and other methods (example: http://www.vogella.com/tutorials/AndroidListView/article.html#adapterown ).
Hope it helps :)
Is it possible to inflate Textview from #android:id/text1 ?
I don't want to create own layout, I would like just get a little modified text.
Here is my code:
First, I created variable to store data
private List<HashMap<String, String>> dataCities = new ArrayList<HashMap<String, String>>();
//Hashmap with keys and values
//id - 0
//name - default
Second, I created custom adapter in onCreate
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
Spinner citiesSpinner = (Spinner) findViewById(R.id.city_sp);
citiesAdapter = new CustomArrayAdapter(this, R.layout.sherlock_spinner_item, dataCities);
citiesSpinner.setAdapter(citiesAdapter);
}
Third, I created my listener. It works but after calling notifyDataSetChanged nothing happens. Why?
#Override
public void onRequestJsonResponded(RequestType type, JSONArray array) {
//my enum type
switch (type) {
case cities:
//returns hashmap in arraylist, ArrayList<HashMap<String,String>> ...
dataCities = parseJSonArray(array);
Log.d(TAG, "End of parsing");
citiesAdapter.notifyDataSetChanged();
break;
case mark:
//...
break;
case model:
break;
}
}
Here is my custom arrayadapter
private class CustomArrayAdapter extends ArrayAdapter<HashMap<String, String>> {
public CustomArrayAdapter(Context context, int textViewResourceId, List<HashMap<String, String>> objects) {
super(context, textViewResourceId, objects);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
View v = layoutInflater.inflate(R.layout.sherlock_spinner_item, null);
TextView tv = (TextView)v.findViewById(android.R.id.text1);
tv.setText(getItem(position).get("name"));
return v;
}
}
Could someone tell me why do I get blank spinner data? (Spinner is empty). And how I get modified text without creating new layout? I would like just to use sherlock spinner item layout. Please help.
Simple, the list in your adapter is not the same as the list that you retrieved:
//In here you update your activity's list to the returned values
dataCities = parseJSonArray(array);
Log.d(TAG, "End of parsing");
//But the adapter is using the original value of dataCities (new ArrayList<HashMap<String, String>>() )
citiesAdapter.notifyDataSetChanged();
Since your adapter depends on ArrayAdapter, the easiest solution might be to just create a new adapter when the data is received.
I want to set the background color depending on the data for the row of the ListView. I implemented a ListActivity but I dont know how to get notified the load is completed so that I can access the rows of the ListView.
public class RouteList extends ListActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.route_list);
CommuteDb db = new CommuteDb(this);
Cursor cursor = db.getRouteList();
ListAdapter adapter = new SimpleCursorAdapter(this, // Context.
R.layout.route_row, //row template
cursor, // Pass in the cursor to bind to.
new String[] { BaseColumns._ID, "Name" }, //Columns from table
new int[] { R.id.id, R.id.name }, //View to display data
0); //FLAG_REGISTER_CONTENT_OBSERVER
setListAdapter(adapter);
ListView listView = (ListView) findViewById(android.R.id.list);
Log.i("RouteList","listView.getChildCount()=" + listView.getChildCount()); //returns always 0
//Loop will not execute because no row yet
for (int i=0; i < listView.getChildCount(); i++) {
View rowView = listView.getChildAt(i);
Log.i("RouteList",rowView.getClass().getName());
rowView.setBackgroundColor(0x88ff0000);
}
If I execute this loop later (for instance on user's request) I am able to get each row and assign the color I want. However I need to do this automatically after data are loaded in ListView.
Thanx for the hint guys.
It works after modifying the code this way:
I added a custom adapter (RouteAdapter) derived from SimpleCursorAdapter:
private class RouteAdapter extends SimpleCursorAdapter {
public RouteAdapter(Context context, int layout, Cursor c,
String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//Let the default getView do the job (inflate the row layout and set views to their values from database
View view = super.getView(position, convertView, parent);
//Get the resulting view (route_row) and do custom loading
TextView isOffer = (TextView) view.findViewById(R.id.isOffer);
if (isOffer.getText().equals("0"))
view.setBackgroundColor(0x8800ff00); //seek=green
else
view.setBackgroundColor(0x88ff0000); //offer=red
return view;
}
}
Then in my original code I just replaced SimpleCursorAdapter by RouteAdapter:
ListAdapter adapter = new RouteAdapter(this, // Context.
I want to add gradient for each row. What I've tried to do is on listview label put this attribut, android:background="#drawable/mygradient". But All I get is this gradient displayed all along the list. What I want is this gradient to be displayed for each item.
Thanks in advance
You can provide a custom adapter for your list view and set the gradient as the background in its getView method. Something like this:
public class MyAdapter extends BaseAdapter {
List<MyDataType> myData;
Context context;
public MyAdaptor(Context ctx, List<MyDataType> myData) {
this.myData = myData;
this.context = ctx;
}
public int getCount() { return myData.size() };
public Object getItem(int pos) { return myData.get(pos); }
public long getItemId(int pos) { return 0L; }
public View getView(int pos, View convertView, ViewGroup parent) {
//this is where you can customise the display of an individual
//list item by setting its background, etc, etc.
...
//and return the view for the list item at the end
return <List item view>;
}
}
Then you can set this adapter as the adapter for your list:
ListView myList = <initialise it here>
myList.setAdapter(new MyAdapter(getContext(), listData);
Now whenever a list item needs to be displayed, getView method will be called, where you'll perform all the necessary display customisation, including setting the background.
Instead of setting background property set Selector of list, as below:
android:listSelector="#drawable/list_selector_background"
I'd like to map an Array of "complex" data to a ListView. In a very simplified form my data model would look like something like this:
class ListPlacesValues {
String idObject;
String name;
String city;
String country;
ArrayList<String> classification;
double distance_quantity;
DistanceUnit distance_unit;
[...more stuff ...]
}
I know that I can convert my complex data into a HashList and then just use a SimpleAdapter:
SimpleAdapter mAdapter = new SimpleAdapter(
this,
hashList,
R.layout.places_listitem,
new String[] { "name", "city", "country"},
new int[] { R.id.name, R.id.city, R.id.country}
);
However, I would rather use my data model directly, but I've no idea where and how to start, so that in the end I can do something like this:
ArrayList<ListPlacesValues> values = getData();
MyAdapter mAdapter = new MyAdapter(
this,
values,
R.layout.places_listitem,
ListPlacesValues { values.name, values.city, values.country},
new int[] { R.id.name, R.id.city, R.id.country}
);
Solution: I found this Android API sample (List14), which was really helpful.
You can extend ArrayAdapter. Here's code example for you. In this example - SearchItem is some custom POJO. Basically you need to override getView() method to build your row by inflating row layout and then populating values based on List of items and current position
class SearchItemsAdapter extends ArrayAdapter<SearchItem> {
Activity context;
List<SearchItem> items;
SearchHeader header;
#SuppressWarnings("unchecked")
public SearchItemsAdapter(final Activity context,
final Map<SearchHeader, List<SearchItem>> result) {
super(context, R.layout.item, (List) ((Object[]) result.values()
.toArray())[0]);
this.context = context;
this.header = result.keySet().iterator().next();
this.items = result.get(this.header);
}
#Override
public View getView(final int position, final View convertView,
final ViewGroup parent) {
final View view = this.context.getLayoutInflater().inflate(
R.layout.item, null);
final SearchItem item = this.items.get(position);
((TextView) view.findViewById(R.id.jt)).setText(item.jt);
((TextView) view.findViewById(R.id.dp)).setText(item.dp);
((TextView) view.findViewById(R.id.cn)).setText(item.cn);
((TextView) view.findViewById(R.id.loc)).setText(item.loc.name);
final TextView body = ((TextView) view.findViewById(R.id.e));
body.setText(item.e);
body.setTag(item.src[0]);
((TextView) view.findViewById(R.id.src)).setText(item.src[1]);
return view;
}
}
There is one pitfall with the convertView in the sample you linked
if(convertView != null){ //reuse
convertView.setAnimation(null);
convertView.setAnyCustomFieldsIdontWantFilledWithData(null);
}
you want to set all animations or unused fields to null otherwise your items might have data in them or animations pending you dont want.