OnItemLongClickListener in ListActivity spricht nicht an

kurztipp

Aktives Mitglied
Hallo,

ich habe eine ListView, die von einem CustomAdapter extends ArrayAdapter erzeugt wird. So weit funktioniert alles. Würde nur gerne einzelne Items gerne wieder durch einen longClick löschen können. Das Problem ist, dass der gesetzte Listener scheinbar nicht angesprochen wird:

Java:
public class MyActivity extends ListActivity {

    private class MyArrayAdapter<O> extends ArrayAdapter<O> {
        private class MyTextWatcher implements TextWatcher {

            private View mView;

            private MyTextWatcher(View view) {
                mView = view;
            }

            public void afterTextChanged(Editable s) {
                // Eigene Implementierung. do something ...
            }

            public void beforeTextChanged(CharSequence s, int start, int count,
                    int after) {
                // do nothing
            }

            public void onTextChanged(CharSequence s, int start, int before,
                    int count) {
                // do nothing
            }
        }

        private ArrayList<O> mList;

        public MyArrayAdapter(Context context, int resource, int viewRessource,
                ArrayList<O> objects) {
            super(context, resource, viewRessource, objects);
            mList = objects;
        }

        @Override
        public View getView(int position, View view, ViewGroup parent) {
            super.getView(position, view, parent);
            // Eigene Implementierung von getView()
        }

    }

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
// ...

        ListView list = getListView();
        list.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                Toast.makeText(DishActivity.this,
                        "Item in position " + position + " clicked",
                        Toast.LENGTH_LONG).show();
            }
        });
        list.setOnItemLongClickListener(new OnItemLongClickListener() {

            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view,
                    int position, long id) {
                Toast.makeText(MyActivity.this,
                        "Item in position " + position + " clicked",
                        Toast.LENGTH_LONG).show();
                return true;
            }
        });
        //...
    }
    //...
}

Leider wird der Toast nicht erzeugt. Ich vermute es liegt am CustomAdapter, da es in einem testweise eingebauten ListFragment mit SimpleCursorAdapter funktioniert wie es soll. Ich kann aber den Implementierungsfehler nicht erkennen.

Gruß
 

dzim

Top Contributor
Hm. Ich verwende in meinem Programm einen ziemlich verfrickelten SimpleCursorAdapter. Einen Grund, warum es nicht funktionieren sollte, sehe ich aber nicht.

Ich habe mir gerade das Tutorial von Lars Vogel angeschaut:
Android ListView - Tutorial

Er verwendet zwei Sachen anders: Zum einen setzt er nicht explizit auf die Liste den "normalen" Click-Listener, sondern verwenden die der ListActivity (und -Fragment) eigene Callback Methode
[c]@Override protected void onListItemClick(ListView l, View v, int position, long id) {...}[/c]
Und was er auch verwendet, ist das ViewHolder Pattern - etwas mit dem ich mich auch gerade erst angefreundet habe und das bisher nur Teil der neuesten Adapter ist...
Ansonsten aber sieht alles gleich aus - auch er verwendet für seine Demo einen ArrayAdapter und daher vermute ich, dass es bei ihm funktioniert - vielleicht also ist es das Ding mit dem normalen Click-Listener, was Probleme bereitet...
 

kurztipp

Aktives Mitglied
Hallo,

Er verwendet zwei Sachen anders: Zum einen setzt er nicht explizit auf die Liste den "normalen" Click-Listener, sondern verwenden die der ListActivity (und -Fragment) eigene Callback Methode
[c]@Override protected void onListItemClick(ListView l, View v, int position, long id) {...}[/c]
Und was er auch verwendet, ist das ViewHolder Pattern
daran liegt es nicht -- das habe ich vergessen zu erwähnen. Natürlich habe ich für den einfachen Klick (der mich an und für sich nicht weiterbringen würde) den @Override Ansatz auch schon probiert:
Java:
    @Override
    protected void onListItemClick(ListView list, View view, int position,
            long id) {
        Toast.makeText(DishActivity.this,
                "Item in position " + position + " clicked", Toast.LENGTH_LONG)
                .show();
    }
