I tryied on Stackoverflow to post my code, but I think there are too many mistake in this to be fixed.
So as my biggest problem is a misunderstanding, I'll just ask for the way I have to develop it.
PROBLEM : I want to do a listView, feeded by a custom cursorAdapter.
Why custom cursorAdapter ?
Because for now, I want to have an editText on the first row of the listView, then items from database, then an other editText at the very last row of the listView. I could solve it with headers and footers, but later I want many other row template in the same listView, and maybe even many cursor, that's the reason.
I tryied to use many types in a adapter following this tutorial : Link about having many row types in one listView
Of course I overrided bindView and newView.
BUT
It's failing. I have many bugs like I don't know how to directly add both editText to my listView, etc ... The biggest problem for me is comprehension, I just need an explanation of how to do that, I mean what exactly should I do in bindView, and what exactly should I do in newView.
Ok I changed my mind, here is the code of my adapter. Watch out, it's maybe horrible.
package com.android.activity;
import android.content.Context;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.TextView;
public class NotesCursorAdapter extends CursorAdapter{
private Context context;
// private Cursor cursor;
private int addNoteTopPosition = 0;
private int addNoteBottomPosition;
private LayoutInflater inflater;
private static final int TYPE_ITEM = 0;
private static final int TYPE_ADD_NOTE_BOTTOM = 1;
private static final int TYPE_ADD_NOTE_TOP = 2;
private static final int TYPE_MAX_COUNT = TYPE_ADD_NOTE_TOP + 1;
public NotesCursorAdapter (Context context, Cursor cursor, int flag){
super(context, cursor);
this.context = context;
addNoteTopPosition = 0;
addNoteBottomPosition = cursor.getCount()+1;
inflater = LayoutInflater.from(this.context);
}
// public
@Override
public int getCount() {
return super.getCount() + 2;
}
@Override
public View getView(int position, View convertView, ViewGroup parent){
ViewHolder holder = null;
int type = getItemViewType(position);
if (convertView == null) {
holder = new ViewHolder();
switch (type) {
case TYPE_ADD_NOTE_TOP:
convertView = inflater.inflate(R.layout.add_note_top, null);
holder.view = (EditText)convertView.findViewById(R.id.add_note_top_id);
break;
case TYPE_ITEM:
convertView = inflater.inflate(R.layout.row_note, null);
holder.view = (TextView)convertView.findViewById(R.id.note);
getCursor().moveToPosition(position - 1);
// System.out.println("modified : " + getCursor().getInt(getCursor().getColumnIndex("_id")));
((TextView) holder.view).setText(getCursor().getInt(getCursor().getColumnIndex("_id")) + getCursor().getString(getCursor().getColumnIndex("content_note")));
break;
case TYPE_ADD_NOTE_BOTTOM:
convertView = inflater.inflate(R.layout.add_note_bottom, null);
holder.view = (EditText)convertView.findViewById(R.id.add_note_bottom_id);
break;
}
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
return convertView;
}
@Override
public int getItemViewType(int position) {
int type;
if (position == addNoteTopPosition){
type = TYPE_ADD_NOTE_TOP;
} else if (position == addNoteBottomPosition){
type = TYPE_ADD_NOTE_BOTTOM;
}else {
type = TYPE_ITEM;
}
return type;
}
@Override
public int getViewTypeCount() {
return TYPE_MAX_COUNT;
}
@Override
public long getItemId(int position) {
return position;
}
public static class ViewHolder {
public View view;
}
}
Note : I'll fix later some optimisation like viewHolder, etc ...
Note 2 : Just in case, here is my main activity code
package com.android.activity;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewDebug.FlagToString;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.CursorAdapter;
import com.android.database.Note;
import com.android.database.NoteDataSource;
public class EverydayNotesAndroid3Activity extends Activity {
/** Called when the activity is first created. */
private Cursor cursorNotes;
private NotesCursorAdapter notesCursorAdapter;
private InputMethodManager imm;
private Activity activity;
private ListView listView;
private int positionItem;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
activity = this;
NoteDataSource.getSingletonObject(activity);
NoteDataSource.getSingletonObject(activity).open();
cursorNotes = NoteDataSource.getSingletonObject(activity).getCursorUpdatedDatabase(); // return all notes in a cursor
startManagingCursor(cursorNotes);
listView = (ListView) findViewById(R.id.main_list);
notesCursorAdapter = new NotesCursorAdapter(activity, cursorNotes, 3);
listView.setAdapter(notesCursorAdapter);
Button b = new Button(activity);
b = (Button) findViewById(R.id.done);
b.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
NoteDataSource.getSingletonObject(activity).createNoteTop("Hello stack overflow world");
cursorNotes = NoteDataSource.getSingletonObject(activity).getCursorUpdatedDatabase();
notesCursorAdapter.changeCursor(cursorNotes);
notesCursorAdapter.notifyDataSetChanged();
}
});
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id){
if (position == 0){
showAlertWindowAddTop(parent, v, position, id);
}else if (position == parent.getChildCount()-1){
showAlertWindowAddBottom(parent, v, position, id);
}else{
showAlertWindowModify(parent, v, position, id);
}
}
});
}
And don't worry about :
1) functions showAlertWindowblabla() ==> it works
2) the createNoteTop function works too, it insert a row on database.
Thanks for reading, looking for your answers.
EDIT : Ok so after some looooooong work for not very much, I'm getting closer of what I want. Unfortunately I still have a bug of display, when I modify a field, I don't know why, he just drop this field somewhere else in the list. I think it's a big mistake (on bindView or newView method) easily fixable, but I'm tired and I don't find where it comes from. So to kill two birds with one stone, I post modified code of my customAdapter.
Its quite common to subclass CursorAdapter
since its meant to be done that way. You would use a ResourceCursorAdapter
, etc in case you have simple needs.
So just a bit of background: ListView
recycles views. Therefore newView is only called for (number of items on the screen) + 1 (the one that is partially visible as you start scrolling). Then these items get reused as they scroll off the screen. Therefore, its pretty much useless to set the text labels in newView as it will anyway be overridden in bindView(which is the place where you should do that), as newView will not be called for every TYPE_ITEM.
So I guess the main problem is that you are changing the contents of the list view by modifying addNoteBottomPosition in your newView
method which is the worst thing you can do, really. A list adapter is meant to produce stable results. That means if you say that getItemViewType(8)
was of type TYPE_ADD_NOTE_BOTTOM, you cannot turn around next time and and say "yay, now its TYPE_ITEM". Which is what you are doing. Your code relies on the fact that newView
is called in a certain order. The other problem is that you are messing around with positions which CursorAdapter
relies upon.
So how do you solve this?
There are many approaches, I think you used probably the more complicated one. The simplest approach is to
Use a ResourceCursorAdapter
for the items only, and use ListView.addHeaderView()
and ListView.addFooterView()
for adding headers and footers.
If you check out the source code of ListView then you will notice that addHeaderView
will in fact use a HeaderViewListAdapter
(http://developer.android.com/reference/android/widget/HeaderViewListAdapter.html) which is basically a wrapper list adapter which wraps around the nested ListAdapter
that you would then provide.
With this approach you don't need to worry about different view types, etc.
In case I couldn't convince you and you still want to subclass CursorAdapter
then you need to review its implementation. You will notice that it relies on correct positions and therefore, if you would really want to mess with positions and have them separate from cursor row positions, then you would need to override all sorts of methods such as getItem()
, getItemId()
.
Long story short, you don't want to go there.