1

I have implemented expandablelistview and its working fine. Now I want to add "load more" button at then end of every child list so if user clicks it it will fetch more items from server. Is there anyway to do that because I can't find anything after searching a lot. Any guidance is much appreciated.

This is my Custom Adapter:

public class ExpandableListAdapterAssets extends BaseExpandableListAdapter implements Filterable {

List<View> groupViews;
ItemFilter mFilter;
boolean showCheckBox;
ArrayList<Asset> assetsToClaim;
private Context _context;
private List<AssetCategory> _listDataHeader; // header titles
// child data in format of header title, child title
private HashMap<Integer, List<Asset>> _listDataChild;
private RequestBackendHelper requestBackendHelper;

public ExpandableListAdapterAssets(Context context, List<AssetCategory> listDataHeader,
                                   HashMap<Integer, List<Asset>> listChildData, boolean b)
{
    this._context = context;
    this._listDataHeader = listDataHeader;
    this._listDataChild = listChildData;
    groupViews = new ArrayList<>();
    showCheckBox = b;
    assetsToClaim = new ArrayList<>();
}

@Override
public Asset getChild(int groupPosition, int childPosition)
{
    return this._listDataChild.get(this._listDataHeader.get(groupPosition).getId())
            .get(childPosition);
}


public List<Asset> getChildList(int groupPosition)
{
    return this._listDataChild.get(this._listDataHeader.get(groupPosition).getId());
}

@Override
public long getChildId(int groupPosition, int childPosition)
{
    return childPosition;
}

@Override
public View getChildView(final int groupPosition, final int childPosition,
                         boolean isLastChild, View convertView, ViewGroup parent)
{

    final Asset assetChild = getChild(groupPosition, childPosition);

    if (convertView == null)
    {
        LayoutInflater infalInflater = (LayoutInflater) this._context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = infalInflater.inflate(R.layout.list_item, null);
    }

    TextView assetName = (TextView) convertView
            .findViewById(R.id.txt_asset_name);

    assetName.setText(assetChild.getName());

    TextView assetValue = (TextView) convertView
            .findViewById(R.id.txt_asset_value);

    assetValue.setText(getFormattedAmount(assetChild.getValue()));

    ((TextView) convertView.findViewById(R.id.txt_asset_category)).setText(assetChild.getAssetCategory());
    ((TextView) convertView.findViewById(R.id.txt_asset_date)).setText(assetChild.getCreationDate());
    if (showCheckBox)
    {
        CheckBox itemSelect = convertView.findViewById(R.id.chkbox_item_select);
        itemSelect.setVisibility(View.VISIBLE);
        itemSelect.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
            {
                if (isChecked)
                {
                    assetsToClaim.add(getChild(groupPosition, childPosition));
                    Log.d("checkbox", "checked: ");
                }
                else
                {
                    assetsToClaim.remove(getChild(groupPosition, childPosition));
                }


            }
        });
    }
    else
    {

    }
    return convertView;
}

@Override
public int getChildrenCount(int groupPosition)
{
    if (_listDataChild == null)
        return 0;
    else if (_listDataChild.size() == 0)
        return 0;

    else
    {
        int position = this._listDataHeader.get(groupPosition).getId();
        int count = this._listDataChild.get(position).size();
        return count;
    }

}

@Override
public Object getGroup(int groupPosition)
{
    return this._listDataHeader.get(groupPosition);
}


@Override
public int getGroupCount()
{
    return this._listDataHeader.size();
}

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

@Override
public View getGroupView(final int groupPosition, boolean isExpanded,
                         View convertView, final ViewGroup parent)
{
    if (convertView == null)
    {
        LayoutInflater infalInflater = (LayoutInflater) this._context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = infalInflater.inflate(R.layout.list_group, null);
    }

    TextView lblListHeader = (TextView) convertView
            .findViewById(R.id.lblListHeader);
    lblListHeader.setTypeface(null, Typeface.BOLD);
    lblListHeader.setText(_listDataHeader.get(groupPosition).getName());
groupViews.add(convertView);
    return convertView;
}

private void requestDeleteCategory(final int groupPosition)
{
    requestBackendHelper = new RequestBackendHelper(_context);
    if (getChildrenCount(groupPosition) == 0)
    {

        requestBackendHelper.deleteCategory(_listDataHeader.get(groupPosition).getId(), _context.getSharedPreferences(MY_PREFS_NAME, Context.MODE_PRIVATE).getString("jwt", "")
                , new VolleyCallBack() {
                    @Override
                    public void onSuccess(String result)
                    {
                        try
                        {
                            JSONObject parentObj = new JSONObject(result);
                            if (parentObj.getBoolean("status"))
                            {
                                requestBackendHelper.createErrorDialog("Category Deleted.", "success");
                                _listDataHeader.remove(groupPosition);
                                _listDataChild.remove(_listDataHeader.get(groupPosition));
                                notifyDataSetChanged();
                            }
                            else
                            {
                                requestBackendHelper.createErrorDialog(parentObj.getString("error"), "");
                            }
                        }
                        catch (JSONException e)
                        {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onFailure(VolleyError error)
                    {
                        requestBackendHelper.createErrorDialog(error.toString(), "");
                    }
                });
    }
    else
    {
        requestBackendHelper.createErrorDialog("The category cannot be deleted until all assets are removed.", "");
    }
}

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

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

public List<View> getGroupViewList()
{
    return groupViews;
}

private void loadEditCategoryAlertDialog(final int groupPosition)
{
    View viewInflated = LayoutInflater.from(_context).inflate(R.layout.activity_add_category_dialog, null);
    final EditText nameInput = viewInflated.findViewById(R.id.categoryNameEditText);
    viewInflated.findViewById(R.id.categoryDescriptionEditText).setVisibility(View.GONE);
    final TextView errorLabel = viewInflated.findViewById(R.id.errorTextView);

    final AlertDialog dialog = new AlertDialog.Builder(_context)
            .setView(viewInflated)
            .setTitle("Edit Category")
            .setPositiveButton(android.R.string.ok, null)
            .setNegativeButton(android.R.string.cancel, null)
            .setCancelable(false)
            .create();

    dialog.setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialogInterface)
        {
            Button okButton = (dialog).getButton(AlertDialog.BUTTON_POSITIVE);
            okButton.setOnClickListener(new DebouncedOnClickListener() {

                @Override
                public void onDebouncedClick(View view)
                {
                    final String categoryName = nameInput.getText().toString();
                    requestRenameCategory(categoryName, groupPosition, dialog, errorLabel);

                }
            });
        }
    });
    dialog.show();
}

