属性动画(下):我也来山寨一个卫星菜单

本文介绍了一种卫星菜单(ArcMenu)的实现方法,通过计算菜单项的位置并添加动画效果,展示了如何在Android应用中创建一个动态且交互良好的卫星菜单。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


序:在网上看到别人写的什么arcmenu菜单,就是卫星菜单,第一眼看到感觉很酷,我对UI方面的东西不是特别的在乎,只要不是太丑陋就行。这里为练手 我也来比划一下,

山寨一个卫星菜单。先看看效果吧:



 下面是实现:实现的逻辑都写在代码的注释里了,总体实现逻辑:

1:计算出每个item的位置

2:添加动画效果

3:处理开关控制逻辑


 首先是布局文件:一个FrameLayout布局

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
     tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="android studio 动画"
        android:layout_marginTop="20dp"

        android:onClick="btnclick"
        android:id="@+id/btn"

        />

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/btn">

        <TextView android:text="item1"
            android:id="@+id/item1"
            android:textSize="25sp"
            android:layout_margin="10dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"

            />
        <TextView android:text="item2"
            android:id="@+id/item2"
            android:textSize="25sp"
            android:layout_margin="10dp"

            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />
        <TextView android:text="关闭"
            android:id="@+id/item3"
            android:textSize="25sp"
            android:layout_margin="10dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />
        <TextView android:text="item4"
            android:id="@+id/item4"
            android:textSize="25sp"
            android:layout_margin="10dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />
        <TextView android:text="item5"
            android:id="@+id/item5"
            android:textSize="25sp"
            android:layout_margin="10dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />

        <!--android:background="@android:drawable/btn_radio"-->
        <RadioButton
            android:id="@+id/radio"
            android:textSize="25sp"
            android:layout_margin="10dp"
            android:button="@null"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:background="@drawable/menu"
            />

    </FrameLayout>


</RelativeLayout>

然后是在activity中 初始化控件,并左相应的逻辑处理:

package com.example.administrator.applicationname;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PointF;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.BaseInterpolator;
import android.view.animation.BounceInterpolator;
import android.view.animation.CycleInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toast;

import org.w3c.dom.Text;

import java.lang.reflect.Array;
import java.text.Format;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IllegalFormatCodePointException;
import java.util.MissingFormatArgumentException;
import java.util.concurrent.ThreadLocalRandom;

public class MainActivity extends Activity implements View.OnClickListener {

    //菜单按钮
    RadioButton radio;

    Button btn;

    //负责存放菜单条目,这里用textview表示。
    ArrayList<TextView> items = new ArrayList<TextView>();
    //负责记录动画:把动画加入到集合中表示动画开启 了。当关闭动画时清空掉。
    ArrayList<ObjectAnimator> animators = new ArrayList<ObjectAnimator>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        btn = (Button) findViewById(R.id.btn);
        radio = (RadioButton) findViewById(R.id.radio);
        TextView item1 = (TextView) findViewById(R.id.item1);
        TextView item2 = (TextView) findViewById(R.id.item2);
        TextView item3_close = (TextView) findViewById(R.id.item3);
        TextView item4 = (TextView) findViewById(R.id.item4);
        TextView item5 = (TextView) findViewById(R.id.item5);
        item3_close.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (radio.isChecked()) {

                    closeAnimator();
                    radio.setChecked(false);
                }
            }
        });
        items.add(item1);
        items.add(item2);
        items.add(item3_close);
        items.add(item4);
        items.add(item5);


        radio.setOnClickListener(this);
        radio.setChecked(false);

    }

    /**
     * 关闭动画这里用reverse,也可以记录动画的轨迹参数来反执行一下。
     */
    public void closeAnimator(){
        for (ObjectAnimator animator : animators) {
            animator.reverse();
        }
        //清楚记录的动画
        animators.clear();

    }


    @Override
    public void onClick(View v) {
        //集合里有数据表示动画已经开启,重复点击 不响应。
        if(animators.size()>0){
            closeAnimator();
            return;

        }


        if(items.size()<3){
            Toast.makeText(this, "最少三个item", Toast.LENGTH_SHORT).show();
        }
        //总度数:90°
        double totalDegree = 90;
        //卫星菜单半径:这里规定半径或者横坐标都可以。总之就是要为计算每个item的位置服务.
        double unit = 300;
        //计算当前卫星菜单被及等分了,4个菜单3等分,5个4等分,因此就是items.size()-1等分
        double countDegrees = items.size()-1;
        //计算出平均等分的度数是多少
        double avgDegree = totalDegree / countDegrees;
        for (int i = 0;i<items.size();i++) {
            //计算当前每个item所占的角度是多少度。从上往下算起(可以根据自己的 布局来定,这里卫星菜单在左上角)
            //计算公式为:当前索引*平均度数:此角度 为当前半径和X方向上所成的角,这关系到计算坐标,因此可以根据布局来自己定义怎么计算。
            //跟我我的布局这里用cos 函数 来计算:
            // x/半径 = cos 当前角度, 因此x = cos当前角度 * 半径;
            // y/半径 = sin 当前角度 ,因此y =sin当前角度 * 半径;
            //baidu:就拿sin30°为列:Math.sin(30*Math.PI/180)思路为PI相当于π,而此时的PI在角度值里相当于180°,所以Math.PI/180得到的结果就是1°,然后再乘以30就得到相应的30°
            double itemDegree = i*avgDegree * Math.PI/180 ;

            double itemX = Math.cos(itemDegree) * unit ;
            double itemY = Math.sin(itemDegree) * unit ;
            TextView item = items.get(i);


            PropertyValuesHolder  translationX = PropertyValuesHolder.ofFloat("translationX", 0f, (float) itemX);
            PropertyValuesHolder  translationY = PropertyValuesHolder.ofFloat("translationY", 0f,(float)itemY);
            ObjectAnimator o =  ObjectAnimator.ofPropertyValuesHolder(item, translationX, translationY);
            o.setDuration(500).start();
            //插值器:
//            o.setInterpolator(new BounceInterpolator());
//            o.setInterpolator(new DecelerateInterpolator());
            radio.setChecked(true);
            animators.add(o);

        }
    }


}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值