文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android自定义ViewGroup实现绚丽的仿支付宝咻一咻雷达脉冲效果

2022-06-06 07:20

关注

去年春节的时候支付宝推行的集福娃活动着实火的不能再火了,更给力的是春晚又可以全民参与咻一咻集福娃活动,集齐五福就可平分亿元大红包,只可惜没有敬业福……那时候在家没事写了个咻一咻插件,只要到了咻一咻的时间点插件就可以自动的点击咻一咻来咻红包,当时只是纯粹练习这部分技术代码没有公开,后续计划写篇关于插件这方面的文章,扯远了(*^__^*) ……我们知道在支付宝的咻一咻页面有个雷达扩散的动画效果,当时感觉动画效果非常棒,于是私下尝试着实现了类似的效果,后来在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<Animator> animators = new ArrayList<Animator>(); 
for (int index = 0; index < mCount; index++) { 
RadarView radarView = new RadarView(getContext()); 
radarView.setScaleX(0); 
radarView.setScaleY(0); 
radarView.setAlpha(1); 
addView(radarView, index, params); 
// 计算时间间隔 
long delay = index * mDuration / mCount; 
// 属性动画 
animators.add(create(radarView, "scaleX", repeatCount, delay, 0, 1)); 
animators.add(create(radarView, "scaleY", repeatCount, delay, 0, 1)); 
animators.add(create(radarView, "alpha", repeatCount, delay, 1, 0)); 
} 
mAnimatorSet = new AnimatorSet(); 
mAnimatorSet.playTogether(animators); 
mAnimatorSet.setInterpolator(new LinearInterpolator()); 
mAnimatorSet.setDuration(mDuration); 
mAnimatorSet.addListener(mAnimatorListener); 
} 
private ObjectAnimator create(View target, String propertyName, int repeatCount, long delay, float from, float to) { 
ObjectAnimator animator = ObjectAnimator.ofFloat(target, propertyName, from, to); 
animator.setRepeatCount(repeatCount); 
animator.setRepeatMode(ObjectAnimator.RESTART); 
animator.setStartDelay(delay); 
return animator; 
} 
private void reset() { 
boolean isStarted = isStarted(); 
clear(); 
build(); 
if (isStarted) { 
start(); 
} 
} 
private class RadarView extends View { 
public RadarView(Context context) { 
super(context); 
} 
@Override 
protected void onDraw(Canvas canvas) { 
if (null == mPaint) { 
mPaint = new Paint(); 
mPaint.setColor(mColor); 
mPaint.setAntiAlias(true); 
// 注意Style的用法,【STROKE:画环】【FILL:画圆】 
mPaint.setStyle(mUseRing ? Style.STROKE : Style.FILL); 
mPaint.setStrokeWidth(mUseRing ? mStrokeWidth : 0); 
} 
// 画圆或环 
canvas.drawCircle(mCenterX, mCenterY, mUseRing ? mRadius - mStrokeWidth : mRadius, mPaint); 
} 
} 
private int dip2px(float dpValue) { 
final float scale = getResources().getDisplayMetrics().density; 
return (int) (dpValue * scale + 0.5f); 
} 
private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() { 
@Override 
public void onAnimationStart(Animator animator) { 
mIsStarted = true; 
} 
@Override 
public void onAnimationEnd(Animator animator) { 
mIsStarted = false; 
} 
@Override 
public void onAnimationCancel(Animator animator) { 
mIsStarted = false; 
} 
@Override 
public void onAnimationRepeat(Animator animator) { 
} 
}; 
}

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


<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" 
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" > 
<View 
android:id="@+id/holder" 
android:layout_width="@dimen/activity_horizontal_margin" 
android:layout_height="10dp" 
android:layout_centerHorizontal="true" /> 
<com.llew.wb.RadarLayout 
android:id="@+id/radarlayout1" 
android:layout_width="match_parent" 
android:layout_height="150dp" 
android:layout_toLeftOf="@id/holder" 
android:background="#bbaacc" > 
</com.llew.wb.RadarLayout> 
<com.llew.wb.RadarLayout 
android:id="@+id/radarlayout2" 
android:layout_width="match_parent" 
android:layout_height="150dp" 
android:layout_toRightOf="@id/holder" 
android:background="#bbaacc" > 
</com.llew.wb.RadarLayout> 
<com.llew.wb.RadarLayout 
android:id="@+id/radarlayout3" 
android:layout_width="match_parent" 
android:layout_height="150dp" 
android:layout_below="@id/radarlayout1" 
android:layout_marginTop="@dimen/activity_horizontal_margin" 
android:layout_toLeftOf="@id/holder" 
android:background="#bbaacc" > 
</com.llew.wb.RadarLayout> 
<com.llew.wb.RadarLayout 
android:id="@+id/radarlayout4" 
android:layout_width="match_parent" 
android:layout_height="150dp" 
android:layout_below="@id/radarlayout1" 
android:layout_marginTop="@dimen/activity_horizontal_margin" 
android:layout_toRightOf="@id/holder" 
android:background="#bbaacc" > 
</com.llew.wb.RadarLayout> 
<Button 
android:layout_width="100dp" 
android:layout_height="wrap_content" 
android:layout_alignParentBottom="true" 
android:layout_centerHorizontal="true" 
android:layout_marginBottom="20dp" 
android:onClick="start" 
android:text="开始" /> 
</RelativeLayout>

在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自定义ViewGroup实现绚丽的仿支付宝咻一咻雷达脉冲效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对编程网网站的支持!

您可能感兴趣的文章:Android Shader应用开发之雷达扫描效果Android仿微信雷达扫描效果的实现方法Android仿微信、QQ附近好友雷达扫描效果Android雷达扫描动态界面制作Android仿支付宝上芝麻信用分雷达图Android动画之雷达扫描效果Android仿微信雷达辐射搜索好友(逻辑清晰实现简单)Android编程简单实现雷达扫描效果基于Android自定义控件实现雷达效果


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