user81358
user81358

Reputation: 21

checkedtextview gets randomly checked and unchecked when i scroll after checking a checkedtextview

I designed a ExpandableListView with CheckedTextView for child items every thing is fine but
when i check a CheckedTextView randomly CheckedTextView in another groups randomly
checking.this ExpandableListView driving me insane. here is my code

   public class ExpandableListAdapter extends BaseExpandableListAdapter {

private Context context;
private List<String> expandableListTitle;
private HashMap<String, List<String>> expandableListDetail;   
public int lastExpandedGroupPosition;    

public ExpandableListAdapter(Context context, List<String> expandableListTitle,
        HashMap<String, List<String>> expandableListDetail) {
    this.context = context;
    this.expandableListTitle = expandableListTitle;
    this.expandableListDetail = expandableListDetail;      
}
@Override
//counts the number of group/parent items so the list knows how many times calls getGroupView() method
public int getGroupCount() {
     return this.expandableListTitle.size();
}

@Override
//counts the number of children items so the list knows how many times calls getChildView() 
method
public int getChildrenCount(int listPosition) {
    return this.expandableListDetail.get(this.expandableListTitle.get(listPosition))
            .size();     
}

@Override
//gets the title of each parent/group
public Object getGroup(int listPosition) {      
     return this.expandableListTitle.get(listPosition);
}

@Override
//gets the name of each item
public Object getChild(int listPosition, int expandedListPosition) {
    return this.expandableListDetail.get(this.expandableListTitle.get(listPosition))
            .get(expandedListPosition);    
}

@Override
public long getGroupId(int listPosition) {
    return listPosition;
}

@Override
public long getChildId(int listPosition, int expandedListPosition) {
    return expandedListPosition;
}

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

@Override
//in this method you must set the text to see the parent/group on the list
public View getGroupView(int listPosition, boolean isExpanded, View convertView, ViewGroup 
parent) {
 String listTitle = (String) getGroup(listPosition);
    if (convertView == null) {
        LayoutInflater layoutInflater = (LayoutInflater) this.context.
                getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = layoutInflater.inflate(R.layout.list_group, null);
    }
    // set category name as tag so view can be found view later
    convertView.setTag(getGroup(listPosition).toString());

    TextView textView = (TextView) convertView.findViewById(R.id.listTitle);

    //"i" is the position of the parent/group in the list
    textView.setText(getGroup(listPosition).toString());

    TextView sub = (TextView) convertView.findViewById(R.id.list_item_text_subscriptions);      


    return convertView;
}


 @Override
 //in this method you must set the text to see the children on the list
 public View getChildView(int listPosition, int expandedListPosition, boolean isLastChild, View
 convertView, ViewGroup parent) {
     final String expandedListText = (String) getChild(listPosition, expandedListPosition);
     if (convertView == null) {
         LayoutInflater layoutInflater = (LayoutInflater) this.context
                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         convertView = layoutInflater.inflate(R.layout.list_item, null);
     }

    CheckedTextView textView = (CheckedTextView)
   convertView.findViewById(R.id.list_item_text_child);       
    textView.setText(expandedListText);  
    return convertView;
}

@Override
public boolean isChildSelectable(int listPosition, int expandedListPosition) {
    return true;
}

}

And here is the data from json response

public class ExpandableListDataPump {

public static HashMap<String, List<String>> getData() {

    ServiceHandler sh = new ServiceHandler();
    String url = "http://****/**/fetch_details/services/news_keywords.json";
    // Making a request to url and getting response
    String jsonStr = sh.makeServiceCall(url, ServiceHandler.POST);

    ArrayList<HashMap<String, String>> dataHashMap = new ArrayList<HashMap<String, String>>();

    try {
        JSONArray jsonArray = new JSONArray(jsonStr);

        for (int i = 0; i < jsonArray.length(); i++) {
            JSONObject c = jsonArray.getJSONObject(i);
            HashMap<String, String> dataArray = new HashMap<String, String>();
            if(!c.isNull("name")){                  
            String region = c.getString("name");
            String state = c.getString("ank_name");
            dataArray.put("name", region);
            dataArray.put("ank_name", state);               
            dataHashMap.add(dataArray);

            }
        }

    } catch (JSONException e) {
        e.printStackTrace();
    }

    HashMap<String, List<String>> expandableListDetail = new HashMap<String, List<String>>();
    Set<String> regionsList = new HashSet<String>();

    for (HashMap<String, String> map : dataHashMap) {
        for (Entry<String, String> mapEntry : map.entrySet()) {
            String key = mapEntry.getKey();
            String value = mapEntry.getValue();
            if (key.equalsIgnoreCase("name")) {

                regionsList.add(value);

            }

        }
    }
    try {
        JSONArray jsonArray = new JSONArray(jsonStr);

        for (String regionName : regionsList) {
            Log.e("Keywords list", regionName);

            List<String> name = new ArrayList<String>();
            for (int i = 0; i < jsonArray.length(); i++) {
                JSONObject c = jsonArray.getJSONObject(i);
                String regionTemp = c.getString("name");
                String nameTemp = c.getString("ank_name");
                if (regionTemp.equalsIgnoreCase(regionName)) {
                    name.add(nameTemp);
                }
            }
            expandableListDetail.put(regionName, name);

        }
    } catch (JSONException e) {
        e.printStackTrace();
    }
    return expandableListDetail;
  }
 }  

Yes list_group.xml is this

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
    android:id="@+id/listTitle"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
    android:textColor="#A4C739"
    android:paddingTop="10dp"
    android:paddingBottom="10dp" />
</LinearLayout>

