热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析AndroidUI设计:自定义ViewGroup打造酷炫的支付宝风格雷达脉冲动画效果

本文深入解析了如何通过自定义ViewGroup实现类似支付宝风格的酷炫雷达脉冲动画效果。文章详细介绍了自定义ViewGroup的原理和实现步骤,并结合实际案例展示了如何在AndroidUI设计中应用这一技术,为开发者提供了宝贵的参考和实践指导。

转载请注明出处:http://blog.csdn.net/llew2011/article/details/52801147

        去年春节的时候支付宝推行的集福娃活动着实火的不能再火了,更给力的是春晚又可以全民参与咻一咻集福娃活动,集齐五福就可平分亿元大红包,只可惜没有敬业福……那时候在家没事写了个咻一咻插件,只要到了咻一咻的时间点插件就可以自动的点击咻一咻来咻红包,当时只是纯粹练习这部分技术代码没有公开,后续计划写篇关于插件这方面的文章,扯远了(*^__^*) ……我们知道在支付宝的咻一咻页面有个雷达扩散的动画效果,当时感觉动画效果非常棒,于是私下尝试着实现了类似的效果,后来在github发现有大神也写有类似效果,于是读了一下大神的代码发现我们的核心思想都是一样的,只是细节不同,然后我就择其善者而从之,把两份代码整合了一下......整合之后的运行效果如下所示:


        开始讲解实现之前我们先分析一下支付宝的咻一咻效果,进入支付宝咻一咻页面后点击了咻一咻按钮,屏幕上先出现一个圆在不断的进行放大操作,在该圆进行放大操作的同时其透明度也在由大到小进行变化,接着该圆没有消失之前又会出现新的圆也在进行同样的动画操作……通过观察我们发现这些圆都是按照固定的时间间隔在依次的执行放大和透明度渐变的动画操作,所以要实现同样的效果,首先要有一个ViewGroup,然后给这个ViewGroup添加固定数量的子View,最后让这些子View执行放大和透明度渐变动画就可以实现该效果了。清楚了这个大纲流程,实现起来就好办了。

        首先定义我们的ViewGroup,由于该ViewGroup仅仅是添加固定数量的子View,然后让这些子View执行一系列动画,所以可以直接继承FrameLayout,代码如下所示:

public class RadarLayout extends FrameLayout {

public RadarLayout(Context context) {
super(context);
}

public RadarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}

public RadarLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
        我们为自己的咻一咻效果控件取名为RadarLayout,radar为雷达的意思,RadarLayout就表示在不断的进行扫描的意思。通过前边的分析我们知道RadarLayout是由固定数量的子View组成的,因此RadarLayout需要有表示子View数量的属性并且该属性外界可访问可修改;由于子View的执行动画是放缩和透明度渐变同时进行的,所以RadarLayout需要用动画集来组装各个动画;由于动画执行时需要知道执行时间所以RadarLayout需要有表示执行时间的属性并且该属性外界可访问可修改;由于RadarLayout的动画效果是子View来执行的,在咻一咻页面是个圆,所以需要定义子View并画在屏幕上,而画在屏幕上需要有画笔,画笔需要有颜色,还需要知道画在哪,所以可定义我们的RadarLayout定义如下所示:
