Android ListView项目:stableIds但位置不正确

时间:2014-12-22 18:21:58

标签: android listview android-listview convertview

不幸的是,我真的认为我必须广泛地解释一下我的情况。

我正在编写一个关于古代拉丁语的Android应用程序:我的目标是向用户展示拉丁动词的整个结合,并在搜索特定的变形形式时给予他们正确的语言分析。 这是我的表现。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android_application.app_name"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".ConjActivity"
            android:label="@string/app_name"
            android:parentActivityName=".MainActivity" >
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.android_application.app_name.MainActivity"/>
        </activity>
    </application>

</manifest>

只要知道为了实现第一个目标,我会自动创建每个动词的所有形式作为字符串,并且每个结合都显示为一个简单的ListView,只有一个TextView字符串作为项目:一个项目=一个变形的语言形式。

现在,我需要自定义我的项目,修改一些textStyle,有时修改它们的对齐等。为了做到这一点,我用这种方式创建了自定义ListAdapter:

private static class ConjAdapter extends ArrayAdapter<String> {

    private ArrayList<Long> ids;
    private HashMap<String, Long> mIdMap;

    public ConjAdapter(Context context, int textViewResourceId, List<String> objects) {
        super(context, textViewResourceId, objects);
        ids = new ArrayList<Long>();
        mIdMap = new HashMap<String, Long>();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View v = super.getView(position, convertView, parent);
        ViewHolder holder;

        if(convertView==null){
            convertView = LayoutInflater.from(context).inflate(R.layout.simple_list_item_1, parent, false);
            holder = new ViewHolder();
            holder.txt = (TextView) convertView.findViewById(R.id.text1);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        long id = getItemId(position);

        if(sel_vb!=null){
            if(ids.contains(id)){ v.setBackgroundColor(context.getResources().getColor(R.color.row_bckgr_RED));
        } else {
                v.setBackgroundColor(Color.TRANSPARENT);
            }
        }

        for(Map.Entry<String, Long> map : mIdMap.entrySet()){
            if(id==map.getValue()){
                holder.txt.setText(map.getKey());
                break;
            }
        }

        String item = (String) holder.txt.getText();
        if(item.equals(context.getResources().getString(R.string.ind))||
                item.equals(context.getResources().getString(R.string.subj))||
                item.equals(context.getResources().getString(R.string.imp))||
                item.equals(context.getResources().getString(R.string.inf))||
                item.equals(context.getResources().getString(R.string.pt))||
                item.equals(context.getResources().getString(R.string.ger))||
                item.equals(context.getResources().getString(R.string.gerv))||
                item.equals(context.getResources().getString(R.string.sup))){
            holder.txt.setGravity(Gravity.CENTER);
            holder.txt.setTextSize(20f);
            holder.txt.setTypeface(null, Typeface.BOLD);
        } else if(item.equals(context.getResources().getString(R.string.pres))||
                item.equals(context.getResources().getString(R.string.impf))||
                item.equals(context.getResources().getString(R.string.fut))||
                item.equals(context.getResources().getString(R.string.pf))||
                item.equals(context.getResources().getString(R.string.ppf))||
                item.equals(context.getResources().getString(R.string.futant))){
            holder.txt.setTypeface(null, Typeface.ITALIC);
            holder.txt.setTextSize(19f);
        } else {
            holder.txt.setPadding(10, 0, 0, 0);
        }

        return convertView;
    }

    @Override
    public long getItemId(int position) {
        String item = getItem(position);
        return mIdMap.get(item);
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }

    static class ViewHolder {
        TextView txt;
    }

}

问题是,在第19项之后出现问题,我没有收到任何错误消息,也没有应用程序崩溃,但我的自定义代码不再起作用,应该具有某些功能的项目已经开启相反的其他。 当我上下滚动列表时,这种情况会恶化。

在我阅读之后,我真的认为这个问题与我的自定义适配器的getView()调用的convertview变量的回收目标有关。

这是我的问题:为什么即使使用稳定的id(我将mIdMap与相关的项目字符串一起存储)也会发生这种情况,以及如何从不正确的位置变量中分离我的项目?

更新

以下是我填充mIdMap和ids的代码:

HashMap<String, Long> tempMap = conjadapt.mIdMap;
for(int i=0, j=0; i<displ_conj.size(); i++, j++){
    tempMap.put(displ_conj.get(i), (long) j);
}
if(sel_vb!=null){
    for(Map.Entry<String, Long> map : tempMap.entrySet()){
        if(map.getKey().equals(sel_vb))
            conjadapt.ids.add(map.getValue());
    }
}

其中displ_conj是我存储数据的ArrayList。 mIdMap存储一个 long 变量,因为getItemId()必须返回一个long,并且我需要在其他地方做一些事情。

2 个答案:

答案 0 :(得分:1)

你说得对,这是一个回收问题,它与稳定的ID无关。正如samgak所指出的那样,你不应该调用超级调用:

View v = super.getView(position, convertView, parent);

这将创建一个全新的View,然后您将修改背景颜色。但是,由于永远不会返回View,因此当方法调用完成时,它会被抛到风中。所以基本上,你的getView方法正在做一些从未使用过的额外工作。你应该完全处理convertView。但是,这并没有引起您所看到的回收问题。

您似乎也试图为sel_vb显示红色背景。我假设这意味着选择动词?知道有一个内置的机制来做到这一点。 ListViews支持名为setItemChecked(position, boolean)的方法。基本上,您可以使用该方法调用突出显示任何位置。请注意,您需要先启用a choice mode。您可以通过样式更改默认的蓝色突出显示,也可以创建自己的布局,而不是使用Android提供的布局。

不幸的是,没有太多文档可以真正解释如何使稳定的ID影响ListView。它只在启用选择模式时使用。它有助于确保在突出显示/检查某些内容时,在屏幕配置或适配器突变等过程中突出显示/检查正确的项目。否则,稳定的id与View代无关。

很难确切地说是什么导致了时髦的回收行为,但它肯定与通过Id映射填充文本的方式有关。特别是这个人:

    for(Map.Entry<String, Long> map : mIdMap.entrySet()){
        if(id==map.getValue()){
            holder.txt.setText(map.getKey());
            break;
        }
    }

我的建议是从getView方法中完全删除所有Id逻辑。而是通过执行以下操作来填充文本:

holder.txt.setText(getItem(position));

还有一个性能考虑因素。我建议查看实现不同的view types。对于您的情况,您可以根据此逻辑使用3种不同的视图类型:

   String item = (String) holder.txt.getText();

    //First type
    if(item.equals(context.getResources().getString(R.string.ind))||
            item.equals(context.getResources().getString(R.string.subj))||
            item.equals(context.getResources().getString(R.string.imp))||
            item.equals(context.getResources().getString(R.string.inf))||
            item.equals(context.getResources().getString(R.string.pt))||
            item.equals(context.getResources().getString(R.string.ger))||
            item.equals(context.getResources().getString(R.string.gerv))||
            item.equals(context.getResources().getString(R.string.sup))){

    //Second type
    } else if(item.equals(context.getResources().getString(R.string.pres))||
            item.equals(context.getResources().getString(R.string.impf))||
            item.equals(context.getResources().getString(R.string.fut))||
            item.equals(context.getResources().getString(R.string.pf))||
            item.equals(context.getResources().getString(R.string.ppf))||
            item.equals(context.getResources().getString(R.string.futant))){

    } else {
       //Third type
    }

然后,您可以创建三个自定义布局以进行膨胀,而不是依赖于内置的simple_list_item_1布局。这样可以改善View回收并消除膨胀,然后修改逻辑。

答案 1 :(得分:1)

解决方案

感谢@JaySoyer和他关于性能问题的精彩提示,我实际上设法解决了我的问题,并且我认为在我的更正之后有用的共享代码。

据我所知,问题在于我尝试过多地以编程方式定义功能,而且我对xml部分的支持太少了。 解决方案实际上与viewTypes有关。覆盖 getViewTypeCount() getItemViewType()并为每个viewType填充一个布局是我的问题的关键。

在我的情况下,我需要4个viewTypes(由于存在不同的分隔符,不同的textStyles等),现在我的适配器看起来像这样:

private static class ConjAdapter extends ArrayAdapter<String> {