And list_item.xml is this

 <?xml version="1.0" encoding="utf-8"?>

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical" android:layout_width="match_parent"
 android:layout_height="wrap_content">

  <CheckedTextView
    android:id="@+id/list_item_text_child"
    android:layout_width="match_parent"
    android:layout_height="?android:attr/listPreferredItemHeightSmall"
    android:textAppearance="?android:attr/textAppearanceListItemSmall"
    android:gravity="center_vertical"
    android:textSize="15sp"
    android:checkMark="?android:attr/textCheckMark"
    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"

   />

 </LinearLayout>

Upvotes: 1

Views: 1160

Answers (2)

nmxprime
nmxprime

Reputation: 1506

I have faced this problem. Actually We have to manage it.

I have used below method

Add checked child index to a list

in getchildView, if checked, check the checkedtextview.

((CheckedTextView) convertView).setChecked(getClicked(
                        groupPosition, childPosition));

Below function will return a boolean whether given child is checked or not

public boolean getClicked(int groupPosition, int childPosition) {
    if (checkedPositions.get(groupPosition) != null) {
        boolean isChecked = checkedPositions.get(groupPosition).get(
                childPosition);
        return isChecked;
    }
    return false;
}

You can use functions like this to index checked items

public void setClicked(int groupPosition, int childPosition) {

SparseBooleanArray checkedChildPositionsSingle = checkedPositions
        .get(groupPosition);
if (checkedChildPositionsSingle == null) {
    checkedChildPositionsSingle = new SparseBooleanArray();
    checkedChildPositionsSingle.put(childPosition, true);
    checkedPositions.put(groupPosition, checkedChildPositionsSingle);
} else {
    boolean oldState = checkedChildPositionsSingle.get(childPosition);
    if (!oldState) {
        checkedChildPositionsSingle.clear();
        checkedChildPositionsSingle.put(childPosition, !oldState);
    }
}
notifyDataSetChanged();

}

Added :

 @Override
 //in this method you must set the text to see the children on the list
 public View getChildView(int listPosition, int expandedListPosition, boolean isLastChild, View
 convertView, ViewGroup parent) {
     final String expandedListText = (String) getChild(listPosition, expandedListPosition);
     if (convertView == null) {
         LayoutInflater layoutInflater = (LayoutInflater) this.context
                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         convertView = layoutInflater.inflate(R.layout.list_item, null);
     }

    CheckedTextView textView = (CheckedTextView)
   convertView.findViewById(R.id.list_item_text_child);       
    textView.setText(expandedListText);  
((CheckedTextView) convertView).setChecked(getClicked(
                            groupPosition, childPosition)); //Added
    return convertView;

}

Create a onChildClickListener in class you are creating the adapter

expandableListView.setOnChildClickListener(new OnChildClickListener() {
                        @Override
                        public boolean onChildClick(ExpandableListView parent,
                                View clickedView, int groupPosition,
                                int childPosition, long id) {
                    expandableListAdapter.setClicked(groupPosition,childPosition);
            }
        }

Note : checkedPositions is an array of SparseBooleanArray

SparseArray<SparseBooleanArray> checkedPositions;

Upvotes: 1

Hemendra Sharma
Hemendra Sharma

Reputation: 1060

Whenever a ListView / ExpandableListView is scrolled, it calls "getView" function of the corresponding adapter on runtime while scrolling. Suppose, you have 10 items in a ListView and top 5 of them are currently showing on the screen. At this moment, the ListView has called "getView" for only those items that are currently shown. Now, when you scroll the list upside, it will start calling "getView" of the adapter one-by-one for 6th, 7th, 8th... items.

On the other hand, a CheckedTextView has its checked state set to default value (FALSE if not set in layout XML). So whenever ListView calls "getView" of adapter, it creates a NEW layout for that item, and according to the new layout, the checked state of CheckedTextView gets back to DEFAULT.

To handle this, you will have to save the Checked-State of each CheckedTextView, and set this value to the CheckedTextView while returning view from "getView" of adapter.

Edit:

At the constructor of ExpandableListAdapter, fill the following array list with default checked-state values...

ArrayList<ArrayList<Boolean>> checkStates = new ArrayList<ArrayList<Boolean>>();

public void MyExpendableListAdapter()
{
       checkStates = new ArrayList<ArrayList<Boolean>>();
       //
       for(int i=0; i<numberOfGroups; i++) // Number of total groups or headers
       {
              ArrayList<Boolean> childrenCheckedStates = new ArrayList<Boolean>();
              for(int j=0; j<groups.get(i).size(); j++) // saving checked state for each children in each group
              {
                     childrenCheckedStates.add(defaultCheckState); // true or false
              }
              checkStates.add(childrenCheckedStates);
       }
}

Now at getChildView...

public View getChildView(final int listPosition, final int expandedListPosition,
        boolean isLastChild, View convertView, ViewGroup parent) {
    final String expandedListText = (String) getChild(listPosition,
            expandedListPosition);
    if (convertView == null) {
        LayoutInflater layoutInflater = (LayoutInflater) this.context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = layoutInflater.inflate(R.layout.list_item, null);
    }

    CheckedTextView textView = (CheckedTextView) convertView
            .findViewById(R.id.list_item_text_child);

    textView.setText(expandedListText);

    textView.setChecked(checkStates.get(listPosition).get(expandedListPosition));

    textView.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            CheckedTextView tv = (CheckedTextView)v;
            checkStates.get(listPosition).set(expandedListPosition, tv.isChecked());
        }
    });

    return convertView;
}

Good Luck. :)

Upvotes: 1

Related Questions