0

我的应用程序中有一个列表视图,它包含两行,一行用于任务,另一行用于警报、日期、严重性。最初,列表项的第一行仅对所有列表项显示,第二行不可见。当我单击列表项时,该项目显示的第二行以及当时单击另一个列表项时,上述列表项关闭了第二行。它对我来说工作正常......我的问题是,如果我打开一个列表项然后滑动列表视图,然后我单击另一个列表项,则上面的列表项无法关闭,因为上面的列表项实例将被更改。请任何人帮助我如何解决这个问题......

int lastselectedPosition == -1
    @Override
   public void onItemClick(AdapterView<?> arg0, View view, int position,
         long id) {
       TextView textviewDate=(TextView)view.findViewById(R.id.taskTimeidDaytoDay);
       selectedtaskDate=textviewDate.getText().toString().trim();
      if (lastselectedPosition == -1) {
         Log.i(TAG,"Loopif:"+lastselectedPosition);
         TextView twTaskTime = (TextView) view
               .findViewById(R.id.taskTimeidDaytoDay);
         TextView twSeverity = (TextView) view
               .findViewById(R.id.severityidDaytoDay);
         TextView twAlarm = (TextView) view
               .findViewById(R.id.alarmidDaytoDay);
         twAlarm.setVisibility(view.VISIBLE);
         twSeverity.setVisibility(view.VISIBLE);
         twTaskTime.setVisibility(view.VISIBLE);
         lastselectedPosition = position;
         lastSelectedItem = arg0.getChildAt(position);
      } else {
         // Log.i(TAG,"LoopElse:"+lastselectedPosition);
         lastSelectedItem.findViewById(R.id.taskTimeidDaytoDay)
               .setVisibility(lastSelectedItem.GONE);
         lastSelectedItem.findViewById(R.id.severityidDaytoDay)
               .setVisibility(lastSelectedItem.GONE);
         lastSelectedItem.findViewById(R.id.alarmidDaytoDay).setVisibility(
                 lastSelectedItem.GONE);
         if (lastselectedPosition != position) {
            view.findViewById(R.id.taskTimeidDaytoDay).setVisibility(
                  view.VISIBLE);
            view.findViewById(R.id.severityidDaytoDay).setVisibility(
                  view.VISIBLE);
            view.findViewById(R.id.alarmidDaytoDay).setVisibility(
                  view.VISIBLE);
            lastselectedPosition = position;
            lastSelectedItem = arg0.getChildAt(position);
         } else {
            lastselectedPosition = -1;
            lastSelectedItem = null;
         }
      }

获取视图():

@Override
public View getView(int position, View view, ViewGroup parent) {
    Log.i("XXXX", "Inside getView");
     final DaytoDayTaskGetterSetter objDaytoDaygetset=getItem(position);
     TextView textviewTask;
     TextView txtviewAlarm ,txtviewTaskTime ,txtviewSeverity;
     Log.i(TAG,"InsideGetView:"+position);
     LayoutInflater inflater=(LayoutInflater)context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
     if(view==null)
     {
         view=inflater.inflate(R.layout.daytodaylistlayout,null);

      }
        Log.i("XXXX", "before first test");
     textviewTask=(TextView)view.findViewById(R.id.tasknameidDaytoDay);
     txtviewAlarm=(TextView)view.findViewById(R.id.alarmidDaytoDay);
     txtviewSeverity=(TextView)view.findViewById(R.id.severityidDaytoDay);
     txtviewTaskTime=(TextView)view.findViewById(R.id.taskTimeidDaytoDay);
     return view;
}

在此处输入图像描述

在此处输入图像描述

首先,我单击“gdfgdtet”列表项,它显示另一行,然后我单击第二个列表项“dfgsdgsd”,当时上面的列表项“gdfgdtet”关闭了第二行。这是正常输出。假设如果我打开“gdfgdtet”列表项,然后在那个时候滑动列表视图“gdfgdtet”“dfgsdgsd”都将打开并崩溃......因为上面的一个列表项引用在我滑动时改变了请如何解决这个问题.. .

4

2 回答 2

4

我会尽力为您提供一个很好的答案,解释您遇到此问题的原因,但总体思路是您必须观看此视频 - http://www.youtube.com/watch?v=wDBM6wVEO70

请接受我的话 - 你似乎不明白ListView+BaseAdapter回收机制的全部内容,我强烈建议你查看我链接到的完整视频,并阅读更多相关信息。

一般来说,您的代码中的具体问题是您持有对 listview 项 ( lastSelectedItem) 的引用,然后假设它仍然代表相同的列表项,然后尝试使用它。这是错误的。在那个阶段(滚动后),视图已经被回收以表示列表中的另一个项目(基于适配器实现)。

listView的childs数量不是adapter.getCount()的大小!!!!!!!!!

listViews 的子项数=屏幕上可见列表项数 + 1 + 页眉 + 页脚

假设您在屏幕上显示了前 5 个项目,然后您正在向下滚动。当您看到 7 项时,您实际上会看到用于显示第一个列表项并被回收的相同视图实例。getView 将在此阶段调用 convertView != null 并在适配器中定位,让您通过将新值(例如不同的文本/图像)放入同一实例来重用该项目

这种机制提供了显示列表中“无限”个项目的列表的能力,并且在内存中只保存了几个视图。假设您在列表中有 5000 个项目的列表,并且每个项目都有不同的视图实例 - 您将在一秒钟内得到 outOfMemory 异常!

在stackoverflow答案的上下文中很难写出完整的解释。试图解释 android 中最重要和最复杂的 UI 组件之一实在是太长了,但这个链接将是一个好的开始:

http://www.youtube.com/watch?v=wDBM6wVEO70

ListView 的回收机制是如何工作的

http://mobile.cs.fsu.edu/the-nuance-of-android-listview-recycling-for-n00bs/

如果您对您的特定问题进行“快速”修复,解决方案将是:

数据结构中的保持表示您的列表项附加字段,指示它是否处于“关闭”或“打开状态。单击项目时 - 相应地更改数据并调用notifyDatasetChanged(). 在getView()检查项目是否打开或关闭并相应地填充它

顺便说一句 - 这不仅是“快速修复”的解决方案,而且还是正确的做法

于 2013-08-10T14:59:24.907 回答
1

您应该注意Tal Kanel's回答并将此视为对它的扩展。从长远来看,他的建议会对您有所帮助。

向类添加一个boolean字段DaytoDayTaskGetterSetter

public class DaytoDayTaskGetterSetter {

....
....
boolean open;

    public DaytoDayTaskGetterSetter (.., .., boolean o) {
        ....
        ....
        open = o;
    }

    ....
    ....

    public boolean shouldOpen() {
        return open;
    }

    public void setOpen(boolean o) {
        open = o;
    }
}

在您的getView()中,检查对象是否设置了其open值:

DaytoDayTaskGetterSetter obj = (DaytoDayTaskGetterSetter) getItem(position);

if (obj.shouldOpen()) {
    // Set visibility to true for the items
} else {
    // Set visibility to false for the items
}

单击列表项时,遍历列表并将open所有列表项设置为 false。使用position检索DaytoDayTaskGetterSetter并将其设置open为 true:

@Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {

    for (DaytoDayTaskGetterSetter obj : listContainingObjects) {
        obj.setOpen(false);
    }

    DaytoDayTaskGetterSetter clickedItem = (DaytoDayTaskGetterSetter)
                                                 yourAdapter.getItem(position);
    clickedItem.setOpen(true);

    yourAdapter.notifyDataSetChanged();
}

编辑1:

@Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {

    DaytoDayTaskGetterSetter clickedItem = (DaytoDayTaskGetterSetter)
                                                 yourAdapter.getItem(position);

    if (clickedItem.shouldOpen()) {
        clickedItem.setOpen(false);
    } else {

        for (DaytoDayTaskGetterSetter obj : listContainingObjects) {
            obj.setOpen(false);
        }

        clickedItem.setOpen(true);
    }

    yourAdapter.notifyDataSetChanged();
}
于 2013-08-12T09:09:26.783 回答