public class RadarLayout extends FrameLayout {    public static final int INFINITE = 0;    private static final int DEFAULT_COUNT = 4;    private static final int DEFAULT_COLOR = Color.rgb(0, 116, 193);    private static final int DEFAULT_DURATION = 7000;    private static final int DEFAULT_REPEAT = INFINITE;    private static final int DEFAULT_STROKE_WIDTH = 2;    private int mCount;    private int mDuration;    private int mRepeat;    private AnimatorSet mAnimatorSet;        private Paint mPaint;    private int mColor;    private float mRadius;    private float mCenterX;    private float mCenterY;    private int mStrokeWidth;    private boolean mIsStarted;    private boolean mUseRing;    public RadarLayout(Context context) {        super(context);        initGlobalparams();    }    public RadarLayout(Context context, AttributeSet attrs) {        super(context, attrs);        initGlobalparams();    }    public RadarLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initGlobalparams();    }        private void initGlobalparams() {    	mColor = DEFAULT_COLOR;    	mCount = DEFAULT_COUNT;        mDuration = DEFAULT_DURATION;        mRepeat = DEFAULT_REPEAT;        mUseRing = false;        mStrokeWidth = dip2px(DEFAULT_STROKE_WIDTH);                build();    }    public synchronized void start() {        if (mAnimatorSet == null || mIsStarted) {            return;        }                mAnimatorSet.start();    }    public synchronized void stop() {        if (mAnimatorSet == null || !mIsStarted) {            return;        }        mAnimatorSet.end();    }    public synchronized boolean isStarted() {        return (mAnimatorSet != null && mIsStarted);    }    public int getCount() {        return mCount;    }    public int getDuration() {        return mDuration;    }    public void setCount(int count) {        if (count <0) {            throw new IllegalArgumentException("Count cannot be negative");        }        if (count != mCount) {            mCount = count;            reset();            invalidate();        }    }    public void setDuration(int millis) {        if (millis <0) {            throw new IllegalArgumentException("Duration cannot be negative");        }        if (millis != mDuration) {            mDuration = millis;            reset();            invalidate();        }    }        public void setColor(int color) {    	if (mColor != color) {			mColor = color;			reset();			invalidate();		}    }        public void setUseRing(boolean useRing) {    	if (mUseRing != useRing) {			mUseRing = useRing;			reset();			invalidate();		}    }    @Override    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);                int width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();        int height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();                // 确定圆的圆点坐标及半径        mCenterX = width * 0.5f;        mCenterY = height * 0.5f;        mRadius = Math.min(width, height) * 0.5f;    }    private void clear() {        stop();        removeAllViews();    }    private void build() {    	        LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT);        int repeatCount = (mRepeat == INFINITE) ? ObjectAnimator.INFINITE : mRepeat;        List animators = new ArrayList();        for (int index = 0; index  

        我们的RadarLayout已经完成了,代码很简单,相信小伙伴们都看的懂,需要注意属性mUseRing的含义,当mUserRing为true时表示使用环形雷达脉冲,否则使用圆形雷达脉冲。其次是属性动画的使用,如果有不明白的请自行查阅,这里就不再多多介绍了。接下来编写我们的activity_main.xml布局文件,如下所示:

    xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:cOntext="com.llew.wb.MainActivity" >

android:id="@+id/holder"
android:layout_
android:layout_
android:layout_centerHorizOntal="true" />

android:id="@+id/radarlayout1"
android:layout_
android:layout_
android:layout_toLeftOf="@id/holder"
android:background="#bbaacc" >


android:id="@+id/radarlayout2"
android:layout_
android:layout_
android:layout_toRightOf="@id/holder"
android:background="#bbaacc" >


android:id="@+id/radarlayout3"
android:layout_
android:layout_
android:layout_below="@id/radarlayout1"
android:layout_marginTop="@dimen/activity_horizontal_margin"
android:layout_toLeftOf="@id/holder"
android:background="#bbaacc" >


android:id="@+id/radarlayout4"
android:layout_
android:layout_
android:layout_below="@id/radarlayout1"
android:layout_marginTop="@dimen/activity_horizontal_margin"
android:layout_toRightOf="@id/holder"
android:background="#bbaacc" >


android:layout_
android:layout_
android:layout_alignParentBottom="true"
android:layout_centerHorizOntal="true"
android:layout_marginBottom="20dp"
android:OnClick="start"
android:text="开始" />

        在activity_main.xml布局中我们添加了4个RadarLayout,目的是对比他们的差异,接下编写我们的MainActivity,代码如下:
