Java Question How to add the ScrollChanged listener to ListView

Informatix

Expert
Licensed User
Longtime User
I'd like to add the ScrollChanged listener to ListView and I'm stuck. My understanding of wrappers is very limited (especially without documentation) and my Java knowledge is far from excellent.
I cannot access to setOnScrollChanged through the wrapper because it's a protected method. So it seems to me that the only way to add the ScrollChanged listener to the ListView (as it is implemented in B4A) is to recreate the wrapper and the adapter. Am I wrong ?
Recreating the ListView is too much work. Is there an easy way ?
 

Informatix

Expert
Licensed User
Longtime User
Could you post your code so far?

Tomas

There's no code currently. I was just trying and evaluating the work to do.
If I recreated the library, I would add this in the SimpleListView class:
B4X:
  public BA ba;
  public String eventName;
  protected void onScrollChanged(int l, int t, int oldl, int oldt)
  {
    super.onScrollChanged(l, t, oldl, oldt);
    if (this.ba != null)
      this.ba.raiseEventFromUI(this, this.eventName + "_scrollchanged", new Object[] { Integer.valueOf(t) });
  }
 

XverhelstX

Well-Known Member
Licensed User
Longtime User
or add your own listener:

First you create your initialize method.

B4X:
private BA ba; 
private String eventName;

/**
    * Initializes this listview extension library.
    * EventName - Events subs prefix. 
    */
   public void Initialize(final BA ba, String EventName) {
      this.ba = ba;
      this.eventName = EventName.toLowerCase(BA.cul);
      
   }

and next you implement your listener:

B4X:
/**
    * Sets a scrolllistener for the listview.
    * @param lvw
    */
   public void setListener(ListViewWrapper lvw) {
      ListView lv =  lvw.getObject();
      lv.setOnScrollListener(l);
      
   }
   
   OnScrollListener l = new OnScrollListener() {

      @Override
      public void onScroll(AbsListView arg0, int arg1, int arg2, int arg3) {
         // TODO Auto-generated method stub
         ba.raiseEvent(this, eventName + "_onscroll", new Object[] { arg1, arg2, arg3 } );
      }

      @Override
      public void onScrollStateChanged(AbsListView arg0, int arg1) {
         // TODO Auto-generated method stub
         ba.raiseEvent(this, eventName + "_onscrollstatechanged", new Object[] { arg1} );
      }
      
   };

The events _onscroll and _onscrollstatechanged of the eventname from initialized are called when something happens while scrolling.
I have no idea what the arguments/paramaters are in the event names.
You can look up the Android developers resource and find out the arguments:
http://developer.android.com/reference/android/widget/ListView.html


Note that you'll need to add the core.jar to your project with your b4shared.jar and android.jar. Then you should import the correct packages. (control-shift-o)

I really suggest you to start watching the video tutorial and then the written tutorials. It will help you a lot on understanding Java.

Tomas
 

Informatix

Expert
Licensed User
Longtime User
Have a look at this thread

http://www.b4x.com/forum/basic4android-getting-started-tutorials/17708-custom-listview-library.html

where Martin has written more or less a tutorial with samples as to implementing a Custom ListView.

Maybe from the sources furnished you can get a hint how to achieve what you want.

I'm not clear in my explanations. I want to get the onScrollChanged events of the existing B4A ListView, without recreating it. I don't think that Erel will add the listener, so I have to find a solution.
 

XverhelstX

Well-Known Member
Licensed User
Longtime User

Informatix

Expert
Licensed User
Longtime User
Last edited:

Informatix

Expert
Licensed User
Longtime User
No idea ? No clue ? Anyone ?

