am.widget.basetabstrip.BaseTabStripGroup.java Source code

Java tutorial

Introduction

Here is the source code for am.widget.basetabstrip.BaseTabStripGroup.java

Source

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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.
 */

package am.widget.basetabstrip;

import android.annotation.TargetApi;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;

import java.lang.ref.WeakReference;
import java.util.ArrayList;

/**
 * BasePagerTabStrip ViewPager?
 *
 * @author Alex
 */
@SuppressWarnings("unused")
@ViewPager.DecorView
public abstract class BaseTabStripGroup extends ViewGroup {

    private ViewPager mPager;
    private final PageListener mPageListener = new PageListener();
    private WeakReference<PagerAdapter> mWatchingAdapter;
    private int mLastKnownPosition = 0;
    private float mLastKnownPositionOffset = -1;
    private int mCurrentPager = 0;
    private int mPosition = 0;
    private boolean clickSelectedItem = false;
    private Drawable mTabItemBackground;
    private ArrayList<Drawable> mTabItemBackgrounds = new ArrayList<>();
    private boolean tabClickable;
    private boolean clickSmoothScroll;
    private TabStripGestureDetector tabStripGestureDetector;
    private OnItemClickListener clickListener;
    private ArrayList<OnChangeListener> changeListeners;
    private final Rect mRefreshRect = new Rect();
    private final Rect mRefreshTempRect = new Rect();

    public BaseTabStripGroup(Context context) {
        super(context);
        initView();
    }

