0

I'm writing filter in Android and want to use BaseExpandableListAdapter with checkboxes. In my situation if the group elements have any child elements, may select group element too. Also all child elements may selected. Offer screenshot: enter image description here

When i open dialog and select neccessary elements, then its selected elements put to text edittext. My problem in this, when i reopen dialog and close/open group elements my selected checkboxes changes. I can't find solution how to remember choices, when reopen dialog.

All my neccessary code offer following:

My entity class:

public class Category implements Listable, Parcelable{
    private int id;
    private String title;
    private String c_date;
    private List<Category> sub_categories = new ArrayList<>();
    private boolean isChecked;
    private int parent;

protected Category(Parcel in) {
        id = in.readInt();
        title = in.readString();
        c_date = in.readString();
        sub_categories = in.createTypedArrayList(Category.CREATOR);
        isChecked = in.readByte() != 0;
        parent = in.readInt();
        m_date = in.readString();
    }

 public static final Creator<Category> CREATOR = new Creator<Category>() {
        @Override
        public Category createFromParcel(Parcel in) {
            return new Category(in);
        }

        @Override
        public Category[] newArray(int size) {
            return new Category[size];
        }
    };

@Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeInt(id);
        parcel.writeString(title);
        parcel.writeString(c_date);
        parcel.writeTypedList(sub_categories);
        parcel.writeByte((byte) (isChecked ? 1 : 0));
        parcel.writeInt(parent);
        parcel.writeString(m_date);
    }
//getters, setters
}    

My adapter class:

public class CategoryAdapter extends BaseExpandableListAdapter {

private Context context;
private ArrayList<Category> categories;
private LayoutInflater inflater;

public CategoryAdapter(Context context,
                       ArrayList<Category> categoryList) {
    this.context = context;
    this.categories = categoryList;
    inflater = LayoutInflater.from(context);
}

public Object getChild(int groupPosition, int childPosition) {
    return categories.get(groupPosition).getSub_categories().get(childPosition);
}

public long getChildId(int groupPosition, int childPosition) {
    return (long)( groupPosition*1024+childPosition );  // Max 1024 children per group
}

private static class ViewHolder {
    private TextView title;
    private CheckBox cb;
}

public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
    View v = convertView;
    ViewHolder viewHolder;
    boolean isChecked = categories.get(groupPosition).getSub_categories().get(childPosition).isChecked();
    if (v != null)
        viewHolder = (ViewHolder) v.getTag();
    else {
        v = inflater.inflate(R.layout.category_child_row, parent, false);
        viewHolder = new ViewHolder();
        viewHolder.title = (TextView)v.findViewById(R.id.title);
        viewHolder.cb = (CheckBox)v.findViewById(R.id.check);
        viewHolder.cb.setChecked(isChecked);
        viewHolder.cb.setOnCheckedChangeListener(new CheckchangeChildListener(groupPosition, childPosition));
        v.setTag(viewHolder);
    }

    final Category c = (Category) getChild( groupPosition, childPosition );
    viewHolder.title.setText(c.getTitle());
    return v;
}

class CheckchangeChildListener implements CompoundButton.OnCheckedChangeListener {
    private int position;
    private int childPosition;

    public CheckchangeChildListener(int position, int childPosition) {
        this.position= position;
        this.childPosition = childPosition;
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView,
                                 boolean isChecked) {
        // TODO Auto-generated method stub
        if (isChecked) {
            categories.get(position).getSub_categories().get(childPosition).setChecked(true);
        } else {
            categories.get(position).getSub_categories().get(childPosition).setChecked(false);
        }
    }
}

public int getChildrenCount(int groupPosition) {
    return categories.get(groupPosition).getSub_categories().size();
}

public Object getGroup(int groupPosition) {
    return categories.get( groupPosition );
}

public int getGroupCount() {
    return categories.size();
}

public long getGroupId(int groupPosition) {
    return (long)( groupPosition*1024 );  // To be consistent with getChildId
}

private static class ViewGroupHolder {
    private TextView title;
    private CheckBox cb;
    private ImageView image;
}

