当前位置: 移动技术网 > IT编程>移动开发>Android > Android 属性动画

Android 属性动画

2020年05月06日  | 移动技术网IT编程  | 我要评论

私奔帝,大脑学院游戏下载,大梦归

一、valueanimator

valueanimator是值的变动,可以控制控件的一些值,从而达到变化动画的效果。

    public void doanimation() {
//        final valueanimator valueanimatorint = valueanimator.ofint(0,400,100,555,250);
        //输入需要变化的值,是个变化的数组,可以有int类型和float类型
        final valueanimator valueanimator = valueanimator.offloat(0.0f,400.0f,100.0f,555.0f,250.0f);
        valueanimator.setduration(9000);//动画持续时间
        //监听动画的变化时间,在变化中对控件进行操作,也可以通过handle来做一些有趣的事情
        valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() {
            @override
            public void onanimationupdate(valueanimator animation) {
                //获得变化的值
                float curvaluefloat = (float) valueanimator.getanimatedvalue();
                //设置为整型
                int curvalue = curvaluefloat.intvalue();
                //改变控件的位置,layout对应的是控件的位置
                valuetv.layout(curvalue, curvalue, curvalue + imageview.getwidth(), curvalue + imageview.getheight());
            }
        });
        valueanimator.start();
    }

监听器三个
image.png

        //监听1
        valueanimator.addpauselistener(new animator.animatorpauselistener() {
            @override
            public void onanimationpause(animator animation) {
                //暂停
            }

            @override
            public void onanimationresume(animator animation) {
                //运行
            }
        });

        //监听2
        valueanimator.addlistener(new animator.animatorlistener() {
            @override
            public void onanimationstart(animator animation) {
                //开始
            }

            @override
            public void onanimationend(animator animation) {
                //结束
            }

            @override
            public void onanimationcancel(animator animation) {
                //取消
            }

            @override
            public void onanimationrepeat(animator animation) {
                //循环一次
            }
        });

        //监听3
        valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() {
            @override
            public void onanimationupdate(valueanimator animation) {
                //数值更新
            }
        });

移除监听器
当移除监听器时,正在执行的动画不会受到影响,但是之后再执行动画,动画的监听效果将不会再呈现。

/** 
 * 移除animatorupdatelistener 
 */  
void removeupdatelistener(animatorupdatelistener listener);  
void removeallupdatelisteners();  

 /** 
  * 移除animatorlistener 
  */  
void removelistener(animatorlistener listener);  
void removealllisteners();

不常用函数

/**
 * 延时多久时间开始,单位是毫秒
 */
public void setstartdelay(long startdelay)
    
/**
 * 完全克隆一个valueanimator实例,包括它所有的设置以及所有对监听器代码的处理
 */
public valueanimator clone()

常用函数

/**
 * 设置动画时长,单位是毫秒
 */
valueanimator setduration(long duration)
    
/**
 * 获取valueanimator在运动时,当前运动点的值
 */
object getanimatedvalue();

/**
 * 开始动画
 */
void start()
    
/**
 * 设置循环次数,设置为infinite表示无限循环
 */
void setrepeatcount(int value)
    
/**
 * 设置循环模式
 * value取值有restart,reverse,
 */
void setrepeatmode(int value)
    
/**
 * 取消动画
 */
void cancel()

效果:

属性动画.gif

二、自定义插值器

1.插值器的理解

首先看看自动自定义的插值器
匀速插值器:
看看继承关系:linearinterpolator---继承--->baseinterpolator---继承--->interpolator---实现-->timeinterpolator
最后看看timeinterpolator都写了啥:
只定义了一个getinterpolation(float input)方法。

package android.animation;

/**
 * a time interpolator defines the rate of change of an animation. this allows animations
 * to have non-linear motion, such as acceleration and deceleration.
 */
public interface timeinterpolator {

    /**
     * maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. this interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input a value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end
     * @return the interpolation value. this value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets.
     */
    float getinterpolation(float input);
}

linearinterpolator的定义

public class linearinterpolator extends baseinterpolator implements nativeinterpolatorfactory {

