对于现在最火的无外乎集五福了,而五福除了加十个好友获得外,最直接的途径就是支付宝的咻一咻了。那么咻一咻具体有哪些实现方式呢?下面我们将一一介绍这几种思路的实现过程。

1.自定义View实现咻一咻

那么这种实现方法需要掌握Canvas以及Paint几乎所有的方法。其对程序员的专业知识要求极高。

用该种方式实现的优点有:

㈠这种是最复杂的实现方法,但其兼容性最高,其支持Android的所有设备。

㈡其对内存要求不大,几乎不占用任何内存。

下面我们来看看是怎样实现其效果的:

public class XiuYiXiuView extends View {/***
     * 中心图片画笔
     */
    private Paint paint;
    /***
     * 水波圆圈画笔
     */
    private Paint circlePaint;
    /***
     * 用bitmap创建画布
     */
    private Bitmap bitmap;
    /***
     * 中心图片
     */
    private Bitmap imageBit;
    /***
     * 画布
     */
    private Canvas canvas;
    /***
     * 屏幕的宽
     */
    private int screenWidth;
    /***
     * 屏幕的高
     */
    private int screenHeight;
    /***
     * 图片右上角坐标
     */
    private Point pointLeftTop;
    /***
     * 图片右下角坐标
     */
    private Point pointRightBottom;
    /***
     * 记录圆圈
     */
    private List<LYJCircle> lyjCircleList;
    /***
     * 标记是否按下按钮,并且源泉是否扩散消失
     */
    private boolean isSpread=false;
    /***
     * 默认没有按动时候的圆圈
     */
    private LYJCircle defaultCircle;
    public XiuYiXiuView(Context context, AttributeSet attrs) {super(context, attrs);
        this.lyjCircleList=new ArrayList<>();
        screenWidth=LYJUtils.getScreenWidth((Activity) context);
        screenHeight=LYJUtils.getScreenHeight((Activity) context);
        bitmap = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_8888); // 设置位图的宽高
        canvas = new Canvas();
        canvas.setBitmap(bitmap);
        paint=new Paint(Paint.DITHER_FLAG);
        paint.setAntiAlias(true);
        circlePaint=new Paint(Paint.DITHER_FLAG);
        circlePaint.setAntiAlias(true);
        imageBit= BitmapFactory.decodeResource(getResources(), R.drawable.bwa_homepage_yuyin);
        pointLeftTop=new Point((screenWidth/2)-(imageBit.getWidth()/2),(screenHeight/2)-(imageBit.getHeight()/2));
        pointRightBottom=new Point(pointLeftTop.x+imageBit.getWidth(),pointLeftTop.y+imageBit.getHeight());
        canvas.drawBitmap(imageBit,pointLeftTop.x,pointLeftTop.y,paint);
        //取图片上的颜色
        Palette.generateAsync(imageBit, new Palette.PaletteAsyncListener() {@Override
            public void onGenerated(Palette palette) {Palette.Swatch swatch1 = palette.getVibrantSwatch(); //充满活力的色板
                circlePaint.setColor(swatch1.getRgb());
                circlePaint.setStyle(Paint.Style.STROKE);
                circlePaint.setStrokeWidth(10);
                circlePaint.setAlpha(100);
                paint.setShadowLayer(15, 0, 0, swatch1.getRgb());//设置阴影效果
                int[] mColors = new int[] {//渲染颜色
                        Color.TRANSPARENT,swatch1.getRgb()};
                //范围,这里可以微调,实现你想要的渐变
                float[] mPositions = new float[] {0f, 0.1f
                };
                Shader shader=new RadialGradient(screenWidth / 2,screenHeight / 2,imageBit.getWidth() / 2 + 10,mColors, mPositions,
                        Shader.TileMode.MIRROR);
                circlePaint.setShader(shader);
                defaultCircle=new LYJCircle(screenWidth / 2, screenHeight / 2, imageBit.getWidth() / 2 + 10);
                clearScreenAndDrawList();
                Message message = handler.obtainMessage(1);
                handler.sendMessageDelayed(message, 1000); //发送message

            }});
    }@Override
    public boolean onTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:break;
            case MotionEvent.ACTION_MOVE:break;
            case MotionEvent.ACTION_UP:isSpread=true;//是否按下图片
                lyjCircleList.add(new LYJCircle(screenWidth / 2, screenHeight / 2, imageBit.getWidth() / 2 + 10));
                clearScreenAndDrawList();
                invalidate();
                break;
            default:break;
        }return true;
    }private Handler handler = new Handler(){public void handleMessage(Message msg){switch (msg.what) {case 1://定时更新界面
                    clearScreenAndDrawList();
                    invalidate();
                    Message message = handler.obtainMessage(1);
                    handler.sendMessageDelayed(message, 200);
            }super.handleMessage(msg);
        }};

    /**
     * 清掉屏幕上所有的圆圈,然后画出集合里面的圆圈
     */
    private void clearScreenAndDrawList() {canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        //判断是否按下图片,并且外圈执行完成没有。
        if(!isSpread){circlePaint.setMaskFilter(null);
                canvas.drawCircle(defaultCircle.getRoundX(), defaultCircle.getRoundY(),defaultCircle.getRadiuLoop(), circlePaint);// 画线
        }else{for (LYJCircle lyjCircle : lyjCircleList) {if(lyjCircle.getSpreadRadiu()==0){}else if(lyjCircle.getSpreadRadiu()>(lyjCircle.getRadiu()+99)){//如果圆圈扩散半径大于图片半径+99,那么设置边缘模糊,也就是淡出的效果
                    circlePaint.setMaskFilter(new BlurMaskFilter(5, BlurMaskFilter.Blur.OUTER));
                    canvas.drawCircle(lyjCircle.getRoundX(), lyjCircle.getRoundY(),lyjCircle.getSpreadRadiu(), circlePaint);// 画线
                }else{//不是则按正常的环形渲染来
                    circlePaint.setMaskFilter(null);
                    canvas.drawCircle(lyjCircle.getRoundX(), lyjCircle.getRoundY(),lyjCircle.getSpreadRadiu(), circlePaint);// 画线
                }}}canvas.drawBitmap(imageBit,pointLeftTop.x,pointLeftTop.y,paint);
        //释放小时了的圆圈
        for(int i=0;i<lyjCircleList.size();i++){if(lyjCircleList.get(i).getSpreadRadiu()==0){lyjCircleList.remove(i);
            }}//如果没有点击图片发射出去的圆圈,那么就恢复默认缩放。
        if(lyjCircleList.size()<=0){isSpread=false;
        }}@Override
    protected void onDraw(Canvas canvas) {canvas.drawBitmap(bitmap, 0, 0, null);
    }
}

