¿Por qué Android recicla el tipo de vista incorrecto en mi SpinnerAdapter?

Estoy tratando de hacer un spinner ActionBar que tiene separadores. He implementado un SpinnerAdapter que tiene 2 tipos de vista de elemento (gracias a getViewTypeCount ). El problema es que me están enviando algunos convertViews del otro tipo.

Aquí está mi SpinnerAdapter:

 public abstract class SeparatorSpinnerAdapter implements SpinnerAdapter { Context mContext; List<Object> mData; int mSeparatorLayoutResId, mActionBarItemLayoutResId, mDropDownItemLayoutResId, mTextViewResId; public static class SpinnerSeparator { public int separatorTextResId; public SpinnerSeparator(final int resId) { separatorTextResId = resId; } } public abstract String getText(int position); public SeparatorSpinnerAdapter(final Context ctx, final List<Object> data, final int separatorLayoutResId, final int actionBarItemLayoutResId, final int dropDownItemLayoutResId, final int textViewResId) { mContext = ctx; mData = data; mSeparatorLayoutResId = separatorLayoutResId; mActionBarItemLayoutResId = actionBarItemLayoutResId; mDropDownItemLayoutResId = dropDownItemLayoutResId; mTextViewResId = textViewResId; } protected String getString(final int resId) { return mContext.getString(resId); } @Override public void registerDataSetObserver(final DataSetObserver observer) { } @Override public void unregisterDataSetObserver(final DataSetObserver observer) { } @Override public int getCount() { if (mData != null) { return mData.size(); } return 0; } @Override public Object getItem(final int position) { return mData == null ? null : mData.get(position); } @Override public boolean isEmpty() { return getCount() == 0; } @Override public long getItemId(final int position) { return 0; } @Override public boolean hasStableIds() { return false; } @Override public View getView(final int position, final View convertView, final ViewGroup parent) { return getView(mActionBarItemLayoutResId, position, convertView, parent); } public boolean isSeparator(final int position) { final Object item = getItem(position); if (item != null) { return item instanceof SpinnerSeparator; } return false; } @Override public int getItemViewType(final int position) { return isSeparator(position) ? 0 : 1; } @Override public int getViewTypeCount() { return 2; } @Override public View getDropDownView(final int position, final View convertView, final ViewGroup parent) { return getView(isSeparator(position) ? mSeparatorLayoutResId : mDropDownItemLayoutResId, position, convertView, parent); } private View getView(final int layoutResId, final int position, final View convertView, final ViewGroup parent) { View v; Log.i("TAG", "getView #" + position + "\tVT=" + getItemViewType(position) + "\tCV=" + (convertView == null ? " null " : convertView.getClass().getSimpleName()) + "\ttext=> " + getText(position)); if (convertView == null) { final LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = li.inflate(layoutResId, parent, false); } else { v = convertView; } final TextView tv = (TextView) v.findViewById(mTextViewResId); if (tv != null) { tv.setText(getText(position)); if (isSeparator(position)) { tv.setOnClickListener(null); tv.setOnTouchListener(null); } } return v; } } 

