I know it's somehow possible to make a ListView that loads more data when the user has reached the bottom of the list. However, I'm working with a ScrollView, which I have a LinearLayout in, and these two components works great with the scrolling and so. But I don't know how I'm supposed to do so it gets an infinite scroll.
I suppose I need to add something that reads what is shown of the LinearLayout on the screen, and when it calculates that it is the bottom of the LinearLayout that is being shown (by using the current position and the height of the View), it triggers an event.
But as I said, I don't know how to accomplish this. So, if anyone can give me some help I would be very grateful.
EDIT: I found this post here on StackOverflow How to trigger an event when scrollView reach the bottom with Android?, but I don't know what to do with the answer:
Given the requirements, you'll likely be extending BaseAdapter (as opposed to CursorAdapter which utilizes a different mechanism).
Here's a snippet for that:
public View getView(int position, View convertView, ViewGroup parent) {
if (position == backingLinkedList.size()) {
//get more items and add them to the backingLinkedList in a background thread
notifyDataSetChanged();
}
}
If you can trigger an event when you reach the bottom of a scrollview, You could try creating your own type of view to hold your data. When you reach the bottom of the view load the next data into your view class and add it into the layout of the scroll view.
So basically create a class that holds a section of what your showing. Add the first one into the scrollview. When you hit the bottom create another view holding your next data and use scrollviews_layout.addview(view_holding_the_new_data)
I know this was solved a long time ago, but I wanted to share my solution for future infinite scrollers.
I used the onScroll method within the OnScrollListener to trigger a background thread to grab more data and adapter.notifyDataSetChanged to notify the ListView of more data to load and create an infinite scroll. For some reason, onScrollStateChanged wasn't triggering, but onScroll did what I needed so I didn't need to bother.
For the longest time I didn't understand how to keep the ListView from reloading and placing me back at the top of the list, but then I realized I only need to initialize a new adapter and call setListAdapter once, and every time after that, only call notifyDataSetChanged. Before, I was also calling setListAdapter, thereby placing me back at the top.
Related
I am designing an android game, and I'm trying to use ListView. The list uses a BaseAdapter, and is filled with an ArrayList. When the user pressed the ok button, I scroll to the top of the list, and then iterate through each soldier in the list. I set the background drawable of one of the child views of the soldier so that it's a short animation that displays "hit" or "miss". I used a Handler.postDelayed() so that each animation for the one before it to finish.
The problem is that I cannot modify the views that are invisible. I will have up to 13 soldiers in my list at a time, but only a maximum of 5 can be displayed. So once I hit the sixth soldier, I get a null pointer exception from using ListView.getChildAt(soldierArrayIndex). My solution was to add smoothScrollTo(soldierArrayIndex) before the getChildAt() call so that it would become visible, but the problem persisted.
So my question isn't exactly "how to fix my code?". I'm more wondering if there is a way to disable the recycling that ListView does. The reason I'm using getChildAt() is because I need to use findViewById() on the view it returns, and then modify the view that was found by ID. However, if the view was never recycled, getChildAt() wouldn't return null.
Another idea I had was to just use a scroll view, enter 13 instances of the Soldier View that I created, and then set the ones I'm not currently using to "gone". My only problem is I don't know how to iterate through those views.
TL;DR: How to update views that are not currently visible in listview (they are currently "recycled")?
I think you are confusing Model and View here.
Adapter Views take an adapter that usually has access to the whole dataset. So even though your ListView will only render as many items as are visible on the screen, you should still be able to access your off-screen items by calling getItem(position) on your adapter.
It seems like you are calling getView directly on your adapter.
I’m suggesting you shouldn’t do this since this is not how AdapterViews and Adapters are designed. Instead, you need to indicate to your ListView when something in your model is changed (via notifyDataSetChanged) and leave it up to your ListView to call your adapter’s getView method on your behalf.
I imagine the sequence of events being something like this:
Update Soldier Object
Call notifyDataSetChanged on your adapter which will tell your ListView it needs to redraw stuff
ListView iterates through the visible Soldier items calling getView(int position, View convertView, ViewGroup parent) on each.
Your getView implementation in your adapter gets the Soldier at the specified position and determines what the View should look like at that point in time.
For "offscreen" Soldiers (ones that are not visible in the scroll area of the ListView,) there is no need for the ListView to render them, so it will not call getView on those positions.
However, if a Soldier that was offscreen is now scrolled into view, the ListView will call your adapter's getView method with that Soldier's position in your array.
Finally, if you want finer-grained control over how an AdapterView should update itself, you might consider RecyclerView as it allows you to notify changes on an item by item basis.
I am creating a TutorialManager that handles all the user tutoring for app usage.
I have a ViewGroup that adds views to itself including a GridView that is filled with items after onMeasure() is called.
I am calling TutorialManagers method that's supposed to perform an action on the gridview but it needs to know when the gridview is filled in order to use any of it items
I added a callback method on the ViewGroup that is called right after all the view have been added
The problem is that the TutorialManager needs to know the X and Y coordinate of the views in order to highlight them using ShowCaseView
I've added a workaround using postDelayed(action, 100); which seems to work on my handset but what if on some device the view layout takes more than 100ms? It'll screw up the whole tutoring system.
I can't figure out any other way than creating a custom view that has a callback method which is called after the views position is known.
What's the common way of handling the issue that the laying view on screen is happening asynchronously?
You can try using an OnGlobalLayoutListener which fires whenever there is a layout change (Ie, your GridView and children have been added and measured. So where ever you initialize the GridView do this:
LayoutListener mLayoutListener = new LayoutListener;
mGridView.getViewTreeObserver().addOnGlobalLayoutListener(mLayoutListener);
private class LayoutListener implements OnGlobalLayoutListener {
#Override
public void onGlobalLayout() {
...Do your calculations here...
mGridView.getViewTreeObserver().removeOnGlobalLayoutListener(this); //If you only need this to execute once.
}
}
Note this will be called quite often. I'd recommend removing as soon as you don't need it.
my viewpager currently only re-renders views that are two views away from what is visually seen.
example (shown respectively)
ViewA, ViewB, CurrentView, ViewD, ViewE
not rendered, rendered, rendered, rendered, not rendered`
how would I force it to reload a particular view X many views away from the currentView, there are some use case scenarios where I want it to, and other cases when I dont want it to. But currently I only know how to reload the entire adapter - where it perhaps pulls from an arraylist
I'm really not sure how to control the Viewpager - very few examples out there. All I know how to do is reset the entire list on "notifyDataSetChanged"
insight appreciated
use this method mViewPager.setOffscreenPageLimit(2) and you can set the limit of offscreen pages
I think your going too need to decouple your view and data logic (array list in this case?). If you don't want the view to change, don't change the data layer. When you call notifyDataSetChanged everything will mimic your data layer. If you want a view to change, change the data layer and call notifyDataSetChanged.
To put it simpler, the view just draws what the data tells it to, and expect that the view could update at any time. It is very similar to how listview works.
I have a TableLayout inside a ScrollView, because of some special needs, I need to do some things when the ScrollView has reached the top. Is there such a listener to listen in on user's scrolling operations? Or, if there is no a ready listener, can I subclass the ScrollView (which will be painful I supposed) to achieve that? What methods do I need to implment? So far, I don't see a solution to this, but I hope it's only because I missed something .....
Thank you!
(In case you're wondering why I want to do this, here is some more background:
I have a flat-file database, I use a TableLayout to show the query results. When the query generates thousands of rows, the time spent on populating the table (using TableLayout.addView() to add rows) becomes unacceptable -- one minute on the emulator! And, there is a special need that when the table shows up, it has to be put at the LAST PAGE. That is, I have to populate the last few rows first, then grow upward, while the display remains unchanged when the table is growing. I tried two approaches so far, none was satisfactory. So I had to downgrade the requirement a little --- I don't populate all 3,000 rows at once, instead, I populate only the last couple of pages, like the last 40 rows or so. This way, the user will see the results displayed immediately. Then, if and when he decides to scroll up, when the program sees it, then I populate a few more pages, etc. Therefore, I need a listener or some way to detect when the table or the scroll view has reached the top. Thank you for reading up to this far!)
You might want to look into extending a BaseAdapter http://developer.android.com/reference/android/widget/ArrayAdapter.html and override getView(int position, View convertView, ViewGroup parent).
In that method you can check the position. So if you start out by populating your list with 10 items and the position is 10 then you know you've reached the bottom of your list and can add more items to your list. You know your at the top of your list if the position is 0. There's also a method in ListView to have your items start at the bottom ( like the messenger app ) http://developer.android.com/reference/android/widget/AbsListView.html#setStackFromBottom(boolean).
I have a listview with radio button. when i click any option and scroll that list view the previous select will cleared.How to do this. The actual problem is when scroll the view on every scorll the view is refreshed, that's why every time the view is refreshed. How to do this I can't get any solution, Plz. Help.
Check your adapter, you are probably don't do the job right on bindView. You have to set again on bindView the values.
I will reformulate the sentence and you will probably will understand.
The newView creates only 5-10 views (as many they fit on the screen), and they are reused for other rows. If you have a ListView with 200 lines in it, actually you have only 5-10 views, and you have to make sure you update the views with the valid changes in bindView. You have to store/save the changes to an object for later reuse.