Indicator (指示器) 可能你们都见的比较多了,在一个APP中,有不少场景都会用到Indicator,好比第一次安装APP 时的引导页,首页上面的广告Banner ,又或者是Tab下面的Indicator。Indicator 通常配合ViewPager 使用,它能很直观的反应出ViewPager 中一共有多少页,当前选中的是哪一页,用户可以很容易的看出是否还能够滑动,用户体验较好。那么本篇文章就讲一下如何经过自定义View来实现一个漂亮简洁的IndicatorView。java
咱们使用Indicator时,常见的有三种形式,第一种是根据ViewPager 的总页数展现一排小圆点,选中和未选中分别标为不一样的颜色。这也是广泛的使用Indicator的效果,效果以下:android
第二种形式是根据ViewPager 的总页数展现一排圆点,而且用数字标出其顺序(1,2,3,4 ...),大概效果以下:git
第三种形式和第二种形式差很少,一样根据ViewPager总页数展现一片圆点,可是用字母标出顺序(A,B,C,D ....), 好比 :魅族手机屏幕的切换的Indicator。效果以下:github
本篇文章就经过自定义View来实现这三种IndicatorView。canvas
首先整理一下思路,CircleIndicatorView ,它是由一组圆 组成的,只不过圆有多种状态,选中、未选中、显示字母、显示数字等等。看着有这么多中状态,感受挺复杂,可是其实不复杂,只是用不一样的变量来控制不一样的状态便可。其余的先不考虑,咱们只要先把这一排圆画出来,剩下的就比较容易了。要肯定一个圆的,须要知道圆的半径和圆心,半径是咱们能够配置的,那么,其实核心就是计算这一排的圆点的圆心位置。圆心 y 的位置是固定的(View 高的通常),所以咱们只须要计算每一个圆的圆心x的位置,看下图,其实很容易就能找到规律,规律以下: 第一个圆的x 就等于圆的半径,从第二个圆开始,当前圆的圆心x 坐标为 上一个圆的x 坐标 + (radius * 2 + mSpace)。 其中mSpace 是圆之间的间距。app
有了上面的规律,咱们只须要一个循环就能找出全部圆的圆心位置。代码以下:maven
/** * 测量每一个圆点的位置 */
private void measureIndicator(){
mIndicators.clear();
float cx = 0;
for(int i=0;i<mCount;i++){
Indicator indicator = new Indicator();
if( i== 0){
cx = mRadius + mStrokeWidth;
}else{
cx += (mRadius + mStrokeWidth) * 2 +mSpace;
}
indicator.cx = cx;
indicator.cy = getMeasuredHeight() / 2;
mIndicators.add(indicator);
}
}复制代码
咱们用Indicator类记录了每一个圆的圆心位置,而且保存在一个列表里面,如今有了全部圆的数据,咱们就能够绘制 了。ide
@Override
protected void onDraw(Canvas canvas) {
for(int i=0;i<mIndicators.size();i++){
Indicator indicator = mIndicators.get(i);
float x = indicator.cx;
float y = indicator.cy;
if(mSelectPosition == i){
mCirclePaint.setStyle(Paint.Style.FILL);
mCirclePaint.setColor(mSelectColor);
}else{
mCirclePaint.setColor(mDotNormalColor);
if(mFillMode != FillMode.NONE){
mCirclePaint.setStyle(Paint.Style.STROKE);
}else{
mCirclePaint.setStyle(Paint.Style.FILL);
}
}
canvas.drawCircle(x,y, mRadius, mCirclePaint);
// 绘制小圆点中的内容
if(mFillMode != FillMode.NONE){
String text = "";
if(mFillMode == FillMode.LETTER){
if(i >= 0 && i<LETTER.length){
text = LETTER[i];
}
}else{
text = String.valueOf(i+1);
}
Rect bound = new Rect();
mTextPaint.getTextBounds(text,0,text.length(),bound);
int textWidth = bound.width();
int textHeight = bound.height();
float textStartX = x - textWidth / 2;
float textStartY = y + textHeight / 2;
canvas.drawText(text,textStartX,textStartY, mTextPaint);
}
}
}复制代码
绘制的代码很简单,无非就是循环咱们保存的列表,拿出每个Indicator,而后绘制圆就好了,除此以外就是一些状态的判断,好比,是否绘制数字、字母和选中状态等等 。gradle
到此,咱们的CircleIndicatorView 的绘制工做算是完成了,而且也可以显示在界面上了,可是它如今仍是一个单独的View。咱们前面说过,IndicatorView 通常是配合ViewPager使用的,它是随着Viewpager的切换而改变的,所以咱们须要将IndicatorView 与ViewPager 进行关联。ui
要让CircleIndicatorView 与ViewPager 关联,首先须要 CircleIndicatorView 实现 ViewPager.OnPageChangeListener
接口。而后在onPageSelected
方法中记录当前页的位置。代码以下:
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
mSelectPosition = position;
invalidate();
}
@Override
public void onPageScrollStateChanged(int state) {
}复制代码
第二步,向外提供一个API ,设置CircleIndicatorView 与 ViewPager 关联。
/** * 与ViewPager 关联 * @param viewPager */
public void setUpWithViewPager(ViewPager viewPager){
releaseViewPager();
if(viewPager == null){
return;
}
mViewPager = viewPager;
mViewPager.addOnPageChangeListener(this);
int count = mViewPager.getAdapter().getCount();
setCount(count);
}复制代码
经过如上两步,就创建了CircleIndicatorView 与ViewPager的关联,只须要调用方法setUpWithViewPager
就OK。
到此就完了吗?固然尚未完,其实还有一个小细节,那就是IndicatorView 因该是能够点击的,点击Indicator小圆点就能切换ViewPager 到对应的页面(如 Iphone 和 魅族手机的 屏幕切换,点击indicator小圆点就能够切换 ,之前没有注意的如今能够去试一下 ),那么咱们也来实现这样一个功能,其实很简单,重写onTouchEvent方法 ,监听ACTION_DOWN 事件,而后获取点击屏幕的坐标,与所绘制的圆位置比较,若是点击区域在圆的范围内,就点击了该Indicator。点击以后,切换Viewpager到对应页面。代码以下:
@Override
public boolean onTouchEvent(MotionEvent event) {
float xPoint = 0;
float yPoint = 0;
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
xPoint = event.getX();
yPoint = event.getY();
handleActionDown(xPoint,yPoint);
break;
}
return super.onTouchEvent(event);
}
private void handleActionDown(float xDis,float yDis){
for(int i=0;i<mIndicators.size();i++){
Indicator indicator = mIndicators.get(i);
if(xDis < (indicator.cx + mRadius+mStrokeWidth)
&& xDis >=(indicator.cx - (mRadius + mStrokeWidth))
&& yDis >= (yDis - (indicator.cy+mStrokeWidth))
&& yDis <(indicator.cy+mRadius+mStrokeWidth)){
// 找到了点击的Indicator
// 切换ViewPager
mViewPager.setCurrentItem(i,false);
// 回调
if(mOnIndicatorClickListener!=null){
mOnIndicatorClickListener.onSelected(i);
}
break;
}
}
}复制代码
到此,咱们自定义IndicatorView 的工做就差很少完成了,可是如今的IndicatorView 还不是很灵活,咱们要让它的可配置性更强,就应该提供更多的API 来让IndicatorView 使用更加灵活方便,所以,最后一步,加上一些自定义属性来提升它的灵活性。自定义了以下一些属性:
属性名 | 属性意义 | 取值 |
---|---|---|
indicatorRadius | 设置指示器圆点的半径 | 单位为 dp 的值 |
indicatorBorderWidth | 设置指示器的border | 单位为 dp 的值 |
indicatorSpace | 设置指示器之间的距离 | 单位为 dp 的值 |
indicatorTextColor | 设置指示器中间的文字颜色 | 颜色值,如:#FFFFFF |
indicatorColor | 设置指示器圆点的颜色 | 颜色值 |
indicatorSelectColor | 设置指示器选中的颜色 | 颜色值 |
fill_mode | 设置指示器的模式 | 枚举值:有三种,分别是letter,number和none |
自定义属性文件以下:
<declare-styleable name="CircleIndicatorView">
<attr name="indicatorRadius" format="dimension"/>
<attr name="indicatorBorderWidth" format="dimension"/>
<attr name="indicatorSpace" format="dimension"/>
<attr name="indicatorTextColor" format="color"/>
<attr name="indicatorColor" format="color"/>
<attr name="indicatorSelectColor" format="color"/>
<attr name="fill_mode">
<enum name="letter" value="0"/>
<enum name="number" value="1"/>
<enum name="none" value="2"/>
</attr>
</declare-styleable>复制代码
经过上面这些属性,咱们就能够很好的定制IndicaotorView 了,好比,自定义圆的大小,颜色,border,文字的颜色,选中的颜色和展现的模式等等。
最终的效果以下:
上面讲了自定义IndicatorView 的思路、过程 和一些关键代码,为了使用方便,已经将CircleIndicatorView封装成了一个库,如下介绍一下CircleIndicatorView API的使用。Github 地址:github.com/pinguo-zhou…
Dependency
1, 最外层build.gradle添加以下代码:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}复制代码
2, app 层buid.gradle dependencies 中 添加以下代码:
compile 'com.github.pinguo-zhouwei:CircleIndicatorView:v1.0.0'复制代码
使用方法以下:
1, xml 添加CircleIndicatorView,配置相关属性
<com.zhouwei.indicatorview.CircleIndicatorView
android:id="@+id/indicator_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="50dp"
android:layout_centerHorizontal="true"
app:indicatorSelectColor="#00A882"
app:fill_mode="letter"
app:indicatorBorderWidth="2dp"
app:indicatorRadius="8dp"
app:indicatorColor="@color/colorAccent"
app:indicatorTextColor="@android:color/white"
/>复制代码
2, 在代码中与ViewPager 关联
// 初始化ViewPager 相关
mViewPager = (ViewPager) findViewById(R.id.viewpager);
mPagerAdapter = new ViewPagerAdapter();
mViewPager.setAdapter(mPagerAdapter);
mIndicatorView = (CircleIndicatorView) findViewById(R.id.indicator_view);
// 关联ViewPager
mIndicatorView.setUpWithViewPager(mViewPager);复制代码
上面全部列举的属性,出了在xml 中配置外,也能够在代码中设置,以下:
// 在代码中设置相关属性
CircleIndicatorView indicatorView = (CircleIndicatorView) findViewById(R.id.indicator_view3);
// 设置半径
indicatorView.setRadius(DisplayUtils.dpToPx(15));
// 设置Border
indicatorView.setBorderWidth(DisplayUtils.dpToPx(2));
// 设置文字颜色
indicatorView.setTextColor(Color.WHITE);
// 设置选中颜色
indicatorView.setSelectColor(Color.parseColor("#FEBB50"));
//
indicatorView.setDotNormalColor(Color.parseColor("#E38A7C"));
// 设置指示器间距
indicatorView.setSpace(DisplayUtils.dpToPx(10));
// 设置模式
indicatorView.setFillMode(CircleIndicatorView.FillMode.LETTER);
// 最重要的一步:关联ViewPager
indicatorView.setUpWithViewPager(mViewPager);复制代码
以上就是经过自定义View的方式实现一个多功能的IndicatorView的所有内容,内容很简单,经过自定义的View方式实现很方便,省得每次还得找设计师要切图啊(能本身动手的,就不找设计师要了。😄)。若有问题欢迎交流,最后,若是你以为不错的话,欢迎github 赏个start啊!!轮子传送门 github.com/pinguo-zhou…