Aber auch hier wird der Toast nicht erzeugt. Also ist es egal, ob ich das explizit über die Liste mit einem Setter setze oder die Methode überschreibe (beides gleichzeitig, nur eines und mit dem Setter for den LongClick zusammen, also alles mal wild durchexerziert).
Mit dem ViewHolder hängt es meiner Meinung nach nicht zusammen, weil es in dem testweise erzeugte ListFragment, das das nicht benutzt, auch funktioniert.

[EDIT]Es muss mit der eigenen Implementierung des ArrayAdapters zu tun haben, denn wenn ich in
Code:
onCreate()
Java:
MyArrayAdapter<MyObject> mAdapter = new MyArrayAdapter<MyObject>(this,
        R.layout.layout,
        R.id.textView, new ArrayList<MyObject>());

setListAdapter(mAdapter);
durch
Java:
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
        android.R.layout.simple_list_item_1, new String[] { "ABC",
                "DEF", "GHI", "JKL" });

setListAdapter(adapter);
ersetze funktioniert alles, wie es soll.
[/EDIT]

Gruß
 
Zuletzt bearbeitet:

dzim

Top Contributor
Dann poste doch mal a) dein Layout und b) verwende immer dieses from-to-binding. Das habe ich noch im Unterschied zu dir. Und ich finde es schon schräg, dass auch der normale Klick nicht funktioniert...

#edit: streich das from-to-Zeig - das verwende ich im Bsp. unten auch nicht...

Ich zeig dir mal kurz ein Beispiel, das bei mir funktioniert: Ich hab vor kurzen einen NavigationDrawer in unsere App eingebaut (auch wenn ich das noch nicht im Store veröffentlicht hab, weil noch die Hilfedokumente angepasst werden müssen :) ).

Das ListFragment
Java:
public class PhoneDrawerFragment extends ListFragment {
	
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.fragment_drawer_phone, container, false);
		return view;
	}
	
	private DrawerItemEnum.DrawerItemArrayAdapter mAdapter = null;
	
	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		if (mAdapter == null) {
			mAdapter = new DrawerItemEnum.DrawerItemArrayAdapter(getActivity());
		}
		this.setListAdapter(null);
		this.setListAdapter(mAdapter);
	}
	
	private IAdditionalHandler mOnItemClickHandler = null;
	
	public void setOnItemClickHandler(IAdditionalHandler onItemClickHandler) {
		this.mOnItemClickHandler = onItemClickHandler;
	}
	
	private View mCurrentSelectedView = null;
	
	@Override
	public void onListItemClick(ListView l, View v, int position, long id) {
		if (mAdapter == null) {
			return;
		}
		super.onListItemClick(l, v, position, id);
		if (mCurrentSelectedView != null)
			mCurrentSelectedView.setSelected(false);
		mCurrentSelectedView = v;
		mCurrentSelectedView.setSelected(true);
		if (SpeedTestConstants.PREF_DEFAULT_LONG == id) {
			return;
		}
		if (mOnItemClickHandler != null) {
			mOnItemClickHandler.handle(mAdapter.getItem(position));
		}
	}
}

Layout des Drawers
[XML]
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:eek:rientation="horizontal" >

<TextView
android:id="@+id/fragment.header"
style="@style/cnlabHeader.Grey"
android:layout_marginLeft="5dp"
android:layout_marginTop="5dp"
android:layout_toLeftOf="@+id/sep"
android:text="@string/drawer.header" />

<View
android:id="@+id/sep"
android:layout_width="5dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_marginLeft="5dp"
android:background="@drawable/sep"
android:visibility="visible" />

