第一篇博客--记录自定义ViewPager绘制指示点

来源:互联网 发布:360浏览器网络连接错误 编辑:程序博客网 时间:2024/06/06 12:03
这是本人的第一篇博客,其实想写博客很久了,但是由于懒,计划一直未能实施。现在,我发现输入的知识越多,如果没有经过自己的思维整理过后输出是不行的,虽然自己可以写出来,知识记忆会越来越模糊,给别人讲解时也会思维混乱,最坏的结果是忘掉了。所以,我决定将学习到的有必要记录下来的知识给写到博客。
今天这篇博客主要是记录本人在如何实现自定义ViewPager绘制指示点的,本人是为了竞赛而自习安卓,如果有错误希望大家能给我指正,我万分感谢!
先发下编码的环境和sdk版本,之所以没用AS是因为竞赛用的是ADT。。。。。。
IDE->ADT
sdkVersion:
再发下我们今天要实现的效果图
       
好,接下来进入正题。
|第一步| 新建一个类(这里我的类名是MyViewPager)继承ViewPager类(要导入 import android.support.v4.view.ViewPager;),重写其父类的两个构造方法。
public class MyViewPager extends ViewPager{
public MyViewPager(Context context, AttributeSet attrs) {//View声明在xml时会调用
super(context, attrs);
}
public MyViewPager(Context context) {
super(context);
}
}
|第二步| 既然要在View画上自己想画的东西,那么我们要准备两样东西:画笔和画布。接下来我们来创建一只画笔,并给它设置样式。
Paint circlePaint;
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
circlePaint = new Paint();//创建一支画笔
circlePaint.setColor(Color.BLUE);//设置画笔绘制的内容为蓝色
}
然后就是画布啦,重写View的onDraw方法,该方法的传递的参数就是画布,系统绘制View时会调用,我们可以用画笔在画布上绘制我们自己的内容。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(100, 100, 15, circlePaint);
}
drawCircle函数的前两个参数是圆心坐标,后面有大用处,第三个参数是圆心半径,第四个参数就是画笔了(没画笔怎么画图是吧?)
运行一下(效果如下图,适配器自己设置哈),我们发现一个实心圆绘制出来啦。

空心圆只要将画笔的的样式设置一下就行了(默认的是FILL):circlePaint.setStyle(Paint.Style.STROKE);。这里就不贴图了。
|第三步| 根据上面的知识在ViewPager的中间绘制5个空心圆(根据页数)
我们慢慢来,将问题分块解决:
[1]、不管中间坐标先绘制5个并列一排的空心圆
[2]、将这5个空心圆视为一个对象,并将其定位到ViewPager的中间

[1] :
先定义几个成员变量
private int circleNum = 5;//圆点个数
private int circleRadius = 15;//圆点半径
private int circleGap = 5;//两个圆点的相隔的距离
private int x = 100;//第一个圆点的圆心X坐标
private int y = 100;//第一个圆点的圆心Y坐标
然后在onDraw方法循环绘制五个点。这里主要是每个圆点的圆心的X坐标不同,我们来算一下就可以了,circleRadius * 2 是整个圆的直径再加上到下一个圆的距离circleGap,最后乘以圆点个数。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < circleNum; i++) {
canvas.drawCircle(x + (circleRadius * 2 + circleGap) * i, y, circleRadius, circlePaint);
}
}
效果如下图:
     
好了,[1]完成,接下来是[2]