public class MainActivity extends Activity {	private RadarLayout layout1;	private RadarLayout layout2;	private RadarLayout layout3;	private RadarLayout layout4;		@Override	protected void onCreate(Bundle savedInstanceState) {		super.onCreate(savedInstanceState);		setContentView(R.layout.activity_main);		layout1 = (RadarLayout) findViewById(R.id.radarlayout1);				layout2 = (RadarLayout) findViewById(R.id.radarlayout2);		layout2.setUseRing(true);		layout2.setCount(2);				layout3 = (RadarLayout) findViewById(R.id.radarlayout3);		layout3.setUseRing(false);		layout3.setColor(Color.RED);				layout4 = (RadarLayout) findViewById(R.id.radarlayout4);		layout4.setCount(7);		layout4.setColor(Color.BLUE);		layout4.setUseRing(true);	}		public void start(View view) {		layout1.start();		layout2.start();		layout3.start();		layout4.start();	}}
        在MainActivity中我们设置了layout1为默认值效果,layout2设置了使用环形效果并且设置了数量为2个;layout3设置为使用圆形并设置圆形的颜色为红色,layout3我们设置了使用环形,设置了环形数量为7个并设置颜色给蓝色。当点击了开始按钮后,我们打开每一个RadarLayout的动画,运行效果如下所示:

        运行效果看起来还不错,基本上实现了仿支付的咻一咻的雷达脉冲效果,主要原理就是利用了属性动画并把这些属性动画集合起来一块播放即可。需要注意的是如果想在低版本兼容属性动画可以使用Jake Wharton大神开源的著名动画兼容库NineOldAndroids,最后感谢收看(*^__^*) ……


        源码下载





