Android版网易云音乐唱片机唱片磁盘旋转及唱片机机械臂动画关键代码实现思路

简介: Android版网易云音乐唱片机唱片磁盘旋转及唱片机机械臂动画关键代码实现思路先看一看我的代码运行结果。代码运行起来初始化状态:点击开始按钮,唱片机的机械臂匀速接近唱片磁盘,同时唱片磁盘也开始匀速顺时针旋转:点击停止按钮,唱片机的机械臂匀速抬离唱片磁盘,同时唱片磁盘停止旋转:实现思路:(一)旋转唱片磁盘。


Android版网易云音乐唱片机唱片磁盘旋转及唱片机机械臂动画关键代码实现思路

先看一看我的代码运行结果。
代码运行起来初始化状态:



点击开始按钮,唱片机的机械臂匀速接近唱片磁盘,同时唱片磁盘也开始匀速顺时针旋转:



点击停止按钮,唱片机的机械臂匀速抬离唱片磁盘,同时唱片磁盘停止旋转:


实现思路:
(一)旋转唱片磁盘。在附录文章12的基础上,实现网易云音乐风格的唱片磁盘。核心代码:

 //最外部的半透明边线
        OvalShape ovalShape0 = new OvalShape();
        ShapeDrawable drawable0 = new ShapeDrawable(ovalShape0);
        drawable0.getPaint().setColor(0x10000000);
        drawable0.getPaint().setStyle(Paint.Style.FILL);
        drawable0.getPaint().setAntiAlias(true);

        //黑色唱片边框
        RoundedBitmapDrawable drawable1 = RoundedBitmapDrawableFactory.create(getResources(), BitmapFactory.decodeResource(getResources(), R.drawable.disc));
        drawable1.setCircular(true);
        drawable1.setAntiAlias(true);

        //内层黑色边线
        OvalShape ovalShape2 = new OvalShape();
        ShapeDrawable drawable2 = new ShapeDrawable(ovalShape2);
        drawable2.getPaint().setColor(Color.BLACK);
        drawable2.getPaint().setStyle(Paint.Style.FILL);
        drawable2.getPaint().setAntiAlias(true);

        //最里面的图像
        RoundedBitmapDrawable drawable3 = RoundedBitmapDrawableFactory.create(getResources(), BitmapFactory.decodeResource(getResources(), R.drawable.zhangphil));
        drawable3.setCircular(true);
        drawable3.setAntiAlias(true);

        Drawable[] layers = new Drawable[4];
        layers[0] = drawable0;
        layers[1] = drawable1;
        layers[2] = drawable2;
        layers[3] = drawable3;

        LayerDrawable layerDrawable = new LayerDrawable(layers);

        int width = 10;
        //针对每一个图层进行填充,使得各个圆环之间相互有间隔,否则就重合成一个了。
        //layerDrawable.setLayerInset(0, width, width, width, width);
        layerDrawable.setLayerInset(1, width , width, width, width );
        layerDrawable.setLayerInset(2, width * 11, width * 11, width * 11, width * 11);
        layerDrawable.setLayerInset(3, width * 12, width * 12, width * 12, width * 12);

        final View discView = findViewById(R.id.myView);
        discView.setBackgroundDrawable(layerDrawable);


唱片磁盘旋转,则通过属性动画ObjectAnimator实现唱片磁盘的顺时针旋转:

 discObjectAnimator = ObjectAnimator.ofFloat(discView, "rotation", 0, 360);
        discObjectAnimator.setDuration(20000);
        //使ObjectAnimator动画匀速平滑旋转
        discObjectAnimator.setInterpolator(new LinearInterpolator());
        //无限循环旋转
        discObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
        discObjectAnimator.setRepeatMode(ValueAnimator.INFINITE);


(二)唱片机的机械臂。在相对布局的顶部放置一个ImageView,这个ImageView就是机械臂的素材,当点击开始按钮时候,就使用Android属性动画操作该ImageView以左上角坐标位置(x=0,y=0)向下顺时针匀速旋转一定角度使得机械臂刚好落入到唱片磁盘内。当点击停止按钮时候,一方面控制唱片磁盘停止旋转,另一方面要操纵机械臂匀速逆时针向上旋转离开唱片磁盘。


