老规矩,先看效果图(虽然这次的有点糙,没关系,自己复制以下代码跑一边就知道了)
首先要做这种字体变大小的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;
}
}