ActionBar, Tabs, Fragment and ViewPager of Android implement tab switching and page caching

Keywords: Fragment Android Java

It feels like Android is full of pits, and every place has to toss people around for half a day.

Today let's talk about Android's ActionBar, Tabs, Fragment, ViewPager to switch tabs and cache pages.

There's no more about their introduction. There's a lot of information on the Internet, just the key parts.

When I was developing, I encountered several difficult problems and spent a lot of time dealing with them, which can be summarized as follows:

1. Which event callback section should I write about Fragment's internal logic processing?

2. ViewPager page switch animation carton, let me have a headache for a long time.

3. How to save the status of Fragment's current view in ViewPager so that Tabs pages will not be reloaded after switching? This place is very frustrating.

4. How tab s in ActionBar scroll through many times


Answer:

1. Fragment's event callback:

package com.ai9475.meitian.ui.fragment;

import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;

import com.ai9475.meitian.R;
import com.ai9475.meitian.view.DiaryList;
import com.ai9475.util.ZLog;

/**
 *
 * Created by ZHOUZ on 14-1-21.
 */
public class DiaryListFragment extends BaseFragment
{
    private static final String TAG = "DiaryListFragment";

    @Override
    public void onAttach(Activity activity)
    {
        ZLog.i(TAG, "onAttach");
        super.onAttach(activity);
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        ZLog.i(TAG, "onCreate");
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        ZLog.i(TAG, "onCreateView");
        return inflater.inflate(R.layout.fragment_diary_list, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState)
    {
        ZLog.i(TAG, "onActivityCreated");
        super.onActivityCreated(savedInstanceState);
        ZLog.i(TAG, "DiaryList0");
        DiaryList diaryList = new DiaryList(
                getActivity().getApplicationContext(),
                (ListView) getView().findViewById(R.id.diaryListCt)

        );
        ZLog.i(TAG, "DiaryList load0");
        diaryList.load("http://m.ai9475.com/?con=meitian_app");
        this.setRetainInstance(true);
    }

    @Override
    public void onStart()
    {
        ZLog.i(TAG, "onStart");
        super.onStart();
    }

    @Override
    public void onResume()
    {
        ZLog.i(TAG, "onResume");
        super.onResume();
    }

    @Override
    public void onPause()
    {
        ZLog.i(TAG, "onPause");
        super.onPause();
    }

    @Override
    public void onStop()
    {
        ZLog.i(TAG, "onStop");
        super.onStop();
    }

    @Override
    public void onDestroyView()
    {
        ZLog.i(TAG, "onDestroyView");
        super.onDestroyView();
    }

    @Override
    public void onDestroy()
    {
        ZLog.i(TAG, "onDestroy");
        super.onDestroy();
    }

    @Override
    public void onDetach()
    {
        ZLog.i(TAG, "onDetach");
        super.onDetach();
    }
}

The on event in the above class is the time callback that Fragment mainly handles. Note that when you override the parent method, you need to call to execute the parent method with the same name, otherwise you will make a mistake.

Mainly override the onCreateView method to return the view object corresponding to the Fragment. Here, some simple configuration can be done before returning the view object, but do not write too time-consuming processing to block the UI main thread.

In addition, the onActivityCreated method is a callback at the end of the onCreate event of the activity, when the view corresponding to the current Fragment has been incorporated into the entire layout, at which time the view object can be obtained using the getView() method.

There are not many other events to say, some of which I am not very clear, and some of which are animated calls.


2. Cutting Page

This problem may arise in two main ways.

1. The ViewPager cache is not used and is reloaded every time you switch.

2. Loading Fragment has time-consuming and resource-consuming logic processing.

Here's the second case. When I didn't deal with the cache problem at first, I had a solution.

<android.support.v4.view.ViewPager
        android:id="@+id/tabsViewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
    </android.support.v4.view.ViewPager>

mViewPager = (ViewPager) findViewById(R.id.tabsViewPager);
mViewPager.setOnPageChangeListener(
                new ViewPager.SimpleOnPageChangeListener() {
                    private static final String TAG = "ViewPager.SimpleOnPageChangeListener";
                    private ArrayList hasLoadedPages = new ArrayList<Integer>();
                    @Override
                    public void onPageSelected(int position) {
                        ZLog.i(TAG, "onPageSelected position:"+ position);
                        getSupportActionBar().setSelectedNavigationItem(position);
                    }

                    @Override
                    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
                    {
                        ZLog.i(TAG, "onPageScrolled position: "+ position +", positionOffset:"+ positionOffset +", positionOffsetPixels:"+ positionOffsetPixels);
                    }

                    @Override
                    public void onPageScrollStateChanged(int state) {
                        ZLog.i(TAG, "onPageScrollStateChanged");
                        int position = mViewPager.getCurrentItem();

                        switch (state) {
                            // Dragging
                            case ViewPager.SCROLL_STATE_DRAGGING :
                                ZLog.i(TAG, "ViewPager.SCROLL_STATE_DRAGGING position:"+ position);
                                break;
                            // The process of settling after dragging release.
                            case ViewPager.SCROLL_STATE_SETTLING :
                                ZLog.i(TAG, "ViewPager.SCROLL_STATE_SETTLING position:"+ position);
                                break;
                            // Switching Animation Completed
                            case ViewPager.SCROLL_STATE_IDLE :
                                ZLog.i(TAG, "ViewPager.SCROLL_STATE_IDLE position:"+ position);
				// Loaded but not reloaded
                                if (hasLoadedPages.contains(position)) break;
                                Fragment fragment = mPager.getFragments().get(position);
                                runCallback(position, fragment);
                                hasLoadedPages.add(position);
                                break;
                        }
                    }

                    public void runCallback(int position, Fragment fragment) {
                        ZLog.i(TAG, "runCallback");
                        DiaryList diaryList;
                        switch (position) {
                            case 0 :
                                ZLog.i(TAG, "DiaryList0");
                                diaryList = new DiaryList(
                                        getApplicationContext(),
                                        (ListView) fragment.getView().findViewById(R.id.diaryListCt)

                                );
                                ZLog.i(TAG, "DiaryList load0");
                                diaryList.load("http://m.ai9475.com/?con=meitian_app");
                                break;
                            case 1:
                                ZLog.i(TAG, "DiaryList1");
                                diaryList = new DiaryList(
                                        getApplicationContext(),
                                        (ListView) fragment.getView().findViewById(R.id.diaryListCt)

                                );
                                ZLog.i(TAG, "DiaryList load1");
                                diaryList.load("http://m.ai9475.com/?con=meitian_app&act=hot");
                                break;
                            case 2:
                                ZLog.i(TAG, "DiaryList2");
                                diaryList = new DiaryList(
                                        getApplicationContext(),
                                        (ListView) fragment.getView().findViewById(R.id.diaryListCt)

                                );
                                ZLog.i(TAG, "DiaryList load2");
                                diaryList.load("http://m.ai9475.com/?con=meitian_app");
                                break;
                        }
                    }
                }

This is mainly used for event monitoring of scroll switching state changes on public void onPageScrollStateChanged(int state) pages

When the scroll animation completes case ViewPager.SCROLL_STATE_IDLE, the logical processing of Fragment is performed, so that the animation will flow smoothly.

But after testing later, we found that there is a very simple solution, that is, the caching function of ViewPager, which is very simple.


3. Caching Tabs page switching without reloading data

I've been struggling here for the longest time, and sometimes I can't get started. Many articles on the Internet have talked about how to save Fragment's data and status, but I haven't mentioned how to save his current view status entirely. I don't know how to save it. Of course, I still don't know how to cache Fragment's status, but it corresponds to the Tab cache of ViewPager. Fragment still found a way to do it. He spent a lot of time doing it himself, but then he happened to find out that he had a way to do it.

// Set how many Tab s to cache for fragment s
        mViewPager.setOffscreenPageLimit(6);

I used 6 listView s to load the image list data in my test, and there was no cartoon phenomenon in the switching animation. It was very smooth. It was done in such a simple sentence.

After configuring this item, ViewPager will not clean up invisible Fragments when switching, trigger any events of Fragments, and therefore will not cause them to reload.


Tabs in ActionBar

After the number of tabs exceeds one screen, for example, if I set six widths above the screen now, it will become a state of horizontal scrolling, without self-realization. I didn't know that I had checked a lot of data in this area, but I only knew that it could be achieved with tabhost, but I didn't use it in ActionBar. I tried several tabs below to find out. Originally, it will be automatic, silent.




Stick the complete code of MainActivity.class:

package com.ai9475.meitian.ui;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.view.Menu;
import android.widget.ListView;

import com.ai9475.meitian.AppManager;
import com.ai9475.meitian.R;
import com.ai9475.meitian.ui.fragment.DiaryListFragment;
import com.ai9475.meitian.ui.fragment.Test2Fragment;
import com.ai9475.meitian.ui.fragment.Test3Fragment;
import com.ai9475.meitian.view.DiaryList;
import com.ai9475.util.ZLog;

import java.util.ArrayList;

public class MainActivity extends BaseActivity
{
    private static final String TAG = "MainActivity";
    private MyTabsPagerAdapter mPager;
    private ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        ZLog.i(TAG, "start");
        // Execute parent initialization method
        super.onCreate(savedInstanceState);
        //requestWindowFeature(Window.FEATURE_NO_TITLE);
        ZLog.i(TAG, "setContentView");
        setContentView(R.layout.activity_main);

        // Sliding Page View Configuration
        ZLog.i(TAG, "MyTabsPagerAdapter start");
        mPager = new MyTabsPagerAdapter(getSupportFragmentManager());
        mPager.getFragments().add(new DiaryListFragment());
        mPager.getFragments().add(new Test2Fragment());
        mPager.getFragments().add(new Test3Fragment());
        mPager.getFragments().add(new Test3Fragment());
        mPager.getFragments().add(new Test3Fragment());
        mPager.getFragments().add(new Test3Fragment());
        // Sliding Paging Container
        mViewPager = (ViewPager) findViewById(R.id.tabsViewPager);
        // How many fragment s are cached
        mViewPager.setOffscreenPageLimit(6);
        mViewPager.setAdapter(mPager);
        // Page sliding events
        mViewPager.setOnPageChangeListener(
                new ViewPager.SimpleOnPageChangeListener() {
                    private static final String TAG = "ViewPager.SimpleOnPageChangeListener";
                    private ArrayList hasLoadedPages = new ArrayList<Integer>();
                    @Override
                    public void onPageSelected(int position) {
                        ZLog.i(TAG, "onPageSelected position:"+ position);
                        getSupportActionBar().setSelectedNavigationItem(position);
                    }

                    @Override
                    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
                    {
                        ZLog.i(TAG, "onPageScrolled position: "+ position +", positionOffset:"+ positionOffset +", positionOffsetPixels:"+ positionOffsetPixels);
                    }

                    @Override
                    public void onPageScrollStateChanged(int state) {
                        ZLog.i(TAG, "onPageScrollStateChanged");
                        int position = mViewPager.getCurrentItem();

                        switch (state) {
                            // Dragging
                            case ViewPager.SCROLL_STATE_DRAGGING :
                                ZLog.i(TAG, "ViewPager.SCROLL_STATE_DRAGGING position:"+ position);
                                break;
                            // The process of settling after dragging release.
                            case ViewPager.SCROLL_STATE_SETTLING :
                                ZLog.i(TAG, "ViewPager.SCROLL_STATE_SETTLING position:"+ position);
                                break;
                            // Switching Animation Completed
                            case ViewPager.SCROLL_STATE_IDLE :
                                ZLog.i(TAG, "ViewPager.SCROLL_STATE_IDLE position:"+ position);
                                /*if (hasLoadedPages.contains(position)) break;
                                Fragment fragment = mPager.getFragments().get(position);
                                runCallback(position, fragment);
                                hasLoadedPages.add(position);*/
                                break;
                        }
                    }

                    public void runCallback(int position, Fragment fragment) {
                        ZLog.i(TAG, "runCallback");
                        DiaryList diaryList;
                        switch (position) {
                            case 0 :
                                ZLog.i(TAG, "DiaryList0");
                                diaryList = new DiaryList(
                                        getApplicationContext(),
                                        (ListView) fragment.getView().findViewById(R.id.diaryListCt)

                                );
                                ZLog.i(TAG, "DiaryList load0");
                                diaryList.load("http://m.ai9475.com/?con=meitian_app");
                                break;
                            case 1:
                                ZLog.i(TAG, "DiaryList1");
                                diaryList = new DiaryList(
                                        getApplicationContext(),
                                        (ListView) fragment.getView().findViewById(R.id.diaryListCt)

                                );
                                ZLog.i(TAG, "DiaryList load1");
                                diaryList.load("http://m.ai9475.com/?con=meitian_app&act=hot");
                                break;
                            case 2:
                                ZLog.i(TAG, "DiaryList2");
                                diaryList = new DiaryList(
                                        getApplicationContext(),
                                        (ListView) fragment.getView().findViewById(R.id.diaryListCt)

                                );
                                ZLog.i(TAG, "DiaryList load2");
                                diaryList.load("http://m.ai9475.com/?con=meitian_app");
                                break;
                        }
                    }
                }
        );

        ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        // Tab page switching
        MyTabListener listener = new MyTabListener();
        // Default home tab
        ActionBar.Tab indexTab = actionBar.newTab()
                .setText(getString(R.string.tab_index))
                .setTabListener(listener);
        actionBar.addTab(indexTab);
        actionBar.addTab(actionBar.newTab()
                .setText(getString(R.string.tab_hot))
                .setTabListener(listener)
        );
        actionBar.addTab(actionBar.newTab()
                .setText(getString(R.string.tab_tag))
                .setTabListener(listener)
        );
        actionBar.addTab(actionBar.newTab()
                .setText(getString(R.string.tab_tag))
                .setTabListener(listener)
        );
        actionBar.addTab(actionBar.newTab()
                .setText(getString(R.string.tab_tag))
                .setTabListener(listener)
        );
        actionBar.addTab(actionBar.newTab()
                .setText(getString(R.string.tab_tag))
                .setTabListener(listener)
        );

        // Display Home Page
        indexTab.select();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    private class MyTabListener implements ActionBar.TabListener
    {
        @Override
        public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft)
        {
            int position = tab.getPosition();
            ZLog.i(TAG, "tab selected: "+ position);
            // data communication
            /*Bundle bundle = new Bundle();
            Fragment fragment = mPager.getItem(tab.getPosition());
            Toast.makeText(getApplicationContext(), "position:"+ tab.getPosition(), Toast.LENGTH_SHORT).show();
            fragment.setArguments(bundle);
            FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            fragmentTransaction.add(R.id.fragmentContainer, fragment);
            fragmentTransaction.commit();*/
            mViewPager.setCurrentItem(position);
        }

        @Override
        public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
            ZLog.i(TAG, "tab reselected: "+ tab.getPosition());
        }

        @Override
        public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)
        {
            ZLog.i(TAG, "tab unselected: "+ tab.getPosition());
        }
    };

    public class MyTabsPagerAdapter extends FragmentPagerAdapter
    {
        private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();

        public MyTabsPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        public ArrayList<Fragment> getFragments() {
            return this.mFragments;
        }

        @Override
        public Fragment getItem(int i) {
            return this.mFragments.get(i);
        }

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




Reproduced in: https://my.oschina.net/zhouz/blog/213092

Posted by brissy_matty on Wed, 12 Jun 2019 15:05:49 -0700