public View getGroupView(final int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
    View v = convertView;
    ViewGroupHolder holder;
    boolean isChecked = categories.get(groupPosition).isChecked();
    if(v != null)
        holder = (ViewGroupHolder)v.getTag();
    else {
        v = inflater.inflate(R.layout.category_parent_row, parent, false);
        holder = new ViewGroupHolder();
        holder.title = (TextView)v.findViewById(R.id.title);
        holder.cb = (CheckBox)v.findViewById(R.id.check);
        holder.cb.setChecked(isChecked);
        holder.image = (ImageView)v.findViewById(R.id.arrow);
        holder.cb.setOnCheckedChangeListener(new CheckchangeGroupListener(groupPosition));
        v.setTag(holder);
    }

    final Category category = (Category)getGroup(groupPosition);
    holder.title.setText(category.getTitle());
    if(getChildrenCount(groupPosition) == 0){
        holder.cb.setVisibility(View.VISIBLE);
        holder.image.setVisibility(View.INVISIBLE);
    } else {
        holder.image.setVisibility(View.VISIBLE);
        holder.cb.setVisibility(View.INVISIBLE);
        holder.image.setImageResource(isExpanded ? R.drawable.ic_chevron_down : R.drawable.ic_chevron_right);
    }
    return v;
}

class CheckchangeGroupListener implements CompoundButton.OnCheckedChangeListener {
    private int position;

    public CheckchangeGroupListener(int position) {
        this.position= position;
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView,
                                 boolean isChecked) {
        // TODO Auto-generated method stub
        if (isChecked) {
            categories.get(position).setChecked(true);
        } else {
            categories.get(position).setChecked(false);
        }
    }
}


public boolean hasStableIds() {
    return true;
}

public boolean isChildSelectable(int groupPosition, int childPosition) {
    return true;
}

public void onGroupCollapsed (int groupPosition) {}
public void onGroupExpanded(int groupPosition) {}

public ArrayList<Category> getSelectedCats(){
    ArrayList<Category> checkedCategories = new ArrayList<>();
    for(Category c: categories){
        if(c.isChecked()) checkedCategories.add(c);
        if(!c.getSub_categories().isEmpty()){
            for(Category category: c.getSub_categories()) {
                if (category.isChecked()) checkedCategories.add(category);
            }
        }
    }
    return checkedCategories;
}

}

My custom EditText:

public class CategoryEditText<T extends Listable> extends EditText {
    List<T> mItems;
    String[] mListableItems;
    CharSequence mHint;
    OnItemSelectedListener<T> onItemSelectedListener;
    DialogFragment newFragment;

    public CategoryEditText(Context context) {
        super(context);
        mHint = getHint();
    }

    public CategoryEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        mHint = getHint();
    }

    public CategoryEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mHint = getHint();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CategoryEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mHint = getHint();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        setFocusable(false);
        setClickable(true);
    }

    public void setItems(List<T> items) {
        this.mItems = items;
        newFragment = CategoryDialogFragment.newInstance((ArrayList<Category>)mItems, this);
        this.mListableItems = new String[items.size()];

        int i = 0;

        for (T item : mItems) {
            mListableItems[i++] = item.getLabel();
        }

        configureOnClickListener();
    }

    private void configureOnClickListener() {
        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {

                Context context = getContext();
                FragmentManager fm = ((Activity) context).getFragmentManager();
                newFragment.show(fm, "dialog");
            }
        });
    }

    public void setOnItemSelectedListener(OnItemSelectedListener<T> onItemSelectedListener) {
        this.onItemSelectedListener = onItemSelectedListener;
    }

    public interface OnItemSelectedListener<T> {
        void onItemSelectedListener(T item, int selectedIndex);
    }
}

My custom dialogfragment to display list:

public class CategoryDialogFragment extends DialogFragment {

    ArrayList<Category> categories;
    static EditText editText;

