8

我正在尝试制作一个具有分隔符的 ActionBar 微调器。我已经实现了SpinnerAdapter具有 2 种项目视图类型的 a(感谢getViewTypeCount)。问题是我正在convertViews从其他类型发送一些。

这是我的 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;
    }
}

一种实现:

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

您可能已经注意到,我设置了一个日志行,getView()以便更好地了解发生了什么:

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

您可能已经了解,我对真实项目的布局是 TextViews,而分隔符布局是 LinearLayout。

如您所见,一个“真实”项目(VT=1在日志中,参见项目#13)正在回收一个分隔视图(CV=LinearLayout)。我原以为Android会提供convertView相同类型的a,因此只有在滚动时必须创建相同类型的视图(即另一个分隔符)时才会回收第一个分隔符。

4

2 回答 2

9

正如 David 发现的,这与 Android 框架有关。如此处所述,框架不希望Spinners 具有不同的视图类型。

这是我用来让我的 SpinnerAdapter 按我想要的方式工作的解决方法:

  • 将视图类型存储在视图的标签中;
  • 如果没有要转换的视图当前视图类型与要转换的视图不同,则膨胀新布局。

这是我的自定义getView方法的代码:

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;
}
于 2013-05-06T14:28:55.073 回答
1

问题在这里:

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

此方法将始终返回相同的View类型,无论是为分隔符还是数据项调用。您需要检查此处的位置并返回适当的视图。

于 2013-05-06T13:04:43.630 回答