2

I'm implementing an ExpandableListView on my app and I try to detect when the scroll hits the bottom of this ExpandableListView. I've tried using onScrollListener and onScrollChangedListener.

For the onScrollListener, it is called way too many times to be an acceptable solution.

However for the onScrollChangedListener, it seems to be what I want. The problem is that as ExpandableListView doesn't have 1 child level but 2, I don't manage to find a way to identify the last visible element.

Has anyone already implemented this kind of behaviour on an ExpandableListView ?

A.Danibo
  • 494
  • 1
  • 4
  • 16

3 Answers3

0

You can done via getting position of last group when it expand

expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
        @Override
        public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
            if(groupPosition == expandableListView.getExpandableListAdapter().getGroupCount()){
            // your code goes here
            }
            return false;
        }
    });
0

Try below sample:-

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">

<ExpandableListView
    android:id="@+id/elvMain"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</ExpandableListView>

</LinearLayout>

MainActivity.java:

public class MainActivity extends AppCompatActivity {
ArrayList<String> mainGroupList;
HashMap<String, ArrayList<String>> mainChildList;
CustomAdapter mainAdapter;
ExpandableListView elvMain;
int newDataSet = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    elvMain = findViewById(R.id.elvMain);
    mainGroupList = new ArrayList<>();
    mainChildList = new HashMap<>();
    for(int i = 0; i < 20; i++){
        ArrayList<String> tmpList = new ArrayList<>();
        for(int j = 0; j< 17; j++){
            tmpList.add("Child " + j);
        }
        mainChildList.put("Group " + i, tmpList);
        mainGroupList.add("Group " + i);
    }
    mainAdapter = new CustomAdapter(this, mainGroupList, mainChildList);
    elvMain.setAdapter(mainAdapter);

    elvMain.setOnScrollListener(new AbsListView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(AbsListView absListView, int i) {}
        @Override
        public void onScroll(AbsListView absListView, int i, int i1, int i2) {
            if(i2 == i + i1){
                View lastView = absListView.getChildAt(i1 - 1);
                if(lastView.getBottom() == absListView.getBottom()){
                    try{
                        String viewTag = lastView.getTag().toString();
                        if(viewTag.equals("Group")){
                            Toast.makeText(getApplicationContext(),
                                    "No more data, unless expanded the last group!!",
                                    Toast.LENGTH_SHORT).show();
                            //elvMain.expandGroup(mainAdapter.getGroupCount() - 1);
                        }
                    }catch(Exception e){
                        addNewData();
                    }
                }
            }
        }
    });
}

private void addNewData(){
    newDataSet++;
    for(int i = 0; i < 5; i++){
        ArrayList<String> tmpList = new ArrayList<>();
        for(int j = 0; j< 7; j++){
            tmpList.add("Child " + j);
        }
        mainChildList.put("Added " + newDataSet + ",Group " + i, tmpList);
        mainGroupList.add("Added " + newDataSet + ",Group " + i);
    }
    mainAdapter.notifyDataSetChanged();
}
}

CustomAdapter.java:

public class CustomAdapter extends BaseExpandableListAdapter {
ArrayList<String> groupList;
HashMap<String, ArrayList<String>> childList;
LayoutInflater inflater;

public CustomAdapter(Context context, ArrayList<String> groupList, HashMap<String, ArrayList<String>> childList) {
    this.inflater = LayoutInflater.from(context);
    this.groupList = groupList;
    this.childList = childList;
}

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

@Override
public int getChildrenCount(int i) {
    return childList.get(groupList.get(i)).size();
}

@Override
public String getGroup(int i) {
    return groupList.get(i);
}

@Override
public String getChild(int i, int i1) {
    return childList.get(groupList.get(i)).get(i1);
}

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

@Override
public long getChildId(int i, int i1) {
    return 0;
}

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

@Override
public View getGroupView(int i, boolean b, View view, ViewGroup viewGroup) {
    if(view == null) view = inflater.inflate(android.R.layout.simple_list_item_1, null);
    TextView textView = view.findViewById(android.R.id.text1);
    textView.setText(getGroup(i));
    view.setTag("Group");
    return view;
}

@Override
public View getChildView(int i, int i1, boolean b, View view, ViewGroup viewGroup) {
    if(view == null) view = inflater.inflate(android.R.layout.simple_list_item_1, null);
    TextView textView = view.findViewById(android.R.id.text1);
    textView.setText(getChild(i, i1));
    return view;
}

@Override
public boolean isChildSelectable(int i, int i1) {
    return false;
}
}

In this sample, I use setTag() to identify the view is "Group". If you use different layouts for group view and child view, then you may use findViewById() to find a view that is inside the group view and so no changes in your adapter.

In addition, inside the try-catch, you can comment out the Toast() and enable the code elvMain.expandGroup(mainAdapter.getGroupCount() - 1); which automatically expand the last group when the list is scrolled to the bottom, so the ExpandableListView becomes endless.

Hope that helps!

i_A_mok
  • 2,744
  • 2
  • 11
  • 15
-1

try this solution Detect when RecyclerView reaches the bottom most position while scrolling - check answer of Kaustubh Bhagwat

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);

        if (!recyclerView.canScrollVertically(1)) {
            Toast.makeText(YourActivity.this, "Last", Toast.LENGTH_LONG).show();

        }
    }
});
Vasudev Vyas
  • 726
  • 1
  • 10
  • 28
  • In the end, `onScrollChangedListener` is not what I need. I need a listener that is triggered only when the user flings, thought that `onScrollChangedListener` was fit for this considering it's the most precise that I could find for `ExpandableListView`. Do you know if it is possible to have it on an `ExpandableListView` ? – A.Danibo Jul 29 '19 at 09:25
  • i am trying to understand your question... you need listener on you reach to last record of expandable listview last record? – Vasudev Vyas Jul 29 '19 at 09:30
  • What I want to do is: Load more data on the `ExpandableListView` when the user is at the end of the list. The first problem is that the `ExpandableListView` has groups and children. And I have no method to determine if the last element is a closed group or the child of an expanded group. The second one is that as the `onScrollListener` and the `onScrollChangeListener` are called way too often, causing too many calls when loading more data. – A.Danibo Jul 29 '19 at 09:42
  • add one child to the end of last group programmatically. if child is visible then load more data... may be work for you. if you like suggetion let me know or you can flag my answer -1 if i am not understand ur question – Vasudev Vyas Jul 29 '19 at 10:00
  • @A.Danibo did you try the solution which I posted above? – Om Infowave Developers Jul 29 '19 at 12:17
  • The problem I'm currently trying to resolve is finding a way to call my data loading when the user tries to scroll when already at the bottom. At the moment, the listener I lastly used (`onScrollChangedListener`) calls my data loading much too often and doesn't wait user input. I have an idea of how to use your tip @OmInfowaveDevelopers but I need to solve this problem first. – A.Danibo Jul 29 '19 at 12:43
  • @A.Danibo your data will be fetched when user reach at the end so onScrollChangedLister event will be triggered by the system so I guess you can't handle that event manually so better solution is when the last group of expandable list is expanded by the user at that moment user is always bottom of the list so new data will be fetched if you find better way share here it will be helpfull for others – Om Infowave Developers Jul 29 '19 at 12:54