Android: ViewPager and Fregment

Main categories:

  1. ViewPager's Brief Explanation
  2. Optimal Implementation of ViewPager
  3. Introduction to Fregment
  4. Optimal Implementation of Fregment
  5. ViewPager in conjunction with Fregment

Introduction and Function of ViewPager

  1. ViewPager is a class in the v4 package of the android extension package, which allows users to switch the current view left and right.
  2. The ViewPager class directly inherits the ViewGroup class, all of which are container classes in which other view classes can be added.
  3. The ViewPager class needs a PagerAdapter adapter class to provide it with data.
  4. ViewPager is often used with Fragments, and provides specialized FragmentPagerAdapter and FragmentStatePagerAdapter classes for ViewPager in Fragments.

ViewPager adapter

Pager Adapter is mentioned in the introduction. Like ListView and other controls, ViewPager needs to set Pager Adapter to complete the binding of pages and data. This Pager Adapter is a base class adapter. We often use it to implement the app boot graph. Its subclasses are FragmentPager Adapter and FragmentStatePager Adapter, the two subclasses of which are FragmentPager Adapter and FragmentStatePager Adapter. Class adapters are used with Fragments, which appear as frequently as listview in Android applications.

ViewPager Best Inheritance Practices:

  1. Cache all fragments and use a List to cache all fragments corresponding to the data source
  2. Update the data source and refresh the Fragment. When the data source is updated, extract the corresponding Fragment from the List and refresh the Adapter.
  3. When deleting data, delete the corresponding Fragment in the List. When deleting an item from the data source, delete the corresponding Fragment in the List, and refresh the Adapter.

CommonViewPagerAdapter class code:

package com.kf.fly.fangweixing.view.fragment.appstore;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

import java.util.ArrayList;
import java.util.List;

/**
 * <pre>
 *     sinlov
 *
 *     /\__/\
 *    /`    '\
 *  ≈≈≈ 0  0 ≈≈≈ Hello world!
 *    \  --  /
 *   /        \
 *  /          \
 * |            |
 *  \  ||  ||  /
 *   \_oo__oo_/≡≡≡≡≡≡≡≡o
 *
 * </pre>
 * Explain the idea of implementation a little:
 * 1,Cache all Fragment s
 * Use a List to cache all fragments corresponding to the data source
 * 2,Update data source, refresh Fragment
 * When the data source is updated, take the corresponding Fragment from the List and refresh the Adapter
 * 3,When deleting data, delete the corresponding Fragment in the List
 * When an item is deleted from the data source, the corresponding Fragment in the List is deleted, and then the Adapter is refreshed.
 */
public class CommonViewPagerAdapter extends FragmentPagerAdapter {



    private final List<Fragment> mFragments = new ArrayList<>();
    private final List<String> mFragmentTitles = new ArrayList<>();

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

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

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

    public void addFragment(Fragment fragment, String title) {
        mFragments.add(fragment);
        mFragmentTitles.add(title);
    }

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

Overview of Fragment

  1. Fragment is an action or part of the user interface in Activity. It mainly supports dynamic display on large screen and more flexible combination or exchange of UI components. By dividing the layout of activity into several fragments, we can edit the presentation of activity at run time, and those changes will be saved in the background stack managed by activity.
  2. Fragments must always be embedded in an activity, and the life cycle of fragments is directly affected by the life cycle of their host activity. You can think of fragment as a module part of activity, which has its own life cycle, receives its own input events, and can be added or deleted at the activity runtime.
  3. Each fragment should be designed as a modular and reusable activity component. That is, you can refer to the same fragment in multiple activities, because fragment defines its own layout and uses its own lifecycle callback behavior.

Fragment Best Practices

  1. Fragment Lazy Loading and ViewPager Cache Processing
  2. FragmentState didn't save Hidden status for us, so let's save it ourselves.
  3. Extract Fragment Common Code, and give the abstract method of points to be used for inheritance implementation.

BaseCompatFragment class code:

package com.kf.fly.fangweixing.android.view.frament;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.kf.fly.fangweixing.android.view.BaseCompatActivity;

/**
 * Created by "sinlov" on 2017/4/11.
 */
public abstract class BaseCompatFragment extends Fragment {

    private static final String STATE_SAVE_IS_HIDDEN = "Fragment:STATE:SAVE_IS_HIDDEN";

    protected String TAG;

    protected BaseCompatActivity mActivity;
    private FragmentManager mFramentManager;

    /**
     * view of this fragment
     */
    protected View contentView;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        TAG = "FLY."+this.getClass().getCanonicalName();
        mActivity = (BaseCompatActivity) getActivity();
        mFramentManager = getFragmentManager();
    }

