Выделение выпадающего списка вверху экрана в списке просмотра после выбора элемента

Мне интересно, почему использование ListView в layout_height = "wrap_content" помешает Spinners в конце списка. Я пропустил различные способы его крепления ниже. Я надеюсь, кто-то может объяснить поведение, или указать, какие знания андроида мне не хватает в рисовании просмотров / ui-событий.

1) Визуальную проблему можно увидеть здесь .

2) После изменения свойства ListItem

android:descendantFocusability="afterDescendants" 

Я получаю лучшее поведение, но что-то происходит. Кажется, что элементы внутри списка не получают события, поэтому изменение свойств имеет смысл для меня. Вот видео о том, как ведут себя спиннеры после обновления этого свойства. Все работает отлично, за исключением случаев, когда я на самом деле выбираю элемент.

3) После установки layout_height = "match_parent" ListView проблема, кажется, исчезает после выбора элемента. Смотрите здесь видео.

Активность:

 public class SelectorActivity extends Activity { public static final String TAG = SelectorActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.v(TAG, "onCreate"); setContentView(R.layout.activity_selector); LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); ListView contents = (ListView) findViewById(R.id.list_view); contents.addHeaderView(new TestView(this)); contents.addFooterView(new View(this)); SimpleBaseAdapter listAdapter = new SimpleBaseAdapter(this); // LOW RANGE LinearLayout lowRange = (LinearLayout) inflater.inflate(R.layout.list_item_edit, null); TextView lowRangeText = (TextView) lowRange.findViewById(R.id.text); EditText lowRangeEditText = (EditText) lowRange.findViewById(android.R.id.edit); // HIGH RANGE LinearLayout highRange = (LinearLayout) inflater.inflate(R.layout.list_item_edit, null); TextView highRangeText = (TextView) highRange.findViewById(R.id.text); EditText highRangeEditText = (EditText) highRange.findViewById(android.R.id.edit); // UNITS LinearLayout units = (LinearLayout) inflater.inflate(R.layout.list_item_units, null); TextView unitsText = (TextView) units.findViewById(android.R.id.text1); // SPINNERS LinearLayout spinners = (LinearLayout) inflater.inflate(R.layout.list_item_spinners, null); Spinner spinner1 = (Spinner) spinners.findViewById(R.id.spinner1); Spinner spinner2 = (Spinner) spinners.findViewById(R.id.spinner2); Spinner spinner3 = (Spinner) spinners.findViewById(R.id.spinner3); DebugAdapterViewListeners.set(spinner1, "spinner1"); // VIEW SETUP lowRangeText.setText("text1"); highRangeText.setText("text2"); unitsText.setText("text3"); // SPINNER SETUP String[] massUnits1 = new String[]{"one","two"}; String[] massUnits2 = new String[]{"three","four"}; String[] timeUnits = new String[]{"five","six"}; ArrayAdapter<String> adapt1 = new ArrayAdapter<String>(this, R.layout.spinner_list_item_centered); ArrayAdapter<String> adapt2 = new ArrayAdapter<String>(this, R.layout.spinner_list_item_centered); ArrayAdapter<String> adapt3 = new ArrayAdapter<String>(this, R.layout.spinner_list_item_centered); adapt1.addAll(massUnits1); adapt2.addAll(massUnits2); adapt3.addAll(timeUnits); spinner1.setAdapter(adapt1); spinner2.setAdapter(adapt2); spinner3.setAdapter(adapt3); listAdapter.addView(lowRange); listAdapter.addView(highRange); listAdapter.addView(units); listAdapter.addView(spinners); contents.setAdapter(listAdapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.selector, menu); return false; } } 

Вот класс SimpleBaseAdapter:

 public class SimpleBaseAdapter extends BaseAdapter { private ArrayList<View> views; private Context context; public SimpleBaseAdapter(Context context) { this.context = context; this.views = new ArrayList<View>(); } public void addView(View view) { this.views.add(view); } @Override public int getCount() { return views.size(); } @Override public Object getItem(int position) { View view = views.get(position); if (view instanceof AbsListView) { return ((AbsListView)view).getItemAtPosition(position); } else if (view instanceof AbsSpinner) { return ((AbsSpinner)view).getItemAtPosition(position); } else { return null; } } @Override public long getItemId(int position) { View view = views.get(position); if (view instanceof AbsListView) { return ((AbsListView)view).getItemIdAtPosition(position); } else if (view instanceof AbsSpinner) { return ((AbsSpinner)view).getItemIdAtPosition(position); } else { return 0; } } @Override public View getView(int position, View convertView, ViewGroup parent) { return views.get(position); } } 

