0

I have one listview in my application,it contains two rows one for task and another one for alarm,date,severity. Initially first row of the list item only displayed for all list item and the second one is invisible. When i click the list item the second row displayed for that item as well as click another list item at that time the above list item closed that second row. Its working fine for me...My problem is if i open one list item and then swipe the listview at then i click the another list item at that time the above one cannot be closed because the above list item instance will be chnaged.please any one help me how to solve this problem...

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

GetView():

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

enter image description here

enter image description here

In first i click the "gdfgdtet" list item it show another row and then i click the second list item "dfgsdgsd" at that time the above list item "gdfgdtet" closed the second row.This is a normal output.Suppose if i open the "gdfgdtet" list item and then swipe the listview at that time both of "gdfgdtet" "dfgsdgsd" will be opened and crashed...because the above one list item reference changed when i am swiping please how to solve this problem...

Satheesh
  • 1,722
  • 25
  • 35

2 Answers2

4

I'll try to provide you a good answer that explains why you are having this problems, but the general idea is that you have to see this video - http://www.youtube.com/watch?v=wDBM6wVEO70

please take my words kindly - you don't seems to understand what ListView + BaseAdapter recycling mechanism is all about, and I strongly recommend you see the full video I linked you to, and read more about that.

in general, the specific problem in your code is that you are holding reference to listview item (lastSelectedItem), then trying to use it latter assuming it's still representing same list item. that's wrong. in that stage (after scrolling) the view already been recycled to represent another item in the list (based on the adapter implementation).

listView's number of childs is not the size of adapter.getCount()!!!!!!!!

listViews's number of childs = number of visible list items on screen + 1 + headers + footers

let's say you have the 5 first items visible on screen, then you are scrolling down. when you see the 7 item you actually see the same view instance that used to show the first list item and been recycled. getView will call in this stage with convertView != null and position in the adapter to let you reuse the item by putting new values such different text/image to the same instance

this mechanism provides ability to display list of "infinite" number of items in the list, and holding in memory only a few number of views. imagine that you have list of 5000 items in the list, and each one of them have different view instance - you would get outOfMemory exception in a sec!

complete explanation about that would be hard to write in stackoverflow answer's context. it just too long trying to explain one of the most important and complex UI components in android, but this links would be a good start:

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

How ListView's recycling mechanism works

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

if you are interstead in "quick" fix for your specific problem, the solution would be:

hold in the data structure represents your list item additional field indicating if it in "close" or "open state. when item been clicked - change the data accordinly and call notifyDatasetChanged(). inside the getView() check if item is open or close and populate it accordinly

by the way - it's not only "quick fix" solution, but also the right thing to do anyway

Community
  • 1
  • 1
Tal Kanel
  • 10,475
  • 10
  • 60
  • 98
  • k i understand how to solve this problem please give me solution – Satheesh Aug 12 '13 at 06:49
  • @Satheesh: I've updated my answer. now it provides you a "quick fix" solutin. – Tal Kanel Aug 12 '13 at 07:06
  • @Satheesh: you are expecting I'll would write you answer that contains full code implementation served on a silver platter. I'm not going to do that for getting reputation. right now my answer pointing exactly what was lack in your knowledge, but also generic answer more developers can benefit from. I'm not going to write in the answer specific implementation for your code. you should appreciate answers like this, and listen to my advice. also I wrote you specifically what you should do anyway. – Tal Kanel Aug 12 '13 at 08:13
  • @Satheesh: I think I put my mind on your question enough, and made an effort to understand what you know and what you don't, to provide you an educated answer that will open your mind – Tal Kanel Aug 12 '13 at 08:16
  • :Thank you kanel i appriciate your way i understand list view how will working... – Satheesh Aug 12 '13 at 12:13
1

You should pay attention to Tal Kanel's answer and consider this one to be an extension to it. His advice will help you in the long run.

Add a boolean field to DaytoDayTaskGetterSetter class:

public class DaytoDayTaskGetterSetter {

....
....
boolean open;

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

    ....
    ....

    public boolean shouldOpen() {
        return open;
    }

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

In your getView(), check if the object has its open value set:

DaytoDayTaskGetterSetter obj = (DaytoDayTaskGetterSetter) getItem(position);

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

On list item click, traverse the list and set open for all list items to false. Use the position to retrieve DaytoDayTaskGetterSetter and set its open to 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();
}

Edit 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();
}
Vikram
  • 51,313
  • 11
  • 93
  • 122
  • k i understand if i click the openable item once again it will be definitely closed but the above coding will be opened the same item – Satheesh Aug 12 '13 at 09:31
  • @Satheesh Please refer to Edit 1 above. At any given time, at most one view will be visible. If an item (who's view is currently not visible) is clicked, its view will be visible. If an item (who's view is currently visible) is clicked, its view's visibility will be toggled. – Vikram Aug 12 '13 at 09:45
  • @vikram: I'm a bit disappointed you encourage this demand to get specific code in such cases. did you see my comments above the answer? – Tal Kanel Aug 12 '13 at 10:44
  • @TalKanel I read through your post and comments, and share your sentiment on this. Satheesh's approach was way off, but it did show an input of effort. I thought a simple logic-driven code example would be better suited to his needs. I did indicate that your post (and advice) will help him in the long run, and I hope that he understood. – Vikram Aug 12 '13 at 11:02