Question

I have an Activity containing a ViewPager that displays N fragments. Each fragment is showing the properties of an object from an ArrayList in my ViewPager's custom adapter (extends FragmentStatePagerAdapter).

The fragment has (among other things) a button that should remove the currently displayed fragment and scroll to the next one with setCurrentItem(position, true) so that if the user scrolls back, the previous item is gone. I do so by using something like this (simplified):

deleteButton.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View v) {

              MyActivity parentActivity = (MyActivity)getActivity();

              // First, scroll to next item (smoothly)
              parentActivity.pager.setCurrentItem(parentActivity.pager.getCurrentItem()+1, true); 

              // Database stuff...
              doSomeDBOperations();

              // Method in Activity that removes the current object (I believe this method is working fine and yes, it calls notifyDataSetChanged())
              parent.removeObject(currentObject);

    }
});

This has the desired behavior as the object represented by the fragment whose delete button was pressed gets removed and the viewpager goes to the next page.

My problem is that the ViewPager doesn't scroll smoothly but rather "jumps instantly" to the next fragment. If I comment the removeObject() call, the smooth scroll works (but the item isn't removed). I believe it's has something to do with the removeObject() being called before the setCurrentItem() has finished the smooth scrolling animation?

Any ideas on how to fix this and achieve item removal + smooth scroll? If my assumption is correct, how can I make sure I get the smooth scroll to finish before removing the object?

EDIT 1:

My assumption seems correct. If I put the parent.removeObject(currentObject) inside

// ...inside the previously shown public void onClick(View v)...
confirm.postDelayed(new Runnable() {

    @Override
    public void run() {
              // Method in Activity that removes the current object (I believe this method is working fine and yes, it calls notifyDataSetChanged())
              parent.removeObject(currentObject);
    }
}, 1000);

so that the removeObject() call waits for a second, it works as expected: scroll to the next item, remove the previous. But this is a very ugly workaround so I'd still like a better approach.

EDIT 2:

I figured out a possible solution (see below).

Was it helpful?

Solution

I ended up overriding the

public void onPageScrollStateChanged(int state) 

method:

Whenever the user presses the delete button in the fragment, the listener sets a bool in the current item (flagging it for deletion) and scrolls to the next one.

When the onPageScrollStateChanged detects that the scroll state changed to ViewPager.SCROLL_STATE_IDLE (which happens when the smooth scroll ends) it checks if the previous item was marked for deletion and, if so, removes it from the ArrayList and calls notifyDataSetChanged().

By doing so, I've managed to get the ViewPager to smoothly scroll to the next position and delete the previous item when the "delete" button is pressed.

EDIT: Code snippet.

@Override
public void onPageScrollStateChanged(int state)
{   

    switch(state)
    {
    case ViewPager.SCROLL_STATE_DRAGGING:

        break;

    case ViewPager.SCROLL_STATE_IDLE:

        int previousPosition = currentPosition - 1;
        if(previousPosition < 0){
            previousPosition = 0;
        }
        MyItem previousItem = itemList.get(previousPosition);
        if(previousItem.isDeleted())
        {
            deleteItem(previousItem); 
            // deleteItem() Does some DB operations, then calls itemList.remove(position) and notifyDataSetChanged()
        }

        break;

    case ViewPager.SCROLL_STATE_SETTLING:
        break;
    }

}

OTHER TIPS

Have you tried ViewPager.OnPageChangeListener? I would call removeObject(n) method in OnPageChangeListener.onPageSelected(n+1) method.

I did something different that works smoothly. The idea is to to remove the current item with animation (setting its alpha to 0), then translating horizontally the left or right item (with animation) to the now invisible item position. After the animation is complete, I do the actual data removal and notfyDataSetChanged() call.

This remove() method I put inside a subclass of ViewPager

public void remove(int position, OnViewRemovedListener onViewRemovedListener) {

    final int childCount = getChildCount();
    if (childCount > 0) {
        View toRemove = getChildAt(position);
        int to = toRemove.getLeft();

        final PagerAdapter adapter = getAdapter();
        toRemove.animate()
                .alpha(0)
                .setDuration(getResources().getInteger(android.R.integer.config_shortAnimTime))
                .setListener(new SimpleAnimatorListener() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        if (childCount == 1) {
                            if (onViewRemovedListener != null) onViewRemovedListener.onRemoved(position, -1);
                            if (adapter!= null) adapter.notifyDataSetChanged();
                        }
                    }
                })
                .start();

        if (childCount > 1) {
            int newPosition = position + 1 <= childCount - 1 ? position + 1 : position - 1;
            View replacement = getChildAt(newPosition);
            int from = replacement.getLeft();
            replacement.animate()
                    .setInterpolator(new DecelerateInterpolator())
                    .setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime))
                    .translationX(to - from)
                    .setListener(new SimpleAnimatorListener() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            if (onViewRemovedListener != null) onViewRemovedListener.onRemoved(position, newPosition);
                            if (adapter!= null) adapter.notifyDataSetChanged();
                        }
                    })
                    .start();
        }
    }
}

public interface OnViewRemovedListener {

    void onRemoved(int position, int newPosition);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top