    public linearinterpolator() {
    }

    public linearinterpolator(context context, attributeset attrs) {
    }

    public float getinterpolation(float input) {
        return input; //数值进度与时间同步
    }

    /** @hide */
    @override
    public long createnativeinterpolator() {
        return nativeinterpolatorfactoryhelper.createlinearinterpolator();
    }
}

accelerateinterpolator开始慢,后面一直加速插值器,也叫幂函数插值器,核心方法

 public float getinterpolation(float input) {
        if (mfactor == 1.0f) {
            return input * input;
        } else {
            //返回的是时间的次幂数,比如 input=3,mdoublefactor=2,
            //那么返回的就是,3的2次方,就是9
            //所以会按照几何倍增,这是一个幂函数
            return (float)math.pow(input, mdoublefactor); 
        }
    }

所有的速度都离不开这个方法:getinterpolation

而最为关键的就是input这个数字。以下是经典解释:

input参数代表了当前动画的进度,而返回值则代表了当前动画的数值进度。

上面的匀速,返回的就是时间的值,所以,动画进度和动画的数值持平。
幂函数的时候,随着动画进度的增加,动画的数值进度也就越来越大,从而一直加速。

input的取值范围是0~1之间,返回值可以超过1,也可以小于0,超过1表示已经超过目标位置,小于0表示远离初始位置。

简单的公式就是

y= -> x

y代表返回的值,也就是动画需要的数值进度,x代表时间进度,->则是通过一些数学手段,来得到想要的y值。

  • 当一些动画定义这些插值器的时候,返回的数值进度越大,速度越快。比如你在匀速运动的时候,时间进度是0.5s,数值进度也是0.5,那就是匀速运动。

2.定义一个简单的插值器

我们用数学中的定义来做一个插值器。
y=1-x
把进度反过来,当进度传入0的时候,数值进度已经在目标位置了。当传入1时,数值则在刚开始的位置。

  class fiveinterpolator implements timeinterpolator {

        @override
        public float getinterpolation(float input) {
            return 1-input;
        }
    }

valueanimator.setinterpolator(``new ``fiveinterpolator())``;
一个简单的自定义插值器就完成了。

三、evaluator

  • evaluator是数值转换器,就是将数值进度转化为具体的数值。
  • 就是0~400的数值变换,当数值进度是50%的时候,那通过evaluator来转换,就变成了200
  • oflnt()函 数对应 evauator 类名为 intevauaor ,而 offloat()函数对应的 evauator 类名为 floatevaluator

自定义数值转换器:

 	//自定义数值转换器
    class myfloatevaluator implements typeevaluator<float>{

        /**
         * @param fraction 代表数值进度的值,就是上面getinterpolation()的返回值
         * @param startvalue  代表offloat(float startvalue,float endvalue)
         * @param endvalue
         * */
        @override
        public float evaluate(float fraction, float startvalue, float endvalue) {
            //初始值
            float startfloat=startvalue;

            //当前值=初始值+总值*进度
            float inputvalue=startfloat+(endvalue-startfloat)*fraction;
            return inputvalue;
        }
    }

使用:

valueanimator.setevaluator(new myfloatevaluator());

所以可以通过插值器和数值转化器来改变控件的数值变化

valueanimator.setinterpolator(new fiveinterpolator());
valueanimator.setevaluator(new myfloatevaluator());

四、argbevaluator

argbevaluator可以把颜色转换过渡。
具体实现:

 //颜色的数值变换
    public void docoloranimation(){
        valueanimator valueanimator=valueanimator.ofint(0xffffff00,0xff0000ff);
        valueanimator.setevaluator(new argbevaluator());
        valueanimator.setduration(3000);
        valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() {
            @override
            public void onanimationupdate(valueanimator animation) {
                int curvalue=(int)animation.getanimatedvalue();
                valuetv.setbackgroundcolor(curvalue);
            }
        });
        valueanimator.start();
    }

效果:

颜色变换.gif

颜色必须包含argb四个值。
image.png

