Custom SimpleCursorAdapter - Sections Problem

MetBo

MetBo

Erfahrenes Mitglied
Threadstarter
Hallo,

Ich versuche gerade in meiner ListView Sections einzublenden.

Beispiel:
== Section 03.09.2011 ==
News1 03.09.2011
News2 03.09.2011
News3 03.09.2011
News4 03.09.2011
== Section 02.09.2011 ==
News5 02.09.2011
News6 02.09.2011
News7 02.09.2011
== Section 01.09.2011 ==
News8 01.09.2011
News9 01.09.2011
Um dies zu realisieren, habe ich eine eigene SimpleCursorAdapter Klasse (NewsAdapter_new) angelegt.

In einer LinkedHashMap<Integer, String> speichere ich mit <Position (Cursor Position), Sectionname> die Sections. Diese Funktion funktioniert soweit sehr gut.

In getView() rufe ich die Methode getItemViewType() auf, um zu entscheiden, ob die aktuelle Cursor Position ein normaler Eintrag oder eine Section ist.


Das Ganze klappt auch soweit ganz gut - es werden Sections und normale Einträge angezeigt. Mein Problem ist nun, dass die Einträge, die normalerweise auf der Section Position wären, verschwinden bzw. nicht angezeigt werden.


Hier meine Custom SimpleCursorAdapter Klasse:
Code:
public class NewsAdapter_new extends SimpleCursorAdapter {
	protected static final String TAG = "NewsAdapter";
	
	protected static final int TYPE_HEADER = 0x00;
	protected static final int TYPE_NORMAL = 0x01;
	protected static final int TYPE_COUNT = 0x02;
	
	protected final LayoutInflater mInflater;
	protected final LinkedHashMap<Integer, String> mSections;
	
	protected int mCurrentSectionMapSize = 0;

	public NewsAdapter_new(Context context, int layout, Cursor c,
			String[] from, int[] to, int flags) {
		super(context, layout, c, from, to, flags);
		
		mInflater = LayoutInflater.from(mContext);
		mSections = getSeparators();
		mCursor.moveToFirst();
	}
	
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		int viewType = getItemViewType(position);
		
		mCursor.moveToPosition(position);
			
			switch (viewType) {
				case TYPE_NORMAL: {
					// Normale Einträge werden hier initialisiert
				
					break;
				}
				case TYPE_HEADER: {
					// Header-View wird hier initialisiert
					break;
				}
			}
		
		return convertView;
	}
	
	@Override
	public int getCount() {
		if (mCursor.getCount() != 0) {
			return super.getCount() + mSections.keySet().size();
		}
		
		return 0;
	}
	
	@Override
    public int getItemViewType(int position) {
		if (mSections == null) {
			return TYPE_NORMAL;
		} else {
			if (mSections.containsKey(position)) {
				return TYPE_HEADER;
			} else {
				return TYPE_NORMAL;
			}
		}
    }
	
	@Override
	public boolean areAllItemsEnabled() {
		return false;
	}
	
	@Override
	public boolean isEnabled(int position) {
		if (mSections == null) {
			return true;
		}
		
		return !mSections.containsKey(position);
	}

    @Override
    public int getViewTypeCount() {
        return TYPE_COUNT;
    }
	
    protected LinkedHashMap<Integer, String> getSeparators() {
    	LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>();
    	String newDate = "";
		String oldDate = "";
		
		mCursor.moveToFirst();
		
		if (!mCursor.isAfterLast()) {
			do {
				String currentString = mCursor.getString(mCursor.getColumnIndex(ArticleCol.DATE));
				newDate = currentString.substring(0, 10);
				
				if (!newDate.contentEquals(oldDate)) {
					map.put(mCursor.getPosition(), Util.formatBackToRealDate(newDate));
				}
				
				oldDate = newDate;
			} while (mCursor.moveToNext());
		}
		
    	return map;
    }
}
Hat jemand mein Problem verstanden und könnte mir eventuell helfen eine Lösung zu finden?

Vielen Dank,
Grüße Metti
 
S

sixi

Fortgeschrittenes Mitglied
Du musst deine "Header" aus der position von "getView" rausrechnen.

Im Konkreten:

getView(0) -> Header 0
getView(1) -> getItem(1-1 = 0)
getView(2) -> getItem(2-1 = 1);
getView(3) -> getItem(3-1 = 2);
getView(4) -> getItem(4-1 = 3);
getView(5) -> Header 1
getView(6) -> getItem(6-2 = 4);
etc.
(für dein Beispiel)
 
MetBo

MetBo

Erfahrenes Mitglied
Threadstarter
Also muss ich für jeden Header getView() extra aufrufen?
 
S

sixi

Fortgeschrittenes Mitglied
Nein - getView wird automatisch aufgerufen.
Es kommen quasi für jede position (von 0 bis anzahlElemente die du per getCount() zurückgibst) Anfragen. DU musst nur in dem getView schauen, dass du für jede position den richtigen View zurückgibst.
Und dazu gehört halt der richtige Typ (was du schon tust), aber auch das richtige Element aus deinen Listen (das meinte ich mit der getView(x) -> ... zuweisung da oben ;))
 
