简易打造一个引导页的指示器
来尝试打造一个简单的指示器
这是一个用作引导页的指示器,具有粘连效果,由于只花了一天的功夫写,一些细节尚未考虑周全
要写出一个这样一个指示器,需要你知道下面两个知识点:
- ViewPager.OnPageChangeListener
- Path类中quadTo的使用
1. ViewPager.OnPageChangeListener
废话不多,先了解OnPageChangeListener的作用,下面贴代码
public interface OnPageChangeListener {
/**
* @param position 计算位移偏移量的对象的位置
* @param positionOffset 计算位移偏移量 (变化值从0f~1f)
* @param positionOffsetPixels 位置偏移像素 这个在本例子中并未用到,所以我也没有去过多了解,可以去自己试试
*/
void onPageScrolled(int position, float positionOffset,@Px int positionOffsetPixels);
/**
* @param position viewpager当前选中的页面
*/
void onPageSelected(int position);
//下面这个是滚动状态改变时才会去调用的方法,个人觉得可以在改变页面时,加上一个段动画,算是个坑吧,我还没填
void onPageScrollStateChanged(int var1);
}
值得注意的是有一点,第一个方法的positionOffset的值只从0~1的,我当时在困惑那该如何判断这个positionOffset是向左偏移还是向右呢?在我写完一半去测试的时候发现,第一个方法的position并不是逻辑上当前选中的那个而是,相对参考的position,什么意思呢,比如:我当前逻辑上currentPosition=2,那么当你拖动viewpager手指向左滑动时,那么第一个方法的position =2;但是,如果你向右滑,那么第一个方法的position =1;
这样就很好解释了positionOffset的值为什么没有方向了。因为他给的position永远是你当前选中的position和将要滑向的position中的左值。
当然了,第二个方法的position传值确实是当前选中的position,而且只在改变position时调用。
2. Path类中quadTo的使用
首先要说path类中有很多的方法,还有填充法则PorterDuffXfermode 和 FillType(什么奇偶法则等等)由于我被这个卡住了,所以在作图的时候画了三个path(逃)
切回主题,quadTo是Path类中,用来画二阶贝塞尔曲线的方法
/**
* Add a quadratic bezier from the last point, approaching control point
* (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
* this contour, the first point is automatically set to (0,0).
*
* @param x1 The x-coordinate of the control point on a quadratic curve
* @param y1 The y-coordinate of the control point on a quadratic curve
* @param x2 The x-coordinate of the end point on a quadratic curve
* @param y2 The y-coordinate of the end point on a quadratic curve
*/
public void quadTo(float x1, float y1, float x2, float y2) {
isSimplePath = false;
nQuadTo(mNativePath, x1, y1, x2, y2);
}
具体的贝塞尔曲线原理不多讲,但是要知道,quadTo的前两个参数是控制点的坐标,后者是结束点坐标。起点坐标就是path当前位置。
这里要找到点有三个,一个圆的上顶点,还有两条斜线的和x轴交点,以及另一个圆的上顶点,什么你看不见,我忘记画了,就在右边那里,自行脑补一下啦。
这样画出的贝塞尔曲线就像红色的那样,当然可能有点不标准,我用ps画的,有点丑见谅。
先说到这吧,贴关键代码
/**
* 这个是拖动的形式
* @param canvas
* @param selectedPosition
* @param selectionOffset
*/
private void drawDragging(Canvas canvas,int selectedPosition, float selectionOffset) {
//TODO 贝塞尔曲线
float absOffset =selectedPosition==currentPosition?selectionOffset:selectionOffset-1;
int[] firstPoint = selectedPosition==currentPosition?getStartCoordinate(selectedPosition):getStartCoordinate(selectedPosition+1);
int[] secondPoint = selectedPosition==currentPosition?getStartCoordinate(selectedPosition):getStartCoordinate(selectedPosition+1);
secondPoint[0]+=(absOffset)*getItemWidth();
int spacing =Math.abs(firstPoint[0]-secondPoint[0]);
// int[] movePoint = selectedPosition==currentPosition?getStartCoordinate(selectedPosition):getStartCoordinate(selectedPosition+1);
float centerPointX = (float) (secondPoint[0]+firstPoint[0])/2+ (getItemWidth())*(1-selectionOffset);
mDragPath.reset();
if(selectionOffset<0.9){
mDragPath.addCircle(
firstPoint[0]+getItemWidth()/2,
firstPoint[1]+getItemHeight()/2,
getRadius(),
selectionOffset>0 ? Path.Direction.CCW : Path.Direction.CW);
}
if(selectionOffset>0.1){
mDragPath.addCircle(
secondPoint[0]+getItemWidth()/2,
secondPoint[1]+getItemHeight()/2,
getRadius(),
selectionOffset>0 ? Path.Direction.CCW : Path.Direction.CW);
}
mDragPath.close();
canvas.drawPath(mDragPath, offsetPaint);
if(selectionOffset>0.9||selectionOffset<0.1){
return;
}
mDragPath.reset();
mDragPath.moveTo(firstPoint[0]+getItemWidth()/2,getItemHeight()/2-getRadius());
mDragPath.quadTo(centerPointX,getHeight()/2, secondPoint[0]+getItemWidth()/2, getItemHeight()/2-getRadius());
mDragPath.lineTo(secondPoint[0]+getItemWidth()/2, getItemHeight()/2+getRadius());
mDragPath.quadTo(centerPointX,getHeight()/2, firstPoint[0]+getItemWidth()/2, getItemHeight()/2+getRadius());
mDragPath.lineTo(firstPoint[0]+getItemWidth()/2,getItemHeight()/2-getRadius());
mDragPath.close();
canvas.drawPath(mDragPath, offsetPaint);
}
不考虑我的代码优雅性的话,关键点在于:根据我这个方法的传参,你需要提前知道当前position是哪一个。然后才能判断圆点是向左滑动还是向右。
PS:2019.1.5 00:51 具体完整的代码,我改天再传,好困。溜了溜了