[2]:
我们先将5个圆点当作一个对象(假定为A),我们来算一下这个对象的宽度, 一共是5个圆点4个间隙,所以,该对象的宽度为A.width = 5 * circleRadius * 2 + circleGap * 4 。
然后,根据整个对象A的宽度来计算第一个点应该绘制的坐标:获取ViewPager的宽度减去A的宽度再除以2再加上一个circleRadius(因为方法drawCircle的参数x坐标是第一个圆点的圆心,所以还要加上一个半径~。。),就是第一个点应该绘制的位置的X坐标啦,Y坐标就根据自己随意指定一个吧,毕竟没学过UI设计,不知道怎么放的能让用户感觉到最好,不过一般放在距离ViewPager的底部不远的地方吧。(废话了,结合[1]贴完整的代码)
private int vpWidth;
private int circleNum = 5;//圆点数量
private int circleRadius = 15;//圆点半径
private int circleGap = 5;//两个圆点的距离
private int x = 100;//第一个圆点的圆心X坐标
private int y = 100;//第一个圆点的圆心Y坐标
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
vpWidth = getWidth() - (getPaddingLeft() + getPaddingRight());//ViewPager内容的实际宽度
x = (vpWidth-(circleNum * circleRadius * 2 + circleGap * (circleNum - 1)))/2 + circleRadius;//[2]步加上
circlePaint.setStyle(Paint.Style.STROKE);
for (int i = 0; i < circleNum; i++) {
canvas.drawCircle(x + (circleRadius * 2 + circleGap) * i, y, circleRadius, circlePaint);
}
}
效果如下图:
好了,[1][2]就都完成啦~

以为这样就那五个点就成功显示在ViewPager中了?No,让我们滑到第二页~
卧槽尼玛,我的点呢。怎么还在上一页鄙视
因此,我们要根据当前所在的页数来更改x的值,因为ViewPager内容的实际宽度应是页数 * 一页的宽度
private int vpWidth;
private int circleNum = 5;//圆点数量
private int circleRadius = 15;//圆点半径
private int circleGap = 5;//两个圆点的距离
private int x = 100;//第一个圆点的圆心X坐标
private int y = 100;//第一个圆点的圆心Y坐标
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
vpWidth = getWidth() - (getPaddingLeft() + getPaddingRight());//获取ViewPager的实际宽度
x = (vpWidth-(circleNum * circleRadius * 2 + circleGap * (circleNum - 1)))/2 + circleRadius;
x = x + getCurrentItem() * vpWidth;//增加的代码,方法getCurrentItem()就是获取当前页,不过后面要还要修改,具体继续看吧。这一行我将其命名为needEdit
circlePaint.setStyle(Paint.Style.STROKE);
for (int i = 0; i < circleNum; i++) {
canvas.drawCircle(x + (circleRadius * 2 + circleGap) * i, y, circleRadius, circlePaint);
}
}
运行效果:
第三步就此全完成了!?NoNoNo,还没完成,我们发现这个五个空心圆并没有相对ViewPager静止,也就是它们并没有随着我们
的滑动时而滑动。怎么办呢?我们要重写ViewPager的onPageScrolled方法。该方法在ViewPger滑动时会调用,实时传入当前所在
页面下标、滑动距离的偏移比例和滑动距离的偏移值,粘上官方的解释
大概翻译下(英语不好,轻喷~)
大概意思是这个方法将在当前页面滑动时被调用(无论你是用手直接滑动屏幕或者调用代码进行滑动),如果你重写这个方法
则在该方法返回之前必须调用其父类的该方法:
@Override
public void onPageScrolled(int position,float positionOffset, int positionOffsetPixels){
super.onPageScrolled(position, positionOffset, positionOffsetPixels);//必须要调用父类的该方法
//下面写我们的需要的逻辑
}

第一个参数:{整数型}当前被显示的页面的位置索引。需要注意的是该值与getCurrentItem返回的索引稍有不同,当ViewPager一开
始左滑上一页时,该值立马变成上一页的索引值,而当一开始右滑下一页时,该值依然是当前页的索引,直到成功滑
动到下一页时该值才变成下一页的索引。
第二个参数:{浮点型} 表示偏移当前页面的比例,值的变化范围[0,1)。以当前页为参照页,右滑值从0.0增加趋近1.0,到下一页时
变为0.0。同理左滑值从1.0减少趋近0.0最后变为0.0。
第三个参数:{整数型}表示偏移当前页面的像素,值的变化范围[0,页面宽度)。以当前页为参照页,右滑值从0增加趋近页面宽度
到下一页时变为0。同理左滑值从页面宽度减少趋近0最后变为0。