圆类:

package com.example.liyuanjing.model;

/**
 * Created by liyuanjing on 2016/2/3.
 */
public class LYJCircle {private int roundX;//圆中心点X坐标
    private int roundY;//圆中心点Y坐标
    private int radiu;//圆半径
    private int currentRadiu;//当前radiu
    private int lastRadiu;//历史radiu
    private int spreadRadiu;//加速半径
    private int[] speed=new int[]{6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6};//半径扩大速度。这里为匀速
    private int speedLast=0;//记录历史值
    public LYJCircle(int roundX,int roundY,int radiu){this.roundX=roundX;
        this.roundY=roundY;
        this.radiu=radiu;
        this.spreadRadiu=radiu;
        this.currentRadiu=this.radiu;
        this.lastRadiu=this.currentRadiu;
    }//获取半径
    public int getRadiu() {return radiu;
    }public void setRadiu(int radiu) {this.radiu = radiu;
    }//获取加速半径
    public int getSpreadRadiu(){if(speedLast>=speed.length){return 0;
        }spreadRadiu+=speed[speedLast];
        ++speedLast;
        return spreadRadiu;
    }//获取循环缩放半径
    public int getRadiuLoop() {if(currentRadiu==lastRadiu){++currentRadiu;
        }else if(currentRadiu>lastRadiu){if(currentRadiu>(radiu+20)){currentRadiu=19+radiu;
                lastRadiu=20+radiu;
            }else{lastRadiu=currentRadiu;
                currentRadiu+=5;
            }}else{if(currentRadiu<(radiu+9)){currentRadiu=10+radiu;
                lastRadiu=9+radiu;
            }else{lastRadiu=currentRadiu;
                currentRadiu-=5;
            }}return currentRadiu;
    }public int getRoundX() {return roundX;
    }public int getRoundY() {return roundY;
    }
}

看看其效果图:

你可以修改如下两个地方,会产生视觉上真真的波纹效果:

①支付宝的背景图片是淡红色,衬托了红色的波纹。当然了你也可以将画布设置为透明淡红色。