五、valueanimation-ofobject

首先看看这个方法是如何传值的。

public static valueanimator ofobject(typeevaluator evaluator, object... values) {
        valueanimator anim = new valueanimator();
        anim.setobjectvalues(values);
        anim.setevaluator(evaluator);
        return anim;
    }

typeevaluator evaluator需要传入自定义的数值转换器
object... values 可变长参数

实例
实现一个字母从a到z的过程

public void doobjectvalue() {
        valueanimator valueanimator = valueanimator.ofobject(new charinterpolator(), new character('a'), new character('z'));
        valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() {
            @override
            public void onanimationupdate(valueanimator animation) {
                char str = (char) animation.getanimatedvalue();
                valuetv.settext(string.valueof(str));
            }
        });
        valueanimator.setduration(7000);
        valueanimator.setinterpolator(new accelerateinterpolator());
        valueanimator.start();
    }

    class charinterpolator implements typeevaluator<character> {

        @override
        public character evaluate(float fraction, character startvalue, character endvalue) {
            int startint = (int) startvalue;  //ascii转换   a代表56 以此递增
            int endint = (int) endvalue;
            int curint = (int) (startint + fraction * (endint - startint));
            char result = (char) curint;
            return result;
        }
    }

效果:

a-z的变换.gif

六、objectanimator

objeceanimation--继承--->valueanimation
与控件之间相关联,从监听动画中解放出来。
先看看这个方法:

objectanimator offloat(object target, string propertyname, float... values)

具体使用

  public void doobjectanimationbyalpha(){
        objectanimator objectanimator=objectanimator.offloat(valuetv,"alpha",1,0,1);
        objectanimator.setduration(6000);
        objectanimator.start();
    }

object target要控制的控件
string propertyname要改变的动画效果
float... values传入的具体变化值
具体效果就是跟视图动画中设置的动画是一样的效果,透明度从1到0再到1.
“alpha”中,是对应view中的setalpha()方法,后面的可变成参数就是可以传入具体是变换数值。
看看view中有多少个set函数:

透明度:alpha

setalpha(@floatrange(from=0.0, to=1.0) float alpha)  //透明度

旋转角度:rotation,rotationx,rotationy

setrotation(float rotation) //围绕z轴旋转,z轴指的是垂直屏幕的方向
setrotationx(float rotationx)  //围绕x轴旋转
setrotationy(float rotationy)  //围绕y轴旋转

平移:translationx,translationy

settranslationx(float translationx)   //x轴屏幕,右为正方向,当前控件为原点
settranslationy(float translationy)

缩放:scalex,scaley

setscalex(float scalex) //x轴缩放
setscaley(float scaley)

看看旋转是三个效果:

 public void doobjectanimationbyalpha(){
        objectanimator objectanimator=objectanimator.offloat(valuetv,"rotationy",360);
        objectanimator.setduration(6000);
        objectanimator.start();
    }

z轴:

z轴旋转.gif

x轴:

x轴旋转.gif

y轴:

y轴旋转.gif

七、自定义objectanimator

因为objectanimator是通过set来反射实现的,所以自己也可以通过这样的操作来实现自己view的set函数,从而实现简单的动画效果。

1.自定义view的set函数

fallingballimageview.java

public class fallingballimageview extends imageview {
    public fallingballimageview(context context, @nullable attributeset attrs) {
        super(context, attrs);
    }

    public void setfallingpos(point pos){
        layout(pos.x,pos.y,pos.x+getwidth(),pos.y+getheight());
    }

}

布局使用

<com.lanjiabin.systemtest.anim.fallingballimageview
                android:id="@+id/imageball"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_margintop="30dp"
                android:background="@drawable/shape"
                android:layout_marginbottom="25dp" />

set函数的名字是setfallingpos,所以在传递反射函数名字的时候,应该是fallingpos或者fallingpos,必须是这两个名字中其中一个的格式,否则就不会正确反射。参数类型是point,所以使用函数是ofobject()

