ListView addHeaderView hace que la posición aumente en uno?

97

A continuación se muestra un fragmento de código con ListView. Agregué una vista vacía y una vista de encabezado. Agregar headerView hace que la posición en onItemClick aumente en uno.

Entonces, sin el headerView el primer elemento de la lista tendría la posición 0, con el headerView la posición del primer elemento de la lista sería 1.

Esto causa errores en mi adaptador, por ejemplo, al llamar a getItem () y usar algunos otros métodos, ver más abajo. Cosa extraña: en el método getView () del adaptador, el primer elemento de la lista se solicita con la posición 0 incluso si se agrega headerView.

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ListView list = (ListView) viewSwitcher.findViewById(R.id.list);
    View emptyView = viewSwitcher.findViewById(R.id.empty);
    list.setEmptyView(emptyView);

    View sectionHeading = inflater.inflate(R.layout.heading, list, false);
    TextView sectionHeadingTextView = (TextView) sectionHeading.findViewById(R.id.headingText);
    sectionHeadingTextView.setText(headerText);
    list.addHeaderView(sectionHeading);

    list.setAdapter(listAdapter);

    list.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            listAdapter.getItem(position);
            //Do something with it
        }
    });
}

Algunos métodos de adaptador:

@Override
public int getViewTypeCount() {
    return TYPE_COUNT;
}

@Override
public int getItemViewType(int position) {
    return (position < items.size()) ? ITEM_NORMAL : ITEM_ADVANCED;
}

    @Override
public Product getItem(int position) {
    return items.get(position);
}
@Override
public boolean areAllItemsEnabled() {
    return false;
}

@Override
public boolean isEnabled(int position) {
    return (position < items.size());
}

¿Es este el comportamiento normal al agregar un headerView? ¿Y cómo solucionar los problemas de mi adaptador?

d1rk
fuente
1
Tu pregunta tenía la respuesta a mi pregunta. +1
Shashank Degloorkar

Respuestas:

113

Me acabo de encontrar con este problema y la mejor manera parece usar el en ListView.getItemAtPosition(position)lugar de ListAdapter.getItem(position)como la ListViewversión da cuenta de los encabezados, es decir: -

Haz esto en su lugar:

myListView.getItemAtPosition(position)
Ian Warwick
fuente
1
¡Muy bien! onItemClickte da parentuna razón. Puede usarparent.getItemAtPosition(position)
Brais Gabin
3
En realidad, esto no funcionó para mí, mientras que usar la solución de adaptador de envoltura / decoración de @whuandroid sí.
Dori
3
también tienes que lanzar el artículo en este caso.
njzk2
No funciona para mí incluso si envío a los padres a ListView. Solo necesito descartar la posición == 0 ...
PawelP
No me funciona. AdapterViewtambién pasa al listAdapter agregado. return (adapter == null || position < 0) ? null : adapter.getItem(position);
philipp
31

Si no le importa el clic en el encabezado, reste el número de vistas del encabezado de la posición para obtener la posición de su adaptador:

        listView.addHeaderView(inflater.inflate(
                R.layout.my_hdr_layout, null), null, false);
        listView.setAdapter(m_adapter);
        listView.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view,
                int position, long id) {
            position -= listView.getHeaderViewsCount();
            final MyObject object = m_adapter.getItem(position);

        }
    });
farid_z
fuente
Esta debería ser la mejor respuesta, no me importa el encabezado, me importa el elemento de la lista.
Ninja
Creo que esta solución no funciona para el último sistema operativo 7.0
sha
27

Si agrega un encabezado a, ListViewese se convierte en el primer elemento de la lista. Puede compensar esto anulando el getCount()para devolver un artículo menos, pero asegúrese de manejar el getItem(), getItemId()y así sucesivamente, ya que el encabezado sería el artículo en la posición 0.

