zoukankan      html  css  js  c++  java
  • Android 两种方式实现类似水波扩散效果

    原文链接 https://mp.weixin.qq.com/s/M19tp_ShOO6esKdozi7Nlg

    两种方式实现类似水波扩散效果,先上图为敬

    1. 自定义view实现
    2. 动画实现

    自定义view实现

    思路分析:通过canvas画圆,每次改变圆半径和透明度,当半径达到一定程度,再次从中心开始绘圆,达到不同层级的效果,通过不断绘制达到view扩散效果

    private Paint centerPaint; //中心圆paint
    private int radius = 100; //中心圆半径
    private Paint spreadPaint; //扩散圆paint
    private float centerX;//圆心x
    private float centerY;//圆心y
    private int distance = 5; //每次圆递增间距
    private int maxRadius = 80; //最大圆半径
    private int delayMilliseconds = 33;//扩散延迟间隔,越大扩散越慢
    private List<Integer> spreadRadius = new ArrayList<>();//扩散圆层级数,元素为扩散的距离
    private List<Integer> alphas = new ArrayList<>();//对应每层圆的透明度
    

    style文件里自定义属性

    <declare-styleable name="SpreadView">
        <!--中心圆颜色-->
        <attr name="spread_center_color" format="color" />
        <!--中心圆半径-->
        <attr name="spread_radius" format="integer" />
        <!--扩散圆颜色-->
        <attr name="spread_spread_color" format="color" />
        <!--扩散间距-->
        <attr name="spread_distance" format="integer" />
        <!--扩散最大半径-->
        <attr name="spread_max_radius" format="integer" />
        <!--扩散延迟间隔-->
        <attr name="spread_delay_milliseconds" format="integer" />
    </declare-styleable>
    
    

    初始化

    
    public SpreadView(Context context) {
        this(context, null, 0);
    }
    
    public SpreadView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
    
    public SpreadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SpreadView, defStyleAttr, 0);
        radius = a.getInt(R.styleable.SpreadView_spread_radius, radius);
        maxRadius = a.getInt(R.styleable.SpreadView_spread_max_radius, maxRadius);
        int centerColor = a.getColor(R.styleable.SpreadView_spread_center_color, ContextCompat.getColor(context, R.color.colorAccent));
        int spreadColor = a.getColor(R.styleable.SpreadView_spread_spread_color, ContextCompat.getColor(context, R.color.colorAccent));
        distance = a.getInt(R.styleable.SpreadView_spread_distance, distance);
        a.recycle();
    
        centerPaint = new Paint();
        centerPaint.setColor(centerColor);
        centerPaint.setAntiAlias(true);
        //最开始不透明且扩散距离为0
        alphas.add(255);
        spreadRadius.add(0);
        spreadPaint = new Paint();
        spreadPaint.setAntiAlias(true);
        spreadPaint.setAlpha(255);
        spreadPaint.setColor(spreadColor);
    }
    

    确定圆心位置

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //圆心位置
        centerX = w / 2;
        centerY = h / 2;
    }
    

    自定义view的绘制

    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < spreadRadius.size(); i++) {
            int alpha = alphas.get(i);
            spreadPaint.setAlpha(alpha);
            int width = spreadRadius.get(i);
            //绘制扩散的圆
            canvas.drawCircle(centerX, centerY, radius + width, spreadPaint);
    
            //每次扩散圆半径递增,圆透明度递减
            if (alpha > 0 && width < 300) {
                alpha = alpha - distance > 0 ? alpha - distance : 1;
                alphas.set(i, alpha);
                spreadRadius.set(i, width + distance);
            }
        }
        //当最外层扩散圆半径达到最大半径时添加新扩散圆
        if (spreadRadius.get(spreadRadius.size() - 1) > maxRadius) {
            spreadRadius.add(0);
            alphas.add(255);
        }
        //超过8个扩散圆,删除最先绘制的圆,即最外层的圆
        if (spreadRadius.size() >= 8) {
            alphas.remove(0);
            spreadRadius.remove(0);
        }
        //中间的圆
        canvas.drawCircle(centerX, centerY, radius, centerPaint);
        //TODO 可以在中间圆绘制文字或者图片
    
        //延迟更新,达到扩散视觉差效果
        postInvalidateDelayed(delayMilliseconds);
    }
    
    

    xml样式

    <com.airsaid.diffuseview.widget.SpreadView
        android:id="@+id/spreadView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:spread_center_color="@color/colorAccent"
        app:spread_delay_milliseconds="35"
        app:spread_distance="5"
        app:spread_max_radius="90"
        app:spread_radius="100"
        app:spread_spread_color="@color/colorAccent" />
    

    效果图

    中心圆处可以自定义写文字,画图片等等...

    动画实现

    思路分析:通过动画实现,imageView不停做动画缩放+渐变
    最中心的imageView保持不变
    中间一层imageView从原始放大到1.4倍,同时从不透明变为半透明
    最外层的imageView从1.4倍放大到1.8倍,同时从半透明变为全透明
    利用shape画一个圆,作为动画基础视图

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
        <corners android:radius="65dp"/>
        <solid android:color="@color/colorAccent"/>
    </shape>
    

    布局视图

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <!--中心imageView-->
        <ImageView
            android:id="@+id/iv_wave"
            android:layout_width="130dp"
            android:layout_height="130dp"
            android:layout_gravity="center"
            android:background="@drawable/shape_circle" />
        <!--中间的imageView-->
        <ImageView
            android:id="@+id/iv_wave_1"
            android:layout_width="130dp"
            android:layout_height="130dp"
            android:layout_gravity="center"
            android:background="@drawable/shape_circle" />
        <!--最外层imageView-->
        <ImageView
            android:id="@+id/iv_wave_2"
            android:layout_width="130dp"
            android:layout_height="130dp"
            android:layout_gravity="center"
            android:background="@drawable/shape_circle" />
    </FrameLayout>
    

    中间imageView的动画

    private void setAnim1() {
        AnimationSet as = new AnimationSet(true);
        //缩放动画,以中心从原始放大到1.4倍
        ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 1.4f, 1.0f, 1.4f,
                ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
                ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
        //渐变动画
        AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.5f);
        scaleAnimation.setDuration(800);
        scaleAnimation.setRepeatCount(Animation.INFINITE);
        alphaAnimation.setRepeatCount(Animation.INFINITE);
        as.setDuration(800);
        as.addAnimation(scaleAnimation);
        as.addAnimation(alphaAnimation);
        iv1.startAnimation(as);
    }
    

    最外层imageView的动画

    private void setAnim2() {
        AnimationSet as = new AnimationSet(true);
        //缩放动画,以中心从1.4倍放大到1.8倍
        ScaleAnimation scaleAnimation = new ScaleAnimation(1.4f, 1.8f, 1.4f, 1.8f,
                ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
                ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
        //渐变动画
        AlphaAnimation alphaAnimation = new AlphaAnimation(0.5f, 0.1f);
        scaleAnimation.setDuration(800);
        scaleAnimation.setRepeatCount(Animation.INFINITE);
        alphaAnimation.setRepeatCount(Animation.INFINITE);
        as.setDuration(800);
        as.addAnimation(scaleAnimation);
        as.addAnimation(alphaAnimation);
        iv2.startAnimation(as);
    }
    

    效果图

    相比较而言,自定义view的效果更好点,动画实现起来更方便点。

    两种方式实现的扩散效果介绍完毕,具体项目里还是要按需变动的。

    欢迎关注我的博客:https://blog.manjiexiang.cn/
    同时欢迎关注微信公众号
    image.png

  • 相关阅读:
    玩家移动
    人物上线(激活玩家之后)
    map 玩家上线
    无锁的环形队列
    随笔
    std::bind
    如何查找文件中的schema约束
    myeclipse便捷导包方式
    21 求1+2!+3!+...+20!的和
    20 求出这个数列的前 20 项之和
  • 原文地址:https://www.cnblogs.com/taixiang/p/9095464.html
Copyright © 2011-2022 走看看