2.自定义evaluator

 class divevaluator implements typeevaluator<point> {
        point point = new point();

        @override
        public point evaluate(float fraction, point startvalue, point endvalue) {
            point.x = (int) (startvalue.x + (endvalue.x - startvalue.x) * fraction);
            if (fraction * 2 <= 1) {
                point.y = (int) (startvalue.y + (endvalue.y - startvalue.y) * fraction);
            } else {
                point.y = endvalue.y;
            }
            return point;
        }
    }

3.实现最终反射调用

  public void doobjectanimationbydiv() {
        objectanimator objectanimator=objectanimator
                .ofobject(
                        ballimageview,  //自定义view的小球
                        "fallingpos",   //反射名字,fallingpos或者fallingpos都可以
                        new divevaluator(), //自定义转换器
                        new point(0,0),     //起始坐标
                        new point(300,300)); //目标坐标
        objectanimator.setduration(4000);   //动画时长
        objectanimator.start();
    }

4.效果

自定义objectanimation.gif

5.get函数

当我们在上述函数的时候,ofobject()传的都是可变长的参数,也就是两个参数以上,当我们只传递一个参数的时候,这个参数只是目标参数,没有初始参数,系统就会默认调用系统自带的get方法,来获得初始值。当没有这个get方法的时候,就会报错,以至于崩溃。
所以想传递一个参数,就需要自定义get()方法,返回的,就是初始值。对应名字也和set的名字类似。
setfallingpos(point pos)的名字就是getfallingpos(point pos)

在自定义view中加入get方法:返回控件的初始point

 public point getfallingpos() {
        int[] location = new int[2];
        this.getlocationonscreen(location);
        return new point(location[0], location[1]);
    }

八、animatorset

1.animatorset理解和使用

animatorset组合动画,对valueanimation和objectanimation都有一样的效果。
有两个播放方法:只管播放的时间,不管动画个体是如何操作的,不管动画的执行时间,循环次数等。
playsequentially()
是顺序播放,当前一个动画播放完毕以后,才会执行下一个动画。当前一个动画是无限循环时,后一个动画也就无法播放。有两个构造方法。

playsequentially(animator... items)
playsequentially(list<animator> items)

playtogether()
是一起播放,同一个时间内,在列表中所有动画同一时间启动。

playtogether(animator... items)
playtogether(collection<animator> items)

具体实例,有一个缩放动画和位移动画,分别实现同时播放和顺序播放。

 public void doanimationset() {
        //缩放
        objectanimator objectanimatorscaley = objectanimator.offloat(ballimageview, "scaley", 0.0f, 1.6f, 1.0f);

        //平移
        objectanimator objectanimatortranslationx = objectanimator.offloat(ballimageview, "translationx", 400);

        //组合动画
        animatorset animator = new animatorset();

        //每个动画的播放时间
        animator.setduration(3000);

        //顺序播放
        animator.playsequentially(objectanimatorscaley, objectanimatortranslationx);

        //一起播放
        animator.playtogether(objectanimatorscaley, objectanimatortranslationx);

        animator.start();
    }

同时播放:动画效果同时体现出来,缩放和位移
组合动画-同时播放.gif

顺序播放:先缩放完毕再位移
组合动画-顺序播放.gif

2.animatorset.builder

  		//组合动画
        animatorset animator = new animatorset();

        //目标动画
        animatorset.builder builder=animator.play(objectanimatorscaley);

        //执行目标动画后再执行该动画
        builder.after(objectanimatorscaley);

        //执行该动画后再执行目标动画
        builder.before(objectanimatorscaley);

        //和目标动画一起播放
        builder.with(objectanimatorscaley);

        //延迟时间执行目标动画
        builder.after(3000);


		//串行方式
        animatorset animator = new animatorset();
        animatorset.builder builder=animator
        	.play(objectanimatorscaley)
            .after(objectanimatorscaley)
            .before(objectanimatorscaley);


		//如果animatorset设置了动画时长,循环次数等,都以animatorset为准,单个设置不起作用。
        //每个动画的播放时间
        animator.setduration(3000);
  		//所有的动画都集中于这个控件上,其它的不起作用
        animator.settarget(ballimageview);

