android - submenus - ListView personalizado y menú contextual. ¿Cómo conseguirlo?
popup menu android (6)
Tengo dos archivos de diseños en mi aplicación. También tengo actividad extiende ListActivity. Cada elemento de esta actividad parece tener en cuenta el archivo de diseño item.xml. Estoy tratando de obtener el menú contextual cuando hago pulsaciones largas en el elemento, pero no lo veo.
En mi actividad intenté registrarForContextMenu (getListView ()) y anular dos métodos
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = this.getIntent().getExtras();
registerForContextMenu(getListView());
new PopulateAdapterTask().execute(ACTION_SELECT);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info;
try {
info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
} catch (ClassCastException e) {
return false;
}
long id = getListAdapter().getItemId(info.position);
Log.d(TAG, "id = " + id);
return true;
}
Main.xml
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp">
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</FrameLayout>
</LinearLayout>
</TabHost>
Item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="15sp"
android:singleLine="true"
android:ellipsize="marquee"
android:scrollHorizontally = "true"
android:maxWidth="200dp"
/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="right"
>
<ImageButton
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:background="@null"
android:paddingRight="10dp"
android:paddingLeft="10dp"
/>
</LinearLayout>
</LinearLayout>
Todo esto no funciona. Tal vez la razón está en LinearLayout? También encuentro un tema similar en Android: ¿No aparece el menú contextual para ListView con miembros definidos por LinearLayout? Pero tengo un elemento de lista más complicado.
¿Cómo obtener el menú contextual en mi caso?
También en mi actividad tengo clase interna extiende ArrayAdapter. En esta clase del método getView puedo configurar OnCreateContextMenuListener en cada Vista, después de que aparezca el menú contextual, pero no sé cómo manejar los clics de los elementos. Si trato de hacer esto en el método onContextItemSelected, el objeto item.getMenuInfo () siempre es nulo y no puedo obtener información de él.
private class ChannelAdapter extends ArrayAdapter<Channel> {
private List<Channel> channels;
public ChannelAdapter(Context context, int textViewResourceId, List<Channel> objects) {
super(context, textViewResourceId, objects);
this.channels = objects;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.station_item, null);
}
v.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
}
});
Gracias. Espero tu ayuda.
El problema básico es que el elemento está siendo dibujado por el segundo diseño de item.xml, por lo que el elemento raíz (LinearLayout) es lo que se está presionando en vez de lo que proporcionó el ListView original. Como tal, cuando infla el diseño item.xml, debe llamar a setOnCreateContextMenuListener como lo ha hecho en el segundo ejemplo. El problema con esto es que no hay forma de que el diseño del item.xml (que es un LinearLayout) se comunique de nuevo a la Actividad en la que se eligió la posición. Esto se debe a que LinearLayout no anula el método getContextMenuInfo () que en un ListView devuelve un AdapterView.AdapterContextMenuInfo (como todo el mundo parece obligar a su ContextMenuInfo a).
Así que, idealmente, usted desea crear su propio descendiente LinearLayout que hace público a getContextMenuInfo, crea uno falso si no lo hay, y cuando se llama onCreateContextMenu en su adaptador personalizado, lo toma de su LinearLayout personalizado y coloca la posición / Identificación allí, que tu actividad puede sacar.
Esto es lo que he hecho en mi propia aplicación y funciona muy bien y es una solución genérica. De hecho, puede poner cualquier cosa que desee allí siempre que implemente la interfaz ContextMenuInfo (que es solo una interfaz de marcador).
En realidad, solo necesitas hacer que se pueda hacer clic en Ver larga llamando
v.setLongClickable(true);
No es necesario configurar el conjunto ficticio setOnCreateContextMenuListener
, porque hace exactamente eso: el elemento hace clic en el elemento durante mucho tiempo.
Los siguientes segmentos de código de la clase anidada ComplexObjectAdapter, enumerados en la respuesta de Georgy Gobozov, no son realmente necesarios:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.item, null);
}
final ComplexObject o = objects.get(position);
if (o != null) {
TextView textlInfo = (TextView) v.findViewById(R.id.info);
textlInfo.setText(o.getName());
ImageView channelIcon = (ImageView) v.findViewById(R.id.icon);
channelIcon.setAdjustViewBounds(true);
channelIcon.setMaxHeight(30);
channelIcon.setMaxWidth(30);
channelIcon.setImageDrawable(o.getLogo());
ImageButton button = (ImageButton) v.findViewById(R.id.button);
button.setImageResource(R.drawable.icon);
// NOT NEEDED
v.setOnCreateContextMenuListener(this);
}
return v;
}
// NOT NEEDED
public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
// empty implementation
}
Simplemente funciona porque dentro de la función setOnCreateContextMenuListener () desde la vista de clase, llama a la función setLongClickable (true):
/**
* Register a callback to be invoked when the context menu for this view is
* being built. If this view is not long clickable, it becomes long clickable.
*
* @param l The callback that will run
*
*/
public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) {
if (!isLongClickable()) {
setLongClickable(true);
}
mOnCreateContextMenuListener = l;
}
Esto significa que el problema se puede resolver configurando la propiedad de clic largo para cada elemento secundario, una vez creado:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.item, null);
// SET LONG CLICKABLE PROPERTY
v.setLongClickable(true);
}
final ComplexObject o = objects.get(position);
if (o != null) {
TextView textlInfo = (TextView) v.findViewById(R.id.info);
textlInfo.setText(o.getName());
ImageView channelIcon = (ImageView) v.findViewById(R.id.icon);
channelIcon.setAdjustViewBounds(true);
channelIcon.setMaxHeight(30);
channelIcon.setMaxWidth(30);
channelIcon.setImageDrawable(o.getLogo());
ImageButton button = (ImageButton) v.findViewById(R.id.button);
button.setImageResource(R.drawable.icon);
// NOT NEEDED
// v.setOnCreateContextMenuListener(this);
}
return v;
}
o también se puede resolver estableciendo esta propiedad en el archivo de diseño XML de los elementos secundarios de la vista de lista, por ejemplo:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:longClickable="true">
<!-- Child elements -->
</LinearLayout>
No creo que quieras adjuntar un menú contextual a los elementos específicos de listView. Al llamar a registerForContextMenu (getListView ()), debe obtener esa funcionalidad de forma gratuita. Conectaría su aplicación a un depurador después de eliminar los enganches contextMenu del código del adaptador y establecer un punto de interrupción dentro de onCreateContextMenu (). Mi sospecha es que se está llamando, pero el diseño que se está inflando no es lo que se espera.
No sé por qué, es necesario establecer un OnCreateContextMenuListener nulo en cada fila de la lista (además de registerForContextMenu (...) e implementar onCreateContextMenu (...) y onContextItemSelected (...)
Tengo solución, mi amigo me ayudó! Espero que esta información sea de ayuda para alguien. Este es un código de clase completo con ArrayAdapter y un diseño de lista complejo y un menú contextual.
public class ComplexListActivity extends ListActivity {
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new ComplexObjectAdapter(this, R.layout.item, getComplexObjects()));
registerForContextMenu(getListView());
}
private List getComplexObjects() {
List<ComplexObject> list = new ArrayList<ComplexObject>();
list.add(new ComplexObject("1", "1", getResources().getDrawable(R.drawable.icon)));
list.add(new ComplexObject("2", "2", getResources().getDrawable(R.drawable.icon)));
list.add(new ComplexObject("3", "3", getResources().getDrawable(R.drawable.icon)));
list.add(new ComplexObject("4", "4", getResources().getDrawable(R.drawable.icon)));
list.add(new ComplexObject("5", "5", getResources().getDrawable(R.drawable.icon)));
list.add(new ComplexObject("6", "6", getResources().getDrawable(R.drawable.icon)));
list.add(new ComplexObject("7", "7", getResources().getDrawable(R.drawable.icon)));
list.add(new ComplexObject("8", "8", getResources().getDrawable(R.drawable.icon)));
list.add(new ComplexObject("9", "9", getResources().getDrawable(R.drawable.icon)));
return list;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.context_menu, menu);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info;
try {
info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
} catch (ClassCastException e) {
Log.e("", "bad menuInfo", e);
return false;
}
long id = getListAdapter().getItemId(info.position);
Log.d("", "id = " + id);
Toast.makeText(this, "id = " + id, Toast.LENGTH_SHORT).show();
return true;
}
private class ComplexObjectAdapter extends ArrayAdapter<ComplexObject> implements View.OnCreateContextMenuListener {
private List<ComplexObject> objects;
public ComplexObjectAdapter(Context context, int textViewResourceId, List<ComplexObject> objects) {
super(context, textViewResourceId, objects);
this.objects = objects;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.item, null);
}
final ComplexObject o = objects.get(position);
if (o != null) {
TextView textlInfo = (TextView) v.findViewById(R.id.info);
textlInfo.setText(o.getName());
ImageView channelIcon = (ImageView) v.findViewById(R.id.icon);
channelIcon.setAdjustViewBounds(true);
channelIcon.setMaxHeight(30);
channelIcon.setMaxWidth(30);
channelIcon.setImageDrawable(o.getLogo());
ImageButton button = (ImageButton) v.findViewById(R.id.button);
button.setImageResource(R.drawable.icon);
v.setOnCreateContextMenuListener(this);
}
return v;
}
public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
// empty implementation
}
}
}
Déjame saber si alguien encontrará un mejor enfoque. ¡Gracias!