private void requestRenameCategory(final String categoryName, final int groupPosition, AlertDialog dialog, TextView errorLabel)
{
    if (categoryName.isEmpty())
    {
        errorLabel.setText(R.string.add_category_validation_error);
    }
    else
    {
        if (requestBackendHelper == null)
            requestBackendHelper = new RequestBackendHelper(_context);
        requestBackendHelper.attemptEditAssetCategory(createEditCategoryJson(categoryName), _listDataHeader.get(groupPosition).getId()
                , _context.getSharedPreferences(MY_PREFS_NAME, Context.MODE_PRIVATE).getString("jwt", "")
                , new VolleyCallBack() {
                    @Override
                    public void onSuccess(String result)
                    {
                        _listDataHeader.get(groupPosition).setName(categoryName);
                        int size = _listDataHeader.get(groupPosition).getAssetCout();
                        for (Asset asset : getChildList(groupPosition))
                            asset.setAssetCategory(categoryName);
                        notifyDataSetChanged();
                                       }
                        notifyDataSetChanged();
                    }

                    @Override
                    public void onFailure(VolleyError error)
                    {

                    }
                });
        dialog.dismiss();
    }
}

private JSONObject createEditCategoryJson(String categoryName)
{
    JSONObject messageObj = new JSONObject();
    JSONObject assetCategoryObj = new JSONObject();
    try
    {
        messageObj.put("name", categoryName);
        assetCategoryObj.put("asset_category", messageObj);
        return assetCategoryObj;
        //contactusObj.put(message);
    }
    catch (JSONException e)
    {
        e.printStackTrace();
    }
    return null;

}

/**
 * <p>Returns a filter that can be used to constrain data with a filtering
 * pattern.</p>
 *
 * <p>This method is usually implemented by {@link Adapter}
 * classes.</p>
 *
 * @return a filter used to constrain data
 */
@Override
public Filter getFilter()
{
    if (mFilter == null)
    {
        mFilter = new ItemFilter();
    }
    return mFilter;
}

private class ItemFilter extends Filter {

    protected FilterResults performFiltering(CharSequence constraint)
    {

        FilterResults results = new FilterResults();

        return results;
    }

    /**
     * <p>Invoked in the UI thread to publish the filtering results in the
     * user interface. Subclasses must implement this method to display the
     * results computed in {@link #performFiltering}.</p>
     *
     * @param constraint the constraint used to filter the data
     * @param results    the results of the filtering operation
     * @see #filter(CharSequence, FilterListener)
     * @see #performFiltering(CharSequence)
     * @see FilterResults
     */
    @Override
    protected void publishResults(CharSequence constraint, FilterResults results)
    {

    }
}

}

this how I'm setting Adapter:

explistAdapter = new ExpandableListAdapterAssets(getActivity(), assetCategories, listDataChild, false);
    expListView.setAdapter(explistAdapter);
Faisal
  • 705
  • 3
  • 16

1 Answers1

1

you'd need to insert that "load more" button with a custom BaseExpandableAdapter. this answer appears related, because it shows how to provide two different types of child views. the tricky thing is, that when the "load more" button had been clicked, it has to be removed, then new items need to be attached together with a new one "load more" button at the bottom - and then the child-adapter needs to be notified. it has to behave alike a virtual list item, where getChildView() provides the current childPosition.

just seen the code provided; with Volley you'd most likely need to fetch one item less on the first load, so that the adapter will not run out of sync due to the one additional virtual item added - or at least add it with view-type 2 at the end of the network request, before adding it into the adapter. another option might be to let getChildCount() return one less; this additional item in every case has to be taken care of when paging.

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • I get your point there. That was my initial thought also. So if I can explain you, my Child for every group is a arraylist which is of size 10 initially. rather then fetching 9 as you suggested isn't it better to fetch 10 and then add 11th Asset object with everything null and just one attribute in (-,negative). While populating expandablelistview I always checkif that `atttribute <0` then hide default layout and show button instead? – Faisal Feb 28 '19 at 02:45
  • 1
    @Faisal it need to be added as an `Asset` item, else it won't fit into the adapter... and some property needs to be there to differ the view-types. even tagging that view with the start position of the next one page might be useful, so that one does not have to depend on `getChildCount()`. in general, the view, the adapter and the network requests need to be in-sync... verbose logging might help to get that right. – Martin Zeitler Feb 28 '19 at 02:58
  • Thank you so much for taking time and replying here. I know what to do now. I'll get back once I complete this. I'll ask you more question if I need more help regarding this question while doing this. – Faisal Feb 28 '19 at 03:02
  • 1
    @Faisal alright. the advance is that one can manipulate the adapter's `ArrayList` as one pleases, until calling to `notifyItemRangedChanged(fromIndex, toIndex)` ...which has to consider the previous last item, which will become the next page's first item with another view-type, upon loading more. – Martin Zeitler Feb 28 '19 at 03:06