com.alex.view.tab.TabLayout.java Source code

Java tutorial

Introduction

Here is the source code for com.alex.view.tab.TabLayout.java

Source

/*
 * Copyright 2016 Alex_ZHOU
 *
 * 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.
 *
 *
 * Copyright 2016 TomeOkin
 *
 * 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 com.alex.view.tab;

import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class TabLayout extends LinearLayout implements ValueAnimator.AnimatorUpdateListener {

    private static final int SHAPE_NONE = 0;
    private int mIndicatorShape = SHAPE_NONE;
    private int mTabCount;
    private int mCurrentTab = 0;
    private int mLastTab = -1;

    private IndicatorPoint mCurrentP = new IndicatorPoint();
    private IndicatorPoint mLastP = new IndicatorPoint();

    private ValueAnimator mValueAnimator;
    private int mIndicatorAnimOffset = 0; // indicator horizontal offset when animation

    private boolean mIndicatorAnimEnabled = true;

    private OnTabSelectedListener mListener;

    public TabLayout(Context context) {
        this(context, null);
    }

    public TabLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public TabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        setWillNotDraw(false); // ? onMeasure ?? onDraw ?
        setClipChildren(false);
        setClipToPadding(false);

        mValueAnimator = ValueAnimator.ofObject(new PointEvaluator(), mLastP, mCurrentP);
        mValueAnimator.addUpdateListener(this);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTabCount = getChildCount();
        initTabsWithListener();
    }

    private void initTabsWithListener() {
        View tabView;
        for (int i = 0; i < mTabCount; i++) {
            tabView = getChildAt(i);
            tabView.setTag(i);
            tabView.setOnClickListener(null);
        }
        setCurrentTab(0);
    }

    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
        View currentTabView = getChildAt(mCurrentTab);
        IndicatorPoint p = (IndicatorPoint) valueAnimator.getAnimatedValue();
        mIndicatorAnimOffset = (int) p.left - currentTabView.getLeft();
        invalidate();
    }

    public void applyConfigurationWithViewPager(@NonNull final ViewPager viewPager,
            final boolean alphaTransformEnabled) {
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                if (mIndicatorShape != SHAPE_NONE && mIndicatorAnimEnabled) {
                    scrollIndicatorTo(position, positionOffset);
                }

                if (alphaTransformEnabled) {
                    scrollTabTo(position, positionOffset);
                }
            }

            @Override
            public void onPageSelected(int position) {
                setCurrentTab(position);
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                if (mIndicatorShape != SHAPE_NONE && mIndicatorAnimEnabled && mIndicatorBounceEnabled) {
                    updateState();
                }
            }
        });

        // ??
        if (mListener == null) {
            mListener = new OnTabSelectedListener() {
                @Override
                public void onTabSelect(View view, int position) {
                    viewPager.setCurrentItem(position, false);
                }

                @Override
                public void onTabReselect(View view, int position) {

                }
            };
            addOnTabSelectedListener();
        }
    }

    public int getTabCount() {
        return getChildCount();
    }

    private boolean mIndicatorBounceEnabled = true;

    /**
     * ?? onPageScrollStateChanged ??
     * onPageScrollStateChanged ???
     */
    public void updateState() {
        mTabClickTrigger = false;
    }

    private float mIndicatorWidth = -1; // equal with tab width

    /**
     * ? onPageScrolled ???
     */
    public void scrollIndicatorTo(int position, float positionOffset) {
        View tabView = getChildAt(position);
        View currentView = getChildAt(mCurrentTab);

        // ?
        mIndicatorAnimOffset = tabView.getLeft() + (int) ((tabView.getWidth() - mIndicatorWidth) / 2)
                + (int) (tabView.getWidth() * positionOffset) - currentView.getLeft()
                - (int) ((currentView.getWidth() - mIndicatorWidth) / 2);

        if (!mTabClickTrigger) {
            mValueAnimator.setInterpolator(null);
            mValueAnimator.setDuration(250);
        }

        invalidate();
    }

    /**
     * ? tab alpha transform  onPageScrolled ???
     */
    public void scrollTabTo(int position, float positionOffset) {
        if (position + 1 > mTabCount) {
            throw new IllegalArgumentException("position must be smaller than tabCount");
        }

        // onScroll: position = min(source, dest), positionOffset = [0, 1]
        // from 0 to 1: position = 0
        // from 1 to 0: position = 0
        View view;
        if (positionOffset > 0) {
            view = getChildAt(position);
            view.setAlpha(1 - positionOffset);
            view = getChildAt(position + 1);
            view.setAlpha(positionOffset);
        }
    }

    public void setOnTabSelectedListener(OnTabSelectedListener listener) {
        mListener = listener;
        addOnTabSelectedListener();
    }

    private boolean mTabClickTrigger = true;

    private void addOnTabSelectedListener() {
        View tabView;
        for (int i = 0; i < mTabCount; i++) {
            tabView = getChildAt(i);
            tabView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = (int) v.getTag();

                    if (mCurrentTab != position) {
                        mTabClickTrigger = true;
                        setCurrentTab(position);
                        if (mListener != null) {
                            mListener.onTabSelect(v, position);
                        }
                    } else {
                        if (mListener != null) {
                            mListener.onTabReselect(v, position);
                        }
                    }
                }
            });
        }
    }

    public void setCurrentTab(int index) {
        if (index >= mTabCount) {
            throw new IllegalArgumentException("index must be smaller than tabCount");
        }

        if (mLastTab != -1 && index == mCurrentTab) {
            return;
        }

        resetState();
        mLastTab = mCurrentTab;
        mCurrentTab = index;
        View currentTabView = getChildAt(mCurrentTab);
        currentTabView.setAlpha(1);

        // change indicator
        //        if (mIndicatorShape != SHAPE_NONE && mIndicatorAnimEnabled) {
        //            calIndicatorOffset();
        //        } else {
        //            invalidate();
        //        }
        invalidate();
    }

    private void resetState() {
        View view;
        for (int i = 0; i < mTabCount; i++) {
            view = getChildAt(mCurrentTab);
            view.setAlpha(0);
        }
    }

    class IndicatorPoint {
        public float left;
        public float right;
    }

    class PointEvaluator implements TypeEvaluator<IndicatorPoint> {
        @Override
        public IndicatorPoint evaluate(float fraction, IndicatorPoint startValue, IndicatorPoint endValue) {
            float left = startValue.left + fraction * (endValue.left - startValue.left);
            float right = startValue.right + fraction * (endValue.right - startValue.right);
            IndicatorPoint point = new IndicatorPoint();
            point.left = left;
            point.right = right;
            return point;
        }
    }

    public interface OnTabSelectedListener {
        void onTabSelect(View view, int position);

        void onTabReselect(View view, int position);
    }

}