第1章、绘图基础
1.1、基本图形绘制
1.1.1、概述
在 Android中,Paint类就是画笔,而Canvas类就是纸,在这里叫作画布。
1.1.2、画笔的基本设置
Paint
paint.setAntiAlias(true); //打开抗锯齿功能
paint.setColor(Color.RED); //设置画笔颜色
paint.setStyle(Paint.Style.FILL); //设置填充样式
paint.setStrokeWidth(50); //设置画笔宽度
ARGB
A 代表透明度(Alpha)
R 代表红色值(Red)
G 代表绿色值(Green)
B 代表蓝色值(Blue)
设置填充样式,对于文字和几何图形都有效:
Paint.Style.FILL//仅填充内部。
Paint.Style.FILL_AND_STROKE//填充内部和描边。
Paint.Style.STROKE//仅描边。
FILL_AND_STROKE比FILL多了一个描边的宽度。
1.1.3、Canvas使用基础
demo地址
com.vcredit.doview.chapter1.DrawShapeView
1、画布背景设置
canvas.drawColor(int color)
canvas.drawARGB(int a, int r, int g, int b)
canvas.drawRGB(int r, int g, int b) //透明度Alpha的值取255
2、画直线
void drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
直线的粗细是与setStrokeWidth有直接关系的。所以,paint.setStrokeWidth在Style起作用时,用于设置描边宽度;在Style不起作用时,用于设置画笔宽度。
3、多条直线
void drawLines(float[] pts, Paint paint)
void drawLines(float[] pts, int offset, int count, Paint paint)
pts:点的集合。两个元素形成一个点,每两个点形成一条直线。
offset:pts集合中跳过的元素个数。
count:pts集合中参与绘制的元素个数。
float[] pts = {10,10,100,100,200,200,400,400};
canvas.drawLines(pts,2,4,paint);
表示点(100,100)和(200,200)这两个点的连线。
4、点
void drawPoint(float x, float y, Paint paint)
点的大小只与setStrokeWidth(width)有关,而与setStyle无关。
5、多个点
void drawPoints(float[] pts, Paint paint)
void drawPoints(float[] pts, int offset, int count, Paint paint)
这几个参数的含义与多条直线中的参数含义相同。
6、矩形工具类RectF和Rect
RectF与Rect中的方法、成员变量完全一样,都是根据4个点构造出一个矩形结构,唯一不同的是:RectF是用来保存float类型数值的矩形结构的;而Rect是用来保存int类型数值的矩形结构的。
//RectF 的构造函数有如下4个,但最常用的还是第二个,即根据 4 个点构造出一个矩形。
RectF()
RectF(float left, float top, float right, float bottom)
RectF(RectF r)
RectF(Rect r)
//Rect的构造函数有如下3个。
Rect()
Rect(int left, int top, int right, int bottom)
Rect(Rect r)
一般而言,要构造一个矩形结构,可以通过以下两种方法来实现。
//方法一:直接构造
Rect rect = new Rect(10,10,100,100);
//方法二:间接构造
Rect rect = new Rect();
rect.set(10,10,100,100);
7、矩形
矩形的绘制方法
void drawRect(float left, float top, float right, float bottom, Paint paint)
void drawRect(RectF rect, Paint paint)
void drawRect(Rect r, Paint paint)
8、圆角矩形
void drawRoundRect(RectF rect, float rx, float ry, Paint paint)
rect:要绘制的矩形;rx:生成圆角的椭圆的X轴半径;ry:生成圆角的椭圆的Y轴半径。
drawRoundRect()函数不能针对每个角设置对应的椭圆,而只能统一设置4个角对应的椭圆。
9、圆形
void drawCircle(float cx, float cy, float radius, Paint paint)
参数是圆心和半径
10、椭圆
void drawOval(RectF oval, Paint paint)
椭圆是根据矩形生成的,以矩形的长为椭圆的X轴,以矩形的宽为椭圆的Y轴。
11、弧
void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)
弧是椭圆的一部分,而椭圆是根据矩形来生成的,所以弧也是根据矩形来生成的。
oval:生成椭圆的矩形。startAngle:弧开始的角度,以 X 轴正方向为 0°。sweepAngle:弧持续的角度,顺时针方向为正值。
useCenter:是否有弧的两边。为true时,表示带有两边;为false时,只有一条弧。
1.1.4、Rect与RectF
1、是否包含点、矩形
//判断是否包含某个点,如果在,则返回true;如果不在,则返回false。
boolean contains(int x, int y)
//rect.contains(mX, mY)
//判断是否包含某个矩形
Boolean contains(int left, int top, int right, int bottom)
boolean contains(Rect r)
postInvalidate()和 nvalidate()函数都是用来重绘控件的,区别是invalidate()函数一定要在主线程中执行,否则就会报错;而postInvalidate()可以在任何线程中执行。因为在 postInvalidate()函数中就是利用handler给主线程发送刷新界面的消息来实现的,所以它可以在任何线程中执行而不会出错。postInvalidate()界面刷新速度可能没有直接调用invalidate()函数那么快。
2、判断两个矩形是否相交
//这是Rect类的一个静态方法,如果相交则返回true,否则返回false。
static boolean intersects(Rect a, Rect b)
//还可以使用 Rect 类中自带的方法来判断当前 Rect 对象与其他矩形是否相交。
boolean intersects(int left, int top, int right, int bottom)
//判断相交并返回结果
//不仅会返回是否相交的结果,而且会把相交部分的矩形赋给当前Rect对象。如果两个矩形不相交,则当前Rect对象的值不变。
boolean intersect(int left, int top, int right, int bottom)
boolean intersect(Rect r)
3、合并
合并两个矩形的意思就是将两个矩形合并成一个矩形,即无论这两个矩形是否相交,取两个矩形最小左上角点作为结果矩形的左上角点,取两个矩形最大右下角点作为结果矩形的右下角点。如果要合并的两个矩形有一方为空,则将有值的一方作为最终结果。
public void union(int left, int top, int right, int bottom)
public void union(Rect r)
合并矩形与某个点:先判断当前矩形与目标合并点的关系,如果不相交,则根据目标点(x,y)的位置,将目标点设置为当前矩形的左上角点或者右下角点。如果当前矩形是一个空矩形,则最后的结果矩形为([0,0],[x,y]),即结果矩形的左上角点为[0,0],右下角点为[x,y]。
public void union(int x, int y)
1.1.5、Color
当需要重绘时就会调用onDraw()函数,所以在 onDraw()函数中创建的变量会一直被重复创建,这样会引起频繁的程序GC(回收内存),进而引起程序卡顿。所以一定要记住,在onDraw()函数中不能创建变量!一般在自定义控件的构造函数中创建变量,即在初始化时一次性创建。
1.2、路径
1.2.1、概述
Path类就代表路径。在Canvas中绘制路径的方法
void drawPath(Path path, Paint paint)
1.2.2、直线路径
//(x1,y1)是直线的起始点
void moveTo(float x1, float y1)
//(x2,y2)是直线的终点,又是下一次绘制直线路径的起始点; lineTo()函数可以一直使用。
void lineTo(float x2, float y2)
//首尾点连接起来,形成闭环
void close()
1.2.3、弧线路径
//弧线是从椭圆上截取的一部分
void arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
//oval:生成椭圆的矩形。
//startAngle:弧开始的角度,以 X 轴正方向为 0°。sweepAngle:弧持续的角度。
//forceMoveTo 的含义是是否强制将弧的起始点作为绘制起始位置。
1.2.4、addXXX系列函数
addXXX系列函数可以让我们直接往Path中添加一些曲线,而不必考虑连贯性。
添加矩形路径
void addRect(float left, float top, float right, float bottom,Path.Direction dir)
void addRect(RectF rect, Path.Direction dir)
Path.Direction 参数有两个值。Path.Direction.CCW:是指创建逆时针方向的矩形路径。Path.Direction.CW:指创建顺时针方向的矩形路径。
添加圆角矩形路径
void addRoundRect(RectF rect, float[] radii, Path.Direction dir)
void addRoundRect(RectF rect, float rx, float ry, Path.Direction dir)
float[] radii
:必须传入8个数值,分4组,第一组对应第一个角(左上角)的用来生成圆角的椭圆的横轴半径和纵轴半径。在第二个构造函数中,只能构建统一的圆角大小,rx:生成圆角的椭圆的横轴半径,ry:生成圆角的椭圆的纵轴半径。
添加圆形路径
void addCircle(float x, float y, float radius, Path.Direction dir)
x和y:圆心坐标;radius:圆半径。
添加椭圆路径
void addOval(RectF oval, Path.Direction dir)
添加弧形路径
void addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle)
void addArc(RectF oval, float startAngle, float sweepAngle)
oval:生成椭圆的矩形;startAngle:弧开始的角度;sweepAngel:弧持续的角度。
1.2.5、填充模式
Path的填充模式是指填充Path的哪部分。
//Path.FillType 表示 Path 的填充模式,它有 4 个枚举值。
FillType.WINDING:默认值,当两个图形相交时,取相交部分显示。
FillType.EVEN_ODD:取 path 所在并不相交的区域。
FillType.INVERSE_WINDING:取 path 的外部区域。
FillType.INVERSE_EVEN_ODD:取 path 的外部和相交区域。
在利用画笔填充图形时,填充的肯定是图形内部,而 path.setFillType()函数就是用来界定哪里算Path内部的算法,进而让Paint填充这部分图像。
1.2.6、重置路径
系统提供了两个重置路径的方法:
void reset()
void rewind()
共同点是都会清空内部所保存的所有路径。
区别:rewind()
函数不会清除内存,但会清除FillType;而reset()
函数则会清除内存,但不会清除FillType。
1.2.7、蜘蛛网状图示例
1.3、文字
1.3.1、Paint设置
Paint与文字相关的设置:
//普通设置
paint.setStrokeWidth(5); //设置画笔宽度
paint.setAntiAlias(true); //指定是否使用抗锯齿功能
paint.setStyle(Paint.Style.FILL); //绘图样式,对于文字和几何图形都有效
paint.setTextAlign(Align.CENTER); //设置文字对齐方式,取值为Align.CENTER、Align.LEFT 或 Align.RIGHT
paint.setTextSize(12); //设置文字大小
//样式设置
paint.setFakeBoldText(true); //设置是否为粗体文字
paint.setUnderlineText(true); //设置下画线
paint.setTextSkewX((float) -0.25); //字体水平倾斜度,默认为0,普通斜体字设为-0.25,负数代表向右倾斜
paint.setStrikeThruText(true); //设置带有删除线效果
//其他设置
paint.setTextScaleX(2); //水平方向拉伸,高度不变,表示拉伸倍数,默认为1表示不拉伸
1.3.2、Canvas绘制文本
//setTextAlign的显示与x、y的设置有关系
canvas.drawText(String text,Float x,Float y,Paint paint);
1、普通绘制
//参数(x,y)就是起始点坐标
void drawText(String text, float x, float y, Paint paint)
//start:表示起始绘制字符所在字符串中的索引。
//end:表示结束绘制字符所在字符串中的索引。x,y:起始点坐标。
void drawText(CharSequence text, int start, int end, float x, float y, Paint paint)
void drawText(String text, int start, int end, float x, float y, Paint paint)
//绘制char类型的数组所组成的字符串。其他参数的含义如下。index:指定起始绘制字符的位置。
//count:指定从起始绘制字符开始绘制几个字符。x,y:起始点坐标。
void drawText(char[] text, int index, int count, float x, float y, Paint paint)
举例
canvas.drawText("床前明月光",2,4, 10,100, paint);//明月
2、逐个指定文字位置
指定每个要绘制的文字的具体位置
void drawPosText(String text, float[] pos, Paint paint)
void drawPosText(char[] text, int index, int count, float[] pos, Paint paint)
index:第一个要绘制的文字的索引。count:要绘制的文字的个数。pos:要绘制的每个文字的具体位置,两个点确定一个文字的位置。
3、沿路径绘制
void drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint)
void drawTextOnPath (char[] text, int index, int count, Path path, float hOffset,float vOffset, Paint paint)
hOffset:与路径起始点的水平偏移量。vOffset:与路径中心的垂直偏移量。
1.3.3、设置字体样式
在Paint中有一个函数是专门用来设置字体样式的。使用这个函数的前提是必须构造Typeface类的一个参数。
Typeface setTypeface(Typeface typeface)
设置字体样式
Typeface defaultFromStyle(int style)
//参数int style的取值如下。
Typeface.NORMAL:正常字体。
Typeface.BOLD:粗体。
Typeface.ITALIC:斜体。
Typeface.BOLD_ITALIC:粗斜体。
举例,设置粗斜体样式:
Typeface typeface = Typeface.defaultFromStyle(Typeface.BOLD_ITALIC);
paint.setTypeface(typeface);
1.4、Region
Region译为“区域”,区域是一块任意形状的封闭图形。
1.4.1、构造Region
直接构造
public Region(Region region) //复制一个 Region 的范围
public Region(Rect r) //创建一个矩形区域
public Region(int left, int top, int right, int bottom) //创建一个矩形区域
间接构造
间接构造主要是通过空构造函数与set系列函数相结合来实现的。
//Region的空构造函数:
public Region()
//set 系列函数:
public void setEmpty() //置空
public boolean set(Region region)//利用新的区域替换原来的区域
public boolean set(Rect r)
public boolean set(int left, int top, int right, int bottom)
public boolean setPath(Path path, Region clip)//根据路径的区域与某区域的交集构造出新的区域
无论调用set系列函数的Region是不是有区域值,当调用set系列函数后,原来的区域值就会被替换成set系列函数里的区域值。
利用setPath可以设置不规则区域
1.4.2、枚举区域——RegionIterator类
//构造函数:根据区域构建对应的矩形集。
RegionIterator(Region region)
//获取下一个矩形,将结果保存在参数 Rect r 中。
boolean next(Rect r)
Canvas中没有直接绘制Region的函数,想要绘制一个区域,就只能通过RegionIterator类构造矩形集来逼近显示区域。
private void drawRegion(Canvas canvas,Region rgn,Paint paint){
RegionIterator iter = new RegionIterator(rgn);
Rect r = new Rect();
while (iter.next(r)) {
canvas.drawRect(r, paint);
}
}
1.4.3、区域相交
Region不是用来绘图的,Region最重要的功能在区域的相交操作中。
union()函数
boolean union(Rect r)
该函数用于与指定矩形取并集,即将Rect所指定的矩形加入当前区域中。举例:
Region region = new Region(10,10,200,100);
region.union(new Rect(10,10,50,300));
区域操作
方法一:
相交操作,并将结果赋给当前的Region对象。如果计算成功,则返回true;否则返回false。
boolean op(Rect r, Op op)
boolean op(int left, int top, int right, int bottom, Op op)
boolean op(Region region, Op op)
Op参数值有如下6个:
public enum Op {
DIFFERENCE(0), //最终区域为region1区域,但是不包括与region2相交的区域
INTERSECT(1), // 最终区域为region1与region2 相交的区域
UNION(2), //最终区域为region1与region2组合在一起的区域
XOR(3), //最终区域为region1和region2区域,但是不包括相交的区域
REVERSE_DIFFERENCE(4),//最终区域为region2区域,但是不包括与region1相交的区域
REPLACE(5); //最终区域为region2的区域
}
方法二:传入两个 Region 对象进行区域操作
boolean op(Rect rect, Region region, Op op)
boolean op(Region region1, Region region2, Region.Op op)
1.4.4、其他函数
几个判断
//该函数用于判断该区域是否为空
public boolean isEmpty();
//该函数用于判断该区域是否是一个矩阵。
public boolean isRect();
//该函数用于判断该区域是否是多个矩阵的组合。
public boolean isComplex();
getBound系列函数
//这两个函数用于返回能够包裹当前路径的最小矩形。
public Rect getBounds()
public boolean getBounds(Rect r)
//这两个函数用于返回当前矩形所对应的 Path 对象。
public Path getBoundaryPath()
public boolean getBoundaryPath(Path path)
是否包含
//该函数用于判断该区域是否包含某个点。
public boolean contains(int x, int y)
//这两个函数用于判断该区域是否包含某个矩形。
public boolean quickContains(Rect r)
public boolean quickContains(int left, int top, int right,int bottom)
是否相交
//这两个函数用于判断该区域是否没有和指定矩形相交。
public boolean quickReject(Rect r)
public boolean quickReject(int left, int top, int right, int bottom);
//该函数用于判断该区域是否没有和指定区域相交。
public boolean quickReject(Region rgn);
平移变换
//该函数用于将Region对象向X轴平移dx距离,向Y轴平移dy距离,并将结果赋给当前的Region对象。X轴向右是正方向,Y轴向下是正方向。
public void translate(int dx, int dy)
//与上一个函数不同的是,该函数将结果赋给dst对象,而当前Region对象的值保持不变。
public void translate(int dx, int dy, Region dst)
1.5、Canvas(画布)
除了在Canvas上绘图以外,还可以对画布进行变换及裁剪等操作。
1.5.1、Canvas变换
平移(Translate)
右是X轴正方向,向下是Y轴正方向。
void translate(float dx, float dy)
举例
canvas.translate(100, 100);
Rect rect = new Rect(0,0,400,220);
canvas.drawRect(rect, paint);
屏幕显示与Canvas的关系
每次调用drawXXX系列函数来绘图时,都会产生一个全新的Canvas透明图层。调用平移、旋转等函数对Canvas进行了操作,不会对之前已经draw的产生影响。
旋转(Rotate)
//旋转的中心点是原点(0,0)
void rotate(float degrees)
//指定旋转的中心点坐标(px,py)
void rotate(float degrees, float px, float py)
缩放(Scale)
//sx是小数表示缩小,sx是整数表示放大
public void scale(float sx, float sy)
//px,py表示缩放中心位置
public void scale(float sx, float sy, float px, float py)
扭曲(Skew)
//sx为X轴方向倾斜角度的正切值,比如在X轴方向上倾斜60°,tan60=1.732
void skew(float sx, float sy)
举例
canvas.skew(1.732f,0);//X 轴倾斜 60°, Y 轴不变
裁剪画布(clip系列函数)
一旦Canvas被裁剪,就不能恢复。在使用clip系列函数时,需要禁用硬件加速功能,setLayerType(LAYER_TYPE_SOFTWARE,null)
。
clip 系列函数如下:
boolean clipPath(Path path)
boolean clipPath(Path path, Region.Op op)
boolean clipRect(Rect rect, Region.Op op)
boolean clipRect(RectF rect, Region.Op op)
boolean clipRect(int left, int top, int right, int bottom)
boolean clipRect(float left, float top, float right, float bottom)
boolean clipRect(RectF rect)
boolean clipRect(float left, float top, float right, float bottom, Region.Op op)
boolean clipRect(Rect rect)
boolean clipRegion(Region region)
boolean clipRegion(Region region, Region.Op op)
1.5.2、画布的保存与恢复
save()和restore()函数
每次调用save()函数,都会先保存当前画布的状态,然后将其放入特定的栈中。每次调用restore()函数,都会把栈中顶层的画布状态取出来,并按照这个状态恢复当前的画布,然后在这个画布上作画。
restoreToCount(int saveCount)函数
在利用save()函数保存画布时,会有一个int类型的返回值。该返回值是当前所保存的画布所在栈的索引。restoreToCount()函数就是一直出栈,直到指定索引的画布出栈为止,即将指定索引的画布作为当前画布。
public void restoreToCount(int saveCount);
1.5.3、圆形头像示例
1.5.4、裁剪动画示例
1.6、控件的使用方法
1.6.1、控件概述
1.6.2、通过XML引入控件
通过XML引入控件,所调用的构造函数:
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
1.6.3、动态添加控件
LinearLayout rootView = (LinearLayout)findViewById(R.id.root);
CustomView customView = new CustomView(this);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
rootView.addView(customView,layoutParams);
LayoutParams的作用就是设置控件的宽和高,对应的是XML中的layout_width
和layout_height
属性。
LayoutParams有三个构造函数:
//指定具体的宽和高:LayoutParams.MATCH_PARENT或者具体值
public LayoutParams(int width, int height)
//从AttributeSet中提取出宽高等属性值
public LayoutParams(Context c, AttributeSet attrs)
//复制一份LayoutParams
public LayoutParams(LayoutParams source)
LinearLayout、FrameLayout、RelativeLayout都有各自的LayoutParams。
addRule
RelativeLayout.LayoutParams的addRule()函数:第一个参数是指RelativeLayout的布局属性,第二个参数是指相对于哪个控件ID来布局。
RelativeLayout rootView = (RelativeLayout) findViewById(R.id.root);
CustomView customView = new CustomView(this);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
layoutParams.addRule(RelativeLayout.RIGHT_OF,R.id.text);
rootView.addView(customView, layoutParams);
设置margin
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(10, 20, 30, 40);
imageView.setLayoutParams(lp);
设置layout_weight(方法一)
构造函数
public LayoutParams(int width, int height, float weight)
示例:
TextView tv_like = new TextView(this);
LinearLayout.LayoutParams LP_LIKE_MW = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f);
tv_like.setText("赞(8)");
tv_like.setTextSize(16);
layout_sub_Lin.addView(tv_like, LP_LIKE_MW);
设置layout_weight(方法二)
LinearLayout rootView = (LinearLayout) findViewById(R.id.root);
CustomView customView = new CustomView(this);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
layoutParams.weight = 1.0f;
rootView.addView(customView, layoutParams);
设置layout_gravity
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.FILL_PARENT);
params.gravity = Gravity.TOP;
params.gravity的取值有Gravity.TOP
、Gravity.BOTTOM
、Gravity.LEFT
、Gravity.RIGHT
、Gravity.CENTER_VERTICAL
、Gravity.CENTER_HORIZONTAL
等这些属性值可|
(或)运算符合并。
设置android:gravity
分别给Button和rootView设置
Button button = new Button(this);
button.setGravity(Gravity.TOP);
button.setText("btn");
rootView.addView(button, layoutParams);
rootView.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
addView
public void addView(View child, int index)
在指定位置添加一个View控件,index的取值有-1、0和正数。当取值为-1时,表示在末尾添加一个View控件,此时的效果就与addView(View child)相同;当取值为0时,表示在容器顶端添加一个View控件;当取值为正数时,表示在对应的索引位置插入一个View控件。
第2章、视图动画
两种类型的动画:View Animation
(视图动画)和Property Animation
(属性动画)。其中,View Animation
包括Tween Animation
(补间动画)和Frame Animation
(逐帧动画);Property Animation
包括ValueAnimator
和ObjectAnimator
。
2.1、视图动画标签
2.1.1、概述
alpha(渐变透)、scale(缩放)、translate(位置移动)、rotate(旋转)set(动画集)。
配置XML动画文件
//首先加载动画
Animation animation = AnimationUtils.loadAnimation(Activity.this,R.a aim.scaleanim);
//然后利用View的startAnimation()函数开始动画
tv.startAnimation(animation);
2.1.2、scale标签
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="700"
android:fillBefore="true"
android:fromXScale="1.0"
android:fromYScale="1.2"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="1"
android:repeatMode="reverse"
android:toXScale="0.4"
android:toYScale="0.6" />
android:fromXScale:动画起始时,控件在X轴方向上相对自身的缩放比例,浮点值,比如1.0 代表自身无变化,0.5代表缩小1倍,2.0代表放大1倍。
android:toXScale:动画结束时,控件在X轴方向上相对自身的缩放比例,浮点值。
android:fromYScale和android:toYScale类似。
android:pivotX:缩放起始点X轴坐标,可以是数值、百分数、百分数p三种样式,如50、50%、50%p。如果是数值,则表示在当前视图本身View的左上角,即原点处加上50px,作为缩放起始点X轴坐标;如果是50%,则表示在当前控件的左上角加上自己宽度的50%作为缩放起始点X轴坐标;如果是50%p,则表示在当前控件的左上角加上父控件宽度的50%作为缩放起始点X轴坐标。
android:pivotY:与android:pivotX一样用法。
Animation继承属性
android:duration:用于设置完成一次动画的持续时间,以毫秒为单位。
android:fillAfter:如果设置为true,则控件动画结束时,将保持动画结束时的状态。
android:fillBefore:如果设置为true,则控件动画结束时,将还原到初始化状态。
android:fillEnabled:与android:fillBefore效果相同。
android:repeatCount:用于指定动画的重复次数,当取值为infinite时,表示无限循环。
android:repeatMode:用于设定重复的类型,有reverse和restart两个值。其中,reverse表示倒序回放:restart表示重放,并且必须与repeatCount一起使用才能看到效果。
android:interpolator:用于设定插值器,其实就是指定的动画效果,比如弹跳效果等。
2.1.3、alpha标签
透明度动画效果。
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fillBefore="true"
android:fromAlpha="1.0"
android:toAlpha="0.1">
</alpha>
android:fromAlpha:开始时的透明度,android:toAlpha:结束时的透明度。0.0表示全透明,1.0表示完全不透明。
2.1.4、rotate标签
旋转动画效果。
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fillAfter="true"
android:fromDegrees="0"
android:pivotX="100%"
android:pivotY="100%"
android:toDegrees="-650">
</rotate>
android:fromDegrees:动画开始旋转时的角度位置;android:toDegrees:动画结束时旋转到的角度位置。正值代表顺时针方向的度数,负值代表逆时针方向的度数。
android:pivotX:旋转中心点X轴坐标;android:pivotY:旋转中心点Y轴坐标。没有指定android:pivotX与android:pivotY属性,旋转中心点是默认的控件坐标原点,即控件左上角位置。
2.1.5、translate标签
位置移动动画效果。
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="-80"
android:toYDelta="-80">
</translate>
android:fromXDelta:起始点X轴坐标,android:fromYDelta:起始点Y轴坐标,android:toXDelta:终点X轴坐标,android:toYDelta:终点Y轴坐标。可以是数值、百分数、百分数p三种样式。
2.1.6、set标签
定义动画集。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:fillAfter="true">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0" />
<scale
android:fromXScale="0.0"
android:fromYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.4"
android:toYScale="1.4" />
<rotate
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="720" />
</set>
在set标签中设直repeateCount属性是无效的,必须对每个动画单独设直才有作用。
2.2、视图动画的代码实现
2.2.1、概述
setDuration(long)
setFillAfter(boolean)
setFillBefore(boolean)
setFillEnabled(boolean)
setRepeatCount(int)
setRepeatMode(int)
setlnterpolator(lnterpolator)
setRepeatMode(int)取值为Animation.RESTART或者Animation.REVERSE;setRepeatCount(int)用于设置循环次数,当设置为Animation.INFINITE时,表示无限循环。
2.2.2、ScaleAnimation
ScaleAnimation(float fromX , float toX, float fromY , float toY)
ScaleAnimation(float fromX , float toX , float fromY , float toY , float pivotX, float pivotY)
ScaleAnimation(float fromX , float toX , float fromY , float toY, intpivotXType,float pivotXVal ue , int pivotYType , float pivotYValue)
pivotXType的取值有三个:Animation.ABSOLUTE
、Animation.RELATIVE_TO_SELF
和Animation.RELATIVE_TO_PARENT
。对应的是android:pivotX
中的三种取值样式:数值、百分数、百分数p。
ScaleAnimation scaleAnim = new ScaleAnimation(O.Of , 1.4f, O.Of, 1.4f, Animation.RELATIVE_TO_SELF , 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnim.setDuration(700);
tv.startAnimation(scaleAnim);
2.2.3、AlphaAnimation
AlphaAnimation(Context context , AttributeSet attrs)
AlphaAnimation(float fromAlpha , float toAlpha)
示例
AlphaAnimation alphaAnim = new AlphaAnimation(1.Of, 0.1f);
alphaAnim.setDuration(3000);
alphaAnim.setFillBefore(true);
tv.startAnimation(alphaAnim);
2.2.4、RotateAnimation
RotateAnimation(Context context , AttributeSet attrs)
RotateAnimation(float fromDegrees , float toDegrees)
RotateAnimation(float fromDegrees , float toDegrees , float pivotX,float pivotY)
RotateAnimation (float fromDegrees , float toDegrees , int pivotXType , float pivotXValue , int pivotYType , float pivotYValue)
示例
RotateAnimation rotateAnim = new RotateAnimation(0 , -650, Animation.RELATIVE_TO_SELF, O.5f , Animation.RELATIVE_TO_SELF , 0.5f);
rotateAnim.setDuration(3000);
rotateAnim.setFillAfter(true);
tv.startAnimation(rotateAnim);
2.2.5、TranslateAnimation
TranslateAnimation(Context context , AttributeSet attrs)
TranslateAnimation(float fromXDelta ,float toXDelta, float fromYDelta, float toYDelta)
TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue , int fromYType , float fromYValue , int toYType , float toYValue)
示例
TranslateAnimation translateAnim = new TranslateAnimation(Animation.ABSOLUTE, 0, Animation.ABSOLUTE, -80, Animation.ABSOLUTE, 0, Animation.ABSOLUTE, -80);
translateAnim.setDuration(2000);
translateAnim.setFillBefore(true);
tv.startAnimation(translateAnim);
2.2.6、AnimationSet
构造函数
AnimationSet(Context context, AttributeSet attrs)
AnimationSet(boolean shareinterpolator)
shareinterpolator当取值为true,在AnimationSet类中定义一个插值器(Interpolator)其下面的所有动画共用该插值器;当取值为false,则表示其下面的动画定义各自的插值器。
增加动画的函数为:
public void addAnimation(Animation a)
示例
Animation alpha_Anim = new AlphaAnimation(1.0f, 0.1f);
Animation scale_Anim = new ScaleAnimation(0.0f, 1.4f, 0.0f, 1.4f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
Animation rotate_Anim = new RotateAnimation(0, 720, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
AnimationSet setAnim = new AnimationSet(true);
setAnim.addAnimation(alpha_Anim);
setAnim.addAnimation(scale_Anim);
setAnim.addAnimation(rotate_Anim);
setAnim.setDuration(3000);
setAnim.setFillAfter(true);
tv.startAnimation(setAnim);
2.2.7、Animation
//取消动画
void cancel()
//将控件重置到动画开始前状态
void reset()
//设置动画监昕
void setAnimationListener(Animation.AnimationListener listener)
监听:
scaleAnim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
2.3、插值器初探
Interpolator只是一个接口,通过实现这个接口就可以自定义动画的变化速率。
使用:
//xml中
android:interpolator=”@android:anim/accelerate_interpolator”
//代码
alphaAnim.setinterpolator(new LinearInterpolator());
2.3.1、AccelerateDeceleratelnterpolator
加速减速插值器,表示在开始与结束的地方速率改变比较慢,在中间的时候加速。效果是先加速后减速。
2.3.2、Acceleratelnterpolator
动画开始的地方速率改变比较慢,然后开始加速。动画一直是加速的。
2.3.3、Deceleratelntelnterpolator
动画开始的一瞬间加速到最大值,然后逐渐变慢。
2.3.4、Linearlnterpolator
动画的速率始终保持恒定。
2.3.5、Bouncelnterpolator
弹跳插值器,模拟了控件自由落地后回弹的效果。
2.3.6、Anticipatelnterpolator
动画开始后,会先往动画反方向移动一段距离,再应用动画。
Anticipatelnterpolator还有一个构造函数:
public Anticipateinterpolator(float tension)
参数tension
对应的XML属性为android:tension
,表示张力值,默认值为2,值越大,初始的偏移量越大,而且速度越快。
2.3.7、Overshootlnterpolator
结束偏移插值器,表示在动画结束时,沿动画方向继续运动一段距离后再结束动画。
Overshootlnterpolator也有另一个构造函数:
public Overshootinterpolator(float tension)
参数tension
对应的XML属性为android:tension
,表示张力值,默认值为2,值越大,结束时的偏移量越大,而且速度越快。
2.3.8、AnticipateOvershootlnterpolator
AnticipateOvershootlnterpolator是Anticipatelnterpolator与Overshootlnterpolator的合体,即在动画开始时向前偏移一段距离,在动画结束时向后偏移一段距离。
AnticipateOvershootlnterpolator也有其他的构造函数
public AnticipateOvershootlnterpolator(float tension)
public AnticipateOvershootlnterpolator(float tension, float extraTension)
tension默认值为2,extraTension默认值为1.5。
2.3.9、Cyclelnterpolator
循环插值器,表示动画循环播放特定的次数,速率沿正弦曲线改变。
Cyclelnterpolator的构造函数如下:
public Cycleinterpolator(float cycles)
参数cycles表示循环次数。
2.4、动画示例
2.4.1、镜头由远及近效果
2.4.2、加载框效果
2.4.3、扫描动画效果
用Animation.setStartOffset(int time)
来延迟各个动画的开始时间。
2.5、逐帧动画
Frame Animation
2.5.1、XML实现
1、概述
XML文件放置在/res
下的drawable目录中:
<?xml version="1.0" encoding="UTF-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@drawable/list_icon_gif_playing1"
android:duration="60" />
<item
android:drawable="@drawable/list_icon_gif_playing2"
android:duration="60" />
<item
android:drawable="@drawable/list_icon_gif_playing3"
android:duration="60" />
<item
android:drawable="@drawable/list_icon_gif_playing4"
android:duration="60" />
<item
android:drawable="@drawable/list_icon_gif_playing5"
android:duration="60" />
</animation-list>
android:oneshot
如果定义为true,那么此动画只会执行1次;如果定义为false,则一直循环。
然后给ImageView设置动画资源。可以通过android:src
实现,也可以通过android:background
实现。
android:background="@drawable/playing_ani"
//或者
android:src="@drawable/playing_ani"
最后代码中开始动画
AnimationDrawable anim = (AnimationDrawable) image.getDrawable();
anim.start();
当我们通过android:src=””
设置动画资源时,对应的取出方式是image.getDrawable()
如果我们通过android:background=””
设置动画资源,那么对应的取出方式就是image.getBackground()
。
2、音乐播放示例
3、AnimationDrawable类
AnimationDrawable有下面几个常用函数
void start()//开始播放逐帧动画
void stop()//停止播放逐帧动画
int getDuration(int index)//得到指定index的帧的持续时间
Drawable getFrame(int index)//得到指定index的帧所对应的Drawable对象
int getNumberOfFrames()//得到当前AnimationDrawable的所有帧数量
boolean isRunning() //判断当前AnimationDrawable是否正在播放
void setOneShot(boolean oneShot)//设置AnimationDrawable是否执行一次,true表示执行一次,false表示循环播放
boolean isOneShot() //判断当前AnimationDrawable是否执行一次,true表示执行一次,返回false表示循环播放。
void addFrame(Drawable frame,int duration)//为AnimationDrawable添加1帧,并设置持续时间。
2.5.2、代码实现
<ImageView
android:id="@+id/frame_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
代码
AnimationDrawable anim = new AnimationDrawable();
for (int i = 1; i <= 14; i++) {
int id = getResources().getIdentifier("list_icon_gif_playing" + i,"drawable", getPackageName());
Drawable drawable = getResources().getDrawable(id);
anim.addFrame(drawable, 60);
}
anim.setOneShot(false);
image.setBackgroundDrawable(anim);
anim.start();
getldentifier()函数的完整声明如下:
int getldentifier(String name, String defType, String defPackage)
//获得string
getResources().getldentifier(”name”,”string”, packdgeName);
//获得array中的数组:
getResources().getidentifier(”name”,”array”, packdgeName);
第3章、属性动画
3.1、ValueAnimator的基本使用
3.1.1、概述
View Animation中的动画类命名都是XXXXAnimation,而Property Animation中的动画类命名都是XXXXAnimator。
视图动画仅能对指定的控件做动画,而属性动画是通过改变控件的某一属性值来做动画的。
3.1.2、ValueAnimator的简单使用
ValueAnimator不会对控件执行任何操作,我们可以给它设定从哪个值运动到哪个值,通过监听这些值的渐变过程来自己操作控件。
//创建实例
ValueAnimator animator = ValueAnimator.ofInt(0, 400);
animator.setDuration(1000);
//添加监昕事件
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int curValue = (Integer)animation.getAnimatedValue() ;
Log.d("qijian","curValue:" + curValue);
}
});
//开启动画
animator.start();
改变控件位置
view.layout();
3.1.3、常用函数
public static ValueAnimator ofint (int... values)
public static ValueAnimator ofFloat(float... values)
//设置动画时长,单位是毫秒
ValueAnimator setDuration(long duration)
//获取 ValueAnimator 在运动时当前运动点的值
Object getAnimatedValue()
//开始动画
void start()
//设置循环次数,设置为ValueAnimator.INFINITE表示无限循环
void setRepeatCount(int value)
//设置循环模式有ValueAnimator.RESTART和ValueAnimator.REVERSE
void setRepeatMode(int value)
//取消动画
void cancel()
重复次数为INFINITE的动画,当Activity结束的时候,必须调用cancel()函数取消动画,否则动画将无限循环,从而导致View无法释放,进一步导致整个Activity无法释放,最终引起内存泄漏。
//监听动画过程中值的实时变化,添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}
//听动画变化时的4个状态,添加方法为:public void addListener(AnimatorListener listener)
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
移除监听
void removeUpdateListener(AnimatorUpdateListener listener);
void removeAllUpdateListeners();
void removeListener(AnimatorListener listener);
void removeAllListeners();
其他不常用函数
//延时多久开始,单位是毫秒
public void setStartDelay(long startDelay)
//完全克隆一个ValueAnimator实例,包括它所有的设置以及所有对监听器代码的处理
public void Animatorclone()
3.1.4、弹跳加载申效果示例
通过setTop(int top)
函数将控件移动到当前位置。这里需要说明的是,getTop
和setTop
函数所得到的和设置的坐标都是相对父控件的坐标位置。
3.2、自定义插值器与Evaluator
对于Animator而言,不仅可以设置插值器,还可以设置Evaluator。
3.2.1、自定义插值器
系统插值器实现的接口:
public interface TimeInterpolator {
float getInterpolation(float input);
}
参数input:取值范围是0~1,表示当前动画的进度,只与时间有关,取0时表示动画刚开始,取1时表示动画结束。
返回值:表示当前实际想要显示的进度。取值可以超过1,也可以小于0。超过1表示己经超过目标值,小于0表示小于开始位置。
自定义插值器,只需实现Timelnterpolator
接口就可以了。
3.2.2、Evaluator
流程:oflnt(0,400)定义动画数值区间 -> 插值器(返面当前数值进度,如0.2) -> Evaluator(根据数值进度计算当前值) -> 监听器退回(在AnimatorUpdatelistener中返回)。
Evaluator用于将从插值器返回的数值进度转换成对应的数值。
public class IntEvaluator Implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction , Integer startValue , Integer endValue) {
int startint = startValue;
return (int) (startint + fraction * (endValue - startint));
}
}
fraction
参数就是插值器中的返回值,表示当前动画的数值进度,以小数表示。startValue
和endValue
分别对应 oflnt(int start,int end)
函数中start
和end
的数值。返回值就是在AnimatorUpdateListener
监听器中通过animation.getAnimatedValue()
函数得到的数值。
使用
animator.setEvaluator(new IntEvaluator());
所以既可以通过重写插值器改变数值进度来改变数值位置,也可以通过改变Evaluator中数值进度所对应的具体数值来改变数值位置。
ArgbEvaluator
除 IntEvaluator
和 FloatEvaluator
外,还有一个名为 ArgbEvaluator
,它是用来实现颜色值过渡转换的。
ValueAnimator animator = ValueAnimator.ofInt(0xffffff00,0xff0000ff);
animator.setEvaluator(new ArgbEvaluator());
animator.setDuration(3000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
int curValue = (Integer) animation.getAnimatedValue();
tv.setBackgroundColor(curValue);
}
});
animator.start();
3.3、ValueAnimator进阶ofObject
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object ...values)
TextView中的字母从A变化到Z实例
3.3.2、示例:抛物动画
3.4、ObjectAnimator
3.4.1、概述
ObjectAnimator
是派生自ValueAnimator
的,所以ValueAnimator
中所能使用的函数在ObjectAnimator
中都可以正常使用。
public static ObjectAnimator ofFloat(Object target,String propertyName,float ...values)
在View中,有关动画共有下面几组set函数。
// 1. 透明 度: alpha
public void setAlpha(float alpha)
// 2. 旋转度数 : rotation 、 rotationX 、 rotationY
public void setRotation(float rotation)
public void setRotationX(float rotationX)
public void setRotationY(float rotationY)
// 3. 平移 : translationX 、 translationY
public void setTranslationX(float translationX)
public void setTranslationY(float translationY)
// 4. 缩放 : scaleX 、 scaleY
public void setScaleX(float scaleX)
public void setScaleY(float scaleY)
3.4.2、ObjectAnimator动画原理
ofFloat(tv, "scaleY",0,3,1)
(定义动画对象及区间) —> 插值器(返面当前数值进度,如0.2) -> Evaluator(根据数值进度计算当前值) -> 调用set函数(根据属性拼装set函数并反射调用,并将当前值作为参数传入)。
ObjectAnimator 只负责把动画过程中的数值传到对应属性的set函数中就结束了。
3.4.3、自定义ObjectAnimator属性
抛物动画例子
3.4.4、何时需要实现对应属性的get函数
当动画只有一个过渡值时,系统才会调用对应属性的get函数来得到动画的初始值。当不存在get函数时,则会取动画参数类型的默认值作为初始值;当无法取得动画参数类型的默认值时,则会直接崩渍。
3.4.5、常用函数
//设置动画时长,单位是毫秒
ValueAnimator setDuration(long duration)
//获取 ValueAnimator 在运动时,当前运动点的值
Object getAnimatedValue () ;
//开始动画
void start ()
//设置循环次数,设置为 INFINITE 表示无限循环
void setRepeatCount(int value)
//设置循环模式,value 取值有 RESTART 和 REVERSE
void setRepeatMode (Int value)
//取消动画
void cancel()
//监听动画变化时的实时值
//添加方法为 public void addUpdateListener(AnimatorUpdateListener listener
public static interface AnimatorUpdateListener {
void onAnimatorUpdate(ValueAnimator animation);
)
//监听器二 : 监听动画变化时的 4 种状态
//添加方法为 public void addListener(AnimatorListener listener)
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
//设置插值器
public void setInterpolator(Timeinterpolator value)
//设置 Evaluator
public void setEvaluator(TypeEvaluator value)
3.5、组合动画AnimatorSet
3.5.1、playSequentially与playTogether
playSequentially:动画依次播放,playTogether:所有动画一起开始。
playTogether和playSequentially函数在开始动画时,只是把每个控件的动画激活,至于每个控件自身的动画是否延时、是否无限循环,只与控件自身的动画设定有关,与playTogether和 playSequentially函数无关,它们只负责到时间后激活动画。
playSequentially函数只有在上一个控件做完动画以后,才会激活下一个控件的动画。如果上一个控件的动画是无限循环的,那么下一个控件就别再指望能做动画了。
3.5.2、AnimatorSet.Builde
//表示要播放哪个动画
public Builder play (Animator anim)
//和前面的动画一起执行
public Builder with(Animator anim)
//先执行这个动画,再执行前面的动画
public Builder before (Animator aηim)
//在执行前面的动画后才执行该动画
public Builder after(Animator anim)
//延迟 n 毫秒之后执行动画
public Builder after(long delay)
3.5.3、AnimatorSet监昕器
public void addListener(AnimatorListener listener);
3.5.4、常用函数
//设置单次动画时长
public AnimatorSet setDuration(long duration);
//设置插值器
public void setinterpolator(Timeinterpolator interpolator)
//设置 ObjectAnimator 动画目标控件
public void setTarget(Object target)
在AnimatorSet中设置以后,会覆盖单个ObjectAnimator中的设置。
//设置延时开始动画时长
public void setStartDelay(long startDelay)
不会覆盖单个动画的延时,而且仅针对性地延长AnimatorSet的激活时间。
3.5.5、示例:路径动画
代码
https://github.com/AdamRight/TeaTool/blob/master/app/src/main/java/com/tea/teatool/pathMenu/PathMenuActivity.java
3.6、Animator动画的XML实现
在 XML 中与 Animator 对应的有三个标签:
<animator/>:对应ValueAnimator
<objectAnimator/> 对应ObjectAnimator
<set/>对应 AnimatorSet
ValueAnimator valueAnimator = (ValueAnimator)Animatorinflater.loadAnimator(MyActivity.this , R.animator.animator);
valueAnimator.start();
ObjectAnimator animator = (ObjectAnimator) Animatorinflater.loadAnimator(MyActivity.this , R.animator.object_animator);
animator.setTarget(mTvl);
animator.start();
第4章属性动画进阶
4.1、PropertyValuesHolder与Keyframe
通过ofPropertyValu巳sHolder
函数来创建实例。
4.1.1、PropertyValuesHolder
ofFloat
PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofFloat("Rotation", 60f, -60f, 40f, -40f, -20f, 20f, 10f, -10f, 0f);
PropertyValuesHolder alphaHolder = PropertyValuesHolder.ofFloat("alpha", 0.1f, 1f, 0.1f, 1f);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mTextView, rotationHolder, alphaHolder);
animator.setDuration(3000);
animator.start();
ofObject
public static PropertyValuesHolder ofObject (String propertyName ,TypeEvaluator evaluator, Object . .. values )
4.1.2、Keyframe
关键帧:解决控制动画速率的问题。
public static Keyframe ofFloat(float fraction , float value)
fraction:表示当前的显示进度,即在插值器中getlnterpolation函数的返回值。
value:表示动画当前所在的数值位置。
设置插值器:
public void setinterpolator(Timeinterpolator interpolator)
使用ofObject函数来制作动画的时候,必须设置Evaluator,因为系统根本无法知道动画的中间值Object真正是什么类型的。
4.1.3 PropertyValuesHolde其他函数
4.1.4、电话晌铃效果示例
借助Keyframe,不需要使用AnimatorSet,也能实现多个动画同时播放。
4.2、ViewPropertyAnimator
4.2.1、概述
4.2.2、常用函数
tv.animate().scaleX(2).scaleY(2).setListener(new Animator.AnimatorListener() {
public void onAnimationStart(Animator animation ) {}
public void onAnimationEnd (Animator animation ) {}
public void onAnimationCancel(Animator animation) {}
public void onAnimationRepeat(Animator animation) {}
});
4.3、为ViewGroup内的组件添加动画
4.3.1、animatelayoutChanges属性
android:animateLayoutChanges=”true/false”
,所有派生自ViewGroup类的控件都具有此属性。动画不能自定义。
4.3.2、LayoutTransition
第一步,创建实例。
LayoutTransition transitioner = new LayoutTransition();
第二步创建动画并进行设置。
ObjectAnimator animOut = ObjectAnimator.ofFloat(null,”rotation”, Of,90f, Of);
transitioner.setAnimator(LayoutTransition.DISAPPEARING , animOut);
第三步将LayoutTransition设置到ViewGroup中。
linearLayout.setLayoutTransition(transitioner);
其中,transitionType表示当前应用动画的对象范围,取值如下:
APPEARING:元素在容器中出现时所定义的动画。
DISAPPEARING:元素在容器中消失时所定义的动画。
CHANGE_APPEARING:由于容器中要显现一个新的元素,其他需要变化的元素所应用的动画。
CHANGE_DISAPPEARING:当容器中某个元素消失时,其他需要变化的元素所应用的动画。
4.3.3、其他函数
4.4、开源动画库NineOldAndroids
第5章动画进阶
5.1、利用PathMeasure实现路径动画
5.1.1、初始化
方式一:
PathMeasure pathMeasure = new PathMeasure() ;
setPath (Path path , boolean forceClosed);
方式二:
PathMeasure(Path path , boolean forceClosed);
boolean forceClosed
表示Path最终是否需要闭合,如果为true,则不管关联的Path是否是闭合的,都会被闭合。对绑定的Path不会产生任何影响,PathMeasure的计算就会包含最后一段闭合的路径,与原来的Path不同。
5.1.2、简单函数使用
//获取计算的路径长度
PathMeasure.getLength()
//判断测量Path时是否计算闭合
public boolean isClosed()
//函数得到的曲线的顺序与Path中添加的顺序相同。getLength等函数针对的都是当前的曲线,而不是整个Path。
PathMeasure.nextContour()
5.1.3、getSegment函数
//截取整个Path中的某个片段,将截取后的 Path 保存到参数 dst 中。
boolean getSegrneηt(float startD ,float stopD, Path dst, boolean startWithMoveTo)
startD、stopD:开始截取位置距离 Path 起始点的长度;结束截取位置距离 Path 起始点的长度。
Path dst:截取的Path将会被添加到dst中。注意是添加,而不是替换。
boolean startWithMoveTo:起始点是否使用 moveTo。
5.1.4、getPosTan函数
//用于得到路径上某一长度的位置以及该位置的正切值
boolean getPosTan(float distance , float[] pos , float[] tan)
float distance
:距离Path起始点的长度。float[] pos
:该点的坐标值。pos[O]
表示x坐标,pos[1]
表示y坐标。float[] tan
:该点的正切值。
箭头加载动画示例
5.1.5、getMatrix函数
//用于得到路径上某一长度的位置以及该位置的正切值的矩阵
boolean getMatrix(float distance , Matrix matrix , int flags)
5.1.6、支付宝支付成功动画示例
5.2、SVG动画
5.2.1、概述
SVG 的全称是Scalable Vector Graphics
(可缩放矢量图形〉。矢量图相对应的是位图,Bitmap就是位图。
对于5.0以下的机型,可以通过引入 com.android.support:appcompat-v7:23.4.0
及以上版本进行支持。
5.2.2、vector标签与图像显示
vector标签指定的是画布大小,path标签则指定的是路径内容。
Android工程中使用SVG图像。
5.2.3、动态Vector
5.2.4、输入搜索动画示例
第6章Paint基本使用
6.1、硬件加速
GPU的英文全称为Graphic Processing Unit
,中文翻译为“图形处理器”。GPU是专门为处理图形任务而产生的芯片。在GPU加速时,实际上是使用OpenGL的相关函数来完成绘制的。
Android提供了不同的禁用方法,分Application、Activity、Window、View4个层级。
在AndroidManifest.xml
文件中为application标签添加如下属性,即可为整个应用程序开启/关闭硬件加速。
<application android:hardwareAccelerated=”true”...>
在activity标签下开启或关闭硬件加速:
<activity android:hardwareAccelerated=”false” />
在Window层级上使用如下代码开启硬件加速(在Window层级上不支持关闭硬件加速:
getWiηdow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
在View层级上使用如下代码关闭硬件加速(在View层级上不支持开启硬件加速)
setLayerType(View.LAYER_TYPE_SOFTWARE,null};
或者使用android:layerType="software"
来关闭硬件加速。
6.2、文字
6.2.1、概述
基线就是四线格中的第三条线。
public void drawText (String text , float x , float y , Paint paint)
y代表的是基线的位置,而不是左上角。x代表所要绘制的文字所在矩形的相对位置。
Align的取值为 Paint.Align.LEFT、Paint.Align.CENTER、Paint.Align.RIGHT
Paint :: setTextAlign(Align align);
6.2.2、绘图四线搭与FontMetrics
ascent:系统推荐的,在绘制单个字符时,字符应当的最高高度所在线。
descent:系统推荐的,在绘制单个字符肘,宇符应当的最低高度所在线。
top:可绘制的最高高度所在线。
bottom:可绘制的最低高度所在线。
ascent变量的值是负数。descent变量的值必然是正数。
ascent线的y坐标 = baseline线的y坐标 + fontMetric.ascent
descent线的y坐标 = baseline线的y坐标 + fontMetric.descent
top线的y坐标 = baseline线的y坐标 + fontMetric.top
bottom线的y坐标 = baseline线的y坐标 + fontMetric.bottom
获取 FontMetrics 对象
Paint paint = new Paint();
Paint.FontMetrics fm = paint.getFontMetrics();//Float类型
Paint.FontMetricsint fmint = paint.getFontMetricsint();//Int类型
6.2.3、常用函数
字符串所占区域的高度、宽度和最小矩形。
高度:bottom线所在位置的y坐标减去top线所在位置的y坐标
Paint.FontMetricsint fm = paint.getFontMetricsint();
int top = baseLineY + fm.top;
int bottom = baseLineY + fm.bottom;
//所占区域的高度
int height = bottom - top;
宽度:
int width = paint.measureText(String text);
最小矩形
//以(0, 0)点所在位置为基线,text要测量最小矩形的字符串,start 要测量起始字符在字符串中的索引,
//end 所要测量的字符的长度,bounds接收测量结果
public void getTextBounds(String text, int start, int end, Rect bounds);
矩形实际位置的坐标如下
Rect minRect = new Rect();
paint.getTextBounds(text , 0 , text. length(), minRect);
//最小矩形,实际top线的位置
int minTop =bounds.top + baselineY;
//最小矩形,实际 bottom 线的位置
int minBottom = bounds.bottom + baselineY;
6.2.4、定点写字示例
当给定中间线位置以后,baseline线的位置为:
baseline = center + (FontMetrics.bottom - FontMetrics.top)/2 - FontMetrics.bottom
6.3、Paint常用函数
6.3.1、基本设置函数
reset()
setColor(int color)
setARGB(int a, int r, int g, int b)
setAlpha(int a)
//Paint.Style.FILL、Paint.Style.FILL_AND_STROE、Paint.Style.STROKE
setStyle(Paint.Style style)
setStrokeWidth(float width)
setAntiAlias(boolean aa)
setStrokeMiter(float miter)
//ComposePathEffect、CornerPathEffect、DashPathEffect、DiscretePathEffect、PathDashPathEffect、SumPathEffect。
setPathEffect(PathEffect effect)
//Paint.Cap.ROUND 、Paint.Cap.SQUARE、Paint.Cap.BUTT
setStrokeCap(Paint.Cap cap)
//Paiηt.Join.MITER、Paiηt.Join.ROUND、Paiηt.Join.BEVEL
setStrokeJoin (Paiηt.Join join)
setDither(boolean dither)
6.3.2、字体相关函数
setTextSize(float textSize)
setFakeBoldText(boolean fakeBoldText)
setUnderlineText(boolean underlineText)
setTextAlign(Paint.Align align)
setTextScaleX(float scaleX)
setTextSkewX(float skewX)
setTypeface(Typeface typeface)
setSubpixelText(boolean subpixelText)
第7章绘图进阶
7.1、贝济埃曲线
7.1.1、概述
一阶贝济埃曲线,可以理解为在由起始点和终点形成的这条直线上匀速移动的点。
二阶贝济埃曲线的移动轨迹是建立在两条一阶贝济埃曲线的中间点的基础上的。
所谓几阶贝济埃曲线,全部是由一条条一阶贝济埃曲线搭起来的。
7.1.2、贝济埃曲线之quadTo
//二阶贝济埃曲线
public void quadTo(float xl , float yl, fl oat x2 , float y2)
public void rQuadTo(float dxl, float dyl , float dx2, float dy2)
//三阶贝济埃曲线
public void cubicTo (float xl , float yl , float x2 , float y2 , float x3 , float y3)
public void rCubicTo(float xl , float yl, float x2 , float y2 , float x3 , float y3 )
起始点是通过Path.moveTo(x,y)
函数来指定的,而如果我们连续调用quadTo()
函数, 那么前一个quadTo()
函数的终点就是下一个quadTo()
函数的起始。
传统捕捉手势轨迹示例
7.1.3、贝济埃曲线之rQuadTo
quadTo()函数定义一个绝对坐标:
path.moveTo(300,400)
path.quadTo(500,300,500,500)
与利用rQuadTo()函数定义相对坐标是等价的:
path.moveTo(300,400)
path.rQuadTo(200,-100,200,100)
7.1.4、波浪效果示例
7.2、setShadowLayer与阴影效果
7.2.1、setShadowlayer()构造函数
public void setShadowLayer(float radius, float dx , float dy , int color)
float radius
:模糊半径,radius
越大越模糊、越小越清晰。如果radius
设置为0则阴影消失不见。float dx
:阴影的横向偏移距离,正值向右偏移,负值向左偏移。float dy
:阴影的纵向偏移距离,正值向下偏移,负值向上偏移。int color
:绘制阴影的画笔颜色,阴影的颜色(对图片阴影无效)。
setShadowLayer()
函数使用的是高斯模糊算法。高斯模糊的具体算法是:对于正在处理的每一个像素,取周围若干个像素的RGB值并且平均,这个平均值就是模糊处理过的像素。如果对图片中的所有像素都这么处理,那么处理完成的图片就会变得模糊。其中,所取周围像素的半径就是模糊半径。所以,模糊半径越大,所得平均像素与原始像素相差就越大,也就越模糊。
7.2.2、清除阴影
setShadowLayer函数的radius参数值设为0, 或者用专门的清除阴影的函数:
public void clearShadowLayer()
7.2.3、给文字添加阴影示例
7.3、BlurMaskFilter发光效果与图片阴影
public MaskFilter setMaskFilter(MaskFilter maskfilter)
public BlurMaskFilter{float radius ,Blur style)
7.3.2、给图片添加纯色阴影
public Bitmap extractAlpha();
extractAlpha会新建一幅仅具有Alpha值的空白图像,而且这幅图像的颜色是在使用canvas.drawBitmap
函数绘制时由传入的画笔颜色指定的。
7.4、Shader与BitmapShader
7.4.1、Shader概述
通过给Shader指定对应的图像、渐变色等来填充图形的。
public Shader setShader (Shader shader)
7.4.2、BitmapShader的基本用法
public BitmapShader(Bitmap bitmap, TileMode tileX , TileMode tileY)
bitmap用来指定图案,tileX用来指定当X轴超出单张图片大小时所使用的重复策略,tileY用来指定当Y轴超出单张图片大小时所使用的重复策略。
TileMode的取值:
TileMode.CLAMP:用边缘色彩来填充多余空间。
TileMode.REPEAT:重复原图像来填充多余空间。
TileMode.MIRROR:重复使用镜像模式的 图像来填充多余空间。
先填充Y轴,然后填充X轴。
7.4.3、望远镜效果示例
7.4.4、生成不规则头像示例
7.5、Shader之LinearGradient
7.5.1、概述
public LinearGradient(float xO , float yO , float xl , float yl , int colorO , int colorl , TileMode tile)
public LinearGradient (float xO , fl 。atyO, floatxl ,floatyl,intcolors[], float positions[] , TileMode tile)
7.5.2、闪光文字效果示例
7.6、Shader之RadialGradient
7.6.1、双色渐变
RadialGradient(float centerX , float centerY , float radius , int centerColor ,int edgeColor, Shader.TileMode tileMode)
7.6.2、多色渐变
RadialGradient(float centerX , float centerY , float radius, int[] colors , float[] stops, Shader.TileMode tileMode)
第8章混合模式
8.1、混合模式之AvoidXfermode
8.1.1、混合模式概述
Xfermode的子类有AvoidXfermode、PixelXorXfermode和PorterDuffXfermode。
在使用Xfermode时,为了保险起见,需要做两件事:
禁用硬件加速:
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
使用离屏绘制:
需要把绘制的核心代码放在canvas.save和canvas.restore函数之间
//新建图层
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(),null ,Canvas.ALL_SAVE_FLAG);
//核心绘制代码
//....
//还原图层
canvas.restoreToCount(layerid);
8.1.2、AvoidXfermode
public AvoidXfermode (int opColor, int tolerance ,Mode mode)
8.1.3、AvoidXfermode绘制原理
8.1.4、AvoidXfermode之Mode.AVOID
8.2、混合模式之PorterDuffXfermode
8.2.1、PorterDuffXfermode概述
public PorterDuffXfermode(PorterDuff .Mode mode)
在Xfermode设置前画出的图像叫作目标图像,即给谁应用Xfermode;在Xfermode设置后画出 的图像叫作源图像,即拿什么应用Xfermode。
8.2.2、颜色叠加相关模式
8.3、PorterDuffXfermode之源图像模式
刮刮卡效果示例
8.4、目标图像模式与真他模式
区域波纹示例