前言
系统自带的Tablayout用的也不错但是有些功能还不能满足我们这边开发,所以我这边自定义了一个tablayout提供了自定义tab线的长度以及,移动速度,以及禁止某个滑动(tablayout基本功能也提供了)
效果图
实现步骤
- 构造方法添加子控件
添加一些xml定义的属性
public MyIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.setGravity(Gravity.CENTER_VERTICAL);
this.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.Indicator, 0, 0);
mColor = array.getColor(R.styleable.Indicator_indicatorColor, Color.RED);
mList = getContext().getResources().getStringArray(array.getResourceId(R.styleable.Indicator_array, 0));
mTextNomal = array.getInteger(R.styleable.Indicator_text_nomal_size, 12);
mTextPress = array.getInteger(R.styleable.Indicator_text_press_size, 13);
mText_Nomal = array.getColor(R.styleable.Indicator_text_nomal_color, Color.GRAY);
mText_Press = array.getColor(R.styleable.Indicator_text_press_color, Color.BLACK);
mSelected = array.getInteger(R.styleable.Indicator_selected, 0);
isFull = array.getBoolean(R.styleable.Indicator_isFull, false);
mAnimationTime = array.getInteger(R.styleable.Indicator_speed, 300);
mBai = array.getFloat(R.styleable.Indicator_multiply, (float) 1.2);
mHeight = array.getInteger(R.styleable.Indicator_line_hegith, 5);
for (int i = 0; i < mList.length; i++) {
mListTitle.add(mList[i]);
}
array.recycle();
}
- onLayout初始化布局
添加控件
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (!mIsCheck) {
mIsCheck = true;
initView();
}
}
添加textview以及线
/**
* 初始化布局
*/
private void initView() {
measure(0, 0);
//获取每个textview布局所占的宽度
mContWidth = getWidth() / mListTitle.size();
//添加线
mLine = new View(getContext());
addView(mLine);
int mWeight;
if (isFull) {
mWeight = mContWidth;
} else {
mWeight = (int) (setLineLength(mListTitle.get(mSelected)) * mBai);
if (mWeight > mContWidth) {
mWeight = mContWidth;
}
}
//设置线的基本属性
LayoutParams mLayoutParams1 = new LayoutParams(mWeight, mHeight);
mLayoutParams1.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
mLine.setLayoutParams(mLayoutParams1);
mLine.setBackgroundColor(mColor);
//获取上一次所在位置
mEndAddress = mContWidth * mSelected + (mContWidth - mWeight) / 2;
//添加textview
for (int i = 0; i < mListTitle.size(); i++) {
addTextView(i);
}
//初始化点击事件
setListener();
}
/**
* 添加textview
*/
private void addTextView(int i) {
//初始化textview
TextView textView = new TextView(getContext());
textView.setText(mListTitle.get(i));
//设置textview基本属性
LayoutParams mLayoutParams = new LayoutParams(mContWidth, LayoutParams.MATCH_PARENT);
mLayoutParams.leftMargin = mContWidth * i;
mLayoutParams.addRule(CENTER_VERTICAL);
textView.setLayoutParams(mLayoutParams);
textView.setGravity(Gravity.CENTER);
if (i == mSelected) {
textView.setTextColor(mText_Press);
textView.setTextSize(mTextPress);
} else {
textView.setTextColor(mText_Nomal);
textView.setTextSize(mTextNomal);
}
//添加textview到布局
mTextList.add(textView);
addView(textView);
}
- 动画
动画就简单了直接一个移动动画就好了
/**
* 动画
*
* @param statX 开始位置
* @param endX 结束位置
*/
private void setAnimation(int statX, int endX) {
AnimationSet mSet = new AnimationSet(true);
TranslateAnimation translate1 = new TranslateAnimation(
statX, endX, 0, 0);
mSet.addAnimation(translate1);
mSet.setFillAfter(true);
mSet.setDuration(mAnimationTime);
mLine.startAnimation(mSet);
}
- 修改下标位置
计算修改下标的位置
/**
* 改变下标
*
* @param position
*/
public void setChanger(int position) {
//改为默认字体颜色修改选中字体颜色
resetColor();
mTextList.get(position).setTextColor(mText_Press);
mTextList.get(position).setTextSize(mTextPress);
mSelected = position;
int mWeight;
if (isFull) {
mWeight = mContWidth;
} else {
mWeight = (int) (setLineLength(mListTitle.get(mSelected)) * mBai);
if (mWeight > mContWidth) {
mWeight = mContWidth;
}
}
/**
* 计算当前选择textview的X点位置
*/
int way = mContWidth * mSelected + (mContWidth - mWeight) / 2;
LayoutParams mLayoutParams = new LayoutParams(mWeight, mHeight);
mLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
mLine.setLayoutParams(mLayoutParams);
setAnimation(mEndAddress, way);
mEndAddress = way;
this.mNowPosition = position;
}
- XML部分属性
线的颜色 : indicatorColor (默认red)
textview显示数组 :array
字体默认颜色:text_nomal_color (默认gray)
字体选中颜色:text_press_color (默认black)
字体默认大小:text_press_color (默认12)
字体选中大小:text_press_size (默认13)
默认选中:selected (默认0)
线是否铺满:isFull (默认false)
移动速度:speed (默认300)
线是字体的倍数:multiply (默认1.2)
线的高度: multiply (默认5)
<declare-styleable name="Indicator">
<attr name="indicatorColor" format="color"/>
<attr name="array" format="integer"/>
<attr name="text_nomal_color" format="color"/>
<attr name="text_press_color" format="color"/>
<attr name="text_nomal_size" format="integer"/>
<attr name="text_press_size" format="integer"/>
<attr name="selected" format="integer"/>
<attr name="isFull" format="boolean"/>
<attr name="speed" format="integer"/>
<attr name="multiply" format="float"/>
<attr name="line_hegith" format="integer"/>
</declare-styleable>
自定义控件代码献上
- JAVA代码
/**
* Created by huangbo on 17/1/22.
* 指示器
*/
public class MyIndicator extends RelativeLayout {
/**
* 线的颜色
*/
private int mColor;
/**
* 线的高度
*/
private int mHeight;
/**
* xml数组
*/
private String[] mList;
/**
* textview数组
*/
private List<TextView> mTextList = new ArrayList<>();
/**
* string数组
*/
private List<String> mListTitle = new ArrayList<>();
/**
* 默认字体大小
*/
private int mTextNomal;
/**
* 被选择字体大小
*/
private int mTextPress;
/**
* 默认字体颜色
*/
private int mText_Nomal;
/**
* 被选择的颜色
*/
private int mText_Press;
/**
* 每个格子个长度
*/
private int mContWidth;
/**
* 被选择的tab
*/
private int mSelected;
/**
* 底线
*/
private View mLine;
/**
* 是否或去过
*/
private boolean mIsCheck;
/**
* 字体的多少倍
*/
private float mBai;
/**
* 记录移动结束位置
*/
private int mEndAddress;
/**
* 禁止滑动表情下标
*/
private int mProhibitPisition = -1;
/**
* 动画时间
*/
private int mAnimationTime;
/**
* 当前选中
*/
private int mNowPosition;
/**
* 是否铺满
*/
private boolean isFull;
public MyIndicator(Context context) {
this(context, null);
}
public MyIndicator(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.setGravity(Gravity.CENTER_VERTICAL);
this.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.Indicator, 0, 0);
mColor = array.getColor(R.styleable.Indicator_indicatorColor, Color.RED);
mList = getContext().getResources().getStringArray(array.getResourceId(R.styleable.Indicator_array, 0));
mTextNomal = array.getInteger(R.styleable.Indicator_text_nomal_size, 12);
mTextPress = array.getInteger(R.styleable.Indicator_text_press_size, 13);
mText_Nomal = array.getColor(R.styleable.Indicator_text_nomal_color, Color.GRAY);
mText_Press = array.getColor(R.styleable.Indicator_text_press_color, Color.BLACK);
mSelected = array.getInteger(R.styleable.Indicator_selected, 0);
isFull = array.getBoolean(R.styleable.Indicator_isFull, false);
mAnimationTime = array.getInteger(R.styleable.Indicator_speed, 300);
mBai = array.getFloat(R.styleable.Indicator_multiply, (float) 1.2);
mHeight = array.getInteger(R.styleable.Indicator_line_hegith, 5);
for (int i = 0; i < mList.length; i++) {
mListTitle.add(mList[i]);
}
array.recycle();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (!mIsCheck) {
mIsCheck = true;
initView();
}
}
/**
* 初始化布局
*/
private void initView() {
measure(0, 0);
//获取每个textview布局所占的宽度
mContWidth = getWidth() / mListTitle.size();
//添加线
mLine = new View(getContext());
addView(mLine);
int mWeight;
if (isFull) {
mWeight = mContWidth;
} else {
mWeight = (int) (setLineLength(mListTitle.get(mSelected)) * mBai);
if (mWeight > mContWidth) {
mWeight = mContWidth;
}
}
//设置线的基本属性
LayoutParams mLayoutParams1 = new LayoutParams(mWeight, mHeight);
mLayoutParams1.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
mLine.setLayoutParams(mLayoutParams1);
mLine.setBackgroundColor(mColor);
//获取上一次所在位置
mEndAddress = mContWidth * mSelected + (mContWidth - mWeight) / 2;
//添加textview
for (int i = 0; i < mListTitle.size(); i++) {
addTextView(i);
}
//初始化点击事件
setListener();
}
/**
* 添加textview
*/
private void addTextView(int i) {
//初始化textview
TextView textView = new TextView(getContext());
textView.setText(mListTitle.get(i));
//设置textview基本属性
LayoutParams mLayoutParams = new LayoutParams(mContWidth, LayoutParams.MATCH_PARENT);
mLayoutParams.leftMargin = mContWidth * i;
mLayoutParams.addRule(CENTER_VERTICAL);
textView.setLayoutParams(mLayoutParams);
textView.setGravity(Gravity.CENTER);
if (i == mSelected) {
textView.setTextColor(mText_Press);
textView.setTextSize(mTextPress);
} else {
textView.setTextColor(mText_Nomal);
textView.setTextSize(mTextNomal);
}
//添加textview到布局
mTextList.add(textView);
addView(textView);
}
/**
* 清空字体颜色
*/
private void resetColor() {
for (int i = 0; i < mTextList.size(); i++) {
mTextList.get(i).setTextColor(getContext().getResources().getColor(R.color.text_hint));
}
}
/**
* 点击tab事件
*/
private void setListener() {
setAnimation(0, mEndAddress);
for (int i = 0; i < mTextList.size(); i++) {
final int finalI = i;
mTextList.get(i).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (finalI != mSelected && mProhibitPisition != finalI) {
setChanger(finalI);
}
if (mOnIndiacatorClickListener != null)
mOnIndiacatorClickListener.onClick(finalI, v);
}
});
}
}
/**
* 动画
*
* @param statX 开始位置
* @param endX 结束位置
*/
private void setAnimation(int statX, int endX) {
AnimationSet mSet = new AnimationSet(true);
TranslateAnimation translate1 = new TranslateAnimation(
statX, endX, 0, 0);
mSet.addAnimation(translate1);
mSet.setFillAfter(true);
mSet.setDuration(mAnimationTime);
mLine.startAnimation(mSet);
}
private OnIndiacatorClickListener mOnIndiacatorClickListener;
/**
* 计算下划线长度
*/
private int setLineLength(String mTextView) {
return ConvertUtils.dp2px(mTextView.length() * mTextPress);
}
/**
* 添加页面
*
* @param charSequence
*/
public void add(CharSequence charSequence, int position) {
removeAllViews();
mTextList.clear();
if (mListTitle.size() == position)
mListTitle.set(position - 1, charSequence.toString());
else
mListTitle.add(charSequence.toString());
initView();
}
/**
* 设置是否铺满
*/
public void setFull() {
isFull = true;
}
/**
* 设置监听
*
* @param onIndiacatorClickListener
*/
public void setIndiacatorListener(OnIndiacatorClickListener onIndiacatorClickListener) {
if (onIndiacatorClickListener != null) {
this.mOnIndiacatorClickListener = onIndiacatorClickListener;
}
}
/**
* 设置禁止滑动页面
*
* @param i
*/
public void setProhibitPositio(int i) {
this.mProhibitPisition = i;
}
/**
* 设置动画时间
*/
public void setAnimationTime(int time) {
this.mAnimationTime = time;
}
/**
* 外部监听
*/
public interface OnIndiacatorClickListener {
void onClick(int position, View view);
}
/**
* 改变下标
*
* @param position
*/
public void setChanger(int position) {
//改为默认字体颜色修改选中字体颜色
resetColor();
mTextList.get(position).setTextColor(mText_Press);
mTextList.get(position).setTextSize(mTextPress);
mSelected = position;
int mWeight;
if (isFull) {
mWeight = mContWidth;
} else {
mWeight = (int) (setLineLength(mListTitle.get(mSelected)) * mBai);
if (mWeight > mContWidth) {
mWeight = mContWidth;
}
}
/**
* 计算当前选择textview的X点位置
*/
int way = mContWidth * mSelected + (mContWidth - mWeight) / 2;
LayoutParams mLayoutParams = new LayoutParams(mWeight, mHeight);
mLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
mLine.setLayoutParams(mLayoutParams);
setAnimation(mEndAddress, way);
mEndAddress = way;
this.mNowPosition = position;
}
/**
* 获取当前下标
*/
public int getPosition() {
return mNowPosition;
}
}
- XML基本使用代码
<demo.com.androiddemo.MyIndicator
android:id="@+id/my"
android:layout_width="match_parent"
android:layout_height="50dp"
app:array="@array/tabs"
app:indicatorColor="@color/btn_red"
app:selected="0"
app:text_nomal_color="@color/text_hint"
app:text_nomal_size="12"
app:text_press_color="@color/text_title"
app:text_press_size="13"
>
</demo.com.androiddemo.MyIndicator>
结语
客官看完肯定觉得非常简单,每个人的思路不一样实现的方式也有很多,如果觉得我的实现方式有问题或者好的实现方式可以给我留言,当然如果有什么特殊需求的话也可以留言给我,我帮你们提供相关需求,以后还会继续更新相关博客希望各位给一个支持
地址
项目github地址:GitHub
依赖
compile 'com.github.q1104133609:MyTabLayout:v0.0.1'