MetBo

MetBo

Erfahrenes Mitglied
Threadstarter
In deinem Beispiel sollte also die Berechnung der korrekten Positionen in getItem() stattfinden. Nun habe ich mal ein bisschen debuggt und mir ist aufgefallen, dass der Adapter gar nicht in die getItem() Funktion springt?!
 
S

sixi

Fortgeschrittenes Mitglied
Tut er auch nicht von alleine, du musst es wenn dann explizit aufrufen.
GetItem() musst du wenn dann explizit aufrufen. MUSST du aber NICHT!

Du musst einfach nur sicherstellen, dass für jede Position der richtige Typ zurückgegeben wird (das was du mit der SWITCH-Anweisung machst!) UND je nach Typ das Richtige Element aus einer deiner Liste...

Also - wenn
getView(0, convertView, parent) aufgerufen wird:
gehst du in deinem Switch in "case TYPE_HEADER:" und gibst den View, passend zu deinem ersten Header aus == deineHeader[0]

getView(1, convertView, parent) aufgerufen wird:
gehst du in deinem Switch in "case TYPE_NORMAL:" und gibst den View, passend zu deinem ersten Element aus deinen Elementen == deineElemente[0] == deineElemente(position-1)

getView(2, convertView, parent) aufgerufen wird:
gehst du in deinem Switch in "case TYPE_NORMAL:" und gibst den View, passend zu deinem zweitem Element aus deinen Elementen == deineElemente[1] == deineElemente(position-1)

...

getView(5, convertView, parent) aufgerufen wird:
gehst du in deinem Switch in "case TYPE_HEADER:" und gibst den View, passend zu deinem zweiten Header aus == deineHeader[1]

getView(6, convertView, parent) aufgerufen wird:
gehst du in deinem Switch in "case TYPE_NORMAL:" und gibst den View, passend zu deinem FÜNFTEN Ersten aus deinen Elementen == deineElemente[5] genau HIER ist die Variable position NICHTMEHR gleich dem X. Element aus deineElemente, sondern deineElemente(position-2)

An dieser Stelle ist es dann nichtmehr das SECHSTE Element, sondern eben das FÜNFTE, weil der View zur position FÜNF ein header ist, und genau dein Problem ja ist, dass du jeweils die Elemente aus deinen Elementen nicht angezeigt kriegst, wo ein header ist...


Die Beispiele sind kein Copy&Paste-Code sondern sollen dir dein Problem aufzeigen ;-) Debugg doch deine Variante einfach mal durch, dann siehst du das getView() mit den Werten Position 0 bis xx aufgerufen wird - und du aber NIE auf die "normalen Elemente" zugreifst, für die Positionen, wo ein Header angezeigt wird.
 
Zuletzt bearbeitet:
MetBo

MetBo

Erfahrenes Mitglied
Threadstarter
Ich habe das Prinzip jetzt verstanden. Ich habe jetzt auch folgendes verändert bzw. eingebunden:

protected LinkedHashMap<Integer, Integer> mTypePosition;
protected LinkedHashMap<Integer, Integer> mOffsetPosition;

@Override
public View getView(int position, View convertView, ViewGroup parent) {
int viewType = getItemViewType(position);

ViewHolder viewHolder = null;
if (convertView == null) {
viewHolder = new ViewHolder();

switch (viewType) {
case TYPE_NORMAL: {
convertView = mInflater.inflate(R.layout.item_article, null);


break;
}
case TYPE_HEADER: {
convertView = mInflater.inflate(R.layout.item_separator, null);

break;
}
}

convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}

switch (viewType) {
case TYPE_NORMAL: {
int pos = position;
for (Entry<Integer, Integer> item : mOffsetPosition.entrySet()) {
if (item.getValue() == position) {
pos = item.getKey();
break;
}
}

mCursor.moveToPosition(pos);

}
case TYPE_HEADER: {

}
}

return convertView;
}

protected LinkedHashMap<Integer, String> getSections() {
LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>();
mTypePosition = new LinkedHashMap<Integer, Integer>();
mOffsetPosition = new LinkedHashMap<Integer, Integer>();
String newDate = "";
String oldDate = "";
int position = 0;
int sectionPosition = 0;

mCursor.moveToFirst();

if (!mCursor.isAfterLast()) {
do {
String currentString = mCursor.getString(mCursor.getColumnIndex(ArticleCol.DATE));
newDate = currentString.substring(0, 10);

if (!newDate.contentEquals(oldDate)) {
map.put(mCursor.getPosition(), Util.formatBackToRealDate(newDate));
sectionPosition++;
mTypePosition.put(position, TYPE_HEADER);
} else {
mTypePosition.put(position, TYPE_NORMAL);
}
mOffsetPosition.put(mCursor.getPosition(), position + sectionPosition);
oldDate = newDate;
position++;
} while (mCursor.moveToNext());
}

return map;
}
Es werden immer noch nicht alle Einträge angezeigt ... ich verzweifel -.-
 
Thread starter Similar threads Forum Replies Date
M Android App Entwicklung 14
Oben Unten