②其为填充圆圈渲染,不是我的边框渲染效果,你可以将circlePaint.setStyle(Paint.Style.STROKE);换成Paint.Style.FILL.然后,微调shader的mPositions实现环形填充渐变。你也许会觉得,你看支付宝咻一咻圆圈弹开的时候内圈有波纹也像外弹开,其实那就是环形渐变,当你圆圈变大后,其渐变的范围也就变大了,自然你看到有颜色周围扩散的迹象。

2.属性动画实现咻一咻

其要掌握的只是基本只需要属性动画,在加一点线程方面有关的知识而已。

下面我们看看其实现步骤:

㈠自定义View实现一个圆即可,代码如下:

public class LYJCircleView extends View {private Bitmap bitmap;
    private Paint paint;
    private Canvas canvas;
    private int screenWidth;
    private int screenHeight;
    private boolean isSpreadFlag=false;//标记是否发射完成

    public boolean isSpreadFlag() {return isSpreadFlag;
    }public void setIsSpreadFlag(boolean isSpreadFlag) {this.isSpreadFlag = isSpreadFlag;
    }public LYJCircleView(Context context,int width,int height,int statusHeight) {super(context);
        screenWidth= LYJUtils.getScreenWidth((Activity) context);
        screenHeight=LYJUtils.getScreenHeight((Activity) context);
        bitmap = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_8888); // 设置位图的宽高
        canvas = new Canvas();
        canvas.setBitmap(bitmap);
        paint=new Paint(Paint.DITHER_FLAG);
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);
        paint.setAlpha(100);
        paint.setShadowLayer(10, 0, 0, Color.RED);
        int[] mColors = new int[] {Color.TRANSPARENT,Color.RED
        };
        float[] mPositions = new float[] {0f, 0.1f
        };
        Shader shader=new RadialGradient(screenWidth / 2,screenHeight / 2,width / 2 + 10,mColors, mPositions,
                Shader.TileMode.MIRROR);
        paint.setShader(shader);
        canvas.drawCircle(screenWidth / 2, (screenHeight - statusHeight) / 2, width / 2 + 10, paint);
        invalidate();
    }@Override
    protected void onDraw(Canvas canvas) {canvas.drawBitmap(bitmap,0,0,null);
    }
}

代码与上面差不多,就不注释了。

㈡实现Activity即可

public class XiuYiXiuActivity extends AppCompatActivity {private ImageButton mImageButton;
    private LYJCircleView lyjCircleView;
    private RelativeLayout relativeLayout;
    private List<LYJCircleView> lyjCircleViewList;
    private int statusBarHeight;
    private Animator anim;
    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.xiuyixiu_activity_main);
        this.mImageButton=(ImageButton)findViewById(R.id.xiuyixiu_imagebutton);
        this.relativeLayout=(RelativeLayout)findViewById(R.id.xiuyixiu_relativelayout);
        this.lyjCircleViewList=new ArrayList<>();
        this.mImageButton.setOnClickListener(new View.OnClickListener() {@Override
            public void onClick(View v) {lyjCircleView.setVisibility(View.GONE);//发射圆圈,即将循环动画View隐藏
                final LYJCircleView item=new LYJCircleView(XiuYiXiuActivity.this, mImageButton.getWidth(), mImageButton.getHeight(), statusBarHeight);
                Animator spreadAnim = AnimatorInflater.loadAnimator(XiuYiXiuActivity.this, R.animator.circle_spread_animator);
                spreadAnim.addListener(new Animator.AnimatorListener() {@Override
                    public void onAnimationStart(Animator animation) {}@Override
                    public void onAnimationEnd(Animator animation) {item.setIsSpreadFlag(true);//动画执行完成,标记一下
                    }@Override
                    public void onAnimationCancel(Animator animation) {}@Override
                    public void onAnimationRepeat(Animator animation) {}});
                spreadAnim.setTarget(item);
                spreadAnim.start();
                lyjCircleViewList.add(item);
                relativeLayout.addView(item);
                relativeLayout.invalidate();
                Message message = handler.obtainMessage(1);
                handler.sendMessageDelayed(message, 10); //发送message,定时释放LYJCircleView
            }});
    }private Handler handler = new Handler(){public void handleMessage(Message msg){switch (msg.what) {case 1:for(int i=0;i<lyjCircleViewList.size();i++){if(lyjCircleViewList.get(i).isSpreadFlag()){relativeLayout.removeView(lyjCircleViewList.get(i));
                            lyjCircleViewList.remove(i);
                            relativeLayout.invalidate();
                        }}if(lyjCircleViewList.size()<=0){lyjCircleView.setVisibility(View.VISIBLE);
                    }Message message = handler.obtainMessage(1);
                    handler.sendMessageDelayed(message, 10);
            }super.handleMessage(msg);
        }};

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {super.onWindowFocusChanged(hasFocus);
        //获取状态栏高度
        Rect frame = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
        statusBarHeight = frame.top;
        this.mImageButton.post(new Runnable() {@Override
            public void run() {lyjCircleView = new LYJCircleView(XiuYiXiuActivity.this, mImageButton.getWidth(), mImageButton.getHeight(), statusBarHeight);
                relativeLayout.addView(lyjCircleView);
                relativeLayout.postInvalidate();
                // 加载动画
                anim = AnimatorInflater.loadAnimator(XiuYiXiuActivity.this, R.animator.circle_scale_animator);
                anim.addListener(new Animator.AnimatorListener() {@Override
                    public void onAnimationStart(Animator animation) {}@Override
                    public void onAnimationEnd(Animator animation) {anim.start();//循环执行动画
                    }@Override
                    public void onAnimationCancel(Animator animation) {}@Override
                    public void onAnimationRepeat(Animator animation) {}});
                anim.setTarget(lyjCircleView);
                anim.start();
            }});
    }
}

