引言:android提供的viewPager+TabLayout的虽然体验不错,但是tabLayout指示器默认只有横线指示器,想要其他效果 ,比较难。 本文主要内容:替换TabLayout,通过自定义ViewGroup 实现与tabLayout 一样的效果,并且支持三角形箭头,支持数据绑定。参考链接:https://www.imooc.com/article/14502
看效果:
具体实现及使用步骤:
一:自定义attr属性
二:自定义ViewGroup
三:代码使用
一,添加attr属性
<!--默认的显示的Tab数量-->
<attr name="default_display_counts" format="integer"/>
<!--高亮文本的颜色-->
<attr name="high_light_color" format="color"/>
<!--正常文本的颜色-->
<attr name="normal_text_color" format="color"/>
<!--图形的颜色-->
<attr name="graphics_color" format="color"/>
<!--文本的大小-->
<attr name="text_size" format="integer"/>
<!--图形的高度-->
<attr name="graphics_height" format="integer"/>
<!--图形的类型-->
<attr name="is_draw_line" format="boolean"/>
<declare-styleable name="ChangeablePagerIndicator">
<!--默认的显示的Tab数量-->
<attr name="default_display_counts"/>
<!--高亮文本的颜色-->
<attr name="high_light_color"/>
<!--正常文本的颜色-->
<attr name="normal_text_color"/>
<!--图形的颜色-->
<attr name="graphics_color"/>
<!--文本的大小-->
<attr name="text_size"/>
<!--图形的高度-->
<attr name="graphics_height"/>
<!--图形的类型-->
<attr name="is_draw_line"/>
</declare-styleable>
小白指引图
二:自定义LinearLayout ,之所以自定LinearLayout因为需要"水平排列字体", 当然其他的布局也可以。为了省事
public class ChangeablePagerIndicator extends LinearLayout {
//默认的高亮文本颜色:白色
public static final int COLOR_TEXT_HIGHLIGHT = Color.parseColor("#FFFFFFFF");
//默认的正常文本颜色:灰色
public static final int COLOR_TEXT_NORMAL = Color.parseColor("#77FFFFFF");
//默认绘制图形的颜色:白色
public static final int COLOR_GRAPHICS = Color.parseColor("#FFFFFFFF");
//默认Tab的文本大小 16sp
public static final int TAB_TEXT_SIZE = 16;
//直线的高度 10dp
public static final int LINE_HEIGHT = 10;
//最小的显示Tab的数量
public static final int DEFAULT_VISIBLE_COUNTS = 3;
//绘制的类型:1.直线 2.三角形
public static final int STYLE_LINE = 100;
public static final int STYLE_TRIANGLE = 200;
//三角形的比例
public static final float TRIANGLE_RADIO = 1 / 6F;
//绘制三角形的画笔
private Paint mTrianglePaint;
//绘制三角形的路径
private Path mTrianglePath;
//三角形的宽
private int mTriangleWidth;
//三角形的高
private int mTriangleHeight;
//绘制直线的画笔
private Paint mLinePaint;
//线的宽度
private int mLineWidth;
//初始位置时三角形的位置
private int mStartX;
//移动时三角形的位置
private int mMoveX;
//绘制的类型
private int mDrawStyle = STYLE_LINE;
//默认显示的tab item数量
private int mDefaultVisibleCounts;
//高亮文本的颜色
private int mHighLightColor;
//普通文本的颜色
private int mNormalTextColor;
//文本大小
private int mTextSize;
//线的高度(strokeWidth)
private int mLineHeight;
//直线或者三角形的颜色
private int mGraphicsColor;
public ChangeablePagerIndicator(Context context) {
this(context, null);
}
public ChangeablePagerIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
//获取自定义的值
getCustomValue(context, attrs);
//初始化三角形的画笔
initTrianglePaint();
//初始化直线的画笔
initLinePaint();
}
/**
* 加载完XML时,调用此方法
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
int coutns = getChildCount();
if (coutns == 0)
return;
for (int i = 0; i < coutns; i++) {
View view = getChildAt(i);
LinearLayout.LayoutParams params = (LayoutParams) view.getLayoutParams();
params.weight = 0;
params.width = getScreenWidth() / mDefaultVisibleCounts;
view.setLayoutParams(params);
}
//设置监听
setItemClickListener();
//直线的宽度
mLineWidth = getScreenWidth() / mDefaultVisibleCounts;
}
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
//创建三角形
createIndicatorTriangle();
}
@Override
protected void dispatchDraw(Canvas canvas) {
canvas.save();
if (mDrawStyle == STYLE_LINE) {
//绘制直线
canvas.drawLine(mMoveX, getHeight(), mMoveX + mLineWidth, getHeight(), mLinePaint);
}
if (mDrawStyle == STYLE_TRIANGLE) {
//绘制三角形
canvas.translate(mStartX + mMoveX, getHeight());
canvas.drawPath(mTrianglePath, mTrianglePaint);
}
canvas.restore();
super.dispatchDraw(canvas);
}
/**
* 获取自定义的值
*
* @param attrs
*/
private void getCustomValue(Context context, AttributeSet attrs) {
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.ChangeablePagerIndicator);
//获取默认显示的可见Tab的数量
mDefaultVisibleCounts = attributes.
getInt(R.styleable.ChangeablePagerIndicator_default_display_counts, DEFAULT_VISIBLE_COUNTS);
mDefaultVisibleCounts = Math.max(DEFAULT_VISIBLE_COUNTS, mDefaultVisibleCounts);
//高亮文本的颜色
mHighLightColor = attributes.getColor(R.styleable.ChangeablePagerIndicator_high_light_color, COLOR_TEXT_HIGHLIGHT);
//普通文本的颜色
mNormalTextColor = attributes.getColor(R.styleable.ChangeablePagerIndicator_normal_text_color, COLOR_TEXT_NORMAL);
//文本大小
mTextSize = attributes.getInt(R.styleable.ChangeablePagerIndicator_text_size, TAB_TEXT_SIZE);
//线的高度(strokeWidth)
mLineHeight = attributes.getInt(R.styleable.ChangeablePagerIndicator_graphics_height, LINE_HEIGHT);
//直线或者三角形的颜色
mGraphicsColor = attributes.getColor(R.styleable.ChangeablePagerIndicator_graphics_color, COLOR_GRAPHICS);
//绘制的类型
boolean drawStyle = attributes.getBoolean(R.styleable.ChangeablePagerIndicator_is_draw_line, true);
mDrawStyle = drawStyle ? STYLE_LINE : STYLE_TRIANGLE;
//直线的宽度
mLineWidth = getScreenWidth() / mDefaultVisibleCounts;
attributes.recycle();
}
/**
* 初始化三角形的画笔
*/
private void initTrianglePaint() {
mTrianglePaint = new Paint();
mTrianglePaint.setAntiAlias(true);
//三角形的画笔颜色
mTrianglePaint.setColor(mGraphicsColor);
mTrianglePaint.setStyle(Paint.Style.FILL);
//绘制角时的弧度
mTrianglePaint.setPathEffect(new CornerPathEffect(2));
}
/**
* 初始化直线的画笔
*/
private void initLinePaint() {
mLinePaint = new Paint(mTrianglePaint);
mLinePaint.setStrokeWidth(mLineHeight);
}
/**
* 创建指示器的三角形
*/
private void createIndicatorTriangle() {
int tabWidth = getScreenWidth() / mDefaultVisibleCounts;
//三角形的宽度
mTriangleWidth = (int) (tabWidth * TRIANGLE_RADIO);
//初始位置时三角形的位置
mStartX = (tabWidth - mTriangleWidth) / 2;
//三角形的高度
mTriangleHeight = mTriangleWidth / 3;
//绘制三三角形路径
mTrianglePath = new Path();
mTrianglePath.moveTo(0, 0);
mTrianglePath.lineTo(mTriangleWidth, 0);
mTrianglePath.lineTo(mTriangleWidth / 2, -mTriangleHeight);
mTrianglePath.close();
}
/**
* 水平滚动
*
* @param position
* @param offset
*/
public void scroll(int position, float offset) {
int tabWidth = getWidth() / mDefaultVisibleCounts;
//指示器移动的距离
mMoveX = (int) (tabWidth * (position + offset));
//移动Tab
if (position >= (mDefaultVisibleCounts - 2) && offset > 0
&& getChildCount() > mDefaultVisibleCounts
&& position != getChildCount() - 2) {
if (mDefaultVisibleCounts != 1) {
int scrollX = (position - (mDefaultVisibleCounts - 2)) * tabWidth
+ (int) (tabWidth * offset);
this.scrollTo(scrollX, 0);
} else {
this.scrollTo((position * tabWidth
+ (int) (tabWidth * offset)), 0);
}
}
//重新绘制
invalidate();
}
/**
* 设置绘制的类型
* 1.STYLE_LINE 直线
* 2.STYLE_TRIANGLE 三角形
*/
public void setDrawStyle(int style) {
mDrawStyle = style;
invalidate();
}
public void setDefaultVisibleCounts(int counts) {
mDefaultVisibleCounts = counts;
if (counts < DEFAULT_VISIBLE_COUNTS)
mDefaultVisibleCounts = DEFAULT_VISIBLE_COUNTS;
//直线的宽度
mLineWidth = getScreenWidth() / mDefaultVisibleCounts;
}
/**
* 不用加载布局文件,代码生成
*
* @param titles
*/
public void setTabItems(List<String> titles) {
if (titles == null)
return;
//首先移除所有的View
this.removeAllViews();
//根据标题生成TextView
for (String title : titles) {
this.addView(generateTitleView(title));
}
//设置监听
setItemClickListener();
}
/**
* 设置ViewPager
*
* @param viewPager
* @param currentPos 当前选的位置
*/
private ViewPager mViewPager;
public void setViewPager(ViewPager viewPager, int currentPos) {
mViewPager = viewPager;
viewPager.addOnPageChangeListener(mListener);
//设置当前选中项
viewPager.setCurrentItem(currentPos);
}
/**
* ViewPager监听
*/
private IndicatorListener mListener = new IndicatorListener() {
@Override
public void scrollChanged(int position, float offset) {
//进行Tab滚动
scroll(position, offset);
}
@Override
public void selected(int position) {
//设置高亮文本,当前ViewPager与Tab一一对应
setHighLightText(position);
}
};
/**
* Tab Item的点击监听
*/
private void setItemClickListener() {
for (int i = 0; i < getChildCount(); i++) {
//点击的位置
final int clickPosition = i;
View view = getChildAt(i);
if (view instanceof TextView) {
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
//ViewPager跳到对应的选中项
mViewPager.setCurrentItem(clickPosition);
}
});
}
}
}
/**
* 根据标题生成TextView
*
* @param title
* @return
*/
private View generateTitleView(String title) {
TextView tv = new TextView(getContext());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
params.width = getScreenWidth() / mDefaultVisibleCounts;
tv.setGravity(Gravity.CENTER);
tv.setText(title);
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSize);
tv.setTextColor(mNormalTextColor);
tv.setLayoutParams(params);
return tv;
}
/**
* 设置高亮显示的文本
* 也就是当前选中的文本
*
* @param position
*/
private void setHighLightText(int position) {
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
if (view instanceof TextView) {
//高亮颜色
if (i == position)
((TextView) view).setTextColor(mHighLightColor);
//正常颜色
else
((TextView) view).setTextColor(mNormalTextColor);
}
}
}
/**
* 获取屏幕的宽度
*
* @return
*/
private int getScreenWidth() {
WindowManager manager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(metrics);
return metrics.widthPixels;
}
}
三 xml布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--绑定字体颜色也可以设置,这里没有列出来-->
<com.xxxx.ChangeablePagerIndicator
android:id="@+id/ln_indicator"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="@dimen/dp35"
android:layout_marginBottom="@dimen/dp10"
app:default_display_counts="3"//默认绑定数据大小
app:is_draw_line="false"//是否需要绘制水平线,false就是绘制三角形
app:graphics_color="@color/white"//绘制线的颜色
android:background="@color/main_color"/>//背景颜色
<android.support.v4.view.ViewPager
android:id="@+id/vp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
四:代码使用如图:
懒得写的小伙伴直接粘贴
ChangeablePagerIndicator mlnIndicator=view.findViewById(R.id.ln_indicator);
mlnIndicator.setTabItems(mTitle);//mTitle ==List<String>(); 绑定数据源
mlnIndicator.setViewPager(mViewPager,1);//绑定viewPager 1代表当前指针位置
目前遇见的问题 :ChangeablePagerIndicator添加android:layout_marginRight=" " android:layout_marginLeft="",会导致字体位置不对称问题,所有尽量不要使用这两个属性,问题研究中!