    /**
     *  FragmentState If we don't save Hidden status for us, we will save it ourselves. After the page restarts, we will decide whether Fragment will be displayed or not.
     *  The solution has changed from the Hidden state of the child Fragment managed by Activity / Father Fragment to the Hidden state of the child Fragment managed by Fragment itself!
     *  Advantages: No matter how deep nested Fragments, peer Fragments and other scenarios, they can all work properly without overlap!
     *  Disadvantage: None.
     *  @param savedInstanceState
     */

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (null != savedInstanceState){
            boolean isSupportHidden = savedInstanceState.getBoolean(STATE_SAVE_IS_HIDDEN);

            FragmentTransaction ft = getFragmentManager().beginTransaction();
            if (isSupportHidden) {
                ft.hide(this);
            } else {
                ft.show(this);
            }
            ft.commit();
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(STATE_SAVE_IS_HIDDEN, isHidden());
    }

    /**
     * Extract Fragment Common Code, and give the abstract method of points to be used for inheritance implementation.
     * create view by method {@link BaseCompatActivity#setContentView(int)}
     * <br>this method can avid multiple loading xml files
     *
     * @param inflater           {@link LayoutInflater}
     * @param container          {@link ViewGroup}
     * @param savedInstanceState {@link Bundle}
     * @return {@link View}
     */
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (null == contentView) {
            initView(savedInstanceState);
            setListeners();
            processLogic(savedInstanceState);
        } else {
            ViewGroup parent = (ViewGroup) contentView.getParent();
            if (null != parent) {
                parent.removeView(contentView);
            }
        }
        return contentView;
    }

    protected void setContentView(@LayoutRes int layoutResID) {
        contentView = LayoutInflater.from(mActivity).inflate(layoutResID, null);
    }

    protected void setContentViewChildMode(@LayoutRes int layoutResID) {
        contentView = LayoutInflater.from(mActivity).inflate(layoutResID, null);
    }

    /**
     * init view
     *
     * @param savedInstanceState {@link Bundle}
     */
    protected abstract void initView(Bundle savedInstanceState);

    /**
     * set view listeners
     */
    protected abstract void setListeners();

    /**
     * process logic and resumes states etc.
     *
     * @param savedInstanceState {@link Bundle}
     */
    protected abstract void processLogic(Bundle savedInstanceState);

    /**
     * find view by id which in content view
     *
     * @param id   view id
     * @param <CV> extends {@link View}
     * @return extends view
     */
    @SuppressWarnings("unchecked")
    protected <CV extends View> CV getViewById(@IdRes int id) {
        return (CV) contentView.findViewById(id);
    }

    protected void showToast(String text){
        Toast.makeText(mActivity, text, Toast.LENGTH_SHORT).show();
    }

    protected void showToast(int id){
        Toast.makeText(mActivity,id,Toast.LENGTH_SHORT).show();
    }


}

ViewPagerFragment class code:

package com.kf.fly.fangweixing.view.fragment.appstore;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;

import com.kf.fly.fangweixing.android.view.frament.BaseCompatFragment;

/**
 *
 * Viewpager + Fragment In the case of fragments, the lifetime of fragments loses its concrete meaning due to the caching mechanism of Viewpager
 * This abstract class customizes a new callback method that will trigger when the fragment visible state changes. Here's an introduction
 *
 * @see #onFragmentVisibleChange(boolean)
 */
public abstract class ViewPagerFragment extends BaseCompatFragment {

    /**
     * rootView Whether to initialize flags to prevent callback functions from triggering when rootView is empty
     */
    private boolean hasCreateView;

    /**
     * Whether Fragment is currently in a visible state flag to prevent callback function triggering due to ViewPager's caching mechanism
     */
    private boolean isFragmentVisible;

    /**
     * onCreateView()The view returned in the onCreateView is modified to protected, so when the subclass inherits the class, the variable must be initialized in onCreateView
     */
    protected View rootView;

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        Log.d(TAG, "setUserVisibleHint() -> isVisibleToUser: " + isVisibleToUser);
        if (rootView == null) {
            return;
        }
        hasCreateView = true;
        if (isVisibleToUser) {
            onFragmentVisibleChange(true);
            isFragmentVisible = true;
            return;
        }
        if (isFragmentVisible) {
            onFragmentVisibleChange(false);
            isFragmentVisible = false;
        }
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initVariable();
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (!hasCreateView && getUserVisibleHint()) {
            onFragmentVisibleChange(true);
            isFragmentVisible = true;
        }
    }

    private void initVariable() {
        hasCreateView = false;
        isFragmentVisible = false;
    }

    /**************************************************************
     *  Customized callback methods, subclasses can be rewritten as required
     *************************************************************/

    /**
     * This method is called back when the visible state of the current fragment changes
     * If the current fragment is loaded for the first time, it waits for onCreateView to call back the method. In other cases, the callback time is the same as {@link# setUserVisibleHint (boolean)}.
     * In this callback method, you can do some loading data operations, even control operations, because with fragment ation's view reuse mechanism, you don't have to worry about null exceptions in control operations.
     *
     * @param isVisible true  Invisible - > visible
     *                  false Visible - > invisible
     */
    protected void onFragmentVisibleChange(boolean isVisible) {
        Log.w(TAG, "onFragmentVisibleChange -> isVisible: " + isVisible);
        if (isVisible){
            onUserVisible();
        }
    }

    /**
     * When fragment visible to lazy loading network data
     */
    protected abstract void onUserVisible();
}

