自定义Indicator 脑洞打开的指示器

时间:2021-07-27 04:50:43
我们的APP中,对ViewPage + Fragment 应用比较多
毕竟是花样展示
这里就设计到ViewPager的指示器的运用了
Android本身带有PagerTitleStrip,但原生的并不一定符合我们的审美
也见过太多自定义的Indicator,大都在控件外绘制
到了这里,大家有没有考虑到,如果我们把这个绘制指示器的功能一并放在我们的自定义控件了呢
这样是不是代码简化了好多

这里带着大家试试

1.首先,自定义控件属性

<resources>

<declare-styleable name="Indicator">
<attr name="color" format="color|reference" />
</declare-styleable>

</resources>

2. 编写我们的自定义控件,这里我是继承LinearLayout ,测量绘制大大简化,都教给系统了

MyIndicator.class

/**
* @author ruyi
* @version 创建时间:2016年5月26日 下午5:55:14
*
*/
public class MyIndicator extends LinearLayout {
private Paint mPaint;

private int mTop;
private int mLeft;
private int mWidth;
private int mHeight = 5;
private int mColor;
private int mChildCount;


public MyIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(LinearLayout.HORIZONTAL);
setBackgroundColor(Color.TRANSPARENT); // 必须设置背景,否则onDraw不执行

TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.Indicator, 0, 0);
mColor = a.getColor(R.styleable.Indicator_color, 0X0000FF);
a.recycle();

mPaint = new Paint();
mPaint.setColor(mColor);
mPaint.setAntiAlias(true);
}

/**
* 布局加载结束 ,获取childView 个数
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mChildCount = getChildCount();
}

/**
* 测量控件宽高, 在本来的高度上在加上指示器条的高度
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mTop = getMeasuredHeight();
int width = getMeasuredWidth();
int height = mTop + mHeight;
mWidth = width / mChildCount; //条目4等分

setMeasuredDimension(width, height);
}

public void scroll(int position, float offset) {
mLeft = (int) ((position + offset) * mWidth);
invalidate();
}

/**
* 除绘制组件外,额外绘制指示器(一个矩形)
*/
@Override
protected void onDraw(Canvas canvas) {
Rect rect = new Rect(mLeft, mTop, mLeft + mWidth, mTop + mHeight);
canvas.drawRect(rect, mPaint);
super.onDraw(canvas);
}
}
这里指示器高度我们固定为5,宽度为屏幕宽度的1/4

在构造方法中初始化属性

在onFinishInflate(),中得到ChildView个数

重写onMeasure()方法,设置自定义控件为 在本来的高度上在加上指示器条的高度

在onDraw()方法里绘制指示器

当然必须提供一个方法来供外界改变指示器的位置方法,来达到动态滑动即 scroll(),设置后再调用重绘

我先贴下布局代码和调用方法,在解释原理

xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ruiyi="http://schemas.android.com/apk/res/com.ruiyi.testtab"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<com.ruiyi.testtab.MyIndicator
android:id="@+id/indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dip"
android:paddingTop="10dip"
android:weightSum="4"
ruiyi:color="#FFD02090" >

<TextView
android:id="@+id/tab_one"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:text="小学" />

<TextView
android:id="@+id/tab_two"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:text="初中" />

<TextView
android:id="@+id/tab_three"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:text="高中" />

<TextView
android:id="@+id/tab_four"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:text="大学" />
</com.ruiyi.testtab.MyIndicator>

<android.support.v4.view.ViewPager
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

</LinearLayout>
设置总权重weightSun = 4 , 每个子View的权重为1

自定义属性赋值,记得命名空间的限制

调用 Activity

mViewpager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
Log.i("ASDFGHJKL", "11111111------"+position);//TODO
}

@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
mIndicator.scroll(position, positionOffset);
}

@Override
public void onPageScrollStateChanged(int position) {
}
});

我简单解释下

指示器矩形框定义mTop, mLeft, mWidth, mHeight = 5;

1)宽度与子View宽度一致

自定义Indicator 脑洞打开的指示器

onMeasure() 中
mLeft = 0;
mTop = getMeasuredHeight();   -----测量控件最原先的高,即上面存在的Title 的高
mWidth = getMeasuredWidth() / mChildCount;  ------宽为屏幕宽度除以子View数量
mHeight = 5;  -----高度固定为5

最初绘制时,指示器得位置为上述值,布局加载结束后draw了一个与子View宽度一致的指示器
当ViewPager滑动时,mLeft = (int) ((position + offset) * mWidth);

2)当设置指示器宽度为子View宽度的一半时

自定义Indicator 脑洞打开的指示器

onMeasure() 中
mLeft = (getMeasuredWidth() / mChildCount)/4;
mTop = getMeasuredHeight();   -----测量控件最原先的高,即上面存在的Title 的高
mWidth = (getMeasuredWidth() / mChildCount)/2;  ------宽为屏幕宽度除以子View数量
mHeight = 5;  -----高度固定为5
最初绘制时,指示器得位置为上述值,布局加载结束后draw了一个与子View宽度一致的指示器(mLeft = 屏幕宽度/16)
当ViewPager滑动时,mLeft = (int)(m/4+(position+offset)*m);

(int position, float offset) 这里的position, offset为ViewPager 滑动监听传递过来的值


嗯,就这样