自定义View学习摘要笔记(一)
来源:互联网 发布:python怎么打开idle 编辑:程序博客网 时间:2024/06/03 21:33
学习资料:1、Canvas之translate、scale、rotate、skew方法讲解!
2、安卓自定义View教程
3、《android群英传》徐宜生
4、Android Canvas的save(),saveLayer()和restore()浅谈
5、Approximate a circle with cubic Bézier curves
canvas基础:
/*** * ①和②的顺序是不能改变的。 * 原因:translate(x,y)是将整个画布(整个屏幕)平移, * 即:先将画布平移到指定位置,再进行绘制。 * * @param canvas 画布 */@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); initPaints(); canvas.drawColor(Color.BLUE); canvas.translate(100, 100);//① canvas.drawRect(0, 0, 200, 200, mPaint1);//② }
/*** * 画空心矩形需要设置Paint.Style.STROKE,默认是FILL. */ mPaint1.setStyle(Paint.Style.STROKE);
关于translate:
关于scale:
scale共两个方法,源代码如下:
/** * Preconcat the current matrix with the specified scale. * * @param sx The amount to scale in X * @param sy The amount to scale in Y */ public void scale(float sx, float sy) { native_scale(mNativeCanvasWrapper, sx, sy); } /** * Preconcat the current matrix with the specified scale. * * @param sx The amount to scale in X * @param sy The amount to scale in Y * @param px The x-coord for the pivot point (unchanged by the scale) * @param py The y-coord for the pivot point (unchanged by the scale) */ public final void scale(float sx, float sy, float px, float py) { translate(px, py); scale(sx, sy); translate(-px, -py); }
所以说,代码:
canvas.scale(a, b, 300, 300);//向左偏移300,向下平移300canvas.drawRect(rect, mPaint1);
和
canvas.translate(300,300);canvas.scale(a,b);canvas.translate(-300,-300);
效果图如下:
上面的缩放比例都是正数,当缩放比例为负数的时候,会根据缩放中心轴(x,y轴)进行翻转,效果图如下:
关于rotate(旋转):
和scale差不多,自己也实现了一个clock的demo,代码如下
private void drawClock(Canvas canvas) { mPaint1.setColor(Color.RED); mPaint1.setStyle(Paint.Style.STROKE); mPaint1.setStrokeWidth(10); canvas.translate(100, 100); canvas.drawOval(new RectF(0, 0, 600, 600), mPaint1); float shortLength = 30; float longLength = 50; for (int i = 0; i < 60; i++) { if (i % 5 == 0) { mPaint1.setStrokeWidth(10); canvas.drawLine(300, 0, 300, longLength, mPaint1); } else { mPaint1.setStrokeWidth(5); canvas.drawLine(300, 0, 300, shortLength, mPaint1); } canvas.rotate(6, 300, 300); } }
效果图:
关于skew:
public void skew (float sx, float sy)
参数含义:
float sx:将画布在x方向上倾斜相应的角度,sx倾斜角度的tan值,
float sy:将画布在y轴方向上倾斜相应的角度,sy为倾斜角度的tan值.
下面的内容中用到的图片来自:安卓自定义View教程
android屏幕的坐标系:
MotionEvent中 get 和 getRaw 的区别
(摘自安卓自定义View教程)
角度问题:
图层的混合模式:
用Paint.setXfermode()可以指定不同的PorterDuff.Mode。
view的绘制流程:
自定义view的构造函数:
public Demo1(Context context) {//使用new Demo1()时会调用 super(context); } public Demo1(Context context, AttributeSet attrs) {//在xml文件中使用时会调用 super(context, attrs); }
view的测量,onMeasure()方法:
测量的模式分为三种:
EXACTLY: 精确测量模式,将layout_width或layout_height指定为具体数值时,系统会使用此模式,也是系统的默认测量模式。
AT_MOST:最大值模式,当layout_width或layout_height为wrap_content时,系统会调用此模式。
UNSPECIFIED:不指定测量模式,一般在自定义view时会使用。
NOTE: 只有自定义的view需要支持wrap_content时,才需要重写onMeasure()方法。
下面是重写onMeasure()方法的模板写法:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); }/*** * 先从MeasureSpec对象中取出具体的测量模式和大小, * 当specMode为EXACTLY时(指定了具体值),那就让result为指定的值, * 如果不是,则给他一个默认值, * 然后判断是不是采用了AT_MOST模式(用了wrap_content属性), * 然后返回specSize和result的最小值。 * * @param widthMeasureSpec 宽度MeasureSpec对象 * @return 测量的宽度 */ private int measureWidth(int widthMeasureSpec) { int result; int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = 200; if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } private int measureHeight(int heightMeasureSpec) { int result; int specMode = MeasureSpec.getMode(heightMeasureSpec); int specSize = MeasureSpec.getSize(heightMeasureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = 200; if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; }
canvas常用操作速查表:
saveLayer()
Android Canvas的save(),saveLayer()和restore()浅谈
saveLayer
Canvas 在一般的情况下可以看作是一张画布,所有的绘图操作如drawBitmap, drawCircle都发生在这张画布上,这张画板还定义了一些属性比如Matrix,颜色等等。但是如果需要实现一些相对复杂的绘图操作,比如多层动 画,地图(地图可以有多个地图层叠加而成,比如:政区层,道路层,兴趣点层)。Canvas提供了图层(Layer)支持,缺省情况可以看作是只有一个图 层Layer。如果需要按层次来绘图,Android的Canvas可以使用SaveLayerXXX, Restore 来创建一些中间层,对于这些Layer是按照“栈结构“来管理的。
创建一个新的Layer到“栈”中,可以使用saveLayer, savaLayerAlpha, 从“栈”中推出一个Layer,可以使用restore,restoreToCount。但Layer入栈时,后续的DrawXXX操作都发生在这个 Layer上,而Layer退栈时,就会把本层绘制的图像“绘制”到上层或是Canvas上,在复制Layer到Canvas上时,可以指定Layer的 透明度(Layer),这是在创建Layer时指定的:public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)本例Layers 介绍了图层的基本用法:Canvas可以看做是由两个图层(Layer)构成的,为了更好的说明问题,我们将代码稍微修改一下,缺省图层绘制一个红色的 圆,在新的图层画一个蓝色的圆,新图层的透明度为0×88。
drawPicture
picture:
A Picture records drawing calls (via the canvas returned by beginRecording) and can then play them back into Canvas (via draw(Canvas) or drawPicture(Picture)).For most content (e.g. text, lines, rectangles), drawing a sequence from a picture can be faster than the equivalent API calls, since the picture performs its playback without incurring any method-call overhead.Picture记录绘画调用(通过开始录制后被返回的画布),然后把他们(绘画的一些调用)放回canvas(通过调用draw(canvas) 或 drawPicture(Picture)方法)。对于大部分内容来说,从picture绘制一个序列会比调用同样可以实现此效果的api要快,因为picture执行他的回放功能,不会导致任何因方法调用而产生的花费。
Picture的相关方法:
使用Picture前请关闭硬件加速(很重要,被坑了)!
在AndroidMenifest文件中application节点下添上android:hardwareAccelerated=”false”以关闭整个应用的硬件加速。
beginRecording 和 endRecording 是成对使用的,一个开始录制,一个是结束录制,两者之间的操作将会存储在Picture中。
绘制Bitmap
绘制文字
看官方API(比较简单)
Path之基本操作
请关闭硬件加速,以免引起不必要的问题
常用方法表
moveTo只改变下次操作的起点,在执行完第一次LineTo的时候,本来的默认点位置是A(200,200),但是moveTo将其改变成为了C(200,100),所以在第二次调用lineTo的时候就是连接C(200,100) 到 B(200,0) 之间的直线.
setLastPoint是重置上一次操作的最后一个点,在执行完第一次的lineTo的时候,最后一个点是A(200,200),而setLastPoint更改最后一个点为C(200,100),所以在实际执行的时候,第一次的lineTo就不是从原点O到A(200,200)的连线了,而变成了从原点O到C(200,100)之间的连线了。
注意: close的作用是封闭路径,与连接当前最后一个点和第一个点并不等价。如果连接了最后一个点和第一个点仍然无法形成封闭图形,则close什么 也不做。
关于Direction
addPath(…)
addArc与arcTo
offset(…)
Path之贝塞尔曲线
1、一阶曲线其实就是前面讲解过的lineTo。
2、二阶曲线对应的方法是quadTo(…)(ps:具体用法见API,作用是使画的曲线更平滑),是由两个数据点,一个控制点构成.
原理:(D、E两点是任取得)
3、三阶曲线对应的方法是cubicTo,由两个数据点和两个控制点来控制曲线状态。
NOTE:三阶曲线相比于二阶曲线可以制作更加复杂的形状,但是对于高阶的曲线,用低阶的曲线组合也可达到相同的效果,就是传说中的降阶。因此我们对贝塞尔曲线的封装方法一般最高只到三阶曲线。
贝塞尔曲线相关函数使用方法
效果图:(有点卡顿)
代码实现(两阶贝塞尔曲线):
public class Demo2 extends View { private PointF mStartPoint = new PointF(); private PointF mEndPoint = new PointF(); private PointF mControlPoint = new PointF(); private float mCenterX, mCenterY; private Path mPath = new Path(); public Demo2(Context context) { super(context); } public Demo2(Context context, AttributeSet attrs) { super(context, attrs); } public Demo2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(21) public Demo2(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mCenterX = w / 2; mCenterY = h / 2; mStartPoint.x = mCenterX - 200; mStartPoint.y = mCenterY; mEndPoint.x = mCenterX + 200; mEndPoint.y = mCenterY; mControlPoint.x = mCenterX; mControlPoint.y = mCenterY - 200; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(10); mPath.reset(); mPath.moveTo(mStartPoint.x, mStartPoint.y); mPath.quadTo(mControlPoint.x, mControlPoint.y, mEndPoint.x, mEndPoint.y); canvas.drawPath(mPath, paint); } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: mControlPoint.set(x, y); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; } invalidate(); return true; }}
那什么时候会使用到贝塞尔曲线呢?下面是大神总结的三种情况:
贝塞尔曲线的主要优点是可以实时控制曲线状态,并可以通过改变控制点的状态实时让曲线进行平滑的状态变化。
path的rXxx方法
和不带r的方法的区别:rXxx方法的坐标使用的是相对位置(基于当前点的位移),而之前方法的坐标是绝对位置(基于当前坐标系的坐标)。
加深理解:若当前点的位置在 (100,100) , 使用了 rLineTo(100,200) 之后,下一个点的位置是在当前点的基础上加上偏移量得到的,即 (100+100, 100+200) 这个位置。
android中的填充模式:
布尔操作(API19)
布尔操作是两个Path之间的运算,主要作用是用一些简单的图形通过一些规则合成一些相对比较复杂,或难以直接得到的图形。
如太极中的阴阳鱼,如果用贝塞尔曲线制作的话,可能需要六段贝塞尔曲线才行,而在这里我们可以用四个Path通过布尔运算得到,而且会相对来说更容易理解一点:
布尔运算方法
重置路径
重置Path有两个方法,分别是reset和rewind,两者区别主要有一下两点:
选择权重: FillType > 数据结构
因为“FillType”影响的是显示效果,而“数据结构”影响的是重建速度。
- 自定义View学习摘要笔记(一)
- View自定义学习摘要笔记(二)
- 自定义View学习摘要笔记(三)
- 自定义View学习笔记(一)
- 自定义View笔记(一)
- Android总结笔记01:自定义View学习(一)
- 自定义VIEW(学习笔记一)-基础知识
- 自定义View笔记一
- 自定义View笔记(一)
- struts2学习笔记摘要一
- View--学习笔记(一)
- 自定义view学习笔记
- 笔记50 | Android自定义View(一)
- 自定义View学习一(圆形头像)
- 学习鸿洋大神的自定义View(一)的笔记
- 自定义View学习笔记(2)->Path
- 自定义View学习笔记(3)->invalidate
- 安全测试学习笔记一(Cookie&Session)(摘要)
- puppet的yum安装配置
- 手动实现的客户端输入校验
- css3背景图案
- Android相关属性的介绍:android:exported
- 学习H5
- 自定义View学习摘要笔记(一)
- 使用Unicode编码输出自己的名字
- laravel框架的使用
- Ubuntu14.04的动、静态IP设置
- 事物回滚与异常
- JAVASE之异常
- 统计svn上两个版本或一段时间内的代码修改行数
- 如何确定Kafka的分区数、key和consumer线程数
- org.springframework.beans.factory.NoSuchBeanDefinitionException: