ViewPager + Adapter in Fragment => laggy swiping - java

I have a ViewPager with some fragments. Each fragment has a ListView in a SlidingDrawer (=invisible before swiping) with an ArrayAdapter.
Adapter is set on onCreateView(), that slows down swiping, because 30 list items have to load each time I swipe, because new fragments are being created.
My Question is, whether it is possible to set the adapter after swiping when it ViewPager is idle? Or is there a better way? The List needs to be already loaded when the SlidingDrawer is expanded.

I had a similar problem... I used listeners. Still, when you swipe two pages back to back it was laggy... I did something like this that improved the experience....
viewpager.setOnPageChangeListener(new OnPageChangeListener() {
int positionCurrent;
boolean dontLoadList;
#Override
public void onPageScrollStateChanged(int state) {
if(state == 0){ // the viewpager is idle as swipping ended
new Handler().postDelayed(new Runnable() {
public void run() {
if(!dontLoadList){
//async thread code to execute loading the list...
}
}
},200);
}
}
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
positionCurrent = position;
if( positionOffset == 0 && positionOffsetPixels == 0 ) // the offset is zero when the swiping ends{
dontLoadList = false;
}
else
dontLoadList = true; // To avoid loading content for list after swiping the pager.
}
}
If you take a few milli seconds to load the list that comes as supplement to the viewpager, its ok in terms of UX rather than giving a bad swiping experience... So, the idea is to wait for 400ms in the thread before loading the list and making sure that you actually dont load content when the user is trying to swipe fast to see the viewpager content...

My Question is, wether it is possible to set the Adapter after swiping
when it Pager is idle?
There is the OnPageChangeListener that you could set on the ViewPager to monitor the swipe gestures. You could then use the onPageSelected()(or the onPageScrollStateChanged() to monitor the current state) method to get notified when a new page has been selected and start from that method the loading of data.
Also, make sure the ListView are responsible for the lag and not some other part of your code.

Related

How to know that last recyclerView item of first or current screen/page is loaded, any Callbacks but gets called once?

I am using a grid layout of recyclerView and have tried several options using either onLayoutCompled methods and also by onScollListner methods, tried this answer but it's stiil calling multiple times as each item keeps loading, nothing is working.
Just want to detect the last recyclerView item of the particular screen is completely loaded or visible to the user according to their screen size Or we can say that when the user's current screen is full of the recyclerView Items, any way to detect that? ANd should be called only once. Any help would be great.
Thank you for your valuable time.
You can use a Sharedpref value which can be set to true in your onScrollStateChanged method based on your condition.
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState != RecyclerView.SCROLL_STATE_IDLE) {
return;
}
boolean isLastPositionVisible = mFollowingLayoutManager.findLastVisibleItemPosition() == getList().size() - 1
if(isLastPositionVisible && getSharedPrefValue()) {
// Call your method
// Set sharedpref value to true.
}
}

How i can load new elements when my ListView is scrolled to bottom?

You can find my code here:
How to correctly build table with data using onPostExecute and ListView
I need to do loading data from the server when my ListView is scroll to the bottom. I tried to looking for solution on Stackoverflow, but it is not helpful for me.
Also if it's not difficult i like to know how it's work for understand all.
Thank's to all
You have 2 solutions:
1) With OnScrollListener
You must have a class that extends ListView and implements OnScrollListener.
When you initialise the view, set it as the scroll listener :
setOnScrollListener(this);
Implement the method onScroll. It's called when you scroll with the arguments firstVisibleItem, visibleItemCount and totalItemCount.
When firstVisibleItem+visibleItemCount==totalItemCount you reached the bottom of the list, you can call your AsyncTask again to load the next items.
2) With a custom adapter
In the method getView that you must overwrite, you have access to the position of the item being rendered, i.e. about to be visible on the screen.
Let's say you store your items in a List items you know when you reached the bottom of the list when position == items.size()-1. You can then call your AsyncTask.
Warning
Be careful with these 2 solutions, if all the items of the list fit in the screen, your AsyncTask may be called very often and for no reason. You must do the necessary checks for that before starting it.
Use the Scroll state change listener in your program...I hope It will help you definitely ....
listStudies.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (Logic Condition) {
//Here also You can do your Logic here and then you can achieve your wishes.....
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (Logic Condition) {
//You just do your Logic here and then you can achieve your wishes.....
}
}
});

Android OnGestureListener issues when view contains a listview