I'm going to try to be more precise and more technical (and correct me if I say something wrong or stupid):
ListView in B4A is a wrapper (ListViewWrapper) around a class (SimpleListView) which extends ListView and uses an adapter (SimpleListAdapter).
The wrapper is an additional layer that prevents me from accessing easily to the protected methods of ListView (I can't use the super reference and super.super does not work). Theoretically, I should have an access since the wrapper extends SimpleListView. It is, de facto, a subclass of ListView. But I don't know how to do.
 

Informatix

Expert
Licensed User
Longtime User
Can you post some code of your efforts so it becomes more clear what you want to do? Right now you aim is not clear at all.
To get the underlying object I.e. the list view you need to use getObject.

Sent from my GT-I9000 using Tapatalk 2

I deleted all the code I tried because I had no success with it, so I have nothing more to offer than my post #3. I don't know how to explain my need more precisely (and english is not my language). ListView has a listener for the scroll change events and I want to set this listener. That's as simple as that. If I wanted to set the onClick listener for example, I would use the reflection library and call the setOnClickListener function. Unfortunately, the listener I want to set is not a public method and I cannot replicate the Reflection library way of doing. As a protected method, the listener is only accessed by subclasses. But I can't access it from inside the wrapper. It's maybe very simple with getObject but I can't figure out what to do.
If I had all the code source of the ListView made by Erel, that would be very easy, as stated in my post #3.
 

thedesolatesoul

Expert
Licensed User
Longtime User
I deleted all the code I tried because I had no success with it, so I have nothing more to offer than my post #3.
You should not have deleted the code and made a note of what error you get. Otherwise we have no idea what you are doing, and what errors you are getting.

ListView has a listener for the scroll change events and I want to set this listener. That's as simple as that.
Is this listener what you want?
AbsListView | Android Developers)

As a protected method, the listener is only accessed by subclasses. But I can't access it from inside the wrapper. It's maybe very simple with getObject but I can't figure out what to do.
If I had all the code source of the ListView made by Erel, that would be very easy, as stated in my post #3.
The ListViewWrapper is a subclass of the listview. You should be able to access the protected method.

I think the solution given by XverhelstX here is the correct one.

I dont understand why you say:
I want to set the OnScrollChanged listener, not create my own listener or use onScroll or onScrollState. That's where the problem is.
What do you want to SET the listener to? You obviously have to create a listener so you can set it to.

If you are trying to get the scrollPosition in pixels, you will have to use the getFirstVisiblePosition (the listener should give you this) to get the first viewable item, get the view at the position, and get the '.top' property of that view. That should give you the scrollPosition.
 

Informatix

Expert
Licensed User
Longtime User
I rewrote the code of one of my tries:

B4X:
package flm.b4a.scrollchanglistview;

import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.objects.ListViewWrapper;

@BA.Events(values = { "ScrollStateChanged(Position as Int, ScrollState as Int)", 
                 "ScrollChanged(Position as Int, firstVisibleItem as Int, visibleItemCount as Int, totalItemCount as Int)" })
@BA.Version(1.0F)
@BA.ShortName("ListViewWithScrollEvents")
@BA.Author("Frédéric Leneuf-Magaud")
public class ListViewWithScrollEvents extends ListViewWrapper implements OnScrollListener{

   private BA ba;
   private String eventName;
   private SimpleListView slv;
   
   @Override
   public void innerInitialize(BA innBa, String innEventName, boolean keepOldObject) {
      super.innerInitialize(innBa, innEventName, keepOldObject);
      ba = innBa;
      eventName = innEventName;
        slv =  this.getObject();
        slv.setOnScrollListener(this);
    }
   
   @Override
   public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
      if (this.ba != null) {
         if (ba.subExists(eventName + "_scrollchanged")) { 
            int Y = slv.getScrollY();
            this.ba.raiseEventFromUI(this, this.eventName + "_scrollchanged", new Object[] { Y, firstVisibleItem, visibleItemCount, totalItemCount });
         }
      }
   }
   @Override
   public void onScrollStateChanged(AbsListView view, int scrollState) {
      if (this.ba != null) {
         if (ba.subExists(eventName + "_scrollstatechanged")) {
            int Y = slv.getScrollY();
            this.ba.raiseEventFromUI(this, this.eventName + "_scrollstatechanged", new Object[] { Y, scrollState });
         }
      }
   }
}

It is very close to the solution given by XverhelstX, but it does not do what I want: getScrollY always returns 0 and I cannot set the onScrollChanged listener.
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
If you are trying to get the scrollPosition in pixels, you will have to use the getFirstVisiblePosition (the listener should give you this) to get the first viewable item, get the view at the position, and get the '.top' property of that view. That should give you the scrollPosition.
With that solution, if you move the list down by about 20dips (a half-item), you get 0 as the ScrollPosition because your move is not enough to go to the next item and fires the event with the right values. That's why I can't use it. I need to know precisely where the scrollbar is when it moves.
 

Informatix

Expert
Licensed User
Longtime User
I looked into the ListView code and found this:
B4X:
void invokeOnItemScrollListener() {
...
   }
        onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
    }

:sign0013:

I lost my time, and more important, you lost your time for me. Thanks for reading me and trying to understand me. I'm very sorry. I should have started by reading this fu*** code.
 
Top