九、实例-卫星菜单

1.实现原理

实现一个放射卫星的效果,点击一下,放射出菜单,再点击一下,收回菜单。
原理就是,将所有的菜单重叠在一起,点击最上面的菜单,按照不同的角度,实现位移,缩放,透明度的效果,将下面的菜单都位移出去。看看位移的计算方式,每个菜单,与主菜单都形成了直角形式,水平x轴的位移和y轴的水平位移都可以计算出来。  就是从主菜单,位移到不同位置的x轴和y轴。
计算公式.png

2.布局

布局非常简单,全部控件叠加在一起,而且子菜单的属性全部一致,省略了一些重复的空间。

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <framelayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margintop="20dp">

        <!-- shadow 代表阴影,对阴影的一些处理-->
        <button
            android:id="@+id/mainbtn"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_gravity="center_vertical|right"
            android:layout_marginright="40dp"
            android:background="@drawable/menu_main"
            android:outlineambientshadowcolor="@android:color/transparent"
            android:outlinespotshadowcolor="@android:color/transparent"
            android:shadowcolor="@android:color/transparent" />

        <button
            android:id="@+id/rockbtn"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_gravity="center_vertical|right"
            android:layout_marginright="20dp"
            android:background="@drawable/menu_rock"
            android:outlineambientshadowcolor="@android:color/transparent"
            android:outlinespotshadowcolor="@android:color/transparent"
            android:shadowcolor="@android:color/transparent"
            android:visibility="gone" />
                
            <!-- 以下省略六个子按钮菜单 -->    
    </framelayout>


</linearlayout>

3.java代码

核心思想就是,添加要控制的子菜单,开启动画方法,关闭动画方法。

public class exampleactivity extends baseactivity {

    private boolean mismenuopen = false;
    private button mmainbtn, mrockbtn, mairbtn, mtrainbtn, mcarbtn, mmotorbikebtn, mbicyclebtn, mwalkbtn;
    list<button> mbtnarray;

    @override
    protected void oncreate(bundle savedinstancestate) {
        super.oncreate(savedinstancestate);
        initview();
        onclick();
    }

    public void initview() {
        setcontentview(r.layout.activity_example);
        mmainbtn = findviewbyid(r.id.mainbtn);

        mrockbtn = findviewbyid(r.id.rockbtn);
        mairbtn = findviewbyid(r.id.airbtn);
        mtrainbtn = findviewbyid(r.id.trainbtn);
        mcarbtn = findviewbyid(r.id.carbtn);
        mmotorbikebtn = findviewbyid(r.id.motorbikebtn);
        mbicyclebtn = findviewbyid(r.id.bicyclebtn);
        mwalkbtn = findviewbyid(r.id.walkbtn);

        //添加子菜单
        mbtnarray = new arraylist<button>();
        mbtnarray.add(mrockbtn);
        mbtnarray.add(mairbtn);
        mbtnarray.add(mtrainbtn);
        mbtnarray.add(mcarbtn);
        mbtnarray.add(mmotorbikebtn);
        mbtnarray.add(mbicyclebtn);
        mbtnarray.add(mwalkbtn);
    }

    public void onclick() {
        mmainbtn.setonclicklistener(new view.onclicklistener() {
            @override
            public void onclick(view v) {
                switchanimation();
            }
        });

        mrockbtn.setonclicklistener(new view.onclicklistener() {
            @override
            public void onclick(view v) {
                toast.maketext(exampleactivity.this, "你选择了火箭出行!", toast.length_short).show();
            }
        });
    }

    //开关
    public void switchanimation() {
        if (!mismenuopen) {
            mismenuopen = true;
            for (int i = 0; i < mbtnarray.size(); i++) {
                doanimationopen(mbtnarray.get(i), i, mbtnarray.size(), 500);
            }
        } else {
            mismenuopen = false;
            for (int i = 0; i < mbtnarray.size(); i++) {
                doanimationclose(mbtnarray.get(i), i, mbtnarray.size(), 500);
            }
        }
    }