㈢布局文件代码如下:

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

    <ImageButton
        android:id="@+id/xiuyixiu_imagebutton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="@drawable/bwa_homepage_yuyin"/>

</RelativeLayout>

当然上面两个实现方法,我都只设置圆边框,没有填充,你可以设置为填充后,在微调渐变值。

其属性动画文件circle_scale_animator.xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together">
    <objectAnimator
        android:duration="1000"
        android:propertyName="scaleX"
        android:valueFrom="1.0"
        android:valueTo="1.2"
        android:valueType="floatType">
    </objectAnimator>
    <objectAnimator
        android:duration="1000"
        android:propertyName="scaleY"
        android:valueFrom="1.0"
        android:valueTo="1.2"
        android:valueType="floatType">
    </objectAnimator>
    <objectAnimator
        android:startOffset="1000"
        android:duration="1000"
        android:propertyName="scaleX"
        android:valueFrom="1.2"
        android:valueTo="1.0"
        android:valueType="floatType">
    </objectAnimator>
    <objectAnimator
        android:startOffset="1000"
        android:duration="1000"
        android:propertyName="scaleY"
        android:valueFrom="1.2"
        android:valueTo="1.0"
        android:valueType="floatType">
    </objectAnimator>
</set>

另一个circle_spread_animator.xml为:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="1000"
        android:propertyName="scaleY"
        android:valueFrom="1.0"
        android:valueTo="2.0"
        android:valueType="floatType">
    </objectAnimator>
    <objectAnimator
        android:duration="1000"
        android:propertyName="scaleX"
        android:valueFrom="1.0"
        android:valueTo="2.0"
        android:valueType="floatType">
    </objectAnimator>
</set>

其效果图如下:

3.Android 5.0逆天实现咻一咻

这个仅标记出来,不做讲解,不过有几个知识提示一下,你就明白了,不过此种方式实现只兼容5.0以上设备,不兼容5.0以下设备。

我们都知道5.0中提供如下两个属性:

android:background="?android:attr/selectableItemBackground"波纹有边界

android:background="?android:attr/selectableItemBackgroundBorderless"波纹超出边界

设置下面这个就会绘制一个圆形的波纹(不管你的控件是不是圆形)。

通过android:colorControlHighlight设置波纹颜色。

那么好了就,介绍这么多了,时间匆促,要过年了。最后附上本文源码:

https://github.com/liyuanjinglyj/XiuYiXiuDemo