Схема работы:

 <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" android:background="@color/green_1" android:orientation="vertical" > <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:headerDividersEnabled="true" android:footerDividersEnabled="true" android:dividerHeight="0.5sp" android:divider="@color/black" android:clipToPadding="false" android:layout_marginTop="18sp" android:layout_marginBottom="18sp" /> </LinearLayout> 

Редактирование списка элементов списка:

 <?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:orientation="horizontal" android:padding="@dimen/row_padding" android:background="@android:color/white" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/text" android:layout_weight="50" android:gravity="top" android:textSize="@dimen/font_size_standard" android:textColor="@drawable/selector_row_item_detail_text" /> <EditText android:layout_width="0dip" android:layout_height="wrap_content" android:id="@android:id/edit" android:layout_weight="50" android:inputType="number" android:gravity="right" /> </LinearLayout> 

Элемент строки строки:

 <?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:id="@+id/spinner_container" android:orientation="horizontal" android:background="@android:color/white" android:paddingTop="@dimen/header_row_padding_vertical" > <Spinner android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="33" android:id="@+id/spinner1" android:gravity="center" android:spinnerMode="dropdown" /> <Spinner android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="33" android:id="@+id/spinner2" android:gravity="center" android:spinnerMode="dialog" /> <Spinner android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="33" android:id="@+id/spinner3" android:gravity="center" android:spinnerMode="dialog" /> </LinearLayout> 

Solutions Collecting From Web of "Выделение выпадающего списка вверху экрана в списке просмотра после выбора элемента"

Проблема, с которой вы столкнулись, – это основное поведение Spinner, поэтому необходим модифицированный Spinner. Это код для счетчика, чья начальная, т.е. По умолчанию визуализация представляет собой «Выбрать элемент» (если приглашение объявлено в .xml, например, android:prompt="@string/Select Item" ), а раскрывающийся список имеет тот же размер оригинального счетчика. Ограничение этого модифицированного счетчика заключается в том, что он не отображает приглашение, если элементы пустые.

