com.aincc.libtest.activity.flip.FlipViewGroup.java Source code

Java tutorial

Introduction

Here is the source code for com.aincc.libtest.activity.flip.FlipViewGroup.java

Source

package com.aincc.libtest.activity.flip;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.opengl.GLSurfaceView;
import android.os.Handler;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.os.ParcelableCompat;
import android.support.v4.os.ParcelableCompatCreatorCallbacks;
import android.support.v4.view.PagerAdapter;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;

import com.aincc.lib.util.Logger;
import com.aincc.libtest.activity.flip.internal.FlipRenderer;

/*
 Copyright 2012 Aphid Mobile
    
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
    
 http://www.apache.org/licenses/LICENSE-2.0
    
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
    
 */

/**
 * 
 * <h3><b>FlipViewGroup</b></h3></br>
 * 
 *  ?? ?   ?
 * <p>
 * 
 * @author aincc@barusoft.com
 * @version 1.0.0
 * @since 1.0.0
 */
public class FlipViewGroup extends ViewGroup {
    /**
     * SurfaceView ? 
     */
    private static final int MSG_SURFACE_CREATED = 1;

    /**
     * Populate  
     */
    private static final int MSG_POPULATE = 2;

    /**
     * ?? ?  
     */
    @SuppressLint("UseSparseArrays")
    private final Map<Integer, View> flipviews = new HashMap<Integer, View>();

    /**
     * ?
     */
    private final ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();

