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 :)
Related
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;
}
}
I have implemented a custom adapter and listItemView. The adapter sets an onlclick listener to a button that is on the listItemView. The onclick listener simply calls a private method I have in the adapter and passes it the position of the item to be removed. I know the position is correct because the database removes the proper item. I have found similar questions but have not been able to adapt the answers to work for me. Ideas and thoughts are greatly appreciated. Thanks.
Here is the full adapter class
public class FoodListAdapter extends ArrayAdapter<FoodListItem> {
//private
private int type;
public FoodListAdapter(Context context, ArrayList<FoodListItem> _objects) {
super(context, 0, _objects);
type = 0;
}
public FoodListAdapter(Context context, ArrayList<FoodListItem> _objects, int _type) {
super(context, 0, _objects);
type = _type;
}
#Override
public View getView(int position, View reusableView, ViewGroup parent)
{
//Cast the reusable view to a listAdpaterItemView
FoodListItemView listItemView = (FoodListItemView) reusableView;
//Check if the listAdapterItem is null
if(listItemView == null)
{
//If it is null, then create a view.
listItemView = FoodListItemView.inflate(parent, this, type);
}
if (type == 2)
{
Button deleteButton = (Button) listItemView.findViewById(R.id.listItemViewDeleteBTN);
deleteButton.setTag(new Integer(position));
}
//Now we need to set the view to display the data.
listItemView.setData(getItem(position));
return listItemView;
}
}
Here is a portion of my code used in fragment. Note that I have a private variable decalred in the class for listAdapter, though I don't think I need that.
private void displayListForDate(Calendar _date)
{
//get the list view
ListView listView = (ListView) getView().findViewById(1);
//Clear the listview by removing the listadapter and setting it to null.
//listView.setAdapter(null);
//First we must get the items.
Global global = (Global) getActivity().getApplicationContext();
DietSQLiteHelper database = global.getDatabase();
//Create a list to hold the items we ate. This list will then be added to the listView.
final ArrayList<FoodListItem> consumedList;
//Add the items to the array.
consumedList = database.getConsumed(_date.getTimeInMillis());
//Create an adapter to be used by the listView
listAdapter = new FoodListAdapter(getActivity().getBaseContext(), consumedList, 2);
//Add the adapter to the listView.
listView.setAdapter(listAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
consumedList.remove(position);
listAdapter.notifyDataSetChanged();
}
});
}
If you didn't implement "equals" method of FoodListItem, try to implements it.
I would suggest,
that you just update the underlying data, in your case its ArrayList<FoodItems>.
In your Adapter make this simple method and change :
private List<FoodListItem> myList = new ArrayList<FoodListItem>();
public FoodListAdapter(Context context, List<FoodListItem> myList) {
super(context, 0, myList);
type = 0;
this.myList = myList;
}
public FoodListAdapter(Context context, List<FoodListItem> myList, int _type) {
super(context, 0, myList);
type = _type;
this.myList = myList;
}
// Also update your getView() method to use myList!
#Override
public View getView(int position, View reusableView, ViewGroup parent)
{
...
listItemView.setData(myList.get(position));
public void removeItem(int positio){
if(myList != null){
myList.remove(position);
}
}
And then in class, you are creating the adapter (Activity/Fragment), just call the method.
// Update the underlying ArrayAdapter
adapter.removeItem(position);
// Notify the adapter, the data has changed
adapter.notifyDataSetChanged();
Also, you shouldnt open connection to your SQLiteDatabase on UI thread, because you are blocking it. You never know, how fast is the reading from disk going to be. If it takes too long, user can think, that your application froze and therefore, he leaves, which you dont want. I would suggest to use AsyncTask, you will find a lot of examples.
I went through and cleaned up my code and it now works, here is the working code. I really don't know exactly the difference other than I updated the IDs that I was using to assign and get views. If anyone can explain the cause for the issue I was having I would appreciate it.
Here is the snippet from my fragment where I create the list view and assign an adapter.
private void displayListForDate(Calendar _date)
{
//get the list view
ListView listView = (ListView) getView().findViewById(R.id.listView);
//Clear the listview by removing the listadapter and setting it to null.
//listView.setAdapter(null);
//First we must get the items.
Global global = (Global) getActivity().getApplicationContext();
DietSQLiteHelper database = global.getDatabase();
//Create a list to hold the items we ate. This list will then be added to the listView.
ArrayList<FoodListItem> consumedList;
//Add the items to the array.
consumedList = database.getConsumed(_date.getTimeInMillis());
//Create an adapter to be used by the listView
listAdapter = new FoodListAdapter(getActivity().getBaseContext(), consumedList, 2);
//Add the adapter to the listView.
listView.setAdapter(listAdapter);
}
and here is my adapter class.
public class FoodListAdapter extends ArrayAdapter<FoodListItem>
{
//private
private int type;
public FoodListAdapter(Context context, ArrayList<FoodListItem> _objects) {
super(context, 0, _objects);
type = 0;
}
public FoodListAdapter(Context context, ArrayList<FoodListItem> _objects, int _type) {
super(context, 0, _objects);
type = _type;
}
#Override
public View getView(int position, View reusableView, ViewGroup parent)
{
//Cast the reusable view to a listAdpaterItemView
FoodListItemView listItemView = (FoodListItemView) reusableView;
//Check if the listAdapterItem is null
if(listItemView == null)
{
//If it is null, then create a view.
listItemView = FoodListItemView.inflate(parent, type);
}
if (type == 2)
{
Button deleteButton = (Button) listItemView.findViewById(R.id.listItemViewDeleteBTN);
deleteButton.setTag(new Integer(position));
deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Integer tag = (Integer) view.getTag();
deleteItem(tag.intValue());
}
});
}
//Now we need to set the view to display the data.
listItemView.setData(getItem(position));
return listItemView;
}
private void deleteItem(int position)
{
FoodListItem item = getItem(position);
Global global = (Global) getContext().getApplicationContext();
DietSQLiteHelper database = global.getDatabase();
database.removeConsumed(item.getID());
remove(getItem(position));
}
}
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'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.