Una implementación:

 public class IssuesMainFilterAdapter extends SeparatorSpinnerAdapter { public IssuesMainFilterAdapter(final Context ctx, final List<Query> queries, final List<Project> projects) { super(ctx, buildDataArray(queries, projects), R.layout.issues_filter_spinner_separator, R.layout.issues_filter_spinner_in_actionbar, R.layout.issues_filter_spinner, R.id.issues_filter_spinner_text); } private static List<Object> buildDataArray(final List<Query> queries, final List<Project> projects) { final List<Object> data = new ArrayList<Object>(); data.add(null); // "ALL" data.add(new SpinnerSeparator(R.string.issue_filter_queries)); data.addAll(queries); data.add(new SpinnerSeparator(R.string.issue_filter_projects)); data.addAll(projects); return data; } @Override public String getText(final int position) { final Object item = getItem(position); if (item == null) { return getString(R.string.issue_filter_all); } else if (item instanceof Query) { return ((Query) item).name; } else if (item instanceof Project) { return ((Project) item).name; } else if (item instanceof SpinnerSeparator) { return getString(((SpinnerSeparator) item).separatorTextResId); } throw new InvalidParameterException("Item has unknown type: " + item); } } 

Como habrás notado, he establecido una línea de registro en getView() para que yo getView() entender mejor lo que está pasando:

 05-06 14:01:28.721 I/TAG( 5879): getView #0 VT=1 CV=TextView text=> #### 05-06 14:01:28.721 I/TAG( 5879): getView #1 VT=0 CV=LinearLayout text=> #### 05-06 14:01:28.729 I/TAG( 5879): getView #2 VT=1 CV=TextView text=> #### 05-06 14:01:28.745 I/TAG( 5879): getView #3 VT=1 CV=TextView text=> #### 05-06 14:01:28.745 I/TAG( 5879): getView #4 VT=0 CV=LinearLayout text=> #### 05-06 14:01:28.745 I/TAG( 5879): getView #5 VT=1 CV=TextView text=> #### 05-06 14:01:28.753 I/TAG( 5879): getView #6 VT=1 CV=TextView text=> #### 05-06 14:01:28.768 I/TAG( 5879): getView #7 VT=1 CV=TextView text=> #### 05-06 14:01:28.768 I/TAG( 5879): getView #8 VT=1 CV=TextView text=> #### 05-06 14:01:28.768 I/TAG( 5879): getView #9 VT=1 CV=TextView text=> #### 05-06 14:01:28.776 I/TAG( 5879): getView #10 VT=1 CV=TextView text=> #### 05-06 14:01:28.792 I/TAG( 5879): getView #11 VT=1 CV=TextView text=> #### 05-06 14:01:32.081 I/TAG( 5879): getView #12 VT=1 CV=TextView text=> #### 05-06 14:01:34.690 I/TAG( 5879): getView #13 VT=1 CV=LinearLayout text=> #### 05-06 14:01:35.573 I/TAG( 5879): getView #14 VT=1 CV=TextView text=> #### 05-06 14:01:37.237 I/TAG( 5879): getView #15 VT=1 CV=TextView text=> #### 

Como usted pudo haber entendido, mis disposiciones para los artículos verdaderos son TextViews, y la disposición del separador es LinearLayout.

Como puede ver, un elemento "real" ( VT=1 en el registro, ver ítem # 13) está reciclando una vista separadora ( CV=LinearLayout ). Hubiera pensado que Android proporcionaría un convertView del mismo tipo, por lo que el primer separador sería reciclado sólo si una vista del mismo tipo (es decir, otro separador) tendría que ser creado al desplazarse.

Como David se enteró, esto está relacionado con el marco de Android. Como se señala aquí , el marco no espera Spinner s para tener diferentes tipos de vista.

Esta es la solución que utilicé para que mi SpinnerAdapter funcionara como quería:

  • Almacenar el tipo de vista en la etiqueta de la vista;
  • Inflar un nuevo diseño si no hay vista para convertir O si el tipo de vista actual difiere de la vista para convertir.

Aquí está el código de mi método getView personalizado:

 private View getView(final int layoutResId, final int position, final View convertView, final ViewGroup parent) { View v; if (convertView == null || (Integer)convertView.getTag() != getItemViewType(position)) { final LayoutInflater li = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = li.inflate(layoutResId, parent, false); } else { v = convertView; } v.setTag(Integer.valueOf(getItemViewType(position))); final TextView tv = (TextView) v.findViewById(mTextViewResId); if (tv != null) { tv.setText(getText(position)); if (isSeparator(position)) { tv.setOnClickListener(null); tv.setOnTouchListener(null); } } return v; } 

El problema está aquí:

 public View getView(final int position, final View convertView, final ViewGroup parent) { return getView(mActionBarItemLayoutResId, position, convertView, parent); } 

Este método siempre devolverá el mismo tipo de View , ya sea llamado para un separador o un elemento de datos. Debe comprobar la posición aquí y devolver una vista apropiada.

  • ¿Cómo puedo leer el archivo json de la tarjeta SD
  • ¿Cómo crear un spinner con título en él?
  • Spinner onItemSelected no se llama usando Marsmallow
  • Cómo cambiar el color de fondo de una lista de hilanderos en Android
  • Android agrega imagen de flecha a spinner
  • El primer elemento de Spinner no debe aparecer en la lista desplegable
  • Cambiar Spinner dropdown icon
  • Cambiar el color de fondo de la flecha giratoria
  • ¿Cómo re tamaño de la Altura de los artículos en Spinner con opción múltiple?
  • Dos widgets uno al lado del otro en diseño lineal
  • Spinner causa error al usar estilo personalizado
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.