¿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:
- Android Spinner con opción múltiple
- ¿Cómo coloco el hilandero debajo del diseño del hilandero cuando se hace clic y muestra el elemento del hilandero en piruleta y arriba?
- ¿El tamaño del texto de Spinner no cambia?
- Botón desplegable de Android
- Cómo agregar un elemento desplegable en la barra de acción
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.
- Adaptador de spinner personalizado simple_spinner_dropdown_item no se muestra correctamente
- ¿Cómo puedo extender el spinner para implementar una nueva función?
- Android: Change Spinner Dropdown ver más
- setOnItemSelectedListener para spinner en Fragmento
- Cambiar el color del pequeño triángulo en spinner en android
- Android- ¿Cómo utilizar Spinner en un AlertDialog?
- ¿Obtener el texto seleccionado de los elementos giratorios?
- ¿Cómo puedo añadir mi spinner a la ActionBar?
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.