Как изменить цвет фона для определенной строки в ListView? (Android)

Я провел несколько дней, пытаясь решить проблему, которая у меня есть с ListViews на Android. Я хотел бы реализовать единый список выбора, используя ListView. Итак, я хотел бы иметь только одну строку с предопределенным цветом фона, а остальное – с другим выделенным цветом. Проблема в том, что когда я нажимаю на определенную строку, это еще одна строка, которая подсвечивается, а не та, которую я нажал. Я добавил несколько сообщений в журнал, что происходит, но все работает нормально. Вот мой код:

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); } } 

Внутри ресурсов (res / layout) я вставил файл с именем list_text_entry.xml со следующим содержимым

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

Например, если я щелкнул элемент «Вариант 11», когда listView ранее прокручивался до тех пор, пока первая строка, которую я вижу, не будет «Вариант 4», строка «Вариант 7» будет выбрана как выбранная, единственная, несущая синий цвет на заднем фоне. Может кто-нибудь объяснить, что здесь происходит со мной? Я публикую ниже сообщения, которые я выбрал для журнала.

 [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 

Как я могу догадаться, что все дети внутри ViewGroup упорядочены сверху донизу, и даже когда я делаю это в коде:

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

Появляется сообщение Option 11 но на самом деле выбран option 7 . Это ошибка от Android?

Я использовал этот код, он хорошо работает, вы должны попробовать следующее:

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

Да, вы должны использовать, как сказал Дэйв:

 view.setBackgroundColor(SELECTED_COLOR); 

и, возможно

 view.refreshDrawableState(); 

Однако, поскольку списки перезаписи Android, он будет повторять выбранный вами цвет на каждом первом элементе, который не отображается на экране. Поэтому, если ваш размер экрана может отображать десять элементов, чем 11, 21 и т.д., будет также отображаться как выбранный при прокрутке.

Чтобы этого избежать, вы должны создать пользовательский адаптер. Затем в getView вам нужно сказать следующее:

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

Где selectedRow является public static int selectedRow в myActivity, который создает ваш список. Там вы храните номер строки, который выбирается при нажатии на список.

Спасибо Dave и ChristianB за ваши ответы. Я до сих пор не знаю, почему Android делает это, поэтому вопрос по-прежнему остается нерешенным. Тем не менее, я нашел способ получить то, что мне нужно, создать пользовательский адаптер. Я дам вам код, если кому-то это понадобится.

 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; } } 

Итак, в инициализации ListView мне просто нужно поставить этот прослушиватель:

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

Это не эффективное решение из-за того, что я расширяюсь от ArrayAdapter, который должен иметь копию всех предоставленных данных. Таким образом, я использую гораздо больше памяти, чем нужно, и в случае, если список становится очень большим, я мог бы иметь проблемы с памятью. Итак, если кто-то знает лучшее решение, напишите!

Я считаю, что это связано с тем, как число ListViews и повторное использование строк. Поэтому вместо того, чтобы использовать parent.getChildAt(i) чтобы получить строку, которую вы хотите обработать, используйте объект View переданный самому onItemClick .

 view.setBackgroundColor(SELECTED_COLOR); 

Listview.setOnItemClickListener (новый 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)); } } } }); 

В принципе, создайте две чертежи – прозрачную, а другую – желаемый цвет. Запросите фокус в нажатой позиции (позиция int как определено) и измените цвет указанной строки. Затем пройдите через родительский список и соответствующим образом измените все остальные строки. Это объясняется тем, что пользователь несколько раз нажимает на список.

  @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; } 

попробуй это,

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

Переопределить метод getView () вашего адаптера:

  @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; }