MainActivity code:

package com.kf.fly.fangweixing;


import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.widget.RadioButton;
import android.widget.RadioGroup;

import com.kf.fly.fangweixing.android.view.BaseCompatActivity;
import com.kf.fly.fangweixing.view.fragment.appstore.CommonViewPagerAdapter;
import com.kf.fly.fangweixing.view.fragment.weixin.WeiXingFragment;

import butterknife.BindView;
import butterknife.ButterKnife;

public class MainActivity extends BaseCompatActivity {

    @BindView(R.id.app_mian_viewpager)
    ViewPager mViewPager;

    @BindView(R.id.main_radiogroup)
    RadioGroup mRadioGroup;

    @BindView(R.id.main_radiobutton_weixing)
    RadioButton radiobuttonWeiXing;

    @BindView(R.id.main_radiobutton_contacts)
    RadioButton radiobuttonContacts;

    @BindView(R.id.main_radiobutton_find)
    RadioButton radiobuttonFind;

    @BindView(R.id.main_radiobutton_my)
    RadioButton radiobuttonMy;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Initialize BindView
        ButterKnife.bind(this);
        initFragment();
        initToolbar();
        monitoringRadioGrop();
    }

    private void initToolbar() {
        Toolbar mToolbar = (Toolbar) findViewById(R.id.tool_bar_main);
        //Add Overflow Menu
        mToolbar.inflateMenu(R.menu.setting_menu);
        // Add Menu Click Events
        mToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.item_search:
                        showToast(R.string.main_menu_search);
                        break;
                    case R.id.item_chat:
                        //Click on the Settings menu
                        showToast(R.string.main_menu_chat);
                        break;
                    case R.id.item_add_person:
                        //Click on the Settings menu
                        showToast(R.string.main_menu_add_person);
                        break;
                    case R.id.item_richscan:
                        //Click on the Settings menu
                        showToast(R.string.main_menu_richscan);
                        break;
                    case R.id.item_money:
                        //Click on the Settings menu
                        showToast(R.string.main_menu_money);
                        break;
                    case R.id.item_help:
                        //Click on the Settings menu
                        showToast(R.string.main_menu_help);
                        break;
                }
                return false;
            }
        });

    }

    private void initFragment() {

        CommonViewPagerAdapter adapter = new CommonViewPagerAdapter(getSupportFragmentManager());
        adapter.addFragment(new WeiXingFragment(), getString(R.string.main_radiobutton_weixing));
        adapter.addFragment(new WeiXingFragment(), getString(R.string.main_radiobutton_weixing));
        adapter.addFragment(new WeiXingFragment(), getString(R.string.main_radiobutton_weixing));
        adapter.addFragment(new WeiXingFragment(), getString(R.string.main_radiobutton_weixing));
        mViewPager.setAdapter(adapter);
        mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

            /**Method of Callback when Page Scrolls
             * position:The current page slides down the corner
             * positionOffset:The offset of the current page slide
             * positionOffsetPixels:Pixel values corresponding to the offset of the current page slide
             * */

            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            /**
             * Callback method when page is selected
             * position  : Represents the location of the page
             **/
            @Override
            public void onPageSelected(int position) {
                switch (position) {
                    case 0:
                        radiobuttonWeiXing.setChecked(true);
                        break;
                    case 1:
                        radiobuttonContacts.setChecked(true);
                        break;
                    case 2:
                        radiobuttonFind.setChecked(true);
                        break;
                    case 3:
                        radiobuttonMy.setChecked(true);
                        break;
                }
            }

            /**Callback method when page scroll status changes
             * The parameters consist of three states:
             *  SCROLL_STATE_IDLE  : Stop state
             *  SCROLL_STATE_DRAGGING: Rolling state
             *  SCROLL_STATE_SETTLING: Selected status
             * */

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

        mViewPager.setCurrentItem(0);
    }




    private void monitoringRadioGrop() {
        mRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                switch (checkedId) {
                    case R.id.main_radiobutton_weixing:
                        showToast(R.string.main_radiobutton_weixing);
                        mViewPager.setCurrentItem(0);
                        break;
                    case R.id.main_radiobutton_contacts:
                        showToast(R.string.main_radiobutton_contects);
                        mViewPager.setCurrentItem(1);
                        break;
                    case R.id.main_radiobutton_find:
                        showToast(R.string.main_radiobutton_find);
                        mViewPager.setCurrentItem(2);
                        break;
                    case R.id.main_radiobutton_my:
                        showToast(R.string.main_radiobutton_my);
                        mViewPager.setCurrentItem(3);
                        break;
                    default:
                        Log.d(TAG, "How to monitor????");
                        break;
                }
            }
        });
    }


    @Override
    protected void processLogic(Bundle savedInstanceState) {

    }
}

Design sketch:

Note: MainActivity has project code that can be used with Last article Together, I hope not. O() O Thank you.

Hope to help you O() O Thank you!!!!!!!!!

Keywords: Fragment Android ButterKnife Java

Added by salhzmzm on Wed, 10 Jul 2019 22:42:20 +0300