Android 之自定义选中变大TabLayout

 

老规矩,先看效果图(虽然这次的有点糙,没关系,自己复制以下代码跑一边就知道了)

Android 之自定义选中变大TabLayout_第1张图片

 

思路

首先要做这种字体变大小的tablayout,改造谷歌提供的tablayout无法满足,别问我为什么知道。

那怎么办,继承viewgroup?将textview塞进去?这个不错,不过摆放就有点麻烦了。

于是我想起了我最熟悉的ConstraintLayout,没错,这样子静态的一下子就能搞出来,

而且只要控制点击文字,下标滑动到相对应位置即可。

 

代码(三步来)

1、先摆好view的位置,静态的先搞出来,在让其动即可需要懂得约束布局,不懂的可以先看看这边


  //添加文字
  private void addText(String title) {
        ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        TextView textView = new TextView(context);
        textView.setId(View.generateViewId());
        //如果是第一个的话先约束于父布局
        if (mViewList.size() == 0) {
            layoutParams.leftToLeft = LayoutParams.PARENT_ID;
        } else {
            layoutParams.leftToRight = mViewList.get(mViewList.size() - 1).getId();
        }
        layoutParams.leftMargin = mLeftMargin;
        layoutParams.topToTop = LayoutParams.PARENT_ID;
        layoutParams.bottomToBottom = LayoutParams.PARENT_ID;
        layoutParams.bottomMargin = mTopMargin;
        textView.setLayoutParams(layoutParams);
        textView.setText(title);
        textView.setTextColor(mDefaultColor);
        textView.setBackgroundResource(0);
        //textview默认边距,要处理掉。因为加动画会使边距加大
        textView.setPadding(mTopMargin, mTopMargin, mTopMargin, -(dpToPx(2)));
        textView.setLineSpacing(-1.0f, 1.0f);
        textView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                //点击事件后面写
            }
        });
        addView(textView);
        mViewList.add(textView);
    }




//添加下标指示器
private void addIndicator() {
    if (mViewList.size() == 0) {
        return;
    }
    mIndicatorView = new ImageView(context);
    ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(
            mIndicatorWidth, dpToPx(4));
    layoutParams.topToBottom = mViewList.get(0).getId();
    layoutParams.leftToLeft = mViewList.get(0).getId();
    layoutParams.rightToRight = mViewList.get(0).getId();
    layoutParams.topMargin = mTopMargin;
    mIndicatorView.setLayoutParams(layoutParams);
    mIndicatorView.setBackgroundResource(R.drawable.other_gradient_bg);
    addView(mIndicatorView);
}

这里通过循环将,添加tab标题集合,然后添加指示器。用约束布局很容易就摆出来了。

2、让指示器动起来

这里是上面textview的点击事件,判断点击是否重复(就是已经选中了然后在次点击选择,此情况是不作处理的),不是重复的话,则执行设置选中textview的颜色变换、指示器和文字的动画执行selectAnim(mCurrentPosition);

                 //如果点击同一个tab
                for (int i = 0; i < mViewList.size(); i++) {
                    if (v.getId() == mViewList.get(i).getId()) {
                        mCurrentPosition = i;
                        if (mLastPosition == i) {
                            return;
                        }
                    }
                }
                selectAnim(mCurrentPosition);
                //记录上一个tab的位置
                for (int i = 0; i < mViewList.size(); i++) {
                    mViewList.get(i).setTextColor(mDefaultColor);
                    if (v.getId() == mViewList.get(i).getId()) {
                        mLastPosition = i;
                        mViewList.get(i).setTextColor(Color.BLACK);
                    }
                }
             

动画又分三步走,选中的文字变大,上一次选中的文字则变小,还有就是指示器的精准滑动,这里先给伪代码

  private void selectAnim(int position) {
        //这次选中的tab变大的动画
        selectBigAnimText(position);

        //指示器滑动的距离和动画
        selectIndicator();


        //上一次选中的tab变小的动画
        selectSamllAnimText();
    }

最难的是指示器的精准滑动,也是让我思考许久的一个难点吧

ssss----hh----aa----kkk

没有图,大家凑合着看上面的吧,如果是从滑动到第四个k这里,指示器要动的距离是:h和a的宽度+三个-----的宽度

因为指示器永远都是在文字的中间下面,则还要计算文字和指示器的绝对值的一半。也就是

