Android动画(七)实战使用

完成效果

在这里插入图片描述
今天给大家带来2个效果:

  • 一个是ListView的滑动进入
  • 另一个是右下角的点击弹框效果

ListView滑动进入动画

第一步,先在布局中使用LIstView,在res/layout_animation创建文件,并使用android:layoutAnimation="@anim/layout_animation"引用到ListView中

在这里插入图片描述

第二步:layout_animation文件根目录为layoutAnimation

<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:delay="0.3"
    android:animationOrder="normal"
    android:animation="@anim/set_buttonanimator_activity"
    >
</layoutAnimation>
  • android:delay= “float” 可以理解为每个个item相隔滑动的时间
  • android:animationOrder= “normal | random | reverse”
    • normal 按顺序弹出
    • random 随机弹出
    • reverse 倒序弹出
  • android:animation = “@anim/XXX” 弹出的动画

第三步: 在anim下创建set_buttonanimator_activity.xml设置向右滑动,并且透明度由0-1,时长为1s

<set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" >
    <translate android:fromXDelta="-50%p" android:toXDelta="0"/>
    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"/>
</set>

第四步:在布局中使用ListView

		ListView listView = findViewById(R.id.listview);

        ArrayList<String> list = new ArrayList<>();
        list.add("测试数据1");
        list.add("测试数据2");
        list.add("测试数据3");
        list.add("测试数据4");
        list.add("测试数据5");
        list.add("测试数据6");
																			这里使用的布局是android中自带的
        ArrayAdapter<String> stringArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_expandable_list_item_1, list);

        listView.setAdapter(stringArrayAdapter);
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:delay="0.3"
    android:animationOrder="normal"
    android:animation="@anim/set_buttonanimator_activity"
    >
</layoutAnimation>

android:animationOrder = “normal” 正序播放效果:

在这里插入图片描述

android:animationOrder=“reverse” 倒序播放效果

在这里插入图片描述

android:animationOrder=“random” 随机效果

在这里插入图片描述

动态代码实现

        //代码设置通过加载XML动画设置文件来创建一个Animation对象;
        Animation animation= AnimationUtils.loadAnimation(this,R.anim.set_buttonanimator_activity);   //得到一个LayoutAnimationController对象;
        LayoutAnimationController controller = new LayoutAnimationController(animation);   //设置控件显示的顺序;
        controller.setOrder(LayoutAnimationController.ORDER_RANDOM);   //设置控件显示间隔时间;
        controller.setDelay(0.3f);   //为ListView设置LayoutAnimationController属性;
        listView.setLayoutAnimation(controller);
        listView.startLayoutAnimation();
  • ORDER_NORMAL = 0 正常顺序
  • ORDER_REVERSE = 1倒序
  • ORDER_RANDOM = 2 随机顺序

大家可以从源码中找到注释来看是什么意思;

在这里插入图片描述

菜单弹框效果:

在这里插入图片描述
滑动的效果很简单,alpha从0 - 1 , scale 从0 - 1,最复杂的就是找到每个控件对应的X,和Y的位置,接下来给大家简单分析一下.

在这里插入图片描述

从这张图中可以简单地看出,想要求得X和Y的距离,就必须获得半径r,和夹角a,半径默认为600,那么每个a的夹角是多少呢?从黄色控件和,紫色控件来看,他们的夹角为直角,就是90°,那么他们吧5个控件分为了4等份,那么每一份的夹角就是90 / 4 = 22.5°,那么蓝色与紫色的夹角就是22.5 * 2 = 45°,红色与紫色就是 22.5 * 3 = 67.5黄色与紫色就是22.5 * 4 = 90°.然后通过数学公式:

X = r * sin(a)
Y = r * cos(a)

即可获得X和Y的坐标
想必大家知道Java提供了一个Math类:

/**
     * Returns the trigonometric sine of an angle.  Special cases:
     * <ul><li>If the argument is NaN or an infinity, then the
     * result is NaN.
     * <li>If the argument is zero, then the result is a zero with the
     * same sign as the argument.</ul>
     *
     * <p>The computed result must be within 1 ulp of the exact result.
     * Results must be semi-monotonic.
     *
     * @param   a   an angle, in radians.
     * @return  the sine of the argument.
     */
     求正弦值
    @CriticalNative
    public static native double sin(double a);
    
 /**
     * Returns the trigonometric cosine of an angle. Special cases:
     * <ul><li>If the argument is NaN or an infinity, then the
     * result is NaN.</ul>
     *
     * <p>The computed result must be within 1 ulp of the exact result.
     * Results must be semi-monotonic.
     *
     * @param   a   an angle, in radians.
     * @return  the cosine of the argument.
     */
     求余弦值
    @CriticalNative
    public static native double cos(double a);

 /**
     * Returns the trigonometric tangent of an angle.  Special cases:
     * <ul><li>If the argument is NaN or an infinity, then the result
     * is NaN.
     * <li>If the argument is zero, then the result is a zero with the
     * same sign as the argument.</ul>
     *
     * <p>The computed result must be within 1 ulp of the exact result.
     * Results must be semi-monotonic.
     *
     * @param   a   an angle, in radians.
     * @return  the tangent of the argument.
     */
     
	求正切值
    @CriticalNative
    public static native double tan(double a);

注意:这里的参数不是填的角度,而是填的角度对应的弧度

接下来看看什么是弧度:

在这里插入图片描述
简单的来说,夹角a对面的就是a的弧度;

使用Math类中的toRadians(flaot)方法,参数填的是角度

/**
     * Converts an angle measured in degrees to an approximately
     * equivalent angle measured in radians.  The conversion from
     * degrees to radians is generally inexact.
     *
     * @param   angdeg   an angle, in degrees
     * @return  the measurement of the angle {@code angdeg}
     *          in radians.
     * @since   1.2
     */
    public static double toRadians(double angdeg) {
        return angdeg / 180.0 * PI;
    }

这样就可以初略的得出:

X = r * sin (Math.toRadians(22))
Y = r * cos (Math.toRadians(22))

接下来直接上代码了:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context=".ButtonAnimatorActivity">
    <Button
        android:id="@+id/openButton"
        style="@style/OpenButton"
        android:background="@color/colorPrimary"
        android:layout_margin="30dp"
        />
    <Button
        android:id="@+id/bt1"
        style="@style/OpenButton"
        android:background="#ff00ff"
        android:visibility="gone"
        android:layout_margin="30dp"
        />
    <Button
        android:id="@+id/bt2"
        style="@style/OpenButton"
        android:background="#00ffff"
        android:visibility="gone"
        android:layout_margin="30dp"
        />
    <Button
        android:id="@+id/bt3"
        style="@style/OpenButton"
        android:background="#0048FF"
        android:visibility="gone"
        android:layout_margin="30dp"
        />
    <Button
        android:id="@+id/bt4"
        style="@style/OpenButton"
        android:background="#FF003B"
        android:layout_margin="30dp"
        android:visibility="gone"
        />
    <Button
        android:id="@+id/bt5"
        style="@style/OpenButton"
        android:visibility="gone"
        android:background="#FFDD00"
        android:layout_margin="30dp"
        />
</RelativeLayout>

将共同的代码抽取到values/styles下,然后在引用,可以减少写很多重复代码;

 <style name="OpenButton" >
        <item name="android:layout_width">50dp</item>
        <item name="android:layout_height">50dp</item>
        <item name="android:layout_alignParentEnd">true</item>
        <item name="android:layout_alignParentBottom">true</item>
    </style>

接下来获取控件Id,并且设置点击事件:

private Button openButton;
    private Button bt1;
    private Button bt2;
    private Button bt3;
    private Button bt4;
    private Button bt5;

	
	openButton = findViewById(R.id.openButton);
	bt1 = findViewById(R.id.bt1);
	bt2 = findViewById(R.id.bt2);
	bt3 = findViewById(R.id.bt3);
	bt4 = findViewById(R.id.bt4);
	bt5 = findViewById(R.id.bt5);

 openButton.setOnClickListener(this);
        bt1.setOnClickListener(this);
        bt2.setOnClickListener(this);
        bt3.setOnClickListener(this);
        bt4.setOnClickListener(this);
        bt5.setOnClickListener(this);
