第六章
屏幕的尺寸信息
dp = px/密度 密度=dpi/160
密度 密度值 分辨率
ldpi 120 240*320
mdpi 160 320*480
hdpi 240 480*800
xhdpi 320 720*1280
xxhdpi 480 1080*1920
单位转换
/**
* px 转换成dp,保证尺寸大小不变
* 加0.5f的目的为了四舍五入 例如 1.1 转换成int是 1 ;
* 而1.9转换成int 也是1; 所有的数加了0.5后 就可以实现四舍五入了,
* 1.1 + 0.5 = 1.6 -> 1;1.9 + 0.5 = 2.4 -> 2
* @param context
* @param pxValue
* @return
*/
public static int px2dip(Context context ,float pxValue){
final float metrics= context.getResources().getDisplayMetrics().density;
return (int) (pxValue/metrics + 0.5f);
}
/**
* dp装换成px,保证尺寸大小不变
* @param context
* @param dipValue
* @return
*/
public static int dip2px(Context context,float dipValue){
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue*scale + 0.5f);
}
/**
* px转换成sp,保持文字大小不变
* @param context
* @param pxValue
* @return
*/
public static int px2sp(Context context,float pxValue){
final float scale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue/scale + 0.5f);
}
/**
* sp转换成px,保持文字大小不变
* @param context
* @param spValue
* @return
*/
public static int sp2px(Context context,float spValue){
final float scale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * scale + 0.5f);
}
系统给我们提供TypeValue类帮助我们转换
protected int dp2px(Context context,int dp){
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,
context.getResources().getDisplayMetrics());
}
protected int sp2px(Context context,int sp){
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX
,sp,context.getResources().getDisplayMetrics());
}
2D绘图基础
系统通过Canvas对象来提供绘图方法
drawPoint:画点
drawLine:画线
drawRect:画矩形
drawVertices:多边形
drawArc:画弧
drawCircle:画圆
Paint一些重要的属性和功能
setAntiAlias():设置画笔的锯齿效果
setColor():设置画笔的颜色
setARGB():设置画笔的A,R,G,B值
setAlpha():设置画笔的Alpha()值
setTextSize()设置画笔字体大小
setStrokeWidth():设置空心边框的宽度
setStyle()设置画笔的风格(Stroke空心,fill实心)
用法
- canvas.drawPoint(x,y,point)画点
- canvas.drawLine(startX,startY,endX,endY,point);绘制直线
float[] opts ={startX1,startY1,endX1,endY1,……..
startXn,startYn,endXn,endYn}
canvas.drawLines(pts,paint);绘制多条直线canvas.drawRect(left,top,right,bottom,point);绘制正方形
- canvas.drawRoundRect(left,top,right,bottom,radiusX,radiusY,point);绘制圆角矩形
- canvas.drawCircle(circleX,circleY,paint);绘制圆
point.setStyle(Paint.Style.STROKE);
canvas.drawArc(left,top,right,bottom,startAngle,sweepAngle,userCenter,paint);绘制弧形或者扇形,这里倒数第二个参数userCenter就是弧形和扇形的区别。userCenter(true)就是扇形 false就是弧drawOval(left,top,right,bottom,paint);绘制椭圆
- drawText(text,startX,startY,paint);
XML绘图
1.shape : rectangle正方形,oval圆,line线,ring
- corners:圆角,只有shape为rectangle 才能用
- gradient渐变
- padding:
- size:大小
- solid:填充色
stroke:这定边框
2.Layer层叠
3.Selectorstate_window_focused = false (没有焦点)
- state_focused = true; state_pressed= false (非触摸模式下获得焦点并单击)
- state_focused=false;atate_pressed= true(触摸模式下单击)
- state_selected = true (选中时)
- state_focused = true (获得焦点时)
Android绘图技巧(跳过了)
Canvas
- Canvas.save():将之前绘制的图像全部保存起来
- Canvas.restore():将save之后的图像和save之前的进行合并
- Canvas.translate(x,y):将坐标原点由(0,0)转移到x,y
- Canvas.rotate()将坐标系旋转一定角度
Layer图层(????????)
SufaceView
SufaceView 与view 的不同
- view主要适用于主动刷新,surfaceview用于被动刷新,例如频繁刷新
- view在主线程对画面进行刷新,surfaceview一般用一个子线程进行界面刷新
- view会图没有双缓冲技术,二surfaceview在底层实现了双缓冲机制
- 总结一句话就是,如果你的自定义view需要频繁的刷新,或者刷新时处理的数据量比较大的话,就可以用SufaceView 来代替view
使用SufaceView 有一套模板代码,可按照其进行编写
public class SurfaceViewTemplate extends SurfaceView
implements SurfaceHolder.Callback, Runnable {
// SurfaceHolder
private SurfaceHolder mHolder;
// 用于绘图的Canvas
private Canvas mCanvas;
// 子线程标志位
private boolean mIsDrawing;
public SurfaceViewTemplate(Context context) {
super(context);
initView();
}
public SurfaceViewTemplate(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
private void initView() {
mHolder = getHolder();
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
//mHolder.setFormat(PixelFormat.OPAQUE);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
@Override
public void run() {
while (mIsDrawing) {
draw();
}
}
private void draw() {
try {
mCanvas = mHolder.lockCanvas();
// draw sth
} catch (Exception e) {
} finally {
if (mCanvas != null)
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
第七章
Android View动画框架
Animation框架定义了透明度,旋转,缩放和位移几种常见的动画,而且控制的是整个view,实现原理是每次绘制视图时View所在的ViewGroup中的drawchild函数获取该view的Animation的Transformation值,然后调用canvas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧,如果动画没有完成,就继续调用invalidate()函数,启动下次绘制来驱动动画,从而完成整个动画的绘制。
视图动画一个非常大的缺点就是不具备交互性,当某个元素发生视图动画的后,其相应事件的位置还依然在动画前的地方,所以视图动画只能做简单的动画效果,避免交互的发生,但是其优点也明显,即效率比较高且使用方便。
视图动画包括了四种动画方式
- 透明度动画
AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setDuration(1000);
view.startAnimation(aa);
2.旋转动画
RotateAnimation ra = new RotateAnimation(0, 360, 100, 100);
ra.setDuration(1000);
view.startAnimation(ra);
//以自己为旋转中心
RotateAnimation ra = new RotateAnimation(0, 360,
RotateAnimation.RELATIVE_TO_SELF, 0.5F,
RotateAnimation.RELATIVE_TO_SELF, 0.5F);
ra.setDuration(1000);
view.startAnimation(ra);
3.位移动画
TranslateAnimation ta = new TranslateAnimation(0, 200, 0, 300);
ta.setDuration(1000);
view.startAnimation(ta);
4.缩放动画
ScaleAnimation sa = new ScaleAnimation(0, 2, 0, 2);
sa.setDuration(1000);
view.startAnimation(sa);
//以自己为中心缩放
ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1,
Animation.RELATIVE_TO_SELF, 0.5F,
Animation.RELATIVE_TO_SELF, 0.5F);
sa.setDuration(1000);
view.startAnimation(sa);
5.动画集合
AnimationSet as = new AnimationSet(true);
as.setDuration(1000);
AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setDuration(1000);
as.addAnimation(aa);
TranslateAnimation ta = new TranslateAnimation(0, 100, 0, 200);
ta.setDuration(1000);
as.addAnimation(ta);
view.startAnimation(as);
Android 属性动画
1.ObjectAnimator
ObjectAnimator 是属性动画框架中最重要的实行类,创建一个ObjectAnimator只需要通过他的静态工厂类直接返回一个ObjectAnimator对象参数包括一个对象和对象的属性名字,但这个属性必须有get和set函数内部会通过java的反射机制来调用set函数修改对象属性值。同样,你也可以调用setInterpolator设置相应的差值器。
简单平移动画的实现:
ObjectAnimator animator = ObjectAnimator.ofFloat(view,
“translationX”, 300);
animator.setDuration(300);
animator.start();
通过ObjectAnimator的静态工厂方法,创建一个ObjectAnimator对象。第一个参数自然是需要操纵View,第二个参数则是要操纵的属性,而最后一个是一个可变数组参数,需要传进去该属性变化的一个取值过程,这里只设置了一个参数,即变化到300.当然,也可以给属性动画设置显示时长,差值器等属性,这些参数与视图方法的设置方式类似。不过,在使用ObjectAnimator的时候,有一点非常重要,那就是要操纵的属性必须具有get,set方法,不然ObjectAnimator就无法起效。
下面是一些属性值介绍
translationX和translationY:这连个属性作为一种增量来控制着view对象从它布局容器的左上角坐标开始。
rotation,rotationX,rotationY,这三个属性控制view围绕他的支点进行2D和3D旋转。
scaleX和scaleY:围绕支点进行缩放
pivotX和piovtY:控制支点位置,默认是view对象的中心点
x和y:view对象在他容器中的最终位置
alpha:透明度0是完全透明,1是不透明
如果一个属性没有set和get方法,怎么办,Google应用层提供两个方法来解决问题,一个是通过自定义的一个属性类或者包装类,来简介的给这个属性增加set,get方法,或者是使用ValueAnimator来实现
包装类:
private static class WrapperView{
private View mTarget;
public WrapperView(View mTarget) {
this.mTarget = mTarget;
}
public int getWidth(){
return mTarget.getLayoutParams().width;
}
public void setWidth(int width){
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
通过以上代码,就给一个属性包装了一层,并给它提供了get,set方法。使用时只需要操纵包装类就可以间接调用到get,set方法
WrapperView wrapper = new WrapperView(mBotton);
ObjectAnimator.ofInt(wrapper,"width",500).setDuration(5000).start();
ValueAnimator
ValueAnimator在属性动画中占有非常重要。ObjectAniamtor继承自ValueAnimator。ValueAnimator本身不提供任何动画效果,更像一个数组发生器,用来产生具有一定规律的数字,然而让调用者来控制动画的实现过程,ValueAnimator使用方法如下:
//数字有0 ---100 连续变化
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
valueAnimator.addUpdateListener(
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
((TextView) view).setText("$ " +
(Integer) animation.getAnimatedValue());
}
});
valueAnimator.setDuration(3000);
valueAnimator.start();
AnimatiorSet
对于一个属性同时作用多个属性动画效果
ObjectAnimator animator0 = ObjectAnimator.ofFloat(
mImageViews.get(0),
"alpha",
1F,
0.5F);
ObjectAnimator animator1 = ObjectAnimator.ofFloat(
mImageViews.get(1),
"translationY",
200F);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(
mImageViews.get(2),
"translationX",
200F);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(
mImageViews.get(3),
"translationY",
-200F);
ObjectAnimator animator4 = ObjectAnimator.ofFloat(
mImageViews.get(4),
"translationX",
-200F);
AnimatorSet set = new AnimatorSet();
set.setDuration(500);
set.setInterpolator(new BounceInterpolator());
set.playTogether(
animator0,
animator1,
animator2,
animator3,
animator4);
set.start();
动画事件的监听
//一般我们只关心onAnimationEnd事件
private void animateClose(final View view) {
int origHeight = view.getHeight();
ValueAnimator animator = createDropAnimator(view, origHeight, 0);
animator.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
view.setVisibility(View.GONE);
}
});
animator.start();
}
view的animate方法
imageView.animate()
.alpha(0)
.y(100)
.x(200)
.setDuration(300)
.withStartAction(new Runnable() {
@Override
public void run() {
}
}).withEndAction(new Runnable() {
@Override
public void run() {
}
}).start();
Android布局动画
布局动画就是指作用在viewgroup上,在viewgroup增加view时添加一个动画过渡效果。
最简单的就是在viewgroup的XML中使用以下代码打开布局动画
android:animateLayoutChanges = “true”
通过上述代码设置,当viewgroup添加view是,子view会呈现逐渐显示的过渡状态,不过这是安卓默认的状态,且无法用自定义的动画来代替这个状态。
linearLayout = (LinearLayout) findViewById(ll);
//设置缩放动画
ScaleAnimation animation = new ScaleAnimation(0,1,0,1);
animation.setDuration(1000);
//设置布局动画的显示效果
LayoutAnimationController controller = new LayoutAnimationController(animation,1000);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
//给布局设置显示动画
linearLayout.setLayoutAnimation(controller);
以上代码,给LinerLayout添加一个视图动画,让子view出现的时候增加一个缩放的效果。
setOrder();是设置view显示的顺序
LayoutAnimationController.ORDER_NORMAL 顺序
LayoutAnimationController.ORDER_RANDOM 随机
LayoutAnimationController.ORDER_REVERSE 反序
Interpollators插值器
插值器是动画中一个非常重要的概览,通过插值器,可以定义动画变换速率,这一点类似物理学中的加速度,其作用主要是控制目标变量的变化值进行对应的变化。
自定义动画
定义一个view基础Aniamtion ,然后重写里面的initialize()和applyTransformation()方法
Android 5.X SVG矢量动画机制
SVG:
- 可伸缩矢量图形
- 定义位于网络的基于网络的矢量的图形
- 使用xml格式定义图形
- 图像在放大或者改变尺寸的情况下图像质量不会有所损失
- 万维网联盟标准
- 与DOM和XSL之类的W3C标准是一个整体
1.path标签
使用path 标签创建SVG,就像用指令的方式创建一只画笔,例如让画笔移动到某一位置,画一条线,画一条曲线,path指令如下
- M=moveto(M X,Y):将画笔移动到指定坐标位置,但未发生绘制
- L = lineto(L X,Y):画直线到指定的坐标位置
- H = horizontal lineto(H X):画水平线到指定的X坐标位置
- V = vertical lineto(V Y):画垂直线到指定Y位置
- C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次贝尔曲线
- S = smooth curveto(S X2,Y2,ENDX,ENDY):三次贝尔曲线
- Q= quadratic Belzier curve( Q X,Y,ENDX,ENDY):二次贝尔曲线
- T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射前面路径后的终点
- A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y)弧线
- Z = closepat():关闭路径
使用上面指定要注意的地方
- 坐标轴以(0,0)为中心,X轴水平向右,Y轴水平向下。
- 所有指令大小写均可,大写局对定位,参照全局坐标系,小写相对定位,参照父容器坐标系
- 指令和数据间的空格可以省略
- 统一指令出现多次可以只用一个
2.SVG常用指令
- L
绘制直线的指令是“L”,代表从当前绘制直线到指定点。L之后的参数是点坐标,例如”L 200 400 ” 绘制直线,还可以使用“H”和 “V” 指令来绘制水平,竖直线,后面的参数是x坐标(H指令),或y坐标(V指令) - M
M指令类似path类中的moveto方法,代表将画笔移动到某个点,但不发生绘制 A
A指令用来绘制一段弧线,且运行弧线不闭合。可以把A命令绘制的弧线想象成椭圆的某一段,A指令下有七个参数- RX,RY所在椭圆的的半轴大小
- XROTATION:椭圆的X轴与水平方向顺时针方向夹角
- FLAG1只有两个值,1代表大角度弧线,0代表小角度弧线
- FLAG2:只有两个值,确定从起点到终点的方向,1为顺时针,0为逆时针
- X,Y轴为终点坐标
在安卓中使用SVG
谷歌在安卓5.x提供了两个api来帮助支持SVG:
- VectorDrawable
- AnimatedVectorDrawable
其中VectorDrawable让你创建基于XML的SVG图形,并结合AnimatedVectorDrawable来显示动画效果
1.VectorDrawable
在xml中创建一个静态的SVG图形,path是SVG树形结构中的最小单位,而通过Group可以让多个path进行组合
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="200dp"
android:width="200dp"
android:viewportHeight="100"
android:viewportWidth="100">
<group android:name="test"
android:rotation="0">
<path android:fillColor="@color/colorPrimary"
android:pathData="M 25 50 a 25,25 0 1,0 50,0"
android:strokeColor="@color/colorAccent"
android:strokeWidth="4"/>
</group>
</vector>
其中包含两组宽高属性,height,width,viewportHeight,viewportWidth,height,width代表改SVG图形具体的大小,viewportHeight,viewportWidth则表示SVG图形划分的比例,上面就是将200dp划分100份了,如果在绘制图形时使用坐标(50,50)则意味着该坐标位于SVG图形正中间,因此,height,width的比例与viewportHeight,viewportWidth的比例,必须保持一致
2.AnimatedVectorDrawable
AnimatedVectorDrawable的作用就是给VectorDrawable提供动画效果
通过AnimatedVectorDrawable 来连接静态的VectorDrawable 和动态的objectAnimator
静态的VectorDrawable
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="100"
android:viewportWidth="100">
<group
android:name="test"
android:rotation="0">
<path
android:pathData="
M 25 50
a 25,25 0 1,0 50,0"
android:strokeColor="@android:color/holo_blue_light"
android:strokeWidth="2" />
</group>
</vector>
动态的objectAnimator
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:interpolator="@android:anim/bounce_interpolator"
android:propertyName="pathData"
android:valueFrom="
M 20,80
L 50,80 80,80"
android:valueTo="
M 20,80
L 50,50 80,80"
android:valueType="pathType" />
AnimatedVectorDrawable连接上面两个
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vector">
<target
android:name="test"
android:animation="@anim/anim_path1" />
</animated-vector>
需要注意的是AnimatedVectorDrawable 中指定的target的name值必须与VectorDrawable中的name 值属性保持一致,