Создайте новый класс с именем NoDefaultSpinner.java и в этой копии вставьте этот код

 public class NoDefaultSpinner extends Spinner { public NoDefaultSpinner(Context context) { super(context); } public NoDefaultSpinner(Context context, AttributeSet attrs) { super(context, attrs); } public NoDefaultSpinner(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public void setAdapter(SpinnerAdapter orig ) { final SpinnerAdapter adapter = newProxy(orig); super.setAdapter(adapter); try { final Method m = AdapterView.class.getDeclaredMethod( "setNextSelectedPositionInt",int.class); m.setAccessible(true); m.invoke(this,-1); final Method n = AdapterView.class.getDeclaredMethod( "setSelectedPositionInt",int.class); n.setAccessible(true); n.invoke(this,-1); } catch( Exception e ) { throw new RuntimeException(e); } } protected SpinnerAdapter newProxy(SpinnerAdapter obj) { return (SpinnerAdapter) java.lang.reflect.Proxy.newProxyInstance( obj.getClass().getClassLoader(), new Class[]{SpinnerAdapter.class}, new SpinnerAdapterProxy(obj)); } /** * Intercepts getView() to display the prompt if position < 0 */ protected class SpinnerAdapterProxy implements InvocationHandler { protected SpinnerAdapter obj; protected Method getView; protected SpinnerAdapterProxy(SpinnerAdapter obj) { this.obj = obj; try { this.getView = SpinnerAdapter.class.getMethod( "getView",int.class,View.class,ViewGroup.class); } catch( Exception e ) { throw new RuntimeException(e); } } public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { try { return m.equals(getView) && (Integer)(args[0])<0 ? getView((Integer)args[0],(View)args[1],(ViewGroup)args[2]) : m.invoke(obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } catch (Exception e) { throw new RuntimeException(e); } } protected View getView(int position, View convertView, ViewGroup parent) throws IllegalAccessException { if( position<0 ) { final TextView v = (TextView) ((LayoutInflater)getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE)).inflate( android.R.layout.simple_spinner_item,parent,false); v.setText(getPrompt()); return v; } return obj.getView(position,convertView,parent); } } } 

В макете строки строки spinner измените тип счетчика на <com.example.appname.NoDefaultSpinner как это

  <com.example.appname.NoDefaultSpinner android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="33" android:id="@+id/spinner1" android:gravity="center" android:spinnerMode="dropdown" /> <com.example.appname.NoDefaultSpinner android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="33" android:id="@+id/spinner2" android:gravity="center" android:spinnerMode="dialog" /> <com.example.appname.NoDefaultSpinner android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="33" android:id="@+id/spinner3" android:gravity="center" android:spinnerMode="dialog" /> 

Упражнение: измените тип Spinner на NoDefaultSpinner как это

 public class SelectorActivity extends Activity { public static final String TAG = SelectorActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.v(TAG, "onCreate"); setContentView(R.layout.activity_selector); LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); ListView contents = (ListView) findViewById(R.id.list_view); contents.addHeaderView(new TestView(this)); contents.addFooterView(new View(this)); SimpleBaseAdapter listAdapter = new SimpleBaseAdapter(this); // LOW RANGE LinearLayout lowRange = (LinearLayout) inflater.inflate(R.layout.list_item_edit, null); TextView lowRangeText = (TextView) lowRange.findViewById(R.id.text); EditText lowRangeEditText = (EditText) lowRange.findViewById(android.R.id.edit); // HIGH RANGE LinearLayout highRange = (LinearLayout) inflater.inflate(R.layout.list_item_edit, null); TextView highRangeText = (TextView) highRange.findViewById(R.id.text); EditText highRangeEditText = (EditText) highRange.findViewById(android.R.id.edit); // UNITS LinearLayout units = (LinearLayout) inflater.inflate(R.layout.list_item_units, null); TextView unitsText = (TextView) units.findViewById(android.R.id.text1); // SPINNERS LinearLayout spinners = (LinearLayout) inflater.inflate(R.layout.list_item_spinners, null); NoDefaultSpinner spinner1 = (NoDefaultSpinner) spinners.findViewById(R.id.spinner1); NoDefaultSpinner spinner2 = (NoDefaultSpinner) spinners.findViewById(R.id.spinner2); NoDefaultSpinner spinner3 = (NoDefaultSpinner) spinners.findViewById(R.id.spinner3); DebugAdapterViewListeners.set(spinner1, "spinner1"); // VIEW SETUP lowRangeText.setText("text1"); highRangeText.setText("text2"); unitsText.setText("text3"); // SPINNER SETUP String[] massUnits1 = new String[]{"one","two"}; String[] massUnits2 = new String[]{"three","four"}; String[] timeUnits = new String[]{"five","six"}; ArrayAdapter<String> adapt1 = new ArrayAdapter<String>(this, R.layout.spinner_list_item_centered); ArrayAdapter<String> adapt2 = new ArrayAdapter<String>(this, R.layout.spinner_list_item_centered); ArrayAdapter<String> adapt3 = new ArrayAdapter<String>(this, R.layout.spinner_list_item_centered); adapt1.addAll(massUnits1); adapt2.addAll(massUnits2); adapt3.addAll(timeUnits); spinner1.setAdapter(adapt1); spinner2.setAdapter(adapt2); spinner3.setAdapter(adapt3); listAdapter.addView(lowRange); listAdapter.addView(highRange); listAdapter.addView(units); listAdapter.addView(spinners); contents.setAdapter(listAdapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.selector, menu); return false; } 

Это решение основано на отражении для вызова AdapterView.setNextSelectedPositionInt() и AdapterView.setSelectedPositionInt() и успешно работает в API 4 до API 19.

Вы должны расширить SpinnerAdapter, а не BaseAdapter. Он имеет getDropdownView (), а также getView (), и я считаю, что он сам обрабатывает некоторые специальные случаи. Я расширяю этот адаптер в аналогичной компоновке на Android 4.2, и я не вижу проблем, которые у вас есть.

Я бы предположил, что разница в том, как getDropdownView () обрабатывает прикрепление представления к корню, будет учитывать эту разницу, но я не заглянул в код, чтобы проверить это