《Android群英传》学习记录(三)

第六章

屏幕的尺寸信息

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.Selector

  • state_window_focused = false (没有焦点)

  • state_focused = true; state_pressed= false (非触摸模式下获得焦点并单击)
  • state_focused=false;atate_pressed= true(触摸模式下单击)
  • state_selected = true (选中时)
  • state_focused = true (获得焦点时)

Android绘图技巧(跳过了)
Canvas

  1. Canvas.save():将之前绘制的图像全部保存起来
  2. Canvas.restore():将save之后的图像和save之前的进行合并
  3. Canvas.translate(x,y):将坐标原点由(0,0)转移到x,y
  4. 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()函数,启动下次绘制来驱动动画,从而完成整个动画的绘制。
视图动画一个非常大的缺点就是不具备交互性,当某个元素发生视图动画的后,其相应事件的位置还依然在动画前的地方,所以视图动画只能做简单的动画效果,避免交互的发生,但是其优点也明显,即效率比较高且使用方便。
视图动画包括了四种动画方式

  1. 透明度动画

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指令下有七个参数

    1. RX,RY所在椭圆的的半轴大小
    2. XROTATION:椭圆的X轴与水平方向顺时针方向夹角
    3. FLAG1只有两个值,1代表大角度弧线,0代表小角度弧线
    4. FLAG2:只有两个值,确定从起点到终点的方向,1为顺时针,0为逆时针
    5. 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 值属性保持一致,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值