¿Cómo puedo cambiar el color de fondo de una fila específica correctamente en un ListView? (Androide)

He pasado algunos días tratando de resolver un problema que tengo con ListViews en Android. Me gustaría implementar un cuadro de lista de selección única usando un ListView. Por lo tanto, me gustaría tener solo una fila con un color de fondo claro predefinido y el resto con otro color preseleccionado. El problema que tengo es que cuando hago clic en una fila específica, hay otra fila que está resaltada y no la que presioné. Agregué varios mensajes para registrar lo que está sucediendo, pero todo parece funcionar bien. Aquí está mi código:

public class TryListViewActivity extends Activity {
    protected static final int NO_SELECTED_COLOR = 0xFF191919;
    protected static final int SELECTED_COLOR = 0xFF3366CC;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ListView listView = new ListView(this);
        ArrayList<String> list = new ArrayList<String>();
        list.add("Option 1");
        list.add("Option 2");
        list.add("Option 3");
        list.add("Option 4");
        list.add("Option 5");
        list.add("Option 6");
        list.add("Option 7");
        list.add("Option 8");
        list.add("Option 9");
        list.add("Option 10");
        list.add("Option 11");
        list.add("Option 12");
        list.add("Option 13");
        list.add("Option 14");
        list.add("Option 15");

        ArrayAdapter<String> listAdapter = new ArrayAdapter<String>(
            this,
            R.layout.list_box_entry,
            list
        );
        listView.setAdapter(listAdapter);

        listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

        // Set the listener
        listView.setOnItemClickListener(
            new AdapterView.OnItemClickListener() {
                public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                    Log.i(
                        "Log",
                        "[SingleSelectionListBox] Item clicked: position="
                        + position + ";id=" + id
                    );

                    // First, set all rows to be unselected
                    int counter  = parent.getCount();
                    Log.i(
                        "Log",
                        "[SingleSelectionListBox] "
                        + counter + " items found inside the parent"
                    );
                    int children = parent.getChildCount();
                    Log.i(
                        "Log",
                        "[SingleSelectionListBox] "
                        + children + " views found inside the parent"
                    );
                    for(int i=0;i<children;i++) {
                        Log.i(
                            "Log",
                            "[SingleSelectionListBox] Child "
                            + i + " has message "
                            + ((TextView)parent.getChildAt(i)).getText()
                        );
                    }

                    // Too inefficient but for now is OK
                    for(int i=0;i<children;i++)
                        parent.getChildAt(i)
                            .setBackgroundColor(NO_SELECTED_COLOR);
                    Log.i("Log",
                        "[SingleSelectionListBox] First visible position: "
                        + parent.getFirstVisiblePosition()
                    );

                    // Set the background color
                    TextView textView = (TextView)(parent.getChildAt(
                        position-parent.getFirstVisiblePosition()));
                    textView.setBackgroundColor(SELECTED_COLOR);
                    Log.i(
                        "Log",
                        "[SingleSelectionListBox] Text inside the "
                        + " View changing the color " + textView.getText()
                    );
                }
            }
        );
        setContentView(listView);
    }
}

Dentro de los recursos (res / layout) inserté un archivo llamado list_text_entry.xml con el siguiente contenido

<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
  android:layout_height="fill_parent" android:gravity="center"
  android:textColor="#FFFFFF" android:padding="10dp" android:textSize="16sp">
</TextView>

Por ejemplo, si hago clic sobre la entrada "Opción 11" cuando listView se ha desplazado hacia abajo hasta que la primera fila que veo es "Opción 4", la fila "Opción 7" aparece como seleccionada siendo la única que tiene un color azul. en el fondo. ¿Alguien puede explicarme lo que me pasa aquí? Publico debajo de los mensajes que recibí lanzando el Registro.

[SingleSelectionListBox] Item clicked: position=10;id=10
[SingleSelectionListBox] 15 items found inside the parent
[SingleSelectionListBox] 11 views found inside the parent
[SingleSelectionListBox] Child 0 has message Option 4
[SingleSelectionListBox] Child 1 has message Option 5
[SingleSelectionListBox] Child 2 has message Option 6
[SingleSelectionListBox] Child 3 has message Option 7
[SingleSelectionListBox] Child 4 has message Option 8
[SingleSelectionListBox] Child 5 has message Option 9
[SingleSelectionListBox] Child 6 has message Option 10
[SingleSelectionListBox] Child 7 has message Option 11
[SingleSelectionListBox] Child 8 has message Option 12
[SingleSelectionListBox] Child 9 has message Option 13
[SingleSelectionListBox] Child 10 has message Option 14
[SingleSelectionListBox] First visible position: 3
[SingleSelectionListBox] Text inside the View changing the color Option 11

Como puedo adivinar, todos los niños dentro de ViewGroup están ordenados de arriba a abajo e incluso cuando hago esto en el código:

TextView textView = (TextView)(parent.getChildAt(
    position-parent.getFirstVisiblePosition()
));
textView.setBackgroundColor(SELECTED_COLOR);

Aparece el mensaje Option 11 pero es realmente el option 7 seleccionado. ¿Es esto un error de Android?

preguntado el 08 de enero de 11 a las 16:01

La próxima vez, utilice cuatro espacios en blanco para marcar el código en lugar de utilizar etiquetas HTML. Ver el Ayuda de edición de Markdown página para más detalles. -

7 Respuestas

He usado este código, funciona bien, deberías probar esto:

listView.getChildAt(0).setBackgroundColor(Color.BLUE);

contestado el 06 de mayo de 15 a las 12:05

Sí, deberías usar como dijo Dave:

view.setBackgroundColor(SELECTED_COLOR);

y quizás

view.refreshDrawableState(); 