akshaydashrath
fuente
6
Anular getCount () para devolver un elemento menos no funciona, porque me pierdo el último elemento de la lista. Si solo hay 1 elemento en la lista, nunca se llama a getView (). Además, el adaptador está desacoplado de la vista de lista y no sabe si se agregó un encabezado a la vista de lista o no.
d1rk
2
En el ListItemClick set position = position - 1 si hay un encabezado adjunto, creo que eso es lo que hice cuando enfrenté este problema en particular, no es muy ordenado, pero es la única forma en la que puedo pensar
akshaydashrath
3
Supongo que la forma más fácil sería establecer position = position- (número de vistas de encabezado agregadas) en OnItemClickListener como sugirió. Cambié mi adaptador para manejar la vista de encabezado en sí, por lo que no tengo que llamar a addHeaderView en la vista de lista. Aunque el adaptador se volvió un poco más complejo.
d1rk
22
El "número de vistas de encabezado agregadas" se puede obtener con:ListView.getHeaderViewsCount()
Juan
2
No veo cómo esta es la respuesta correcta. @Ian Warwick publicó el correcto, aunque supongo que es un poco tarde.
Sidón
14

Al implementar AdapterView.OnItemClickListener, simplemente restamos los encabezados de la posición.

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    position -= mListView.getHeaderViewsCount();
    // what ever else follows...
}
philipp
fuente
13

No es necesario cambiar ningún código. Simplemente use el siguiente código.

 list.setOnItemClickListener(new OnItemClickListener() {
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
      parent.getAdapter().getItem(position);
         //Do something with it
     }
});
Ramesh Akula
fuente
1
Esto normalmente necesitará conversión, ya que el artículo que se devuelve es solo "Objeto".
desarrollador de Android
9

No use su adaptador, use el adaptador 'decorado' de getAdapter.

cycDroid
fuente
+1 este IMO es el camino a seguir: el adaptador que suministra está envuelto con otro adaptador que tiene en cuenta el desplazamiento del índice cuando proporciona un encabezado. Este código para esto lo usa @Ramesh en otra parte de esta página (¡+1 para usted también!)
Dori
5

Una solución a esto es mapear el id al index. Básicamente, haga que la identificación devuelva el índice en la lista y el oyente de clics lo haría:

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            listAdapter.getItem((int) id);
}
Heinrisch
fuente
3

Hola, podemos resolver esto de esta manera, primero crea su vista de encabezado, después de inyectar en su lista y el último paso no puede establecer el setOnClickListener. funciona para mí. cualquier comentario bienvenido

ViewGroup headerView = (ViewGroup) getLayoutInflater().inflate(R.layout.your_header, list,false);
list.addHeaderView(headerView);
headerView.setEnabled(false);
headerView.setOnClickListener(null);
Ezequiel García
fuente
Está muy abajo en la lista de respuestas, pero esto es realmente útil. Si no realiza estos cambios, se podrá hacer clic en la vista del encabezado, con efectos dominó, etc.
Scott Ferguson
0

No para agregar muchas cosas nuevas que @Ramesh no agregó, pero algo de contexto adicional:

personList.setOnItemClickListener(new OnItemClickListener() {
    @SuppressWarnings("unchecked")
    public void onItemClick( AdapterView<?> parent, View view, int position, long duration) {
        Map<String, Object> person = (Map<String, Object>) parent.getAdapter().getItem(position);
        if (person != null){

            // If the header was clicked on a null  is returned

            OnPersonUiChangeListener listener = (OnPersonUiChangeListener) getActivity();
            listener.onSelectedPersonChanged(person);
        }
    }
});
Peter Shannon
fuente
0

Use getSelectedItemPosition () Devuelve la posición del elemento seleccionado actualmente dentro del conjunto de datos del adaptador. Este método no necesita preocuparse por el tamaño de los encabezados o pies de página.

luhaiwork
fuente
0

Esto podría ayudar a alguien, mejorando la respuesta de Farid_z y Philipp, podemos aplicar correctamente setItemChecked y setTitle con algo como esto

        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
        position += 1;
        mDrawerList.setItemChecked(position, true);
        mDrawerList.setSelection(position);
        position -= 1;
        setTitle(mNavigationDrawerItemTitles[position]);
        mDrawerLayout.closeDrawer(mDrawerList);
truespan
fuente