先看示例效果图:
自定义View的核心为上半部分的指示器,该需求为的是实现等级列表的展示:每个ViewPager指向一个等级,当滑动到一个ViewPager时其指示的圆点变大,用户所达到的等级用黄色的线画圆点,未达到的等级则用白色来表示。
下面考虑具体实现:指示器布局包含在一个宽度很长的线性布局中,然后就是画所有的线和圆点(注意线长度最好根据屏幕宽度和布局左边的margin综合计算得到,以保证除第二个圆点在屏幕水平居中的位置),接着根据传入的数据重新画选中部分的黄色的线和圆点以及对应的文字,最后根据当前选中的ViewPager加粗对应的圆点。详细代码如下:
@Override
protected void onDraw(Canvas canvas) {
//底线
rectF.left = startX;
rectF.top = startY - lineHeight / 2;
rectF.right = startX + lineLength * (number - 1);
rectF.bottom = startY + lineHeight / 2;
mPaint.setColor(lineDefColor);
canvas.drawRect(rectF, mPaint);
//选中线
if(curPage - curCircle == 1){ //展示最后的不满一级的进度时以去掉两端半圆的中间矩形的长度为进度条的总长度计算
rectF.right = startX + lineLength * curCircle + circleRadius + (lineLength - circleRadius * (MULTIPLE_RADIUS + 1)) * curPercent;
}else {
rectF.right = startX + lineLength * curCircle + circleRadius + (lineLength - circleRadius * 2) * curPercent;
}
mPaint.setColor(lineSelColor);
canvas.drawRect(rectF, mPaint);
//画圆点和圆点下方的字
mFirstTextPaint.setColor(firstTextColor);
mSecondTextPaint.setColor(secondTextColor);
for(int i = 0; i < number; i++){
if(i <= curCircle){ //达到等级的圆点
mPaint.setColor(circleSelColor);
canvas.drawCircle(startX + i * lineLength, startY, circleRadius, mPaint);
}else { //未达到等级的圆点
mPaint.setColor(circleDefColor);
canvas.drawCircle(startX + i * lineLength, startY, circleRadius, mPaint);
}
//字
//first Text
textRectF.left = startX + i * lineLength - 2 * circleDiameter;
textRectF.top = startY + circleDiameter;
textRectF.right = startX + i * lineLength - 2 * circleDiameter + textLength;
textRectF.bottom = startY + circleDiameter + circleRadius;
canvas.drawRect(textRectF, mTranPaint);
mFirstTextPaint.setTextSize(firstTextSize);
if(firstStrings != null && i < firstStrings.length){
//坐标:左下角
canvas.drawText(firstStrings[i], textRectF.left, textRectF.bottom, mFirstTextPaint);
}
//second Text
textRectF.left = startX + i * lineLength - circleDiameter - circleRadius * 1.1f;
textRectF.top = startY + 2 * circleDiameter;
textRectF.right = startX + i * lineLength - circleDiameter - circleRadius * 1.1f + textLength;
textRectF.bottom = startY + 2 * circleDiameter + circleRadius;
canvas.drawRect(textRectF, mTranPaint);
mSecondTextPaint.setTextSize(secondTextSize);
if (secondStrings != null && i < secondStrings.length) {
//坐标:左下角
canvas.drawText(secondStrings[i], textRectF.left, textRectF.bottom, mSecondTextPaint);
}
if(i == curPage){
//选中圆点-->加大
if(curPage > curCircle){
mPaint.setColor(circleDefColor);
}else{
mPaint.setColor(circleSelColor);
}
canvas.drawCircle(startX + i * lineLength, startY, circleRadius * MULTIPLE_RADIUS, mPaint);
}
}
}
ViewPager用线性布局包含,注意两者都需要声明 android:clipChildren的属性为false,以允许ViewPager超出父控件来展示,布局代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
android:orientation="vertical"
tools:context="com.sty.viewpager.tab.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:clipChildren="false">
<LinearLayout
android:id="@+id/ll_level_bar"
android:layout_width="10000dp"
android:layout_height="0dp"
android:layout_weight="1.3"
android:orientation="vertical">
<com.sty.viewpager.tab.view.CustomTabSliding
android:id="@+id/cts_level_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:circleDefColor="@android:color/white"
app:circleSelColor="#FFDF25"
app:lineDefColor="#89D19B"
app:lineSelColor="#FFDF25"
app:firstTextColor="#FFDF25"
app:secondTextColor="@android:color/white"
app:firstTextSize="12sp"
app:secondTextSize="12sp"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="6"
android:orientation="vertical"
android:clipChildren="false">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="32dp"
android:layout_marginRight="32dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:clipChildren="false" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
当ViewPager滑动时,给自定义View设置一个属性动画移动相应的距离,然后刷新布局:
private void startAnimator(int movePage){
float curTranslationX = ctsLevelBar.getTranslationX();
final ObjectAnimator animator = ObjectAnimator.ofFloat(ctsLevelBar, "translationX",
curTranslationX, curTranslationX - movePage * ctsLevelBar.getLineLength());
animator.setDuration(150);
animator.start();
}
源码传送门:https://github.com/tianyalu/ViewPagerTabSliding
如果喜欢的话欢迎给个star。