    public static CategoryDialogFragment newInstance(ArrayList<Category> categories, EditText customEditText) {
        CategoryDialogFragment frag = new CategoryDialogFragment();
        Bundle args = new Bundle();
        args.putParcelableArrayList("categories", categories);
        frag.setArguments(args);
        editText = customEditText;
        return frag;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        categories = getArguments().getParcelableArrayList("categories");
        View v = LayoutInflater.from(getActivity()).inflate(R.layout.select_category_layout, null);
        ExpandableListView expandableListView = (ExpandableListView) v.findViewById(R.id.list);
        final CategoryAdapter categoryAdapter = new CategoryAdapter(getActivity(), categories);
        expandableListView.setAdapter(categoryAdapter);

        MaterialDialog dialog = new MaterialDialog.Builder(getActivity())
                .title(R.string.shop_category)
                .customView(v, false)
                .positiveText(R.string.select)
                .negativeText(android.R.string.cancel)
                .onPositive(new MaterialDialog.SingleButtonCallback() {
                    @Override
                    public void onClick(@NonNull MaterialDialog materialDialog, @NonNull DialogAction dialogAction) {
                        ArrayList<Category> cats = categoryAdapter.getSelectedCats();
                        String selectedText = "";
                        for(int i=0; i<cats.size(); i++){
                            selectedText += cats.get(i).getTitle();
                            if(i+1 < cats.size()){
                                selectedText += " | ";
                            }
                        }
                        editText.setText(selectedText);
                        materialDialog.dismiss();
                    }
                })
                .build();

        return dialog;
    }
}

I tried to reading on the net about that but I didn`t find how to solve my problem.

1 Answers1

0

The problem is your getView() method. I'd suggest reading up on how to correctly write a list adapter class.

private static class ViewHolder {
    private TextView title;
    private ChecBbox cb;
}

public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
    View v = convertView;
    ViewHolder holder;
    boolean isChecked = categories.get(groupPosition).getSub_categories().get(childPosition).isChecked();
    if( convertView != null )
        viewHolder = (ViewHolder) v.getTag();
    else {
        v = inflater.inflate(R.layout.category_child_row, parent, false);
        viewHolder = new ViewHolder();
        holder.title = (TextView)v.findViewById(R.id.title);
        holder.cb = (CheckBox)v.findViewById(R.id.check);
        holder.cb.setChecked(isChecked);
        cb.setOnCheckedChangeListener(new CheckchangeChildListener(groupPosition, childPosition));
        v.setTag(viewHolder);
    }

    final Category c = (Category) getChild( groupPosition, childPosition );
    title.setText(c.getTitle());

    return v;
}

Checked changed listener:

class CheckchangeGroupListener implements CompoundButton.OnCheckedChangeListener {
private int groupPosition, childPosition;

public CheckchangeGroupListener(int groupPosition, int childPosition) {
    this.groupPosition= groupPosition;
    this.childPosition = childPosition;
}

@Override
public void onCheckedChanged(CompoundButton buttonView,
                             boolean isChecked) {
    // TODO Auto-generated method stub
    if (isChecked) {
        categories.get(groupPosition).getSub_categories().get(childPosition).setChecked(true);
    } else {
        categories.get(groupPosition).getSub_categories().get(childPosition).setChecked(false);
    }
}
Alex Loper
  • 135
  • 10
  • Thank you for answer. I added ViewHolder class inside my adapter class. Unfortunately, i didn't get expected result. Doesn't save previus selected items. Could you check my adapter class code? maybe i made a mistake. – Aibol Maxotov May 12 '16 at 16:42
  • See my edited response. Create a boolean outside of the inflater block to hold the state of the checkbox so that when it's inflated it was be set correctly. Then remove the `if(categories.get(groupPosition).getSub_categories().get(childPosition).isChecked()){` code block. – Alex Loper May 12 '16 at 22:00
  • I edited my adapter code. Result is not bad. When i select elements from one group, after then reopen dialog its my selected elements checkbox save your states. Problem in this, when i open another group my selected checkboxes changed and select another items automatically. I don't know why. Incorrect works. – Aibol Maxotov May 13 '16 at 19:03
  • Edited response again. You were saving the checked positions of only the selected group. – Alex Loper May 13 '16 at 19:15
  • I have two CheckedChangeListener class. One of for ChildView checkbox and another of for GroupView checkbox. I pass two parameter(groupPosition, childPosition) to CheckchangeChildListener class and pass one parameter(groupPosition) to CheckchangeGroupListener class. Its correct? – Aibol Maxotov May 14 '16 at 06:27
  • Code which you suggested, i use this in CheckchangeChildListener class. Then in CheckchangeGroupListener class i pass only one parameter(groupPosition). I call this listener from groupView method. – Aibol Maxotov May 14 '16 at 13:38