<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/fragment.header"
android:layout_alignParentBottom="true"
android:layout_alignRight="@+id/fragment.header"
android:layout_below="@+id/fragment.header"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:cacheColorHint="#00000000"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:fadeScrollbars="false"
android:fastScrollEnabled="true"
android:listSelector="@drawable/result_list_selector"
android:scrollbarAlwaysDrawVerticalTrack="true" >
</ListView>

</RelativeLayout>
[/XML]

Die ominöse Enumeration mit dem Adapter (für so Kleinkram kombiniere ich das häufig - vielleicht nicht gerade der sauberste Stil, aber auf jeden Fall schnell ;-) )
Java:
public enum DrawerItemEnum {
	
	MAIN(R.drawable.ic_action_start_dark, R.string.drawer_measurement),
	HELP(R.drawable.ic_action_help, R.string.drawer_help),
	SETTINGS(R.drawable.ic_action_settings, R.string.drawer_settings),
	VIEW_RESULTS(R.drawable.ic_action_ref_server, R.string.drawer_view_results),
	SEND_FEEDBACK(R.drawable.ic_action_content_new_email_dark, R.string.drawer_send_feedback),
	CHANGELOG(R.drawable.ic_action_info, R.string.drawer_changelog),
	ABOUT(R.drawable.cnlab, R.string.drawer_about), ;
	
	private final int mItemIconId;
	private final int mItemTextId;
	private DrawerItemEnum(final int itemIconId, final int itemTextId) {
		this.mItemIconId = itemIconId;
		this.mItemTextId = itemTextId;
	}
	public int getItemIconId() {
		return mItemIconId;
	}
	public int getItemTextId() {
		return mItemTextId;
	}
	public static DrawerItemEnum findBySortId(int sortId) {
		for (DrawerItemEnum o : values()) {
			if (o.mItemIconId == sortId)
				return o;
		}
		return null;
	}
	public static DrawerItemEnum[] getDefaultValues() {
		return new DrawerItemEnum[] { MAIN, SETTINGS, VIEW_RESULTS, SEND_FEEDBACK, CHANGELOG, ABOUT };
	}
	
	/**
	 * The adapter...
	 */
	public static final class DrawerItemArrayAdapter extends ArrayAdapter<DrawerItemEnum> {
		
		private Context mContext;
		private LayoutInflater mInflater;
		
		public DrawerItemArrayAdapter(Context context) {
			super(context, R.layout.simple_selection_item, android.R.layout.simple_list_item_single_choice, getDefaultValues());
			this.mContext = context;
			this.mInflater = LayoutInflater.from(getContext());
		}
		
		private DrawerItemEnum selection = MAIN;
		public void setSelection(DrawerItemEnum selection) {
			this.selection = selection;
		}
		public DrawerItemEnum getSelection() {
			return selection;
		}
		
		@Override
		public View getDropDownView(int position, View convertView, ViewGroup parent) {
			final DrawerItemEnum currentItem = getItem(position);
			View v = convertView;
			ViewHolder vh = null;
			if (v == null) {
				switch (currentItem) {
				default:
					vh = new ViewHolder();
					v = createView(position, convertView, parent, currentItem, vh);
					break;
				}
			} else {
				vh = (ViewHolder) v.getTag();
			}
			return v;
		}
		
		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			final DrawerItemEnum currentItem = getItem(position);
			View v = convertView;
			ViewHolder vh = null;
			if (v == null) {
				switch (currentItem) {
				default:
					vh = new ViewHolder();
					v = createView(position, convertView, parent, currentItem, vh);
					break;
				}
			} else {
				vh = (ViewHolder) v.getTag();
			}
			return v;
		}
		
