热门标签 | 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,最后感谢收看(*^__^*) ……


        源码下载





推荐阅读
  • 本文介绍了在Android项目中实现时间轴效果的方法,通过自定义ListView的Item布局和适配器逻辑,实现了动态显示和隐藏时间标签的功能。文中详细描述了布局文件、适配器代码以及时间格式化工具类的具体实现。 ... [详细]
  • 深入解析 Android IPC 中的 Messenger 机制
    本文详细介绍了 Android 中基于消息传递的进程间通信(IPC)机制——Messenger。通过实例和源码分析,帮助开发者更好地理解和使用这一高效的通信工具。 ... [详细]
  • ListView简单使用
    先上效果:主要实现了Listview的绑定和点击事件。项目资源结构如下:先创建一个动物类,用来装载数据:Animal类如下:packagecom.example.simplelis ... [详细]
  • 本文详细介绍了如何在Android应用中使用GridView组件以网格形式展示数据(如文本和图像)。通过行列布局,实现类似矩阵的数据展示效果。 ... [详细]
  • 本文探讨了使用C#在SQL Server和Access数据库中批量插入多条数据的性能差异。通过具体代码示例,详细分析了两种数据库的执行效率,并提供了优化建议。 ... [详细]
  • 在创建新的Android项目时,您可能会遇到aapt错误,提示无法打开libstdc++.so.6共享对象文件。本文将探讨该问题的原因及解决方案。 ... [详细]
  • 本文介绍如何使用 Android 的 Canvas 和 View 组件创建一个简单的绘图板应用程序,支持触摸绘画和保存图片功能。 ... [详细]
  • Qt QTableView 内嵌控件的实现方法
    本文详细介绍了在 Qt QTableView 中嵌入控件的多种方法,包括使用 QItemDelegate、setIndexWidget 和 setIndexWidget 结合布局管理器。每种方法都有其适用场景和优缺点。 ... [详细]
  • 在 Android 开发中,通过 Intent 启动 Activity 或 Service 时,可以使用 putExtra 方法传递数据。接收方可以通过 getIntent().getExtras() 获取这些数据。本文将介绍如何使用 RoboGuice 框架简化这一过程,特别是 @InjectExtra 注解的使用。 ... [详细]
  • 本文详细介绍了 Android 开发中 layout_gravity 属性的使用方法及其在不同布局下的效果,旨在帮助开发者更好地理解和利用这一属性来精确控制视图的布局。 ... [详细]
  • 本文介绍如何在Windows Forms应用程序中使用C#实现DataGridView的多列排序功能,包括升序和降序排序。 ... [详细]
  • 本文详细介绍了 iBatis.NET 中的 Iterate 元素,它用于遍历集合并重复生成每个项目的主体内容。通过该元素,可以实现类似于 foreach 的功能,尽管 iBatis.NET 并未直接提供 foreach 标签。 ... [详细]
  • 本文详细探讨了HTML表单中GET和POST请求的区别,包括它们的工作原理、数据传输方式、安全性及适用场景。同时,通过实例展示了如何在Servlet中处理这两种请求。 ... [详细]
  • 本文详细探讨了Android Activity中View的绘制流程和动画机制,包括Activity的生命周期、View的测量、布局和绘制过程以及动画对View的影响。通过实验验证,澄清了一些常见的误解,并提供了代码示例和执行结果。 ... [详细]
  • 当unique验证运到图片上传时
    2019独角兽企业重金招聘Python工程师标准model:public$imageFile;publicfunctionrules(){return[[[na ... [详细]
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社区 版权所有