sin embargo, debido a que las listas de reciclaje de Android, repetirá el color seleccionado en cada primer elemento que no se muestre en la pantalla. Entonces, si el tamaño de su pantalla puede mostrar diez elementos, el 11, el 21, etc., también se mostrarán como seleccionados cuando se desplace.

Para evitar esto, debe crear un adaptador personalizado. Luego, en getView, debe decir esto:

if (myActivity.selectedRow != position){
    v.setBackgroundColor(Color.TRANSPARENT);
} else {
    v.setBackgroundColor(SELECTED_COLOUR);
}

Donde selectedRow es un public static int selectedRow dentro de myActivity, la actividad que crea su lista. Allí almacena el número de fila que se selecciona al hacer clic en la lista.

Respondido el 17 de junio de 12 a las 12:06

Gracias Dave y ChristianB por sus respuestas. Sigo sin saber por qué lo hace Android, por lo que la pregunta sigue sin resolverse. Sin embargo, encontré una manera de obtener lo que necesitaba creando un Adaptador personalizado. Te daré el código en caso de que alguien pueda necesitarlo.

public class CustomAdapter extends ArrayAdapter<String> {

    protected static final int NO_SELECTED_COLOR = 0xFF191919;
    protected static final int SELECTED_COLOR = 0xFF3366CC;

    private ArrayList<String> items;
    private LayoutInflater mInflater;
    private int viewResourceId;
    private int selectedPosition;

    public CustomAdapter(Activity activity,int resourceId,
        ArrayList<String> list) {
        super(activity,resourceId,list);

        // Sets the layout inflater
        mInflater = (LayoutInflater)activity
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        // Set a copy of the layout to inflate
        viewResourceId = resourceId;

        // Set a copy of the list
        items = list;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TextView tv = (TextView)convertView;
        if (tv == null) {
            tv = (TextView)mInflater.inflate(viewResourceId, null);
        }
        tv.setText(items.get(position));

        // Change the background color
        if (position==selectedPosition) tv.setBackgroundColor(SELECTED_COLOR);
        else tv.setBackgroundColor(NO_SELECTED_COLOR);

        return tv;
    }

    public void setSelected(int position) {
        selectedPosition = position;
    }
}

Entonces, en la inicialización de ListView solo necesito poner este oyente:

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    public void onItemClick(AdapterView<?> parent, View view,
        int position, long id) {
        ((CustomAdapter)listAdapter).setSelected(position);
        listView.invalidate();
    }
});

Esta no es una solución eficiente debido a que me estoy extendiendo desde ArrayAdapter, que se supone que tiene una copia de todos los datos proporcionados. Por lo tanto, estoy usando mucha más memoria de la necesaria y, en caso de que la lista sea muy grande, podría tener problemas de memoria. Entonces, si alguien conoce una solución mejor, ¡publíquela!

Respondido el 17 de junio de 12 a las 12:06

Inflar cada vista es una buena solución a este problema. Si le preocupan las implicaciones de la memoria (que, a menos que esté mostrando miles de Stringno es una gran preocupación) es posible que desee utilizar CursorAdapter en su lugar, y almacene sus datos en una base de datos SQLite. - David

Creo que esto se debe a la forma en que ListViews numera y reutiliza las filas. Entonces, en lugar de usar parent.getChildAt(i) para obtener la fila que desea manipular, use el View objeto pasado a onItemClick misma.

view.setBackgroundColor(SELECTED_COLOR);

Respondido el 08 de enero de 11 a las 19:01

Hola Dave, lamento decir que lo probé antes y que el resultado de usar directamente la Vista proporcionada en la función es exactamente el mismo. Por esta razón, traté de encontrar una forma alternativa más compleja, pero no funciona tan bien. ¡Pero gracias por tu respuesta! - Charlie

listview.setOnItemClickListener (nuevo AdapterView.OnItemClickListener () {

  @Override
public void onItemClick(AdapterView<?> parent, final View view, int position, long   id) 
{

    View v;

    int count = parent.getChildCount();
    v =parent.getChildAt(position);
    parent.requestChildFocus(v, view);  v.setBackground(res.getDrawable(R.drawable.transparent_button));

            for (int i=0; i<count; i++)
            {
                if (i!= position)
                {
                    v = parent.getChildAt(i);t  v.setBackground(res.getDrawable(R.drawable.not_clicked));

                }
            }

        }

    });

Básicamente, cree dos elementos de diseño: uno transparente y otro del color deseado. Solicite el enfoque en la posición en la que se hizo clic (posición int como se define) y cambie el color de dicha fila. Luego, recorra la vista de lista principal y cambie todas las demás filas en consecuencia. Esto tiene en cuenta cuando un usuario hace clic en la vista de lista varias veces.

Respondido el 27 de junio de 13 a las 17:06

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
    {
   LayoutInflater inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

  View itemView = inflater.inflate(R.layout.verselayout, parent, false); 
    txttitle = (TextView) itemView.findViewById(R.id.Versetxt);
    if (position%2 == 0) {
       txttitle.setTextColor(Color.parseColor("#FFFFFF"));
    }
 else
       {
    txttitle.setTextColor(Color.parseColor("#FFFF00"));
       }

     return itemView;
    }

Respondido el 24 de Septiembre de 15 a las 14:09

prueba esto,

OnItemClickListener onitemclick = new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> adapter, View arg1, int position, long id) {
                selectedItem= position;
                adapter.notifyDataSetChanged();
        }
    };

anule el método getView () de su adaptador:

 @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final View view = View.inflate(context, R.layout.item_list, null);

        if (position == selectedItem) {
            // set your color
        }

        return view;
    }

Respondido el 24 de Septiembre de 15 a las 15:09

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.