    private ArrayList<Long> ids;
    private HashMap<String, Long> mIdMap;
    private ViewHolder holder;

    private final int
            IND_MODE_TYPE = 0,
            ELSE_MODE_TYPE = 1,
            TENSE_TYPE = 2,
            FORM_TYPE = 3;


    public ConjAdapter(Context context, int textViewResourceId, List<String> objects) {
        super(context, textViewResourceId, objects);
        ids = new ArrayList<Long>();
        mIdMap = new HashMap<String, Long>();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if(convertView==null){
            if(getItemViewType(position)==IND_MODE_TYPE){
                convertView = LayoutInflater.from(context).inflate(R.layout.item_mode_ind, parent, false);
                holder = new ViewHolder();
                holder.txt = (TextView) convertView.findViewById(R.id.text1);
                convertView.setTag(holder);
            }else if(getItemViewType(position)==ELSE_MODE_TYPE){
                convertView = LayoutInflater.from(context).inflate(R.layout.item_mode_else, parent, false);
                holder = new ViewHolder();
                holder.txt = (TextView) convertView.findViewById(R.id.text1);
                holder.divider1 = (View) convertView.findViewById(R.id.conj_divider1);
                convertView.setTag(holder);
            } else if(getItemViewType(position)==TENSE_TYPE){
                convertView = LayoutInflater.from(context).inflate(R.layout.item_tense, parent, false);
                holder = new ViewHolder();
                holder.divider2 = (View) convertView.findViewById(R.id.conj_divider2);
                holder.txt = (TextView) convertView.findViewById(R.id.text1);
                convertView.setTag(holder);
            } else {
                convertView = LayoutInflater.from(context).inflate(R.layout.item_form, parent, false);
                holder = new ViewHolder();
                holder.txt = (TextView) convertView.findViewById(R.id.text1);
                convertView.setTag(holder);
            }
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        long id = getItemId(position);

        if(sel_vb!=null){
            if(ids.contains(id)){
                convertView.setBackgroundColor(context.getResources().getColor(R.color.row_bckgr_RED));
            } else {
                convertView.setBackgroundColor(Color.TRANSPARENT);
            }
        }

        holder.txt.setText(getItem(position));

        return convertView;
    }

    @Override
    public long getItemId(int position) {

        String item = getItem(position);
        return mIdMap.get(item);
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }

    @Override
    public int getItemViewType(int position) {
        String item = getItem(position);

        if(item.equals(context.getResources().getString(R.string.ind))){
            return IND_MODE_TYPE;
        } else if(item.equals(context.getResources().getString(R.string.subj))||
                item.equals(context.getResources().getString(R.string.imp))||
                item.equals(context.getResources().getString(R.string.inf))||
                item.equals(context.getResources().getString(R.string.pt))||
                item.equals(context.getResources().getString(R.string.ger))||
                item.equals(context.getResources().getString(R.string.gerv))||
                item.equals(context.getResources().getString(R.string.sup))){
            return ELSE_MODE_TYPE;
        } else if(item.equals(context.getResources().getString(R.string.pres))||
                item.equals(context.getResources().getString(R.string.impf))||
                item.equals(context.getResources().getString(R.string.fut))||
                item.equals(context.getResources().getString(R.string.pf))||
                item.equals(context.getResources().getString(R.string.ppf))||
                item.equals(context.getResources().getString(R.string.futant))){
            return TENSE_TYPE;
        } else {
            return FORM_TYPE;
        }
    }

    @Override
    public int getViewTypeCount() {
        return 4;
    }

    static class ViewHolder {
        TextView txt;
        View divider1, divider2;
    }

}

正如您所看到的,我只需创建与viewTypes数量一样多的布局(通过xml正确管理所有需要的设置),并在对实际viewType进行简单检查后对其进行充气,并且&#39是的。

希望这很有用!