推荐阅读
  • Oracle培训(三十七)——深入解析Hibernate第三章:实体关联关系映射详解
    在本节Oracle培训中,我们将深入探讨Hibernate第三章的内容,重点讲解实体关联关系映射的详细知识点。首先,回顾了Hibernate的基本概念和映射基础,随后详细分析了不同类型的实体关联关系,包括一对一、一对多和多对多关系的映射方法及其应用场景。通过具体的示例和代码片段,帮助读者更好地理解和掌握这些复杂的映射技术。此外,还讨论了如何优化关联关系的性能,以及常见的问题和解决方案。 ... [详细]
  • 深入解析MyBatis的高级映射技术
    在前一章节中,我们探讨了MyBatis的基本对象映射方法,其中对象属性与数据库表字段之间实现了直接的一对一映射。然而,在实际开发中,这种简单的映射方式往往难以满足复杂业务需求。本文将深入分析MyBatis的高级映射技术,介绍如何通过配置和注解实现更为灵活的对象与数据库表之间的映射关系,包括嵌套结果、联合查询和动态SQL等高级功能,以提高开发效率和代码可维护性。 ... [详细]
  • 本文深入探讨了Android事件分发机制的源代码,重点分析了DecorView作为Activity根布局的角色及其在事件传递中的作用。同时,详细解析了PhoneWindow在Activity窗口管理中的关键功能,以及它如何与DecorView协同工作,确保用户交互事件的高效处理。 ... [详细]
  • 在第七天的深度学习课程中,我们将重点探讨DGL框架的高级应用,特别是在官方文档指导下进行数据集的下载与预处理。通过详细的步骤说明和实用技巧,帮助读者高效地构建和优化图神经网络的数据管道。此外,我们还将介绍如何利用DGL提供的模块化工具,实现数据的快速加载和预处理,以提升模型训练的效率和准确性。 ... [详细]
  • MySQL日志分析在应急响应中的应用与优化策略
    在应急响应中,MySQL日志分析对于检测和应对数据库攻击具有重要意义。常见的攻击手段包括弱口令、SQL注入、权限提升和备份数据窃取。通过对MySQL日志的深入分析,不仅可以及时发现潜在的攻击行为,还能详细还原攻击过程并追踪攻击源头。此外,优化日志记录和分析策略,能够提高安全响应效率,增强系统的整体安全性。 ... [详细]
  • 在前一篇文章中,我们介绍了如何使用Requests库发送GET请求。本文将深入探讨如何通过Requests库发送POST请求,包括参数格式、请求封装等关键技巧,并通过“历史上的今天”API实例进行详细说明。 ... [详细]
  • 火狐浏览器中使用JavaScript为audio标签的src属性赋值时遇到的问题及解决方案
    在火狐浏览器中,使用JavaScript为``标签的`src`属性赋值时可能会遇到兼容性问题。本文详细探讨了这一问题的成因,并提供了一种有效的解决方案,确保音频文件能够在火狐浏览器中正常播放。通过调整JavaScript代码,可以避免常见的加载失败或播放中断现象,提升用户体验。 ... [详细]
  • 本文详细介绍了如何使用C++实现邻接表数据结构。邻接表是一种用于表示图的数据结构,特别适用于稀疏图的存储。通过定义最大顶点数和顶点类型,本文展示了如何利用标准模板库(STL)中的容器来构建和操作邻接表,从而高效地管理和查询图中的节点及其连接关系。此外,还提供了具体的代码示例,帮助读者更好地理解和应用这一数据结构。 ... [详细]
  • 精通jQuery:深入解析事件处理机制与应用技巧
    本文详细探讨了jQuery的事件处理机制及其应用技巧,通过具体的代码示例,逐一解析了每个jQuery代码片段与其对应的HTML结构。文章以标记为基准,CSS作为通用样式,确保每段代码都能独立运行。HTML和CSS代码统一放置在文章末尾,方便读者参考和实践。 ... [详细]
  • Python 并发编程进阶:从初学者到高手的进程与模块开发指南
    Python 并发编程进阶:从初学者到高手的进程与模块开发指南 ... [详细]
  • 生成树协议(STP)由IEEE 802.1D-1998标准定义,主要用于防止网络中的环路问题,但其收敛速度较慢,不适用于需要快速恢复的环境。为了解决这一问题,快速生成树协议(RSTP)和多生成树协议(MSTP)应运而生。RSTP在IEEE 802.1w中定义,显著提高了网络的收敛速度,特别是在点对点链路和边缘端口上表现优异。MSTP则进一步扩展了RSTP的功能,支持多个生成树实例,能够更好地实现负载均衡和资源优化。这些协议在现代网络设计中发挥着重要作用,广泛应用于企业级网络和数据中心。 ... [详细]
  • Spring框架下发送嵌入图片邮件时遇到的技术挑战与解决方案
    在Spring框架中发送嵌入图片的HTML格式邮件时,常遇到技术挑战。一种有效的解决方案是在邮件内容中直接使用``标签来引用图片。此外,还可以通过MimeMessageHelper类的addInline方法将图片作为内联资源添加到邮件中,确保图片能够正确显示。这种方法不仅提高了邮件的可读性,还增强了用户体验。 ... [详细]
  • 24点纸牌智力挑战:经典数学益智游戏
    24点纸牌智力挑战是一款广受欢迎的经典数学益智游戏。玩家需要从一副扑克牌中随机抽取四张牌,通过加、减、乘、除等运算,在最短时间内计算出结果为24,最先达成目标的玩家获胜。游戏中,J、Q、K分别代表11、12、13,增加了游戏的复杂性和趣味性。 ... [详细]
  • 本章深入探讨了Java中的多态特性,这是面向对象编程的核心概念之一。多态指的是同一操作作用于不同的对象时,可以有不同的解释和执行方式。在Java中,多态通过父类引用变量引用子类对象来实现,即 `父类类型 引用变量名 = new 子类类型();`。这种方式允许程序在运行时根据实际对象的类型动态地选择合适的方法执行,从而提高代码的灵活性和可扩展性。此外,本章还详细介绍了多态的应用场景和注意事项,帮助读者更好地理解和运用这一重要概念。 ... [详细]
  • 在Java中,一个类可以实现多个接口,但是否能够继承多个类则存在限制。本文探讨了Java中实现多继承的方法及其局限性,详细分析了通过接口、抽象类和组合等技术手段来模拟多继承的策略,并讨论了这些方法的优势和潜在问题。 ... [详细]
author-avatar
给糖就不骗你
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有