我写的这个例子需要布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/background">

    <View
        android:id="@+id/myView"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_centerInParent="true" />

    <ImageView
        android:id="@+id/needle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:adjustViewBounds="true"
        android:maxHeight="180dp"
        android:src="@drawable/needle" />

    <Button
        android:id="@+id/startButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:text="开始" />

    <Button
        android:id="@+id/stopButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:text="停止" />

</RelativeLayout>


上层全部完整Java代码:

package zhangphil.app;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.os.Bundle;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;


public class MainActivity extends Activity {
    private ObjectAnimator discObjectAnimator,neddleObjectAnimator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //最外部的半透明边线
        OvalShape ovalShape0 = new OvalShape();
        ShapeDrawable drawable0 = new ShapeDrawable(ovalShape0);
        drawable0.getPaint().setColor(0x10000000);
        drawable0.getPaint().setStyle(Paint.Style.FILL);
        drawable0.getPaint().setAntiAlias(true);

        //黑色唱片边框
        RoundedBitmapDrawable drawable1 = RoundedBitmapDrawableFactory.create(getResources(), BitmapFactory.decodeResource(getResources(), R.drawable.disc));
        drawable1.setCircular(true);
        drawable1.setAntiAlias(true);

        //内层黑色边线
        OvalShape ovalShape2 = new OvalShape();
        ShapeDrawable drawable2 = new ShapeDrawable(ovalShape2);
        drawable2.getPaint().setColor(Color.BLACK);
        drawable2.getPaint().setStyle(Paint.Style.FILL);
        drawable2.getPaint().setAntiAlias(true);

        //最里面的图像
        RoundedBitmapDrawable drawable3 = RoundedBitmapDrawableFactory.create(getResources(), BitmapFactory.decodeResource(getResources(), R.drawable.zhangphil));
        drawable3.setCircular(true);
        drawable3.setAntiAlias(true);

        Drawable[] layers = new Drawable[4];
        layers[0] = drawable0;
        layers[1] = drawable1;
        layers[2] = drawable2;
        layers[3] = drawable3;

        LayerDrawable layerDrawable = new LayerDrawable(layers);

        int width = 10;
        //针对每一个图层进行填充,使得各个圆环之间相互有间隔,否则就重合成一个了。
        //layerDrawable.setLayerInset(0, width, width, width, width);
        layerDrawable.setLayerInset(1, width , width, width, width );
        layerDrawable.setLayerInset(2, width * 11, width * 11, width * 11, width * 11);
        layerDrawable.setLayerInset(3, width * 12, width * 12, width * 12, width * 12);

        final View discView = findViewById(R.id.myView);
        discView.setBackgroundDrawable(layerDrawable);

        ImageView needleImage= (ImageView) findViewById(R.id.needle);

        findViewById(R.id.startButton).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                discObjectAnimator.start();
                neddleObjectAnimator.start();
            }
        });

        findViewById(R.id.stopButton).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                discObjectAnimator.cancel();
                neddleObjectAnimator.reverse();
            }
        });

        discObjectAnimator = ObjectAnimator.ofFloat(discView, "rotation", 0, 360);
        discObjectAnimator.setDuration(20000);
        //使ObjectAnimator动画匀速平滑旋转
        discObjectAnimator.setInterpolator(new LinearInterpolator());
        //无限循环旋转
        discObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
        discObjectAnimator.setRepeatMode(ValueAnimator.INFINITE);


        neddleObjectAnimator = ObjectAnimator.ofFloat(needleImage, "rotation", 0, 25);
        needleImage.setPivotX(0);
        needleImage.setPivotY(0);
        neddleObjectAnimator.setDuration(800);
        neddleObjectAnimator.setInterpolator(new LinearInterpolator());
    }
}



遗留问题:
(1)实际上,右上角机械臂这个ImageView以(x=0,y=0)的坐标位置旋转不是很恰当,因为这个机械臂作为图像素材,它本身的左上角肯定有部分空间位置,使得旋转时候不能够刚好以图中机械臂左上角那个螺丝衔接的位置旋转,也许是把机械臂衔接螺丝那个位置遮挡起来就像网易云音乐的做法,但是更完美的处理方法,我个人感觉勇应该分段设计这个机械臂:在素材设计上,把这个机械臂分为两段设计,最上的那个衔接螺丝单独抽出来,固定在布局上,然后螺丝下面把机械臂的尾部遮挡起来,旋转只选择机械臂,这样旋转就非常逼真。
(2)从代码中可以看出里面很多位置、角度、距离的值都是写死的,比如机械臂旋转的角度,机械臂的最大高度等等,作为demo说明解决方案没问题,但是在涉及到千百种Android屏幕尺寸各异的设备时候,情况变得比较复杂,要精心计算这些空间位置距离。
(3)发现一个Android自身的问题。在本例中,如果把背景图片background.jpg换成更高质量和分辨率的图如1080 X 1920,则动画执行非常之卡。但是如果把背景图片质量换成低分辨率图540 X 960则不影响动画执行效率。


