0

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

我正在编写一个关于古代拉丁语的安卓应用程序:我的目标是向用户展示拉丁动词的整个变位,并在他们搜索特定的变形形式时为他们提供正确的语言分析。这是我的清单。

<?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 并且我需要在其他地方做一些事情。

4

2 回答 2

1

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

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

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

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

不幸的是,没有太多文档能够真正解释启用稳定 ID 如何影响ListView. 它仅在启用选择模式时使用。它有助于确保在屏幕配置或适配器突变等事情期间突出显示/检查正确的项目,同时突出显示/检查某些内容。View否则,stable ids 与生成无关。

很难确切地说是什么导致了这种时髦的回收行为,但这绝对与您如何通过 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));

另一个性能考虑。我建议研究实现不同的视图类型。对于您的情况,您可以根据此逻辑拥有 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 布局。这改善了视图回收并摆脱了膨胀和后来的修改逻辑。

于 2014-12-23T17:28:52.360 回答
1

解决方案

感谢@JaySoyer 和他关于性能问题的重要提示,我实际上设法解决了我的问题,我认为在更正后分享代码很有用。

从我了解到的情况来看,问题在于我尝试过多地以编程方式定义功能,而我留给 xml 部分的内容太少。解决方案确实与视图类型有关。覆盖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;
    }

}

如您所见,我只需要创建与 viewType 数量一样多的布局(通过 xml 正确管理所有需要的设置),并在简单检查实际 viewType 后膨胀正确的布局,仅此而已。

希望这很有用!

于 2014-12-27T10:37:20.813 回答