    /**
     * 
     */
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == MSG_SURFACE_CREATED) {
                Logger.v("FlipViewGroup::handleMessage() MSG_SURFACE_CREATED");
                requestLayout();
                return true;
            } else if (msg.what == MSG_POPULATE) {
                Logger.v("FlipViewGroup::handleMessage() MSG_POPULATE");
                populate();
                return true;
            }
            return false;
        }
    });

    /**
     * 
     */
    private FlipAdapter adapter;

    /**
     * 
     */
    private FlipAdapter.DataSetObserver observer;

    /**
     * ?  
     */
    private OnPageChangeListener pageChangeListener;

    /**
     * Surface 
     */
    private GLSurfaceView surfaceView;

    /**
     * ?
     */
    private FlipRenderer renderer;

    /**
     * ? ? ??
     */
    private int prevItem = -1;

    /**
     *  ? ??
     */
    private int currentItem = -1;

    /**
     * ?  ? ??
     */
    private int restoredCurrentItem = -1;

    /**
     * ?  ?
     */
    private Parcelable restoredAdapterState = null;

    /**
     * ? ? ?
     */
    private ClassLoader restoredClassLoader = null;

    /**
     * 
     */
    private int childWidthMeasureSpec;

    /**
     * 
     */
    private int childHeightMeasureSpec;

    /**
     * ?  ?
     */
    private boolean inLayout;

    /**
     * ?   ? 
     */
    private boolean populatePending;

    /**
     *  ?  
     */
    private boolean firstLayout = true;

    /**
     * 
     * @since 1.0.0
     * @param context
     */
    public FlipViewGroup(Context context) {
        super(context);
        initFlipViewGroup();
    }

    /**
     * 
     * @since 1.0.0
     * @param context
     * @param attr
     */
    public FlipViewGroup(Context context, AttributeSet attr) {
        super(context, attr);
        initFlipViewGroup();
    }

    /**
     * 
     * @since 1.0.0
     * @param context
     * @param attr
     * @param defStyle
     */
    public FlipViewGroup(Context context, AttributeSet attr, int defStyle) {
        super(context, attr, defStyle);
        initFlipViewGroup();
    }

    /**
     * @return the SurfaceView
     */
    public GLSurfaceView getSurfaceView() {
        return surfaceView;
    }

    /**
     * @return the Renderer
     */
    public FlipRenderer getRenderer() {
        return renderer;
    }

    /**
     * @since 1.0.0
     */
    public void onResume() {
        surfaceView.onResume();
    }

    /**
     * @since 1.0.0
     */
    public void onPause() {
        surfaceView.onPause();
    }

    /**
     *  
     * 
     * @since 1.0.0
     * @param adapter
     */
    public void setAdapter(FlipAdapter adapter) {
        // ? ? ?    .
        if (adapter != null) {
            adapter.setDataSetObserver(null);
            adapter.startUpdate(this);
            for (int i = 0; i < items.size(); i++) {
                final ItemInfo ii = items.get(i);
                adapter.destroyItem(this, ii.position, ii.object);
            }
            adapter.finishUpdate(this);
            items.clear();
            removeAllViews(); // remove all view include SurfaceView
            currentItem = 0;
        }

        // Surface  .
        setupSurfaceView();

        //  .
        this.adapter = adapter;
        if (adapter != null) {
            if (observer == null) {
                observer = new DataSetObserver();
            }
            adapter.setDataSetObserver(observer);
            populatePending = false;
            if (restoredCurrentItem >= 0) {
                adapter.restoreState(restoredAdapterState, restoredClassLoader);
                setCurrentItemInternal(restoredCurrentItem, true);
                restoredCurrentItem = -1;
                restoredAdapterState = null;
                restoredClassLoader = null;
            } else {
                //     ? .
                populate();
            }
        }
    }

    /**
     * 
     * @since 1.0.0
     * @return the adapter
     */
    public FlipAdapter getAdapter() {
        return adapter;
    }

    /**
     * 
     * @since 1.0.0
     * @param listener
     */
    public void setOnPageChangeListener(OnPageChangeListener listener) {
        pageChangeListener = listener;
    }

    // TODO: ? ?    ? ??   ?    ??
    /**
     * @param item
     *            Item index to select
     */
    public void setCurrentItem(int item) {
        prevItem = currentItem;
        populatePending = false;
        setCurrentItemInternal(item, false);
    }

    /**
     * 
     * @since 1.0.0
     * @return the current item index
     */
    public int getCurrentItem() {
        return currentItem;
    }

    /**
     * Surface ? ?   ? .
     * 
     * @since 1.0.0
     */
    public void reloadTexture() {
        handler.sendMessage(Message.obtain(handler, MSG_SURFACE_CREATED));
    }

    /**
     *      ? UIThread .
     * 
     * @since 1.0.0
     */
    public void requestPopulate() {
        handler.sendMessage(Message.obtain(handler, MSG_POPULATE));
    }

    /**
     *  
     * 
     * @since 1.0.0
     */
    private void initFlipViewGroup() {
        Logger.v("FlipViewGroup::initFlipViewGroup()");

        // TODO:  ? .
    }

    /**
     * Surface  <br>
     *    ? .
     * 
     * @since 1.0.0
     */
    private void setupSurfaceView() {
        Logger.v("FlipViewGroup::setupSurfaceView()");
        surfaceView = new GLSurfaceView(getContext());
        renderer = new FlipRenderer(this);
        surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        surfaceView.setZOrderOnTop(true);
        surfaceView.setRenderer(renderer);
        // surfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT); // ?  
        surfaceView.getHolder().setFormat(PixelFormat.OPAQUE);
        surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
        renderer.getCards().setFlipViewGroup(this);
        addView(surfaceView);
    }

    /**
     *  ??  UIThread ?     ? .
     * 
     * @since 1.0.0
     * @param item
     * @param always
     */
    private void setCurrentItemInternal(int item, boolean always) {
        if (adapter == null || adapter.getCount() <= 0) {
            return;
        }
        if (!always && currentItem == item && items.size() != 0) {
            return;
        }
        if (item < 0) {
            item = 0;
        } else if (item >= adapter.getCount()) {
            item = adapter.getCount() - 1;
        }
        currentItem = item;
        requestPopulate();
    }

    /**
     *  ?   ? ?
     * 
     * @since 1.0.0
     * @param position
     * @param index
     */
    private void addNewItem(int position, int index) {
        ItemInfo ii = new ItemInfo();
        ii.position = position;
        ii.object = adapter.instantiateItem(this, position);
        if (index < 0) {
            items.add(ii);
        } else {
            items.add(index, ii);
        }
    }

    /**
     * ??     
     * 
     * @since 1.0.0
     */
    private void dataSetChanged() {
        boolean needPopulate = items.size() < 3 && items.size() < adapter.getCount();
        int newCurrItem = -1;

        for (int i = 0; i < items.size(); i++) {
            final ItemInfo ii = items.get(i);
            final int newPos = adapter.getItemPosition(ii.object);

            if (newPos == PagerAdapter.POSITION_UNCHANGED) {
                continue;
            }

            if (newPos == PagerAdapter.POSITION_NONE) {
                items.remove(i);
                i--;
                adapter.destroyItem(this, ii.position, ii.object);
                needPopulate = true;

                if (currentItem == ii.position) {
                    // Keep the current item in the valid range
                    newCurrItem = Math.max(0, Math.min(currentItem, adapter.getCount() - 1));
                }
                continue;
            }

            if (ii.position != newPos) {
                if (ii.position == currentItem) {
                    // Our current item changed position. Follow it.
                    newCurrItem = newPos;
                }

                ii.position = newPos;
                needPopulate = true;
            }
        }

        Collections.sort(items, COMPARATOR);

        if (newCurrItem >= 0) {
            // TODO This currently causes a jump.
            setCurrentItemInternal(newCurrItem, true);
            needPopulate = true;
        }
        if (needPopulate) {
            populate();
            requestLayout();
        }
    }

    /**
     *      ? .
     * 
     * @since 1.0.0
     */
    private void populate() {
        if (adapter == null) {
            return;
        }
        if (populatePending) {
            Logger.i("FlipViewGroup::populate is pending, skipping for now...");
            return;
        }
        if (getWindowToken() == null) {
            return;
        }

        // 
        adapter.startUpdate(this);

        final int startPos = Math.max(0, currentItem - 2);
        final int N = adapter.getCount();
        final int endPos = Math.min(N - 1, currentItem + 2);

        Logger.v("FlipViewGroup::populating: startPos=" + startPos + " endPos=" + endPos);

        // Add and remove pages in the existing list.
        int lastPos = -1;
        for (int i = 0; i < items.size(); i++) {
            ItemInfo ii = items.get(i);
            if (ii.position < startPos || ii.position > endPos) {
                Logger.i("FlipViewGroup::removing: " + ii.position + " @ " + i);
                items.remove(i);
                i--;
                adapter.destroyItem(this, ii.position, ii.object);
            } else if (lastPos < endPos && ii.position > startPos) {
                // The next item is outside of our range, but we have a gap
                // between it and the last item where we want to have a page
                // shown. Fill in the gap.
                lastPos++;
                if (lastPos < startPos) {
                    lastPos = startPos;
                }
                while (lastPos <= endPos && lastPos < ii.position) {
                    Logger.i("FlipViewGroup::inserting: " + lastPos + " @ " + i);
                    addNewItem(lastPos, i);
                    lastPos++;
                    i++;
                }
            }
            lastPos = ii.position;
        }

        // Add any new pages we need at the end.
        lastPos = items.size() > 0 ? items.get(items.size() - 1).position : -1;
        if (lastPos < endPos) {
            lastPos++;
            lastPos = lastPos > startPos ? lastPos : startPos;
            while (lastPos <= endPos) {
                Logger.i("FlipViewGroup::appending: " + lastPos);
                addNewItem(lastPos, -1);
                lastPos++;
            }
        }

        if (Logger.isDebug) {
            Logger.i("FlipViewGroup::Current page list:");
            for (int i = 0; i < items.size(); i++) {
                Logger.i("FlipViewGroup::#" + i + ": page " + items.get(i).position);
            }
        }

        ItemInfo curItem = null;
        for (int i = 0; i < items.size(); i++) {
            if (items.get(i).position == currentItem) {
                curItem = items.get(i);
                break;
            }
        }
        adapter.setPrimaryItem(this, currentItem, curItem != null ? curItem.object : null);

        // 
        adapter.finishUpdate(this);

        if (hasFocus()) {
            View currentFocused = findFocus();
            ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;
            if (ii == null || ii.position != currentItem) {
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    ii = infoForChild(child);
                    if (ii != null && ii.position == currentItem) {
                        if (child.requestFocus(FOCUS_FORWARD)) {
                            break;
                        }
                    }
                }
            }
        }
    }

    /**
     * @return ?? ? ? ?  
     */
    private View getPrevPrevView() {
        return flipviews.get(currentItem - 2);
    }

    /**
     * @return ?? ? ?  
     */
    private View getPrevView() {
        return flipviews.get(currentItem - 1);
    }

    /**
     * @return ?  
     */
    private View getCurrentView() {
        return flipviews.get(currentItem);
    }

    /**
     * @return ?? ? ?  
     */
    private View getNextView() {
        return flipviews.get(currentItem + 1);
    }

    /**
     * @return ?? ? ? ?  
     */
    private View getNextNextView() {
        return flipviews.get(currentItem + 2);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            int tempCurrentViewId = currentItem;
            if (event.getY() < getHeight() / 2) {
                if (tempCurrentViewId > 0) {
                    tempCurrentViewId--;
                }
                renderer.getCards().handleTouchEventDown(true, tempCurrentViewId, getPrevPrevView());
            } else {
                if (tempCurrentViewId < adapter.getCount() - 1) {
                    tempCurrentViewId++;
                }
                renderer.getCards().handleTouchEventDown(false, tempCurrentViewId, getNextNextView());
            }
        }

        return renderer.getCards().handleTouchEvent(event);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Logger.i("--------------------------------------------------------------------");
        Logger.i("FlipViewGroup::onLayout() " + changed + " " + l + ", " + t + ", " + r + ", " + b + "; child "
                + (null != adapter ? adapter.getCount() : 0));

        inLayout = true;
        populate();
        inLayout = false;

        final int count = getChildCount();
        flipviews.clear();
        for (int ii = 0; ii < count; ii++) {
            View child = getChildAt(ii);
            ItemInfo info = null;
            if (child.getVisibility() != GONE && (info = infoForChild(child)) != null) {
                child.layout(0, 0, r - l, b - t);
            }

            if (null != info) {
                flipviews.put(Integer.valueOf(info.position), child);
            }
        }

        int w = r - l;
        int h = b - t;
        surfaceView.layout(0, 0, w, h);

        if (null != adapter && adapter.getCount() >= 2) {
            Logger.i("FlipViewGroup::onLayout() firstLayout = " + firstLayout);
            if (firstLayout) {
                firstLayout = renderer.updateTexture(getCurrentView(), getPrevView(), getNextView());
                renderer.getCards().reloadFirstTexture();
            }
        }

        if (null != pageChangeListener && renderer.isCreated()) {
            if (prevItem != currentItem) {
                pageChangeListener.onPageSelected(currentItem);
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec));
        childWidthMeasureSpec = MeasureSpec
                .makeMeasureSpec(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY);
        childHeightMeasureSpec = MeasureSpec
                .makeMeasureSpec(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY);

        inLayout = true;
        populate();
        inLayout = false;

        final int size = getChildCount();
        for (int i = 0; i < size; ++i) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    }

    @Override
    public void addView(View child, int index, LayoutParams params) {
        if (inLayout) {
            addViewInLayout(child, index, params);
            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } else {
            super.addView(child, index, params);
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        firstLayout = true;
    }

    /**
     * We only want the current page that is being shown to be focusable.
     */
    @Override
    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
        final int focusableCount = views.size();

        final int descendantFocusability = getDescendantFocusability();

        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
            for (int i = 0; i < getChildCount(); i++) {
                final View child = getChildAt(i);
                if (child.getVisibility() == VISIBLE) {
                    ItemInfo ii = infoForChild(child);
                    if (ii != null && ii.position == currentItem) {
                        child.addFocusables(views, direction, focusableMode);
                    }
                }
            }
        }

        // we add ourselves (if focusable) in all cases except for when we are
        // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
        // to avoid the focus search finding layouts when a more precise search
        // among the focusable children would be more interesting.
        if (descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
        // No focusable descendants
                (focusableCount == views.size())) {
            // Note that we can't call the superclass here, because it will
            // add all views in. So we need to do the same thing View does.
            if (!isFocusable()) {
                return;
            }
            if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE && isInTouchMode()
                    && !isFocusableInTouchMode()) {
                return;
            }
            if (views != null) {
                views.add(this);
            }
        }
    }

    /**
     * We only want the current page that is being shown to be touchable.
     */
    @Override
    public void addTouchables(ArrayList<View> views) {
        // Note that we don't call super.addTouchables(), which means that
        // we don't call View.addTouchables(). This is okay because a ViewPager
        // is itself not touchable.
        for (int i = 0; i < getChildCount(); i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == VISIBLE) {
                ItemInfo ii = infoForChild(child);
                if (ii != null && ii.position == currentItem) {
                    child.addTouchables(views);
                }
            }
        }
    }

    /**
     * We only want the current page that is being shown to be focusable.
     */
    @Override
    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
        int index;
        int increment;
        int end;
        int count = getChildCount();
        if ((direction & FOCUS_FORWARD) != 0) {
            index = 0;
            increment = 1;
            end = count;
        } else {
            index = count - 1;
            increment = -1;
            end = -1;
        }
        for (int i = index; i != end; i += increment) {
            View child = getChildAt(i);
            if (child.getVisibility() == VISIBLE) {
                ItemInfo ii = infoForChild(child);
                if (ii != null && ii.position == currentItem) {
                    if (child.requestFocus(direction, previouslyFocusedRect)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    @Override
    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
        // ViewPagers should only report accessibility info for the current page,
        // otherwise things get very confusing.

        // TODO: Should this note something about the paging container?

        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == VISIBLE) {
                final ItemInfo ii = infoForChild(child);
                if (ii != null && ii.position == currentItem && child.dispatchPopulateAccessibilityEvent(event)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 
     * <h3><b>DataSetObserver</b></h3></br>
     * 
     * @author aincc@barusoft.com
     * @version 1.0.0
     * @since 1.0.0
     */
    private class DataSetObserver implements FlipAdapter.DataSetObserver {
        @Override
        public void onDataSetChanged() {
            dataSetChanged();
        }
    }

    /**
     * ?
     */
    private static final Comparator<ItemInfo> COMPARATOR = new Comparator<ItemInfo>() {
        @Override
        public int compare(ItemInfo lhs, ItemInfo rhs) {
            return lhs.position - rhs.position;
        }
    };

    /**
     * 
     * <h3><b>ItemInfo</b></h3></br>
     * 
     * @author aincc@barusoft.com
     * @version 1.0.0
     * @since 1.0.0
     */
    static class ItemInfo {
        Object object;
        int position;
        boolean flipping;
    }

    /**
     *  ???  ItemInfo  
     * 
     * @since 1.0.0
     * @param child
     * @return the ItemInfo
     */
    ItemInfo infoForChild(View child) {
        for (int i = 0; i < items.size(); i++) {
            ItemInfo ii = items.get(i);
            if (adapter.isViewFromObject(child, ii.object)) {
                return ii;
            }
        }
        return null;
    }

    /**
     * 
     * @since 1.0.0
     * @param child
     * @return
     */
    ItemInfo infoForAnyChild(View child) {
        ViewParent parent;
        while ((parent = child.getParent()) != this) {
            if (parent == null || !(parent instanceof View)) {
                return null;
            }
            child = (View) parent;
        }
        return infoForChild(child);
    }

    /**
     * 
     * <h3><b>OnPageChangeListener</b></h3></br>
     * 
     * @author aincc@barusoft.com
     * @version 1.0.0
     * @since 1.0.0
     */
    public interface OnPageChangeListener {

        /**
         *  ? ??  ?.
         * 
         * @param position
         */
        public void onPageSelected(int position);
    }

    /**
     * Simple implementation of the {@link OnPageChangeListener} interface with stub
     * implementations of each method. Extend this if you do not intend to override
     * every method of {@link OnPageChangeListener}.
     */
    public static class SimpleOnPageChangeListener implements OnPageChangeListener {
        @Override
        public void onPageSelected(int position) {
            // This space for rent
        }
    }

    /**
     * 
     * <h3><b>SavedState</b></h3></br>
     * 
     * @author aincc@barusoft.com
     * @version 1.0.0
     * @since 1.0.0
     */
    public static class SavedState extends BaseSavedState {
        int position;
        Parcelable adapterState;
        ClassLoader loader;

        public SavedState(Parcelable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(position);
            out.writeParcelable(adapterState, flags);
        }

        @Override
        public String toString() {
            return "FragmentPager.SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " position="
                    + position + "}";
        }

        public static final Parcelable.Creator<SavedState> CREATOR = ParcelableCompat
                .newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
                    @Override
                    public SavedState createFromParcel(Parcel in, ClassLoader loader) {
                        return new SavedState(in, loader);
                    }

                    @Override
                    public SavedState[] newArray(int size) {
                        return new SavedState[size];
                    }
                });

        SavedState(Parcel in, ClassLoader loader) {
            super(in);
            if (loader == null) {
                loader = getClass().getClassLoader();
            }
            position = in.readInt();
            adapterState = in.readParcelable(loader);
            this.loader = loader;
        }
    }

    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.position = currentItem;
        if (adapter != null) {
            ss.adapterState = adapter.saveState();
        }
        return ss;
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }

        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());

        if (adapter != null) {
            adapter.restoreState(ss.adapterState, ss.loader);
            setCurrentItemInternal(ss.position, true);
        } else {
            restoredCurrentItem = ss.position;
            restoredAdapterState = ss.adapterState;
            restoredClassLoader = ss.loader;
        }
    }
}