s和指示器的宽度差除以2+k和指示器的宽度差除以2,还有一个距离就是指示器本身的宽度

大家可以自己画图加深理解一下

private void selectIndicator() {
        int offset = 0;
        for (int i = 1; i < mCurrentPosition; i++) {
            offset += mViewList.get(i).getWidth();
        }
        if (mCurrentPosition==0){
            offset += 0;
        }else{
            offset += Math.abs(mViewList.get(mCurrentPosition).getWidth() - mIndicatorWidth) / 2 +
                    Math.abs(mViewList.get(0).getWidth() - mIndicatorWidth) / 2 +
                    mLeftMargin * mCurrentPosition
                    + mIndicatorWidth;
        }
        ObjectAnimator translation = ObjectAnimator.ofFloat(mIndicatorView, "translationX", offset);
        translation.start();
    }

 

最后放出全部代码,当然还有挺多细节还没完善,有兴趣可以加入自定义熟悉,和一些改造,记得在评论放链接就好了

//这是用法
ScaleTabLayout mTabLayout=(ScaleTabLayout)findViewById(R.id.trends_tab_layout);

mTabLayout.setTitleList(list);//添加tab标题集合
mTabLayout.setDefaultSelectPosition(0);
mTabLayout.addOnScaleTabSelectedListener(new ScaleTabLayout.OnScaleTabSelectedListener() {
            @Override
            public void onScaleTabSelected(int lastPosition, int currentPosition) {
                //这个回调可以和viewpage联动
                viewPager.setCurrentItem(currentPosition, true);
            }
        });

 