设置此变量是为了设置点击按钮出现不同的效果,因为这里需要打开和关闭
Boolean buttonType = false;

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.openButton) {
            if (!buttonType) {
                buttonType = true;
                打开bt1 - bt5
                setOpenButton(bt1,0,5,600);
                setOpenButton(bt2,1,5,600);
                setOpenButton(bt3,2,5,600);
                setOpenButton(bt4,3,5,600);
                setOpenButton(bt5,4,5,600);
            }else{
                buttonType = false;
                关闭bt1 - bt5 
                setCloseButton(bt1,0,5,600);
                setCloseButton(bt2,1,5,600);
                setCloseButton(bt3,2,5,600);
                setCloseButton(bt4,3,5,600);
                setCloseButton(bt5,4,5,600);
            }
        }else{
            Toast.makeText(this, "点击了"+v.getId(), Toast.LENGTH_SHORT).show();
        }
    }

打开按钮方法:

/**
     *
     * @param button    控件对象
     * @param index     当前按钮下标
     * @param number    按钮总个数
     * @param radio     圆的半径
     */
    private void setOpenButton(Button button, int index, int number, int radio) {
        button.setVisibility(View.VISIBLE);
        double degree = Math.toRadians(90)/(number - 1) * index;
       Log.i("setOpenButton",degree+"");
       这里需要注意,获取到结果之后要加负号(-),因为正数是往右下角弹出的,咋们现在需要向左上角弹出.
       float x = (float) -(radio * Math.sin(degree));
        float y = (float) -(radio * Math.cos(degree));

        Log.i("szjdegree"+index,x+"\t\t"+y);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(
                ObjectAnimator.ofFloat(button,"translationX",0,x),
                ObjectAnimator.ofFloat(button,"translationY",0,y),
                ObjectAnimator.ofFloat(button,"alpha",0,1),
                ObjectAnimator.ofFloat(button,"scaleX",0,1),
                ObjectAnimator.ofFloat(button,"scaleY",0,1)

        );
		
        animatorSet.setDuration(500);
        animatorSet.start();
    }

按钮的关闭方法:

private void setCloseButton(final Button button, int index, int number, int radio) {
        double degree =   Math.toRadians(90) / (number - 1) * index;
        int x = (int) - (radio * Math.sin(degree));
        int y = (int) - (radio * Math.cos(degree));
        Log.i("setCloseButton",degree+"\n");
        Log.i("setXXX"+ index,x+"\t\t"+y);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(
                ObjectAnimator.ofFloat(button,"translationX",x , 0),
                ObjectAnimator.ofFloat(button,"translationY",y , 0),
                ObjectAnimator.ofFloat(button,"alpha",1 , 0),
                ObjectAnimator.ofFloat(button,"scaleX",1 , 0),
                ObjectAnimator.ofFloat(button,"scaleY",1 , 0)

        );

        animatorSet.setDuration(500);
        animatorSet.start();
        
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                button.setVisibility(View.INVISIBLE);
            }
        },500);

    }

按钮的打开和关闭都是一样的,只是有一点不同:

  • 打开是从0 - X && 0 - Y
  • 关闭时从 X - 0 &* Y - 0

要有细心地朋友可能发现在关闭的时候还多了一个方法:

 new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                button.setVisibility(View.INVISIBLE);
            }
        },500);

为什么要加这个东西呢?咋们先看看不加这个东西的效果:
在这里插入图片描述
为什么会出现这种情况呢?因为在第二次缩放控件的时候,他只是回到了绿色控件的地方,并且透明度(alpha)由1变到了0,只是他的状态不存在了,可是他的值还有,所以他不显示的时候需要吧他隐藏掉,
我这里用到的是handler的延时操作:

 new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                button.setVisibility(View.INVISIBLE);
            }
        },500);

因为代码执行的很快很快,所以不用担心有什么问题,如果觉得不保险,也可以对animatorSet监听,当动画结束的时候会响应onAnimationEnd()事件

animatorSet.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                button.setVisibility(View.INVISIBLE);
            }

            @Override
            public void onAnimationCancel(Animator animation) {
            
            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });

菜单点击效果参考链接:

启舰.
ListView滑动效果参考文档:: 启舰.

Git地址链接: langyangyang.

请各位大佬留下宝贵的建议.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s10g

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值