vxh.viet
11/2/2016 - 5:28 AM

Android Fragment + ViewPager

Android Fragment + ViewPager

Source: StackOverflow, StackOverflow, Blog

Requirement: we will create a custom ViewPager class that allow enable/disable spiwing. This ViewPager contains 3 fragments MyCameraFragment, PhotoFragment, MissionFragment. The MyCameraFragment is the special one. We will need to enable auto orientation (sensor) only for this fragment.

Answer:

MyViewPager

public class MyViewPager extends ViewPager {

    private boolean enabled;

    public MyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.enabled = true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (this.enabled) {
            return super.onTouchEvent(event);
        }

        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (this.enabled) {
            return super.onInterceptTouchEvent(event);
        }

        return false;
    }

    public void setPagingEnabled(boolean enabled) {
        this.enabled = enabled;
    }
}

On your main activity, HomeActivity in this case:

private void initUI() {
        // set content view
        setContentView(R.layout.activity_home);

        // Instantiate a ViewPager and a PagerAdapter.
        mPager = (MyViewPager) findViewById(R.id.pager);
        mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), this);
        mPager.setAdapter(mPagerAdapter);
        mPager.setOffscreenPageLimit(3);
        mPager.setCurrentItem(MyPagerAdapter.PHOTO_FRAGMENT);
        mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                if(position == MyPagerAdapter.CAMERA_FRAGMENT){
                    DisplayUtil.setAutoOrientationEnabled(getApplicationContext(), true); //enable auto orientation
                    DisplayUtil.keepScreenOn(HomeActivity.this);
                }else{
                    DisplayUtil.setAutoOrientationEnabled(getApplicationContext(), false); //disable auto orientation
                    DisplayUtil.clearKeepScreenOn(HomeActivity.this);
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        
        //disable page swiping if the camera is in landscape mode
        if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
            mPager.setPagingEnabled(false);
        }else{
            mPager.setPagingEnabled(true);
        }
        mPager.setCurrentItem(MyPagerAdapter.CAMERA_FRAGMENT);
    }
    
    //util methods
    public static void setAutoOrientationEnabled(Context context, boolean enabled) {
        Settings.System.putInt(context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, enabled ? 1 : 0);
    }

    public static void keepScreenOn(Activity act) {
        act.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    }

    public static void clearKeepScreenOn(Activity act) {
        act.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    }

MyPagerAdapter

public class MyPagerAdapter extends FragmentStatePagerAdapter {
    public static final int CAMERA_FRAGMENT = 0;
    public static final int PHOTO_FRAGMENT = 1;
    public static final int MISSION_FRAGMENT = 2;
    private static final int TOTAL_PAGE = 3;
    private Context mContext;
    //private SparseArray<Fragment> registeredFragmentList = new SparseArray<>();

    public MyPagerAdapter(FragmentManager fm, Context ctx) {
        super(fm);
        mContext = ctx;
    }

    //see http://stackoverflow.com/a/29269509/1602807
    @Override
    public Fragment getItem(int position) {
        // Do NOT try to save references to the Fragments in getItem(),
        // because getItem() is not always called. If the Fragment
        // was already created then it will be retrieved from the FragmentManger
        // and not here (i.e. getItem() won't be called again).
        switch (position) {
            case CAMERA_FRAGMENT:
                File output = new File(DiskUtil.getVideoFolder(), DiskUtil.getCameraVideoName());

                return MyCameraFragment.newVideoInstance(Uri.fromFile(output),
                        true,
                        1, //AbstractCameraActivity.Quality.HIGH
                        0,
                        0);
            case PHOTO_FRAGMENT:
                return Fragment.instantiate(mContext, PhotoFragment.class.getName());
            case MISSION_FRAGMENT:
                return Fragment.instantiate(mContext, MissionFragment.class.getName());
            default:
                // This should never happen. Always account for each position above
                return null;
        }
    }

    @Override
    public int getCount() {
        return TOTAL_PAGE;
    }

    //enable this back if we want to access the current fragment in ViewPager
    /*@Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        registeredFragmentList.put(position, fragment);
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragmentList.remove(position);
        super.destroyItem(container, position, object);
    }

    *//**
     * This method is used to get the current fragment out of the ViewPager (see http://stackoverflow.com/a/15261142/1602807)
     * @param position
     * @return the current Fragment
     *//*
    public Fragment getRegisteredFragment(int position) {
        return registeredFragmentList.get(position);
    }*/
}

MyCameraFragment

//manually handling configuration change, see http://blog.wittchen.biz.pl/how-to-change-fragment-layout-on-orientation-change/
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        LayoutInflater inflater = LayoutInflater.from(getActivity());
        ViewGroup container = (ViewGroup) getView();
        try {
            container.removeAllViewsInLayout();
            getPreparedUI(inflater, container, true); //this should be the one to do all the UI init stuffs
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return getPreparedUI(inflater, container, false);
    }

    private View getPreparedUI(LayoutInflater inflater, ViewGroup container, boolean isOnConfigurationChanged){
        View v = null;

        if(mHasFlash){
            if(!isOnConfigurationChanged) {
                v = inflater.inflate(R.layout.mycwac_cam2_fragment, container, false);
            }else {
                v = inflater.inflate(R.layout.mycwac_cam2_fragment, container, true);
            }
        }else{
            if(!isOnConfigurationChanged) {
                v = inflater.inflate(R.layout.mycwac_cam2_fragment_without_flash, container, false);
            }else {
                v = inflater.inflate(R.layout.mycwac_cam2_fragment_without_flash, container, true);
            }
        }

        //other init stuffs
        
        return v;
    }

Finally in the AndroidManifest we just need to tell the system we will handle the configuration change our self with this:

<activity android:name=".activities.HomeActivity" android:configChanges="orientation|screenSize"/>

Generally this is considered to be bad pratice, but since the ViewPager automatically handle Fragment'states for us, we can't really do much with configuartion change. Maybe change to a different approach, see this