    public BaseTabStripGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    @TargetApi(11)
    public BaseTabStripGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    @TargetApi(21)
    public BaseTabStripGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initView();
    }

    private void initView() {
        setItemClickable(false);
        setClickSmoothScroll(false);
        tabStripGestureDetector = new TabStripGestureDetector();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        final ViewParent parent = getParent();
        if (mPager == null && parent != null && parent instanceof ViewPager) {
            bindViewPager((ViewPager) parent);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        bindViewPager(null);
        clearItemBackground();
    }

    /**
     * ?ViewPager
     *
     * @param pager ?ViewPager
     */
    public void bindViewPager(ViewPager pager) {
        PagerAdapter oldAdapter = null;
        PagerAdapter newAdapter = null;
        if (mPager != null) {
            mPager.removeOnPageChangeListener(mPageListener);
            mPager.removeOnAdapterChangeListener(mPageListener);
            oldAdapter = mPager.getAdapter();
        }
        mPager = pager;
        if (mPager != null) {
            mPager.addOnPageChangeListener(mPageListener);
            mPager.addOnAdapterChangeListener(mPageListener);
            newAdapter = mPager.getAdapter();
        }
        bindPagerAdapter(oldAdapter, newAdapter);
    }

    private void clearItemBackground() {
        for (Drawable drawable : mTabItemBackgrounds) {
            drawable.setCallback(null);
        }
        mTabItemBackgrounds.clear();
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (!tabClickable) {
            return super.onTouchEvent(ev);
        }
        final boolean tab = tabStripGestureDetector.onTouchEvent(ev);
        return super.onTouchEvent(ev) || tab;
    }

    @Override
    @Deprecated
    public boolean performClick() {
        return super.performClick();
    }

    /**
     * 
     *
     * @param position ?
     * @return ?
     */
    @SuppressWarnings("unused")
    public boolean performClick(int position) {
        return performClick(position, false, true);
    }

    /**
     * 
     *
     * @param position       ?
     * @param smoothScroll   ?
     * @param notifyListener ??
     * @return ?
     */
    public boolean performClick(int position, boolean smoothScroll, boolean notifyListener) {
        if (getViewPager() != null && position >= 0 && position < getItemCount()) {
            clickSelectedItem = position == mPosition;
            mPosition = position;
            if (!clickSelectedItem) {
                if (!smoothScroll) {
                    mCurrentPager = position;
                    mLastKnownPosition = mCurrentPager;
                    mLastKnownPositionOffset = 0;
                    jumpTo(mCurrentPager);
                    notifyJumpTo(mCurrentPager);
                }
                getViewPager().setCurrentItem(position, smoothScroll);
            }
            if (clickListener != null && notifyListener) {
                clickListener.onItemClick(mPosition);
            }
            playSoundEffect(SoundEffectConstants.CLICK);
            return true;
        }
        return false;
    }

    /**
     * ?PagerAdapter
     *
     * @param oldAdapter Adapter
     * @param newAdapter Adapter
     */
    protected void bindPagerAdapter(PagerAdapter oldAdapter, PagerAdapter newAdapter) {
        if (oldAdapter != null) {
            oldAdapter.unregisterDataSetObserver(mPageListener);
            mWatchingAdapter = null;
        }
        if (newAdapter != null) {
            newAdapter.registerDataSetObserver(mPageListener);
            mWatchingAdapter = new WeakReference<>(newAdapter);
        }
        createItemBackgrounds();
        onBindPagerAdapter();
        checkCurrentItem();
        requestLayout();
        invalidate();
    }

    /**
     * ??
     *
     * @return ??
     */
    protected boolean hasItemBackgrounds() {
        return mTabItemBackground != null;
    }

    /**
     * ?
     */
    protected void createItemBackgrounds() {
        if (!hasItemBackgrounds())
            return;
        int count = getItemCount();
        if (count > 0) {
            for (int i = 0; i < count; i++) {
                if (i < mTabItemBackgrounds.size()) {
                    mTabItemBackgrounds.get(i).setState(onCreateDrawableState(0));
                } else {
                    Drawable tag;
                    if (mTabItemBackground.getConstantState() != null) {
                        tag = mTabItemBackground.getConstantState().newDrawable().mutate();
                    } else {
                        tag = mTabItemBackground.mutate();
                    }
                    tag.setCallback(this);
                    mTabItemBackgrounds.add(tag);
                }
            }
        } else {
            for (Drawable drawable : mTabItemBackgrounds) {
                drawable.setState(onCreateDrawableState(0));
            }
        }
    }

    /**
     * ??
     */
    protected void recreateItemBackgrounds() {
        clearItemBackground();
        if (!hasItemBackgrounds())
            return;
        int count = getItemCount();
        for (int i = 0; i < count; i++) {
            Drawable tag;
            if (mTabItemBackground.getConstantState() != null) {
                tag = mTabItemBackground.getConstantState().newDrawable().mutate();
            } else {
                tag = mTabItemBackground.mutate();
            }
            tag.setCallback(this);
            mTabItemBackgrounds.add(tag);
        }
    }

    /**
     * ???
     *
     * @return ??
     */
    public int getCurrentItem() {
        return mPager == null ? -1 : mPager.getCurrentItem();
    }

    /**
     * ?
     */
    public void checkCurrentItem() {
        final int position = getCurrentItem();
        mPosition = position;
        if (position >= 0 && position != mCurrentPager) {
            mCurrentPager = position;
            mLastKnownPosition = mCurrentPager;
            mLastKnownPositionOffset = 0;
            jumpTo(mCurrentPager);
            notifyJumpTo(mCurrentPager);
        }
    }

    /**
     * ?PagerAdapter
     */
    protected void onBindPagerAdapter() {

    }

    /**
     * Position
     *
     * @param x X??
     * @param y Y??
     * @return ???
     */
    @SuppressWarnings("unused")
    protected int pointToPosition(float x, float y) {
        return 0;
    }

    @Override
    protected void drawableStateChanged() {
        final float downMotionX = tabStripGestureDetector.getDownMotionX();
        final float downMotionY = tabStripGestureDetector.getDownMotionY();
        int position = pointToPosition(downMotionX, downMotionY);
        if (position >= 0 && position < mTabItemBackgrounds.size()) {
            Drawable tag = mTabItemBackgrounds.get(position);
            DrawableCompat.setHotspot(tag, getHotspotX(tag, position, downMotionX, downMotionY),
                    getHotspotY(tag, position, downMotionX, downMotionY));
            if (tag.isStateful()) {
                tag.setState(getDrawableState());
            }
        }
        super.drawableStateChanged();
    }

    /**
     * set hotspot's x location
     *
     * @param background 
     * @param position   Position
     * @param motionX    ?X
     * @param motionY    ?Y
     * @return x location
     */
    @SuppressWarnings("unused")
    protected float getHotspotX(Drawable background, int position, float motionX, float motionY) {
        return background.getIntrinsicWidth() * 0.5f;
    }

    /**
     * set hotspot's y location
     *
     * @param background 
     * @param position   Position
     * @param motionX    ?X
     * @param motionY    ?Y
     * @return y location
     */
    @SuppressWarnings("unused")
    protected float getHotspotY(Drawable background, int position, float motionX, float motionY) {
        return background.getIntrinsicHeight() * 0.5f;
    }

    @Override
    protected boolean verifyDrawable(@NonNull Drawable who) {
        boolean isTag = false;
        for (Drawable tag : mTabItemBackgrounds) {
            if (who == tag) {
                isTag = true;
                break;
            }
        }
        return isTag || super.verifyDrawable(who);
    }

    /**
     * ?Tab?
     *
     * @param position ?
     * @return 
     */
    @SuppressWarnings("unused")
    protected Drawable getItemBackground(int position) {
        return position < mTabItemBackgrounds.size() ? mTabItemBackgrounds.get(position) : null;
    }

    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        BaseTabStripSavedState ss = new BaseTabStripSavedState(superState);
        ss.currentPager = mCurrentPager;
        return ss;
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        BaseTabStripSavedState ss = (BaseTabStripSavedState) state;
        performClick(ss.currentPager, false, false);
        super.onRestoreInstanceState(ss.getSuperState());
    }

    static class BaseTabStripSavedState extends BaseSavedState {
        int currentPager;

        BaseTabStripSavedState(Parcelable superState) {
            super(superState);
        }

        private BaseTabStripSavedState(Parcel in) {
            super(in);
            currentPager = in.readInt();
        }

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

        public static final Creator<BaseTabStripSavedState> CREATOR = new Creator<BaseTabStripSavedState>() {
            public BaseTabStripSavedState createFromParcel(Parcel in) {
                return new BaseTabStripSavedState(in);
            }

            public BaseTabStripSavedState[] newArray(int size) {
                return new BaseTabStripSavedState[size];
            }
        };
    }

    private class PageListener extends DataSetObserver
            implements ViewPager.OnPageChangeListener, ViewPager.OnAdapterChangeListener {
        private int mScrollState;

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            updateView(position, positionOffset, false);
        }

        @Override
        public void onPageSelected(int position) {
            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
                float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0;
                updateView(position, offset, false);
            }
            mPosition = position;
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            mScrollState = state;
        }

        @Override
        public void onAdapterChanged(@NonNull ViewPager viewPager, @Nullable PagerAdapter oldAdapter,
                @Nullable PagerAdapter newAdapter) {
            bindPagerAdapter(oldAdapter, newAdapter);
        }

        @Override
        public void onChanged() {
            final float offset = mLastKnownPositionOffset >= 0 ? mLastKnownPositionOffset : 0;
            final int position = getCurrentItem();
            mPosition = position;
            updateView(position, offset, true);
        }
    }

    /**
     * View
     *
     * @param position       ?
     * @param positionOffset ???
     * @param force          ?
     */
    private void updateView(int position, float positionOffset, boolean force) {
        if (mLastKnownPositionOffset == -1) {
            mLastKnownPositionOffset = positionOffset;
        }
        if (!force && positionOffset == mLastKnownPositionOffset) {
            return;
        }
        float mPositionOffset = positionOffset;
        if (mLastKnownPositionOffset == 0 || mLastKnownPositionOffset == 1)
            if (mPositionOffset > 0.5f)
                mLastKnownPositionOffset = 1;
            else
                mLastKnownPositionOffset = 0;
        int nextPager;
        if (position > mLastKnownPosition) {
            mLastKnownPosition = position - 1;
            if (mLastKnownPositionOffset > mPositionOffset) {
                if (mPositionOffset == 0) {
                    mPositionOffset = 1;
                } else {
                    mLastKnownPosition = position;
                }
                mCurrentPager = mLastKnownPosition;
                nextPager = mLastKnownPosition + 1;
                gotoRight(mCurrentPager, nextPager, mPositionOffset);
                notifyGotoRight(mCurrentPager, nextPager, mPositionOffset);
            } else {
                mCurrentPager = mLastKnownPosition + 1;
                nextPager = mLastKnownPosition;
                gotoLeft(mCurrentPager, nextPager, mPositionOffset);
                notifyGotoLeft(mCurrentPager, nextPager, mPositionOffset);
            }
        } else {
            mLastKnownPosition = position;
            if (mLastKnownPositionOffset > mPositionOffset) {
                mCurrentPager = mLastKnownPosition + 1;
                nextPager = mLastKnownPosition;
                gotoLeft(mCurrentPager, nextPager, mPositionOffset);
                notifyGotoLeft(mCurrentPager, nextPager, mPositionOffset);
            } else {
                mPositionOffset = mPositionOffset == 0 ? 1 : mPositionOffset;
                mCurrentPager = mLastKnownPosition;
                nextPager = mLastKnownPosition + 1;
                gotoRight(mCurrentPager, nextPager, mPositionOffset);
                notifyGotoRight(mCurrentPager, nextPager, mPositionOffset);
            }
        }
        mLastKnownPosition = position;
        mLastKnownPositionOffset = positionOffset;
    }

    /**
     * 
     *
     * @param current ?
     */
    private void notifyJumpTo(int current) {
        if (changeListeners == null)
            return;
        for (OnChangeListener listener : changeListeners) {
            listener.jumpTo(current);
        }
    }

    /**
     * ?
     *
     * @param current ?
     * @param next    
     * @param offset  ??
     */
    private void notifyGotoLeft(int current, int next, float offset) {
        if (changeListeners == null)
            return;
        for (OnChangeListener listener : changeListeners) {
            listener.gotoLeft(current, next, offset);
        }
    }

    /**
     * ??
     *
     * @param current ?
     * @param next    
     * @param offset  ??
     */
    private void notifyGotoRight(int current, int next, float offset) {
        if (changeListeners == null)
            return;
        for (OnChangeListener listener : changeListeners) {
            listener.gotoRight(current, next, offset);
        }
    }

    /**
     * 
     *
     * @param current ?
     */
    protected abstract void jumpTo(int current);

    /**
     * ?
     *
     * @param current ?
     * @param next    
     * @param offset  ??
     */
    protected abstract void gotoLeft(int current, int next, float offset);

    /**
     * ??
     *
     * @param current ?
     * @param next    
     * @param offset  ??
     */
    protected abstract void gotoRight(int current, int next, float offset);

    private class TabStripGestureDetector {

        private final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
        private float mDownMotionX;
        private float mDownMotionY;
        private int mDownPosition;
        private int mLastPosition;
        private long mLastUpTime;

        boolean onTouchEvent(MotionEvent ev) {
            boolean isClick = false;
            final int action = ev.getAction();
            final float x = ev.getX();
            final float y = ev.getY();

            switch (action) {
            case MotionEvent.ACTION_DOWN:
                mDownMotionX = x;
                mDownMotionY = y;
                mDownPosition = pointToPosition(x, y);
                mLastPosition = getCurrentItem();
                break;
            case MotionEvent.ACTION_UP:
                final int clickPosition = pointToPosition(x, y);
                if (mDownPosition == clickPosition) {
                    isClick = onItemClick(clickPosition);
                    if (mLastPosition == clickPosition) {
                        final long downTime = ev.getDownTime();
                        if (mLastUpTime != 0 && downTime - mLastUpTime < DOUBLE_TAP_TIMEOUT) {
                            onDoubleClick(clickPosition);
                        } else {
                            onSelectedClick(clickPosition);
                        }
                    }
                    mLastUpTime = ev.getEventTime();
                } else {
                    mLastUpTime = 0;
                }
                break;
            }
            return isClick;
        }

        private boolean onItemClick(int position) {
            performClick(position, clickSmoothScroll, true);
            return true;
        }

        private void onSelectedClick(int position) {
            if (clickSelectedItem && clickListener != null) {
                clickListener.onSelectedClick(position);
            }
        }

        /**
         * ??
         *
         * @param position ?
         */
        private void onDoubleClick(int position) {
            if (clickListener != null) {
                clickListener.onDoubleClick(position);
            }
        }

        float getDownMotionX() {
            return mDownMotionX;
        }

        float getDownMotionY() {
            return mDownMotionY;
        }
    }

    /**
     * ?ViewPager
     *
     * @return ViewPager
     */
    protected ViewPager getViewPager() {
        return mPager;
    }

    /**
     * ??
     *
     * @return ?
     */
    protected int getItemCount() {
        try {
            return mWatchingAdapter.get().getCount();
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * ??
     *
     * @param position ???
     * @return ?
     */
    @SuppressWarnings("unused")
    protected CharSequence getItemText(int position) {
        try {
            return mWatchingAdapter.get().getPageTitle(position);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Tab?
     *
     * @param background 
     */
    public void setItemBackground(Drawable background) {
        if (mTabItemBackground != background) {
            mTabItemBackground = background;
            recreateItemBackgrounds();
            requestLayout();
            invalidate();
        }
    }

    /**
     * Tab?
     *
     * @param background 
     */
    @SuppressWarnings("unused")
    public void setItemBackground(int background) {
        setItemBackground(ContextCompat.getDrawable(getContext(), background));
    }

    /**
     * Tab??
     *
     * @param clickable ??
     */
    public void setItemClickable(boolean clickable) {
        tabClickable = clickable;
        if (tabClickable) {
            setClickable(true);
        }
    }

    /**
     * Tab??
     *
     * @return Tab??
     */
    @SuppressWarnings("unused")
    public boolean isTabClickable() {
        return tabClickable;
    }

    /**
     * ?
     *
     * @return ?
     */
    @SuppressWarnings("unused")
    public boolean isClickSmoothScroll() {
        return clickSmoothScroll;
    }

    /**
     * ?
     *
     * @param smooth ?
     */
    public void setClickSmoothScroll(boolean smooth) {
        clickSmoothScroll = smooth;
    }

    /**
     * ?
     *
     * @param listener ?
     */
    @SuppressWarnings("unused")
    public void setOnItemClickListener(OnItemClickListener listener) {
        clickListener = listener;
    }

    /**
     * ?
     */
    public interface OnItemClickListener {
        /**
         * ?
         *
         * @param position ?
         */
        void onItemClick(int position);

        /**
         * ?
         *
         * @param position ?
         */
        void onSelectedClick(int position);

        /**
         * ??
         *
         * @param position ?
         */
        void onDoubleClick(int position);
    }

    /**
     * ??
     *
     * @param listener ??
     */
    @SuppressWarnings("unused")
    public void addOnChangeListener(OnChangeListener listener) {
        if (listener == null)
            return;
        if (changeListeners == null)
            changeListeners = new ArrayList<>();
        changeListeners.add(listener);
        listener.jumpTo(mCurrentPager);
    }

    /**
     * ??
     *
     * @param listener ??
     */
    @SuppressWarnings("unused")
    public void removeOnChangeListener(OnChangeListener listener) {
        if (changeListeners == null)
            return;
        changeListeners.remove(listener);
    }

    /**
     * ?Tag
     *
     * @return Tag
     */
    @SuppressWarnings("unused")
    protected Drawable getDefaultTagBackground() {
        final float density = getResources().getDisplayMetrics().density;
        final GradientDrawable mBackground = new GradientDrawable();
        mBackground.setShape(GradientDrawable.RECTANGLE);
        mBackground.setColor(0xffff4444);
        mBackground.setCornerRadius(100000 * density);
        mBackground.setSize((int) (10 * density), (int) (10 * density));
        return mBackground;
    }

    /**
     * ???
     *
     * @return ??
     */
    @SuppressWarnings("unused")
    protected int getMinItemBackgroundWidth() {
        if (!hasItemBackgrounds())
            return 0;
        return mTabItemBackground.getMinimumWidth();
    }

    /**
     * ???
     *
     * @return ??
     */
    @SuppressWarnings("unused")
    protected int getMinItemBackgroundHeight() {
        if (!hasItemBackgrounds())
            return 0;
        return mTabItemBackground.getMinimumHeight();
    }

    /**
     * ???
     *
     * @param normalColor   
     * @param selectedColor 
     * @param offset        ??
     * @return ??
     */
    @SuppressWarnings("unused")
    protected int getColor(int normalColor, int selectedColor, float offset) {
        int normalAlpha = Color.alpha(normalColor);
        int normalRed = Color.red(normalColor);
        int normalGreen = Color.green(normalColor);
        int normalBlue = Color.blue(normalColor);
        int selectedAlpha = Color.alpha(selectedColor);
        int selectedRed = Color.red(selectedColor);
        int selectedGreen = Color.green(selectedColor);
        int selectedBlue = Color.blue(selectedColor);
        int a = (int) Math.ceil((selectedAlpha - normalAlpha) * offset);
        int r = (int) Math.ceil((selectedRed - normalRed) * offset);
        int g = (int) Math.ceil((selectedGreen - normalGreen) * offset);
        int b = (int) Math.ceil((selectedBlue - normalBlue) * offset);
        return Color.argb(normalAlpha + a, normalRed + r, normalGreen + g, normalBlue + b);
    }

    /**
     * 
     *
     * @param dirty 
     */
    @SuppressWarnings("unused")
    public void invalidate(int... dirty) {
        if (dirty == null || dirty.length <= 0)
            return;
        mRefreshRect.set(0, 0, 0, 0);
        for (int position : dirty) {
            mRefreshTempRect.set(0, 0, 0, 0);
            getItemRect(position, mRefreshTempRect);
            mRefreshRect.union(mRefreshTempRect);
        }
        if (!mRefreshTempRect.isEmpty())
            invalidate(mRefreshTempRect);
    }

    /**
     * ??
     *
     * @param position ?
     * @param bound    
     */
    @SuppressWarnings("unused")
    protected void getItemRect(int position, Rect bound) {

    }

    /**
     * ??
     */
    public interface OnChangeListener {
        /**
         * ??
         *
         * @param correct ??
         */
        void jumpTo(int correct);

        /**
         * 
         *
         * @param correct ??
         * @param next    ??
         * @param offset  
         */
        void gotoLeft(int correct, int next, float offset);

        /**
         * ?
         *
         * @param correct ??
         * @param next    ??
         * @param offset  
         */
        void gotoRight(int correct, int next, float offset);
    }

    /**
     * ?Adapter
     */
    @SuppressWarnings("unused")
    public interface ItemTabAdapter {

        /**
         * ??
         *
         * @param position Item?
         * @return ??
         */
        boolean isTagEnable(int position);

        /**
         * ?
         *
         * @param position Item?
         * @return 
         */
        String getTag(int position);
    }

    /**
     * Tag?
     */
    @SuppressWarnings("all")
    protected static class TagLocation {
        public static final int LOCATION_CONTENT = 0;//
        public static final int LOCATION_EDGE = 1;// 
        private int location = LOCATION_CONTENT;// ?

        private int paddingLeft;
        private int paddingRight;
        private int paddingTop;
        private int paddingBottom;

        private int marginLeft;
        private int marginRight;
        private int marginTop;
        private int marginBottom;

        public TagLocation() {
            this(LOCATION_CONTENT);
        }

        public TagLocation(int location) {
            setLocation(location);
            setPadding(0, 0, 0, 0);
            setMargin(0, 0, 0, 0);
        }

        public boolean setLocation(int location) {
            if (location != LOCATION_CONTENT && location != LOCATION_EDGE)
                return false;
            if (this.location != location) {
                this.location = location;
                return true;
            }
            return false;
        }

        public boolean setPadding(int left, int top, int right, int bottom) {
            if (left < 0 || top < 0 || right < 0 || bottom < 0 || (paddingLeft == left && paddingTop == top
                    && paddingRight == right && paddingBottom == bottom))
                return false;
            paddingLeft = left;
            paddingTop = top;
            paddingRight = right;
            paddingBottom = bottom;
            return true;
        }

        public boolean setMargin(int left, int top, int right, int bottom) {
            if (left < 0 || top < 0 || right < 0 || bottom < 0
                    || (marginLeft == left && marginTop == top && marginRight == right && marginBottom == bottom))
                return false;
            marginLeft = left;
            marginTop = top;
            marginRight = right;
            marginBottom = bottom;
            return true;
        }

        public int getLocation() {
            return location;
        }

        public int getPaddingLeft() {
            return paddingLeft;
        }

        public int getPaddingRight() {
            return paddingRight;
        }

        public int getPaddingTop() {
            return paddingTop;
        }

        public int getPaddingBottom() {
            return paddingBottom;
        }

        public int getMarginLeft() {
            return marginLeft;
        }

        public int getMarginRight() {
            return marginRight;
        }

        public int getMarginTop() {
            return marginTop;
        }

        public int getMarginBottom() {
            return marginBottom;
        }
    }
}