我们暂时要用的是第一个参数的值和第三个参数的值。定义两个成员变量接收他们的值
private int position;
private int positionOffsetPixels;
@Override
public void onPageScrolled(int position,float positionOffset, int positionOffsetPixels){
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
this.position = position;
this.positionOffsetPixels = positionOffsetPixels;
}
然后将onDraw方法的needEdit行替换成
x = x + (position * vpWidth) + positionOffsetPixels;
效果如下图。大家可以试试用getCurrentItem代替position会出现什么效果,功力不够,不能表达。。请大家帮忙描述一下。
至此,第三步算是真正完成了,一个那么小的功能居然搞那么多。一波三折,我学的时候就是这样的,我也不知道该怎么办,只能遇到问题解决问题吧。

|第四步| 根据当前显示的页面给对应的空心圆处绘制实心圆。
我们要实现的效果是实心圆随着我们的滑动而滑动并且根据滑动距离的偏移来实时绘制实心圆所在位置。
假定当前页是第一页,实心圆应与第一个空心圆重合,也就是绘制的圆心坐标相同。在我们右滑到第二页的过程中,实心圆的
圆心的X坐标变化是从0到35(circleRadius * 2 + circleGap),ViewPager的变化是从0到1280(页面宽度,我的是1280),所以滑
动时实心圆的X坐标应该用ViewPager的偏移量除以其总宽度然后乘以35。不过我们不用这么计算,还记得onPageScrolled方法第二
个参数传回什么吗?它就是滑动偏移占总宽度的比例,我们可以用它直接乘以35即可。
好了,分析完了,可以写代码了。在onDraw方法的后面加上这两句:
circlePaint.setStyle(Paint.Style.FILL);//设置为实心
canvas.drawCircle(x + (circleRadius * 2 + circleGap) * position + ((circleRadius * 2 + circleGap) * positionOffset), y, circleRadius,
circlePaint);
相信到这里可以感受到position和getCurrentItem的区别了。。。还是没法表达啊,尴尬~
效果如图:
这样我们最初的目的就达到了,ViewPager自绘指示点,调用setCurrentItem方法会有一个bug,下一篇再接上。
接下来贴完整的代码:

package 所在的包;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;

public class MyViewPager extends ViewPager {
private Paint circlePaint;
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
circlePaint = new Paint();
circlePaint.setColor(Color.RED);
}
public MyViewPager(Context context) {
super(context);
}
private int vpWidth;
private int circleNum = 5;//圆点数量
private int circleRadius = 15;//圆点半径
private int circleGap = 5;//两个圆点的距离
private int x = 100;//第一个圆点的圆心X坐标
private int y = 100;//第一个圆点的圆心Y坐标
private int position;
private float positionOffset;
private int positionOffsetPixels;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
vpWidth = getWidth() - (getPaddingLeft() + getPaddingRight());
x = (vpWidth-(circleNum * circleRadius * 2 + circleGap * (circleNum - 1)))/2 + circleRadius;
x = x + (position * vpWidth) + positionOffsetPixels;
circlePaint.setStyle(Paint.Style.STROKE);
for (int i = 0; i < circleNum; i++) {
canvas.drawCircle(x + (circleRadius * 2 + circleGap) * i, y, circleRadius, circlePaint);
}
circlePaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(x + (circleRadius * 2 + circleGap) * position + ((circleRadius * 2 + circleGap) * positionOffset), y, circleRadius, circlePaint);
}
@Override
public void onPageScrolled(int position,float positionOffset, int positionOffsetPixels){
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
this.position = position;
this.positionOffset = positionOffset;
this.positionOffsetPixels = positionOffsetPixels;
}
}
最后发表一下写一篇博客的感受,明显发现自己的不足,重新过一遍知识点时漏东漏西,学习东西学得不扎实,写的过程中还忘记了一些知识,然后再去查。写一篇博客,真的不容易!最后,希望大家如果发现我写的内容有误,真诚地请各位指正!
0 0
原创粉丝点击