I try to create ListView. It contains few custom views created by single xml-template + one view for indicating progress. ProgressView always must be at the bottom of my custom ListView. So, here's the code:
public class SearchActivity extends Activity implements OnXMLDataGetterComplete {
private ListView mMainLayout;
private HotelsListAdapter mHotelsListAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
mHotelsListAdapter = new HotelsListAdapter(); //create my custom adapter
mHotelsListAdapter.setLoading(true); //sets that my ListView will be show ProgressView at the bottom
mMainLayout = (ListView) findViewById(R.id.search_activity_layout);
mMainLayout.setAdapter(mHotelsListAdapter); //set adapter for my ListView
getData(); //start to receive data from web
}
//this function runs after HTTP-query
public void onGetData(final List data) { //receives list of some data
runOnUiThread(new Runnable() {
@Override
public void run() {
Iterator<XMLDataGetter.HotelListXMLParser.HotelListXMLEntry> it = data.iterator();
while (it.hasNext()) {
XMLDataGetter.HotelListXMLParser.HotelListXMLEntry item = it.next();
mHotelsListAdapter.setLoading(data.size() != 0);
HotelItemView hotel = new HotelItemView(SearchActivity.this); //create some custom view by received data
...
mHotelsListAdapter.addItem(hotel); //add this view to the adapter
}
mHotelsListAdapter.notifyDataSetChanged(); //THIS NOT WORKS
}
}
}
public class HotelsListAdapter extends BaseAdapter { //ny custom adapter
private List<HotelItemView> mItems = new ArrayList<HotelItemView>(); //list, which contains all created custom views
private boolean mIsLoading = false; //variable, which determines, whether ProgressView will be shown
private RelativeLayout mLoadingView; //layout of activity
public HotelsListAdapter() {
LayoutInflater inflater = (LayoutInflater) ((Context) SearchActivity.this).getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mLoadingView = (RelativeLayout) inflater.inflate(R.layout.hotel_list_loading, null); //inflate ProgressView
}
@Override
public boolean areAllItemsEnabled() {
return true;
}
@Override
public boolean isEnabled(int position) {
return true;
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
}
@Override
public int getCount() {
if (mIsLoading)
return mItems.size() + 1; //custom adapter contains children vies + ProgressView
else
return mItems.size(); //custom adapter contains only children views
}
@Override
public Object getItem(int position) {
if (position > mItems.size() - 1)
return mLoadingView; //return ProgressView
else
return mItems.get(position); //return child view
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return (View) getItem(position);
}
@Override
public int getItemViewType(int position) {
return 0;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public boolean isEmpty() {
return false;
}
public void addItem(HotelItemView item) {
mItems.add(item);
}
public void clear() {
mItems.clear();
}
public void setLoading(boolean loading) {
mIsLoading = loading;
}
public boolean isLoading() {
return mIsLoading;
}
}
}
This example with ProgressBarView, but I tried to remove references about it:
@Override
public int getCount() {
return mItems.size(); //custom adapter contains only children views
}
@Override
public Object getItem(int position) {
return mItems.get(position); //return child view
}
and notifyDataSetChanged() does not work anyway. What is my problem? I can't to display added views.
I tried to add mMainLayout.invalidateViews() before otifyDataSetChanged(). So I saw my views list, but every three-five times I got exception "The content of the adapter has changed but ListView did not receive a notification". What should I do?
Main thing is I don't need to clear my adapter. Adding child view must be incremental. Also I tried to extend ListAdapter, but it have not such method at all
Maybe don't override the registerDataSetObserver Method of the Adapter.
Some other hints:
I'd suggest you to use an ArrayAdapter if theres no reason to not use it, because it will save you some of the base config and handle the list management for you. Also, don't save the Views inside the list to display, but the items holding the data. The ListAdapters can recycle the Views for you, so you don't have one View for each item in your List, but one View for each item that is visible. In the getView Method, if the 'convertView' Parameter is not empty, this view can be reused, so just fill its data and return it. This will increase the performance of your List and decrease the memory consumption greatly.
For the Async stuff, better use an AsyncTask, which is better integrated with the Android system and does stuff like running things in the UI Thread before and after the background task much easier.