content: package april.yun;
  
  import android.animation.Animator;
  import android.animation.ValueAnimator;
  import android.content.Context;
  import android.content.res.TypedArray;
  import android.graphics.Canvas;
  import android.graphics.drawable.Drawable;
  import android.graphics.drawable.StateListDrawable;
  import android.os.Build;
  import android.os.Parcelable;
  import android.text.TextPaint;
  import android.text.TextUtils;
  import android.util.AttributeSet;
  import android.util.Log;
  import android.util.TypedValue;
  import android.view.Gravity;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.animation.DecelerateInterpolator;
  import android.widget.Checkable;
  import android.widget.FrameLayout;
  import android.widget.HorizontalScrollView;
  import android.widget.LinearLayout;
  import android.widget.TextView;
  import androidx.annotation.DrawableRes;
  import androidx.annotation.Keep;
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import androidx.annotation.Size;
  import androidx.core.content.ContextCompat;
  import androidx.core.view.NestedScrollingChild2;
  import androidx.core.view.NestedScrollingChildHelper;
  import androidx.core.view.ViewCompat;
  import androidx.viewpager.widget.ViewPager;
  import androidx.viewpager.widget.ViewPager.OnPageChangeListener;
  import april.yun.other.Damping;
  import april.yun.other.JTabStyleDelegate;
  import april.yun.other.OntheSamePositionClickListener;
  import april.yun.other.SavedState;
  import april.yun.tabstyle.JTabStyle;
  import april.yun.widget.PromptView;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Locale;
  
  /**
   * @author yun.
   * @date 2016/12/21
   * @des [一句话描述]
   * @since [https://github.com/ZuYun]
   * <p><a href="https://github.com/ZuYun">github</a>
   */
  public class JPagerSlidingTabStrip extends HorizontalScrollView implements ISlidingTabStrip, NestedScrollingChild2,
      ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
  
    private static final String TAG = JPagerSlidingTabStrip.class.getSimpleName();
    public static String PROMPTMSGFORMART = "%d";
    public static boolean dampingAble = false;
    private JTabStyleDelegate mTabStyleDelegate;
    private int scrollOffset = 52;
    private JTabStyle mJTabStyle;
    private int mLastCheckedPosition = -1;
    private int mState = -1;
    //            case com.android.internal.R.styleable.TextView_drawablePadding:
    //    drawablePadding = a.getDimensionPixelSize(attr, drawablePadding);
    private LinearLayout.LayoutParams defaultTabLayoutParams;
    private LinearLayout.LayoutParams expandedTabLayoutParams;
  
    private final PageListener pageListener = new PageListener();
    public OnPageChangeListener delegatePageListener;
  
    private LinearLayout tabsContainer;
    private ViewPager pager;
    private Locale locale;
    private int mTabCount;
    private int lastScrollX = 0;
    private float currentPositionOffset = 0f;
    private List<TextPaint> mTextPaints = new ArrayList<>();
    private ValueAnimator mValueAnimator = ValueAnimator.ofFloat(1, 0).setDuration(400);
    private int mLastLastCheckPosition = -1;
    private int[] mIconIds = new int[1];
    private int[][] mIconsIds = new int[1][2];
    private OntheSamePositionClickListener mSamePositionClickListener;
    private Damping mDamping;
    private NestedScrollingChildHelper mScrollingChildHelper = new NestedScrollingChildHelper(this);
    private int mLastEventY;
    private static final int[] ATTRS = new int[]{android.R.attr.nestedScrollingEnabled};
  
    @Keep
    public JPagerSlidingTabStrip(Context context) {
      this(context, null);
    }
  
  
    @Keep
    public JPagerSlidingTabStrip(Context context, AttributeSet attrs) {
      this(context, attrs, 0);
    }
  
  
    @Keep
    public JPagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) {
      super(context, attrs, defStyle);
      TypedArray sa = context.obtainStyledAttributes(attrs, ATTRS);
      boolean nestedEnable = sa.getBoolean(0, true);
      sa.recycle();
  
      setFillViewport(true);
      setWillNotDraw(false);
      tabsContainer = new LinearLayout(context);
      tabsContainer.setGravity(Gravity.CENTER_VERTICAL);
      tabsContainer.setOrientation(LinearLayout.HORIZONTAL);
      tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
      FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(-1, -1);
      layoutParams.gravity = Gravity.CENTER_VERTICAL;
      addView(tabsContainer, layoutParams);
  
      defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
      expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f);
      //expandedTabLayoutParams.gravity=Gravity.CENTER_VERTICAL;
      //defaultTabLayoutParams.gravity=Gravity.CENTER_VERTICAL;
      if (locale == null) {
        locale = getResources().getConfiguration().locale;
      }
      mTabStyleDelegate = new JTabStyleDelegate().obtainAttrs(this, attrs, getContext());
      mJTabStyle = mTabStyleDelegate.getJTabStyle();
      if (dampingAble) {
        mDamping = Damping.wrapper(this);
      }
      mScrollingChildHelper.setNestedScrollingEnabled(nestedEnable);
    }
  
    @Keep
    public void bindViewPager(ViewPager pager) {
      this.pager = pager;
      if (pager.getAdapter() == null) {
        throw new IllegalStateException("ViewPager does not have adapter instance.");
      }
  
      pager.addOnPageChangeListener(pageListener);
  
      notifyDataSetChanged();
    }
  
    @Keep
    public void setOnPageChangeListener(OnPageChangeListener listener) {
      this.delegatePageListener = listener;
    }
  
  
    public void notifyDataSetChanged() {
  
      tabsContainer.removeAllViews();
      mTabCount = pager.getAdapter().getCount();
      if (mJTabStyle.needChildView()) {
        for (int i = 0; i < mTabCount; i++) {
          if (pager.getAdapter() instanceof IconTabProvider) {
            //有提供icon
            Log.d(TAG, "haove tabIcon");
            if (((IconTabProvider) pager.getAdapter()).getPageIconResIds(i) != null) {
              addIconTab(i, pager.getAdapter().getPageTitle(i),
                  ((IconTabProvider) pager.getAdapter()).getPageIconResIds(i));
            } else {
              addIconTab(i, pager.getAdapter().getPageTitle(i),
                  ((IconTabProvider) pager.getAdapter()).getPageIconResId(i));
            }
          } else {
            //没有提供icon
            Log.d(TAG, "haove no tabIcon");
            mTabStyleDelegate.setNotDrawIcon(true);
            addTextTab(i, pager.getAdapter().getPageTitle(i));
          }
        }
        updateTabStyles();
        check(mTabStyleDelegate.setCurrentPosition(pager.getCurrentItem()));
      }
    }
  
  
    private void addTextTab(final int position, CharSequence title) {
      addIconTab(position, title, 0);
    }
  
  
    private void addIconTab(final int position, CharSequence title, @NonNull @Size(min = 1) int... resId) {
      if (TextUtils.isEmpty(title)) {
        Log.e(TAG, "title is null ");
        return;
      }
      PromptView tab = new PromptView(getContext());
      tab.setGravity(Gravity.CENTER);
      tab.MSGFORMART = PROMPTMSGFORMART;
      tab.setMaxLines(mTabStyleDelegate.getMaxLines());
      tab.setColor_bg(mTabStyleDelegate.getPromptBgColor());
      tab.setColor_num(mTabStyleDelegate.getPromptNumColor());
      setPadding(0, getPaddingTop(), 0, getPaddingBottom());
      if (!mTabStyleDelegate.isNotDrawIcon()) {
        if (mTabStyleDelegate.getTabIconGravity() == Gravity.NO_GRAVITY) {
          setPadding(0, 0, 0, 0);//disable pading
          if (resId.length > 1) {
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
              tab.setBackground(getListDrable(resId));
            } else {
              tab.setBackgroundDrawable(getListDrable(resId));
            }
          } else {
            tab.setBackgroundResource(resId[0]);
          }
        } else {
  
          mTabStyleDelegate.setShouldExpand(true);
          tab.setCompoundDrawablePadding(mTabStyleDelegate.getDrawablePading());
          Drawable tabIcon = ContextCompat.getDrawable(getContext(), resId[0]);
          if (resId.length > 1) {
            tabIcon = getListDrable(resId);
          }
          switch (mTabStyleDelegate.getTabIconGravity()) {
            case Gravity.TOP:
              //有图片 需要设置pading不然可能图片和文字的pading很大
              expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, 1.0f);
              tab.setCompoundDrawablesWithIntrinsicBounds(null, tabIcon, null, null);
              break;
            case Gravity.BOTTOM:
              //有图片 需要设置pading不然可能图片和文字的pading很大
              expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, 1.0f);
              tab.setCompoundDrawablesWithIntrinsicBounds(null, null, null, tabIcon);
              break;
            case Gravity.LEFT:
              tab.setCompoundDrawablesWithIntrinsicBounds(tabIcon, null, null, null);
              break;
            case Gravity.RIGHT:
              tab.setCompoundDrawablesWithIntrinsicBounds(null, null, tabIcon, null);
              break;
            default:
              tab.setCompoundDrawablesWithIntrinsicBounds(null, tabIcon, null, null);
              break;
          }
        }
      }
      tab.setText(title);
      addTab(position, tab);
      //        if(mTabStyleDelegate.getCurrentPosition() == 0) {
      //            pageListener.onPageSelected(0);
      //        }
    }
  
  
    private StateListDrawable getListDrable(@NonNull int... resId) {
      StateListDrawable listDrawable = new StateListDrawable();
      listDrawable.addState(new int[]{android.R.attr.state_checked}, ContextCompat.getDrawable(getContext(), resId[0]));
      listDrawable.addState(new int[]{android.R.attr.state_pressed}, ContextCompat.getDrawable(getContext(), resId[0]));
      listDrawable.addState(new int[]{}, ContextCompat.getDrawable(getContext(), resId[1]));
      return listDrawable;
    }
  
  
    private void addTab(final int position, View tab) {
      //        tab.setFocusable(true);
      tab.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          setCurrentPosition(position);
        }
      });
      tab.setPadding(mTabStyleDelegate.getTabPadding(), 0, mTabStyleDelegate.getTabPadding(), 0);
      tabsContainer.addView(tab, position, mTabStyleDelegate.isShouldExpand() ? expandedTabLayoutParams : defaultTabLayoutParams);
    }
  
    private void updateTabStyles() {
  
      for (int i = 0; i < mTabCount; i++) {
  
        View v = tabsContainer.getChildAt(i);
        if (v instanceof TextView) {
          TextView tab = (TextView) v;
          tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTabStyleDelegate.getTabTextSize());
          tab.setTypeface(mTabStyleDelegate.getTabTypeface(), mTabStyleDelegate.getTabTypefaceStyle());
          if (mTabStyleDelegate.getTabTextColorStateList() == null) {
            tab.setTextColor(mTabStyleDelegate.getTabTextColor());
          } else {
            tab.setTextColor(mTabStyleDelegate.getTabTextColorStateList());
          }
          // setAllCaps() is only available from API 14, so the upper case is made manually if we are on a
          // pre-ICS-build
          tab.setAllCaps(mTabStyleDelegate.isTextAllCaps());
        }
      }
      mJTabStyle.afterSetViewPager(tabsContainer);
    }
  
  
    private void scrollToChild(int position, int offset) {
      if (!mJTabStyle.needChildView()) {
        return;
      }
      if (mTabCount == 0) {
        return;
      }
      int newScrollX = tabsContainer.getChildAt(position).getLeft() + offset;
  
      if (position > 0 || offset > 0) {
        newScrollX -= scrollOffset;
      }
      if (newScrollX != lastScrollX) {
        lastScrollX = newScrollX;
        scrollTo(newScrollX, 0);
      }
    }
  
  
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
      super.onSizeChanged(w, h, oldw, oldh);
      scrollOffset = mTabStyleDelegate.getScrollOffset();
      if (!mJTabStyle.needChildView() || tabsContainer.getChildCount() > 0) {
        mJTabStyle.onSizeChanged(w, h, oldw, oldh);
      }
    }
  
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
      switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
          mLastEventY = ((int) ev.getY());
          startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
          break;
        case MotionEvent.ACTION_MOVE:
          int dy = (int) (mLastEventY-ev.getY());
          mLastEventY = (int) ev.getY();
          dispatchNestedPreScroll(0, dy, null, null, ViewCompat.TYPE_TOUCH);
          dispatchNestedScroll(0, dy, 0, 0, null, ViewCompat.TYPE_TOUCH);
          break;
        case MotionEvent.ACTION_UP:
          stopNestedScroll(ViewCompat.TYPE_TOUCH);
          break;
      }
      return super.onInterceptTouchEvent(ev);
    }
  
  
    @Override
    protected void onDraw(Canvas canvas) {
      super.onDraw(canvas);
  
      if (mJTabStyle.needChildView() && tabsContainer.getChildCount() == 0 || isInEditMode() || mTabCount == 0) {
        return;
      }
      mJTabStyle.onDraw(canvas, tabsContainer, currentPositionOffset, mLastCheckedPosition);
    }
  
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
      super.onLayout(changed, l, t, r, b);
      mJTabStyle.afterLayout();
    }
  
  
    @Keep
    public void bindTitles(String... titles) {
      bindTitles(0, titles);
    }
  
    /**
     * 设置title的icon必须在bindTitles之前调用
     */
    @Keep
    public JPagerSlidingTabStrip coifigTabIcons(@DrawableRes @Size(min = 1) int... iconIds) {
      mIconIds = iconIds;
      return this;
    }
  
    /**
     * 两张图片为一组[选中,默认]
     */
    @Keep
    public JPagerSlidingTabStrip coifigTabIcons(@DrawableRes @Size(min = 1) int[]... iconsIds) {
      mIconsIds = iconsIds;
      return this;
    }
  
    @Keep
    public void bindTitles(int current, String... titles) {
      if (titles.length == 1) {
        for (int i = 0; i < titles.length; i++) {
          addTextTab(i, titles[i]);
        }
      } else {
        if (titles.length == mIconsIds.length) {
          for (int i = 0; i < titles.length; i++) {
            addIconTab(i, titles[i], mIconsIds[i][0], mIconsIds[i][1]);
          }
        } else if (titles.length == mIconIds.length) {
          for (int i = 0; i < titles.length; i++) {
            addIconTab(i, titles[i], mIconIds[i]);
          }
        } else {
          for (int i = 0; i < titles.length; i++) {
            addTextTab(i, titles[i]);
          }
        }
      }
      setCurrentPosition(current);
      setTag(current);
      mTabCount = titles.length;
      updateTabStyles();
    }
  
    @Keep
    public void setCurrentPosition(int position) {
      setTag(position);
      if (mLastCheckedPosition == position || mValueAnimator.isRunning()) {
        if (mSamePositionClickListener != null) {
          mSamePositionClickListener.onClickTheSamePosition(mLastCheckedPosition);
          return;
        }
      }
      if (pager != null) {
        pager.setCurrentItem(position);
      } else {
        tabsContainer.setTag(true);
        mValueAnimator = ValueAnimator.ofFloat(1, 0);
        if (mLastCheckedPosition != -1 && mLastCheckedPosition < position) {
          tabsContainer.setTag(false);
          mValueAnimator = ValueAnimator.ofFloat(0, 1);
        }
        check(position);
        mValueAnimator.setDuration(400).setInterpolator(new DecelerateInterpolator());
        mValueAnimator.addUpdateListener(this);
        mValueAnimator.addListener(this);
        mValueAnimator.start();
      }
    }
  
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
      float positionOffset = (float) animation.getAnimatedValue();
      mTabStyleDelegate.setCurrentPosition(mLastCheckedPosition);
      currentPositionOffset = positionOffset;
      invalidate();
    }
  
    @Override
    public void onAnimationStart(Animator animation) {
      if (!((Boolean) tabsContainer.getTag())) {
        mLastCheckedPosition -= 1;
        mLastCheckedPosition = Math.max(mLastCheckedPosition, 0);
      }
    }
  
    @Override
    public void onAnimationEnd(Animator animation) {
      if (!((Boolean) tabsContainer.getTag())) {
        mLastCheckedPosition += 1;
        invalidate();
      }
    }
  
    @Override
    public void onAnimationCancel(Animator animation) {
  
    }
  
    @Override
    public void onAnimationRepeat(Animator animation) {
  
    }
  
    @Override
    public boolean startNestedScroll(int axes, int type) {
      return mScrollingChildHelper.startNestedScroll(axes, type);
    }
  
    @Override
    public void stopNestedScroll(int type) {
      mScrollingChildHelper.stopNestedScroll(type);
    }
  
    @Override
    public boolean hasNestedScrollingParent(int type) {
      return mScrollingChildHelper.hasNestedScrollingParent(type);
    }
  
    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow, int type) {
      return mScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow, type);
    }
  
    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed, @Nullable int[] offsetInWindow, int type) {
      return mScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type);
    }
  
    private class PageListener implements OnPageChangeListener {
  
      private int mSelectedPosition;
  
  
      @Override
      public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        mTabStyleDelegate.setCurrentPosition(position);
        currentPositionOffset = positionOffset;
        if (mLastCheckedPosition != mSelectedPosition) {
          check(mSelectedPosition);
        }
        if (mJTabStyle.needChildView()) {
          scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth()));
        }
        invalidate();
  
        if (delegatePageListener != null) {
          delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
        }
      }
  
  
      @Override
      public void onPageScrollStateChanged(int state) {
        //setCurrentItem触发 2--0
        //由手指滑动触发 1--2--0
        if (state == 1) {
          mJTabStyle.scrollSelected(true);
        }
        if (state == 2) {
          mJTabStyle.scrollSelected(mState == 1);//由手指滑动触发 1--2--0
        }
        mState = state;
        if (state == ViewPager.SCROLL_STATE_IDLE) {
          mJTabStyle.scrollSelected(false);
          scrollToChild(pager.getCurrentItem(), 0);
        }
  
        if (delegatePageListener != null) {
          delegatePageListener.onPageScrollStateChanged(state);
        }
      }
  
  
      @Override
      public void onPageSelected(int position) {
        mSelectedPosition = position;
        //check(position);
        if (delegatePageListener != null) {
          delegatePageListener.onPageSelected(position);
        }
      }
    }
  
  
    //单选
    private void check(int position) {
      if (mLastCheckedPosition != -1 && mJTabStyle.needChildView() && tabsContainer.getChildAt(mLastCheckedPosition) != null) {
        ((Checkable) tabsContainer.getChildAt(mLastCheckedPosition)).setChecked(false);
      }
      mLastLastCheckPosition = mLastCheckedPosition;
      mLastCheckedPosition = position;
      if (pager == null && delegatePageListener != null) {
        delegatePageListener.onPageSelected(position);
      }
      if (!mJTabStyle.needChildView()) {
        return;
      }
  
      if (tabsContainer.getChildAt(position) != null) {
        ((Checkable) tabsContainer.getChildAt(position)).setChecked(true);
      }
    }
  
  
    @Override
    public void onRestoreInstanceState(Parcelable state) {
      SavedState savedState = (SavedState) state;
      super.onRestoreInstanceState(savedState.getSuperState());
      mTabStyleDelegate.setCurrentPosition(savedState.currentPosition);
      requestLayout();
    }
  
  
    @Override
    public Parcelable onSaveInstanceState() {
      Parcelable superState = super.onSaveInstanceState();
      SavedState savedState = new SavedState(superState);
      savedState.currentPosition = mTabStyleDelegate.getCurrentPosition();
      return savedState;
    }
  
    @Keep
    public JTabStyleDelegate getTabStyleDelegate() {
      return mTabStyleDelegate;
    }
  
    @Keep
    public void setJTabStyle(JTabStyle JTabStyle) {
      mJTabStyle = JTabStyle;
    }
  
    @Keep
    public int getTabCount() {
      return mTabCount;
    }
  
  
    public int getState() {
      return mState;
    }
  
    @Keep
    public ViewGroup getTabsContainer() {
      return tabsContainer;
    }
  
    @Keep
    public ISlidingTabStrip setPromptNum(int index, int num) {
      if (index < tabsContainer.getChildCount()) {
        ((PromptView) tabsContainer.getChildAt(index)).setPromptNum(num);
      }
      return this;
    }
  
    boolean overScrollBy;
  
    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
      overScrollBy = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
      if (overScrollBy && mDamping != null) {
        mDamping.overScrollBy(deltaX);
      }
      return overScrollBy;
    }
  
    @Override
    protected void onAttachedToWindow() {
      super.onAttachedToWindow();
    }
  
  
    @Override
    protected void onDetachedFromWindow() {
      super.onDetachedFromWindow();
      mValueAnimator.cancel();
    }
  
    @Keep
    public JPagerSlidingTabStrip setOntheSamePositionClickListener(OntheSamePositionClickListener samePositionClickListener) {
      mSamePositionClickListener = samePositionClickListener;
      return this;
    }
  }
  