import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Color;
import android.support.constraint.ConstraintLayout;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class ScaleTabLayout extends ConstraintLayout {

    private ImageView mIndicatorView;

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

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

    public ScaleTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        mDefaultColor=context.getResources().getColor(R.color.editPersonTextColorSecond);
    }

    private Context context;
    private ArrayList mViewList = new ArrayList<>();
    private List mTitleList;
    private int mLastPosition = 0;
    private int mCurrentPosition = 0;
    private int mLeftMargin = dpToPx(15);
    private int mIndicatorWidth = dpToPx(30);
    private int mTopMargin = dpToPx(10);
    private int mDefaultColor;

    private void addIndicator() {
        if (mViewList.size() == 0) {
            return;
        }
        mIndicatorView = new ImageView(context);
        ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(
                mIndicatorWidth, dpToPx(4));
        layoutParams.topToBottom = mViewList.get(0).getId();
        layoutParams.leftToLeft = mViewList.get(0).getId();
        layoutParams.rightToRight = mViewList.get(0).getId();
        layoutParams.topMargin = mTopMargin;
        mIndicatorView.setLayoutParams(layoutParams);
        mIndicatorView.setBackgroundResource(R.drawable.other_gradient_bg);
        addView(mIndicatorView);

        if (mCurrentPosition == 0) {
            return;
        }
        mIndicatorView.postDelayed(new Runnable() {
            @Override
            public void run() {
                selectIndicator();
            }
        }, 200);


    }

    private void addText(String title) {
        ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        TextView textView = new TextView(context);
        textView.setId(View.generateViewId());
        if (mViewList.size() == 0) {
            layoutParams.leftToLeft = LayoutParams.PARENT_ID;
        } else {
            layoutParams.leftToRight = mViewList.get(mViewList.size() - 1).getId();
        }
        layoutParams.leftMargin = mLeftMargin;
        layoutParams.topToTop = LayoutParams.PARENT_ID;
        layoutParams.bottomToBottom = LayoutParams.PARENT_ID;
        layoutParams.bottomMargin = mTopMargin;
        textView.setLayoutParams(layoutParams);
        textView.setText(title);
        textView.setTextColor(mDefaultColor);
        textView.setBackgroundResource(0);
        textView.setPadding(mTopMargin, mTopMargin, mTopMargin, -(dpToPx(2)));
        textView.setLineSpacing(-1.0f, 1.0f);
        textView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                //如果点击同一个tab
                for (int i = 0; i < mViewList.size(); i++) {
                    if (v.getId() == mViewList.get(i).getId()) {
                        mCurrentPosition = i;
                        if (mLastPosition == i) {
                            return;
                        }
                    }
                }
                selectAnim(mCurrentPosition);
                //记录上一个tab的位置
                for (int i = 0; i < mViewList.size(); i++) {
                    mViewList.get(i).setTextColor(mDefaultColor);
                    if (v.getId() == mViewList.get(i).getId()) {
                        mLastPosition = i;
                        mViewList.get(i).setTextColor(Color.BLACK);
                    }
                }
                if (mListener != null) {
                    mListener.onScaleTabSelected(mLastPosition, mCurrentPosition);
                }
            }
        });
        addView(textView);
        mViewList.add(textView);

        if (mViewList.size() - 1 == mCurrentPosition) {
            textView.postDelayed(new Runnable() {
                @Override
                public void run() {
                    selectBigAnimText(mCurrentPosition);
                }
            }, 200);
        }
    }


    public void setDefaultSelectPosition(int position) {
        if (position >= mTitleList.size()) {
            return;
        }
//        mDefaultPosition = position;
        mCurrentPosition = position;
        mLastPosition = position;
        for (int i = 0; i < mTitleList.size(); i++) {
            addText(mTitleList.get(i));
        }
        addIndicator();
    }

    //如果不是通过点击选中tab,而是viewpager那些选的,坐这里
    public void selectPosition(int position) {
        if (position >= mTitleList.size()||position==mCurrentPosition) {
            return;
        }
        mLastPosition = mCurrentPosition;
        mCurrentPosition = position;
        selectAnim(position);
        mLastPosition = position;
        for (int i = 0; i < mViewList.size(); i++) {
            mViewList.get(i).setTextColor(mDefaultColor);
            if (i==mLastPosition){
                mViewList.get(i).setTextColor(Color.BLACK);
            }
        }
    }


    private void selectAnim(int position) {
        //这次选中的tab变大的动画
        selectBigAnimText(position);

        //指示器滑动的距离和动画
        selectIndicator();


        //上一次选中的tab变小的动画
        TextView lastText = mViewList.get(mLastPosition);
        AnimatorSet lastAnimatorSet = new AnimatorSet();  //组合动画
        ObjectAnimator lastScaleX = ObjectAnimator.ofFloat(lastText, "scaleX", 1.6f, 1.0f);
        ObjectAnimator lastScaleY = ObjectAnimator.ofFloat(lastText, "scaleY", 1.6f, 1.0f);
        lastAnimatorSet.setDuration(200);  //动画时间
        lastAnimatorSet.setInterpolator(new DecelerateInterpolator());  //设置插值器
        lastAnimatorSet.play(lastScaleX).with(lastScaleY);  //同时执行
        lastAnimatorSet.start();  //启动动画
    }

    private void selectIndicator() {
        int offset = 0;
        for (int i = 1; i < mCurrentPosition; i++) {
            offset += mViewList.get(i).getWidth();
        }
        if (mCurrentPosition==0){
            offset += 0;
        }else{
            offset += Math.abs(mViewList.get(mCurrentPosition).getWidth() - mIndicatorWidth) / 2 +
                    Math.abs(mViewList.get(0).getWidth() - mIndicatorWidth) / 2 +
                    mLeftMargin * mCurrentPosition
                    + mIndicatorWidth;
        }
        ObjectAnimator translation = ObjectAnimator.ofFloat(mIndicatorView, "translationX", offset);
        translation.start();
    }


    private void selectBigAnimText(int position) {
        mViewList.get(position).setPivotX(mViewList.get(position).getWidth() / 2);
        mViewList.get(position).setPivotY(mViewList.get(position).getHeight());
        AnimatorSet animatorSet = new AnimatorSet();  //组合动画
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(mViewList.get(position), "scaleX", 1.0f, 1.6f);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(mViewList.get(position), "scaleY", 1.0f, 1.6f);
        animatorSet.setDuration(200);  //动画时间
        animatorSet.setInterpolator(new DecelerateInterpolator());  //设置插值器
        animatorSet.play(scaleX).with(scaleY);  //同时执行
        animatorSet.start();  //启动动画
    }

    int dpToPx(int dps) {
        return Math.round(getResources().getDisplayMetrics().density * dps);
    }


    public interface OnScaleTabSelectedListener {
        public void onScaleTabSelected(int lastPosition, int currentPosition);
    }

    private OnScaleTabSelectedListener mListener;

    public void addOnScaleTabSelectedListener(OnScaleTabSelectedListener listener) {
        this.mListener = listener;
    }

    public void setTitleList(List titleList) {
        mTitleList = titleList;
    }

}

 

你可能感兴趣的:(Android 之自定义选中变大TabLayout)