一.『转转APP』效果预览
注意看Indicator指示器
二.动作分析
- 左边部分
左边部分是一个白色的圆圈,圆圈中用红色字体显示当前页数,在ViewPager滑动时,白色圆圈绕Y轴旋转,在旋转180度时,即显示背面的时候,圆圈中字变成下一页的页码。
- 右边部分
右边部分比较简单,一个带缺口的椭圆,上面显示着总页数。
三.各个击破难点
1. 用canvas绘制绕Y轴旋转
Canvas 绘制图片时,如果是在绕Z轴旋转是比较简单,使用Matrix矩阵:
Matrix matrix = new Matrix();
matrix.setRotate(angle);
Bimtap newBitmap = Bitmap.createBitmap(oldBitmap,0,0,oldBitmap.getWidth(),
oldBitmap.getHeight(),matrix,true);
但是这只能在绘制图片时进行旋转,如果要对一个整体旋转则无法做到,比如,上面转转的指示器坐标的圆圈,圆圈是一个圆,上面还有数字,要实现圆和数字一起旋转,所以无法通过上述方法完成。
而且这个问题我在谷歌上搜索,也不太好表述,刚开始搜索「android canvas 3D旋转」,结果也不太相符,后来搜索「android canvas绕y轴 旋转」,才找到符合的。
使用 Android.graphics.Camera 的 rotateY 接口实现绕 Y 轴旋转时矩阵的运算。
首先声明camera
camera = new Camera();
在Ondraw方法中,首先对canvas状态进行保存,使用的是canvas.save()
,然后将camera状态保存,将camera旋转一个角度rotateAngle,在获取camera的矩阵赋值到matrix中,最后将camera状态恢复至旋转之前的状态。然后将matrix追加到当前canvas的矩阵中,使用的是concat方法,这时候整个画布相当于旋转了rotateAngle角度,画完圆之后再恢复至原来的状态。
canvas.save();
Matrix matrix = new Matrix();
camera.save();
camera.rotateY(rotateAngle);
camera.getMatrix(matrix);
camera.restore();
mPaint.setColor(textBackgroundColor);
int centerX = diameter / 2;
int centerY = diameter / 2;
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
canvas.concat(matrix);
canvas.drawCircle(diameter / 2, diameter / 2, diameter / 2, mPaint);
canvas.restore();
2. canvas drawText文字垂直居中
这个问题,如果没有遇到还真不知道会出现这种问题,一般想象的,我设置画笔的gravity属性为Paint.Align.CENTER,在将文字的中点设置下不就ok了么,如下面的代码:
textPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("of " + maxPage, rect.centerX(), rect.centerY(), textPaint);
出现的效果,如图所示,发现文字是偏上:
这是怎么回事呢?其实我们设置的文字爱中心点处绘制,实际上,文字是以基准线为锚点进行绘制的,英文叫做baseline.
在维基百科中可以看到基线的解释:
所以我们如果想让文字居中,则需要将绘制点的Y坐标向下移动。
基线到字体顶端的距离为top,基线到字体底端距离为bottom,则有以下等式:
(top + baseline)+ (bottom + baseline) / 2 = centenY()
那么经过计算就可以算出baseline的值,通过Paint.FontMetrics获取top和bottom值,代码如下:
//画文字
Rect rect = new Rect(diameter, 0, (int) (diameter * 3.4f), diameter);//右边的背景图片的Rect
Paint textPaint = new Paint();
textPaint.setColor(Color.WHITE);
textPaint.setTextSize((int) (diameter * 0.6f));
textPaint.setStyle(Paint.Style.FILL);
//该方法即为设置基线上那个点究竟是left,center,还是right 这里我设置为center
textPaint.setTextAlign(Paint.Align.CENTER);
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float top = fontMetrics.top;//为基线到字体上边框的距离,即上图中的top
float bottom = fontMetrics.bottom;//为基线到字体下边框的距离,即上图中的bottom
int baseLineY = (int) (rect.centerY() - top / 2 - bottom / 2);//基线中间点的y轴计算公式
canvas.drawText("of " + maxPage, rect.centerX(), baseLineY, textPaint);
3. 自动轮播
自动轮播,通过Handler+Timer即可。
//定时自动播放
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
if (mViewPager.getCurrentItem() == Integer.MAX_VALUE - 1) {
currentIndex = -1;
}
currentIndex = mViewPager.getCurrentItem();
message.arg1 = currentIndex + 1;
mHandler.sendMessage(message);
}
},1000,2000);
然后自定义Handler.
//定时轮播图片,需要在主线程里面修改 UI
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
mViewPager.setCurrentItem(msg.arg1,true);
}
}
};
四.代码实现
代码在Github上,请不要吝啬Star噢。
联系方式和建议
微博:orzangleli
所有原创文章版权归orzangleli所有,转载需声明:www.orzangleli.com/2016/11/10/…