我写的这个例子中用到的一些素材。
整个背景图background.jpg:



唱片机的机械臂needle.png



唱片磁盘包括头像的外部黑色外圈disc.png:


头像是我csdn的博客头像:



附录:
1,《Android动画Animation的两种加载执行方式》链接地址:http://blog.csdn.net/zhangphil/article/details/47394225
2,《Android AnimationDrawable动画与APP启动引导页面》链接地址:http://blog.csdn.net/zhangphil/article/details/47416915  
3,《Android layer-list(1)》链接地址:http://blog.csdn.net/zhangphil/article/details/517209244
4,《Android layer-list:联合shape(2)》链接地址:http://blog.csdn.net/zhangphil/article/details/51721283 
5,《Android layer-list(3)》链接地址:http://blog.csdn.net/zhangphil/article/details/51721816
6,《Android Property Animation属性动画初识:透明渐变(1)》链接地址:http://blog.csdn.net/zhangphil/article/details/50484224   
7,《Android Property Animation属性动画:rotation旋转(2)》链接地址:http://blog.csdn.net/zhangphil/article/details/50495555  
8,《Android Property Animation属性动画初识:位移translation(3)》链接地址:http://blog.csdn.net/zhangphil/article/details/50495844 
9,《Android Property Animation属性动画:scale缩放动画(4)》链接地址:http://blog.csdn.net/zhangphil/article/details/50497404
10,《Android Property Animation属性动画集:AnimatorSet(5)》链接地址:http://blog.csdn.net/zhangphil/article/details/50498531
11,《Android ImageView加载圆形图片且同时绘制圆形图片的外部边缘边线及边框》链接地址:http://blog.csdn.net/zhangphil/article/details/51944262
12,《Android View加载圆形图片且同时绘制圆形图片的外部边缘边线及边框:LayerDrawable实现》链接:http://blog.csdn.net/zhangphil/article/details/52035255

相关文章
|
1月前
|
Ubuntu 网络协议 Java
【Android平板编程】远程Ubuntu服务器code-server编程写代码
【Android平板编程】远程Ubuntu服务器code-server编程写代码
|
3月前
|
人工智能 IDE 开发工具
Studio Bot - 让 AI 帮我写 Android 代码
Studio Bot - 让 AI 帮我写 Android 代码
161 1
|
21天前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
23 1
Android开发之使用OpenGL实现翻书动画
|
2月前
|
Ubuntu 网络协议 Linux
【Linux】Android平板上远程连接Ubuntu服务器code-server进行代码开发
【Linux】Android平板上远程连接Ubuntu服务器code-server进行代码开发
57 0
|
3月前
|
安全 算法 JavaScript
安卓逆向 -- 关键代码定位与分析技术
安卓逆向 -- 关键代码定位与分析技术
43 0
|
3月前
|
安全 Android开发 数据安全/隐私保护
代码安全之代码混淆及加固(Android)
代码安全之代码混淆及加固(Android)
42 0
|
3月前
|
XML 开发工具 Android开发
Android动画效果-更新中
Android动画效果-更新中
59 1
|
4月前
|
XML Android开发 数据格式
[Android]动画
[Android]动画
33 0
|
4月前
|
API Android开发 开发者
【Android App】Vulkan实现宇宙中旋转雷达动画效果(附源码和原始视频 超详细必看)
【Android App】Vulkan实现宇宙中旋转雷达动画效果(附源码和原始视频 超详细必看)
68 1
|
4月前
|
XML 小程序 Java
【Android App】给三维魔方贴图以及旋转动画讲解和实战(附源码和演示视频 超详细必看)
【Android App】给三维魔方贴图以及旋转动画讲解和实战(附源码和演示视频 超详细必看)
28 0