实现支付宝咻一咻的几种思路相关推荐

  1. 咻一咻 android代码,支付宝咻一咻怎么用 Android帮你实现咻一咻

    对于之前最火的无外乎集五福了,而五福除了加十个好友获得外,最直接的途径就是支付宝的咻一咻了.那么咻一咻具体有哪些实现方式呢?下面我们将一一介绍这几种思路的实现过程. 1.自定义View实现咻一咻 那么 ...

  2. Android特效专辑(十二)——仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View...

    Android特效专辑(十二)--仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View 先来看看这个效果 这是我的在Only上添加的效果,说实话,Only现在都还只是半成品,台面都上不了,怪自己技术 ...

  3. Android仿支付宝UI功能开发,Android 自定义view仿支付宝咻一咻功能

    支付宝上有一个咻一咻的功能,就是点击图片后四周有水波纹的这种效果,今天也写一个类似的功能. 效果如下所示: 思路: 就是几个圆的半径不断在变大,这个可以使用动画缩放实现,还有透明动画 还有就是这是好几 ...

  4. 波纹扩散特效(仿支付宝咻一咻功能)

    波纹扩散效果之仿支付宝咻一咻功能实现波纹扩散特效,具体内容如下所示: 先来看看这个效果 这是我的在Only上添加的效果,说实话,Only现在都还只是半成品,台面都上不了,怪自己技术不行,也太懒了  P ...

  5. 模仿 Android支付宝咻一咻功能实现

    下午无聊,之前本来一直想仿写支付宝的咻一咻功能 首先上效果图 然后创建一个自定义的 RounImageView 继承ImageView package com.android.xiong.gridla ...

  6. Android仿支付宝咻一咻动画

    Android特效专辑(十二)--仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View 先来看看这个效果 这是我的在Only上添加的效果,说实话,Only现在都还只是半成品,台面都上不了,怪自己技术 ...

  7. Android特效专辑(十二)——仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View

    Android特效专辑(十二)--仿支付宝咻一咻功能实现波纹扩散特效,精细小巧的View 先来看看这个效果 这是我的在Only上添加的效果,说实话,Only现在都还只是半成品,台面都上不了,怪自己技术 ...

  8. 支付宝福卡破解——咻一咻的ui怎么实现

    通过春节的无节操营销,支付宝的咻一咻功能让许多人手酸过.心酸过(没能中敬业福),在吐槽之余好奇的程序员总会猜想这ui怎么实现的呢? 在不看smali代码的情况下应该都会猜想咻一咻的ui实现是这样的:点 ...

  9. Flutter(十八)——支付宝咻一咻动画实践

    本文目录 咻一咻设计 代码实现咻一咻 三个动画的实现 构建圆 透明效果 咻一咻设计 对于支付宝咻一咻功能,是在2016年的时候上线到支付宝的,那个时候好像是专门为了集五福而设计的功能,现在肯定已经不在 ...

最新文章

  1. (转)测试用例的设计方法(全)之二 错误推断、因果图
  2. 聊聊flink的Tumbling Window
  3. mybatis使用时遇到的一些问题------模糊查询、处理大于号小于号、相关函数替换空值...
  4. spring mvc学习(12)---使用idea创建第一个maven项目
  5. rsyslod服务配置
  6. 硬盘全新安装windows 7
  7. pic单片机c语言读eeprom,PIC单片机的EEPROM读写实例及说明
  8. 【报告分享】如何嫁给“改变世界的男人”-程序员之理想女友大调查.pdf(附下载链接)...
  9. LINQ 标准的查询操作符 生成操作符 Range()、Empty()和Repear()
  10. 通俗易懂的图解机器学习之机器学习概论
  11. 基于Pytorch实现人脸关键点检测模型MTCNN
  12. 柯美smb扫描出现服务器连接错误_为什么震旦复印机扫描提示错误扫描SMB跳ED09C7?...
  13. SharedPreferences牛刀小试
  14. Unity lua os.time超过2038年1月19日3时14分07秒会出问题的解决办法(新千年虫问题、C#时间)
  15. Fragment already added问题的解决
  16. 宝刀——《荒原的呼唤》选载之一
  17. 【中科院信工所】-2021考研经验-记录一段每天都在思考如何学习的日子
  18. 凹凸世界手游服务器维修,凹凸世界手游进不去怎么办 解决方法介绍
  19. MI 小米米家智能平台
  20. 老款Tplink路由器如何桥接

热门文章

  1. Linux字符集详解
  2. window下安装pytorch(不用下载cuda和cudnn)(用清华镜像)
  3. 安装稳定版python 3.9.2
  4. 2021年安全员-A证(山东省)考试及安全员-A证(山东省)考试技巧
  5. stm32f407探索者开发板(十)——时钟系统精讲
  6. 移动端如何让页面强制横屏
  7. 为什么C++模板声明与定义要放在同一文件中?
  8. android 光标居右,Android EditText内容右对齐,光标位于hint之后
  9. html按钮超链接错误403,电脑使用浏览器打开网页提示网站拒绝显示此网页和HTTP 403禁用的解决方法...
  10. 人脸特征点检测(一)