Im having some issues with the Android onGestureListener, I have 3 Linear Layouts sitting next to each other horizontally in a Linear Layout, the Linear Layout position is set to the middle Layout onCreate, the middle layout also contains a listview inside of it, what I would like to happen is when I swipe left or right the layout moves, this seems to not pick up the gestures when I attempt to swipe left or right on the Linear Layout with the listview inside of it, but if I swipe right or left on the other views that dont have anything in them it picks up the gesture and the view animates accordingly, has anyone had this issue before or have an idea how to fix it? any help will go a long way thanks
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
return gestureDetector.onTouchEvent(event);
}
SimpleOnGestureListener simpleOnGestureListener = new SimpleOnGestureListener() {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
float sensitvity = 50;
if ((e1.getX() - e2.getX()) > sensitvity) {
slideLeft();
} else if ((e2.getX() - e1.getX()) > sensitvity) {
slideRight();
}
return true;
}
};
private void slideRight() {
if (swipeDirection > -1) {
if (swipeDirection == 0) {
layoutContainer.animate().translationX(theDistance - 0)
.setDuration(500);
} else {
//go to home
layoutContainer.animate().translationX(0).setDuration(500);
}
swipeDirection--;
}
}
private void slideLeft() {
if (swipeDirection < 1) {
if (swipeDirection == 0) {
layoutContainer.animate().translationX(0 - theDistance)
.setDuration(500);
} else {
layoutContainer.animate().translationX(0).setDuration(500);
}
swipeDirection++;
}
}
The ListView itself has a gesture listener built in already (for scrolling the list), and possibly it also has an onItemClickListener on the individual items in the list. It can be that this interferes with your swipe behavior of the whole layout.
The solution explained here by Pinhassi has worked best for me so far:
Android Swipe on List
probably you will need to expand on the onItemClickListener of your ListView and include the swipe detector mentioned above. Also, putting #Override in front of the line where you declare the onItemClick might be needed to override the listview listeners. That way, you will maintain clickable list items and you can perform swipes on them.

ViewPager and AsyncTask for loading the Items

I've a ViewPager with an PageAdapter loading ImageViews.
The images displayed come from the internet and I use an AsyncTask for loading the Bitmap and then (if the Page hasn't been destroyed) assign it to the ImageView.
#Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView img = new ImageView(container.getContext());
new LoadImageTask(img,Data.getImageUrL(position)).execute();
container.addView(img );
return img;
}
So the ImageView has two states: loading or loaded. I need to track if the user waits for the image. So I implement this at the LoadImageTask onPostExecute:
if (loadListener != null) {
loadListener.onImageLoaded(position);
}
And in the listener:
public void onImageLoaded(int position) {
if (position == mViewPager.getCurrentItem()) {
// Save record ..
}
}
Since I only need to count the images displayed at the "Current Item of the ViewPager" this method is not enought because if the user moves to the item 5 for example, the ViewPager loads in cache the item 6. If the user waits enought time for this load (beeing the item 5 the current) the callback to onImageLoaded is lost (because the item 6 is not the currentItem at the moment, and then when the user moves to the item 6 I've no way of knowing if the image is loaded or not.
I was thinking in adding something like [[PSEUDOCODE]]:
mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
if (mViewPager.isLoaded(posistion)){
// Save record ...
}
}
But I dont know how to retrieve if the corresponding view loaded the image or not at this moment. How can I do it?

Android: HorizontalScrollView inside ScrollView

I have multiple HorizontalScrollViews inside a ScrollView. Horizontal scroll isn't smooth at all. I have to scroll almost perfectly horizontally for scrolling to work. Is there a simple fix to tweak this ??? Thanks!
You can use Recycler view with Staggered layout manager
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.HORIZONTAL);
RecyclerViewAdapter recyclerViewAdapter = newRecyclerViewAdapter(this);
recyclerView.setAdapter(recyclerViewAdapter); //Don't miss to initialize your adapter
This class creates a ScrollView containing a HorizontalScrollView combined into one class. You can put stuff inside it using the AddChild() method. The dispatchTouchEvent overide keeps the scrolling smooth so you can pan around with a single slide of the finger.
(I recently used this to wrap a programmatically created TextView)
class MultiScrollView extends ScrollView
{
public HorizontalScrollView hscroll;
public MultiScrollView ( Context context )
{
super( context );
}
public void AddChild( View child )
{
hscroll.addView( child );
}
#Override
public boolean dispatchTouchEvent( MotionEvent event )
{
hscroll.dispatchTouchEvent(event);
onTouchEvent(event);
return true;
}
}
If you are using the horizontal scroll view solution from (http://www.dev-smart.com/archives/34) the solution for the cross focus problem between the scroll view and the list view is blocking the focus to the scroll view once you have focus on the list view.
From a technical point of view you should add the following line to the onScroll function inside the HorizontalListView class.
getParent().requestDisallowInterceptTouchEvent(true);
Hope this helps.
I've found the solution and still can't believe that this is what you have to do to make this work normal! Just added blank onClickListener to the each item in the HorizontalScrollView:
item.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
After this slide is really smooth, both upwards and downwards.
In general, you shouldn't be using nested ScrollViews in Android at all, the behaviour of scrolling in this way is unnatural too.
You may want to rethink your layout design, is it anything that couldn't be achieved with an expandable list?
While David's answer works, it has a downside. It passes ScrollView's MotionEvent object directly to HorizontalScrollView.onTouchEvent(), so if HorizontalScrollView or its children try to get the event coordinates, they will get the wrong coordinates which based on ScrollView.
My solution:
public class CustomScrollView extends ScrollView{
/*************skip initialization*************/
#Override
public boolean onInterceptTouchEvent(MotionEvent e){
//returning false means ScrollView is not interested at any events,
//so ScrollView's onTouchEvent() won't be called,
//and all of the events will be passed to ScrollView's child
return false;
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//manually call ScrollView's onTouchEvent(),
//the vertical scrolling happens there.
onTouchEvent(ev);
//dispatch the event,
//ScrollView's child will have every event.
return super.dispatchTouchEvent(ev);
}
}
Just wrap this CustomScrollView around the HorizontalScrollView in your layout file.

Categories