0

The problem I have occurs when I try to dynamically switch (not replace using FragmentTransaction) fragment in viewpager. This viewpager is inside other viewpager but this is not a problem. It uses ViewPagerStateAdapter which also works fine.

Now when this viewpager parent fragment is opened, the swipe functionality is turned off. One method has to be executed, which results in swipe being unlocked.

Swipe between fragments works well, smooth and fast when I execute this specific method on button click.

But when this same method is executed autodynamically from asynctask result, which is executed from onCreateView(), it results in swipe messed up (very slow, hard to swipe to next/prev fragment).

This is pager adapter:

public class ViewPagerAdapter extends FragmentStatePagerAdapter {

    private final List<ABaseFragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();
    private final List<String> mFragmentTagList = new ArrayList<>();
    private WeakReference<Context> context;
    private int pagerID;
    private FragmentManager fragmentManager;
    private int previous = 0;

    public ViewPagerAdapter(FragmentManager manager, int pagerID, Context ctx) {
        super(manager);
        this.pagerID = pagerID;
        this.fragmentManager = manager;
        this.context = new WeakReference<>(ctx);
    }

    public ViewPagerAdapter(FragmentManager manager, int pagerID) {
        super(manager);
        this.pagerID = pagerID;
        this.fragmentManager = manager;
    }

    @Override
    public ABaseFragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getItemPosition(@NonNull Object object) {
        return POSITION_NONE;
    }

    @Override
    public int getCount() {
        return mFragmentList.size();
    }

    public void addFragment(ABaseFragment fragment, String title, String tag) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
        mFragmentTagList.add(tag);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mFragmentTitleList.get(position);
    }

    public String getPageTag(int position) {
        return mFragmentTagList.get(position);
    }

    public ABaseFragment getPageFragmentByPosition(int position) {
        return mFragmentList.get(position);
    }

    public ABaseFragment getPageFragmentByTag(String fragTag) {
        return mFragmentList.get(mFragmentTagList.indexOf(fragTag));
    }

    public int getPagePositionByTag(String fragmentTag) {
        return mFragmentTagList.indexOf(fragmentTag);
    }

    public ABaseFragment getPageFragmentByClass(String fragClassName) {
        if (fragClassName != null && !fragClassName.trim().isEmpty())
            for (ABaseFragment pagerFragment : mFragmentList) {
                if (pagerFragment.getClass().getName().trim().equals(fragClassName.trim()))
                    return pagerFragment;
            }
        return null;
    }

    public void onPageChanged(int position) {
        // logic when viewpager page changes here...

        previous = position;
    }
}

This is how fragment is switched dynamically:

public void switchPagerFragment(String fragmentTag) {
    pager.setCurrentItem(pagerAdapter.getPagePositionByTag(fragmentTag));
    pagerAdapter.notifyDataSetChanged();
}

And this is the first fragment in viewpager, which executes asynctask, which in result executes the method which then results in swipe unlocked

public class ParamsFragment extends ABaseFragment implements IBackPressCallback {

    /**
     * This method will only be called once when the retained
     * Fragment is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Retain this fragment across configuration changes.
        setRetainInstance(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        // Inflate the layout for this fragment
        View rootView = inflater.inflate(R.layout.fragment_params, container, false);
        relativeDummy = (RelativeLayout) rootView.findViewById(R.id.layParamsDummy);

        executeTask();

        return rootView;
    }

    private void executeTask() {
        DateRequest request = new DateRequest();
        request.setParams(params);
        task = new DateTask();
        task.setResultHandler(callback);
        task = (AbstractTask<DateRequest, DateResponse>) task
                .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, request);
    }

    public void updateSwipe(boolean working) {
        // here try to unlock the swipe
        swipeable = working;
        if (pager != null)
            pager.setSwipeable(swipeable);
    }

    // CALLBACKS
    private TaskResultHandler<DateResponse> callback = new TaskResultHandler<DateResponse>() {
        @Override
        public void handle(final DateResponse result, final Exception ex) {
            if (result != null) {
                updateSwipe(true);
                ((MainActivity) setupActivity().get()).switchPagerFragment(Consts.TAG_FRAG_TOTALS);
            } else
                Toast.makeText(setupActivity().get(), "date task error", Toast.LENGTH_LONG).show();
        }
    };
}

And this is my custom swipeless pager:

public class SwipelessPager extends ViewPager {

    private Boolean swipeable = true;

    public SwipelessPager(Context context) {
        super(context);
    }

    public SwipelessPager(Context context, AttributeSet attrs){
        super(context,attrs);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return swipeable ? super.onInterceptTouchEvent(event) : false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return swipeable ? super.onTouchEvent(event) : false;
    }

    @Override
    public boolean canScrollHorizontally(int direction) {
        return swipeable ? super.canScrollHorizontally(direction) : false;
    }

    public void setSwipeable(Boolean swipeable){
        //When swipeable = false not work the scroll and when swipeable = true work the scroll
        this.swipeable = swipeable;
    }

    public Boolean getSwipeable() {
        return swipeable;
    }
}

When I call updateSwipe() in some button click listener, everything works smooth and nice.

I thought that this might be related to user interface. So I enclosed this method like this:

public void updateSwipe(boolean working) {
    setupActivity().get().runOnUiThread(new Runnable() {
        swipeable = working;
        if (pager != null)
            pager.setSwipeable(swipeable);
    });
}

It didnt help.

10101101
  • 193
  • 1
  • 13
  • Try to use `FragmentPagerAdapter`, and override `getItemId`. – EpicPandaForce Apr 15 '19 at 02:13
  • Then my pager breaks down and is working like that even when not change pages dynamically. Sorry but I forgot to mention this, i spent several days to get this pager working in say not dynamic, (user guided) swipes and this was the only solution which worked for me (with FragmentStatePager). – 10101101 Apr 15 '19 at 02:16
  • Please check if the following scenario also works: https://stackoverflow.com/questions/49046773/singleton-object-becomes-null-after-app-is-resumed/49107399#49107399 – EpicPandaForce Apr 15 '19 at 04:12
  • Im not sure what exactly check... when I run app from android studio, then navigate to this fragment, then click home button, then terminate app, then open it again it runs again... but it no longer shows same fragment which exited... instead it shows the first fragment of app... Im testing on android 7 – 10101101 Apr 16 '19 at 01:54

1 Answers1

0

It turned out that onCreateView() of fragment was wrong place to calling executeTask().

Instead it should be called from onAttach() of fragment.

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    if(pager != null)
        pager.setSwipeable(swipeable);

    executeTask();
}

/**
 * Set the callback to null so we don't accidentally leak the
 * Activity instance.
 */
@Override
public void onDetach() {
    super.onDetach();

    if (task != null && task.getStatus() == AsyncTask.Status.RUNNING)
        task.cancel(true);
    task = null;
}
10101101
  • 193
  • 1
  • 13