		private View createView(int position, View convertView, ViewGroup parent, DrawerItemEnum currentItem, ViewHolder outViewHolder) {
			View v = mInflater.inflate(R.layout.drawer_row, parent, false);
			v = mInflater.inflate(R.layout.drawer_row, parent, false);
			v.setSelected(currentItem == selection);
			outViewHolder.icon = (ImageView) v.findViewById(R.id.icon);
			if (outViewHolder.icon != null) {
				outViewHolder.icon.getLayoutParams().height = UIUtils.getDipsFromPixel(getContext(), 32);
				outViewHolder.icon.getLayoutParams().width = UIUtils.getDipsFromPixel(getContext(), 32);
				if (SpeedTestConstants.PREF_DEFAULT_INTEGER != currentItem.mItemIconId) {
					outViewHolder.icon.setImageResource(currentItem.mItemIconId);
				} else {
					outViewHolder.icon.setImageResource(R.drawable.empty);
				}
			}
			outViewHolder.text = (TextView) v.findViewById(R.id.text);
			if (outViewHolder.text != null) {
				if (ABOUT == currentItem) {
					outViewHolder.text.setText(mContext.getString(currentItem.mItemTextId,
							(mContext instanceof BaseMainActivity) ? ((BaseMainActivity) mContext).getVersion() : "?"));
				} else {
					outViewHolder.text.setText(currentItem.mItemTextId);
				}
			}
			v.setBackgroundResource(((position % 2) != 0) ? R.drawable.drawer_list_selector_2 : R.drawable.drawer_list_selector);
			v.setTag(outViewHolder);
			return v;
		}
		
		/**
		 * the ViewHolder for the respective pattern
		 */
		private static class ViewHolder {
			ImageView icon;
			TextView text;
		}
	}
}

Also der einfache Klick funktioniert bei mir hervorragend. Das witzigste ist noch: R.layout.simple_selection_item (ein eigenes Layout mit RadioButton), android.R.layout.simple_list_item_single_choice (irgend so ein Kram von Android, der mir völlig egal ist!) wird bei mir in
Code:
#getView()
bzw.
Code:
getDrowpDownView()
(für Auswahlboxen, eigentlich unnötig, kostet mich aber kaum Arbeit, wie man sieht) sowieso durch das hier
Code:
View v = mInflater.inflate(R.layout.drawer_row, parent, false);
überschrieben.

Also ich wüsste einfach nicht, warum es bei dir nicht gehen sollte!
 
Zuletzt bearbeitet:

kurztipp

Aktives Mitglied
Hallo,

Dann poste doch mal a) dein Layout

an die Layouts hab ich auch bereits gedacht, aber mir fällt nichts unaufälliges auf :D

[XML]<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:eek:rientation="vertical">

<EditText
android:id="@+id/ET_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Name"
android:inputType="text" >
</EditText>

<!-- ... -->

<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<Button
android:id="@+id/BTN_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:eek:nClick="save"
android:text="@string/BTN_save" />

</LinearLayout>[/XML]

[XML]<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:eek:rientation="horizontal" >

<TextView
android:id="@+id/TV_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<EditText
android:id="@+id/ET_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:digits="0123456789.,"
android:ems="6" >
</EditText>

<TextView
android:id="@+id/TV_valA_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/DI_calories" />
<TextView
android:id="@+id/TV_valA_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<!--- ... -->

<TextView
android:id="@+id/TV_valD_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/DI_calories" />
<TextView
android:id="@+id/TV_valD_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>
[/XML]
 

dzim

Top Contributor
Hm... Nein. Ich muss passen. Ich seh' den Grund (jedenfalls im Moment noch) nicht.
Das Layout meiner einzelnen Zeilen in der Liste, ist auch nichts besonderes:

[XML]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="50dp"
android:background="@drawable/drawer_list_selector"
android:eek:rientation="horizontal" >

<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="5dp"
android:contentDescription="@string/three_dots"
android:scaleType="fitCenter"
android:src="@drawable/empty" />

<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:text="@string/three_dots"
android:textColor="@color/drawer_list_textcolor_selector"
android:textStyle="bold" />

</LinearLayout>
[/XML]

