# OnItemLongClickListener in ListActivity spricht nicht an



## kurztipp (6. Dez 2013)

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:


```
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 (6. Dez 2013)

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 (6. Dez 2013)

Hallo,



dzim hat gesagt.:


> 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:

```
@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 
	
	
	
	





```
onCreate()
```


```
MyArrayAdapter<MyObject> mAdapter = new MyArrayAdapter<MyObject>(this,
        R.layout.layout,
        R.id.textView, new ArrayList<MyObject>());

setListAdapter(mAdapter);
```
 durch

```
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ß


----------



## dzim (6. Dez 2013)

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

```
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"
    androidrientation="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 ;-) )

```
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 
	
	
	
	





```
#getView()
```
 bzw. 
	
	
	
	





```
getDrowpDownView()
```
 (für Auswahlboxen, eigentlich unnötig, kostet mich aber kaum Arbeit, wie man sieht) sowieso durch das hier 
	
	
	
	





```
View v = mInflater.inflate(R.layout.drawer_row, parent, false);
```
 überschrieben.

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


----------



## kurztipp (6. Dez 2013)

Hallo,



dzim hat gesagt.:


> Dann poste doch mal a) dein Layout



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



Spoiler: Activity



[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"
	androidrientation="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"
		androidnClick="save"
		android:text="@string/BTN_save" />

</LinearLayout>[/XML]





Spoiler: ListView Layout



[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"
	androidrientation="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 (6. Dez 2013)

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"
    androidrientation="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:

```
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 (6. Dez 2013)

Hallo,



dzim hat gesagt.:


> *#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:
> 
> ...



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.) 
	
	
	
	





```
android:descendantFocusability="blocksDescendants"
```
 mit auf den Weg zu geben.

Gruß


----------



## dzim (7. Dez 2013)

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


----------



## kurztipp (7. Dez 2013)

kurztipp hat gesagt.:


> Am einfachsten ist, im Layout des ListViews dem Wurzelelement (LinearLayout und dergl.)
> 
> 
> 
> ...



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ß


----------