    /**
     * 开启动画/展开菜单
     * @param view   要控制的控件/子菜单
     * @param index  要控制控件的顺序
     * @param total  子菜单的总数
     * @param radius 主菜单到子菜单的距离/半径
     * */
    private void doanimationopen(view view, int index, int total, int radius) {

        //显示菜单
        if (view.getvisibility() != view.visible) {
            view.setvisibility(view.visible);
        }

        //计算每个菜单的角度,toradians()将度数转换为弧度
        //七个子菜单,有六个夹角角,180/(7-1)*2  2代表第二个夹角
        double degree = math.toradians(180) / (total - 1) * index;

        //x轴位移
        int translationx = -(int) (radius * math.sin(degree));

        //y轴位移
        int translationy = -(int) (radius * math.cos(degree));
        animatorset animatorset = new animatorset();

        //动画合集
        animatorset.playtogether(

                //从原来控件的位置往x轴移动多少
                objectanimator.offloat(view, "translationx", 0, translationx),

                //从原来控件的位置往y轴移动多少
                objectanimator.offloat(view, "translationy", 0, translationy),

                //x轴缩放
                objectanimator.offloat(view, "scalex", 0.01f, 1f),

                //y轴缩放
                objectanimator.offloat(view, "scaley", 0.01f, 1.0f),

                //透明度
                objectanimator.offloat(view, "alpha", 0.01f, 1.0f)
        );
        animatorset.setduration(500);
        animatorset.start();
    }

    //关闭菜单/动画
    private void doanimationclose(view view, int index, int total, int radius) {
        if (view.getvisibility() != view.visible) {
            view.setvisibility(view.visible);
        }
        double degree = math.toradians(180) / (total - 1) * index;
        int translationx = -(int) (radius * math.sin(degree));
        int translationy = -(int) (radius * math.cos(degree));
        animatorset animatorset = new animatorset();

        animatorset.playtogether(
                objectanimator.offloat(view, "translationx", translationx, 0),
                objectanimator.offloat(view, "translationy", translationy, 0),
                objectanimator.offloat(view, "scalex", 1.0f, 0.01f),
                objectanimator.offloat(view, "scaley", 1.0f, 0.01f),
                objectanimator.offloat(view, "alpha", 1.0f, 0.01f)
        );
        animatorset.setduration(500);
        animatorset.start();
    }
}

3.效果

卫星菜单.gif

十、xml实现animator

1.animator

在animator下建立animator.xml

image.png

xml代码:

<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:valuefrom="0"
    android:valueto="300"
    android:valuetype="inttype" />

java代码使用

  valueanimator valueanimator=(valueanimator)animatorinflater.loadanimator(
                                exampleactivity.this,
                                r.animator.animator);

                valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() {
                    @override
                    public void onanimationupdate(valueanimator animation) {
                        int offset=(int)animation.getanimatedvalue();
                        mrockbtn.layout(offset,offset,mrockbtn.getwidth()+offset,mrockbtn.getheight()+offset);
                    }
                });
                valueanimator.start();

2.objectanimator

object_animator.xml

<?xml version="1.0" encoding="utf-8"?>
<objectanimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"  //时间
    android:interpolator="@android:anim/accelerate_interpolator"  //插值器
    android:propertyname="string"  //要映射的名字
    android:repeatcount="11"      //循环次数
    android:repeatmode="restart"  //循环模式
    android:startoffset="777"     
    android:valuefrom="99"  //开始
    android:valueto="199"   //目标
    android:valuetype="inttype"  //数据类型
    />

java代码使用

  objectanimator animator=(objectanimator)animatorinflater.loadanimator(
                        exampleactivity.this,
                        r.animator.object_animator);
                animator.settarget(mairbtn);
                animator.start();

十一、后语

包括其它的知识点,都只能说一些基础的内容,很多方法和拓展知识都没有说道,需要自己去探索,多阅读sdk源码。属性动画,有一些高级的内容,后续会持续拓展。

编程中我们会遇到多少挫折?表放弃,沙漠尽头必是绿洲。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网