#edit: Noch während ich schrieb, hatte ich eine Erscheinung: Der Grund ist wahrscheinlich dein EditText - das wird den Fokus "rauben". Du musst also die Events auch an den Parent weiterreichen...

Ich brauchte das mal, um ein Event an die darüber liegende Activity weiter zu senden. Vielleicht reicht dir das, wenn du es auf deinen EditView abstimmst:
Java:
mView.setOnTouchListener(new OnTouchListener() {
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		getActivity().onTouchEvent(event); // XXX hack: dispatch touch event to parent activity
		return false;
	}
});
Wie genau, kann ich dir jetzt nicht sagen, aber ich vermute, dass ist die Richtung, in die du schauen müsstest.
 

kurztipp

Aktives Mitglied
Hallo,

#edit: Noch während ich schrieb, hatte ich eine Erscheinung: Der Grund ist wahrscheinlich dein EditText - das wird den Fokus "rauben". Du musst also die Events auch an den Parent weiterreichen...

Ich brauchte das mal, um ein Event an die darüber liegende Activity weiter zu senden. Vielleicht reicht dir das, wenn du es auf deinen EditView abstimmst:
Java:
mView.setOnTouchListener(new OnTouchListener() {
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		getActivity().onTouchEvent(event); // XXX hack: dispatch touch event to parent activity
		return false;
	}
});
Wie genau, kann ich dir jetzt nicht sagen, aber ich vermute, dass ist die Richtung, in die du schauen müsstest.

eine Erscheinung, ja? ;) Aber sie ist ein riesen Schritt in die richtige Richtung gewesen.

Tatsächlich hat EditText das ClickEvent absorbiert. Es hat nämlich alles funktioniert, wie es soll, nachdem ich das ListViewLayout ohne EditText getestet hab.
Gemäß diesem Bugreport (von 2009 o.0) schlucken alle Views, die focusable oder clickable sind die Listener der ListView. Der Beigefügte Bugfix hilft wenig, weil er zu tief in den Androidsource eingreift. Weiteres googlen brachte diverse Lösungsvorschläge:
Die beste (IMHO) hab ich hier gefunden: setOnItemClickListener() not working on custom ListView @ Android - Stack Overflow
Am einfachsten ist, im Layout des ListViews dem Wurzelelement (LinearLayout und dergl.)
Code:
android:descendantFocusability="blocksDescendants"
mit auf den Weg zu geben.

Gruß
 

dzim

Top Contributor
Das kannte ich noch nicht - werde ich mal versuchen, mir zu merken, wenn ich es mal brauchen sollte ;-)
 

kurztipp

Aktives Mitglied
Am einfachsten ist, im Layout des ListViews dem Wurzelelement (LinearLayout und dergl.)
Code:
android:descendantFocusability="blocksDescendants"
mit auf den Weg zu geben.

Wenn man Buttons oder Checkboxen benutzt, denen man dann eigene onClickListener in der getView Methode zuweist. Für EditText ist das nicht praktikabel, weil man dann nichts mehr reinschreiben kann. Insofern ist das Problem doch noch nicht gelöst.

Ich muss also den Klick dann erst noch an das EditText weitergeben und verarbeiten. Weiß aber noch nicht, wie ich das mache. Der andere Ansatz wäre jedem anderen View einen onClickListener zuzuweisen, was mich aber vor das Problem stellt, dass ich noch nicht weiß, wie ich die Position (also die row im Adapter) des Views ermittle, das geklickt wurde, da ich ja nur onClick und nicht onItemClick nutzen kann.

Übrigen ist es logisch, dass ein View den Klick konsumieren muss und das andere leer ausgeht. Woher soll Android wissen, ob ich, wenn ich auf das EditText klicke, das ListView oder eben das EditText anklicken will.


Gruß
 
Ähnliche Java Themen

Ähnliche Java Themen

Neue Themen


Oben