涟漪效应源代码究竟是如何工作的?

时间:2016-02-10 02:57:08

标签: java android

我最近发现了以下代码:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

public class RippleViewCreator extends FrameLayout {
    private float duration = 150;
    private int frameRate = 15;
    private float speed = 1;
    private float radius = 0;
    private Paint paint = new Paint();
    private float endRadius = 0;
    private float rippleX = 0;
    private float rippleY = 0;
    private int width = 0;
    private int height = 0;
    private Handler handler = new Handler();
    private int touchAction;

    public RippleViewCreator(Context context) {
        this(context, null, 0);
    }

    public RippleViewCreator(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RippleViewCreator(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        if (isInEditMode())
            return;

        paint.setStyle(Paint.Style.FILL);
        paint.setColor(getResources().getColor(R.color.control_highlight_color));
        paint.setAntiAlias(true);

        setWillNotDraw(true);
        setDrawingCacheEnabled(true);
        setClickable(true);
    }

    public static void addRippleToView(View v) {
        ViewGroup parent = (ViewGroup) v.getParent();
        int index = -1;
        if (parent != null) {
            index = parent.indexOfChild(v);
            parent.removeView(v);
        }
        RippleViewCreator rippleViewCreator = new RippleViewCreator(v.getContext());
        rippleViewCreator.setLayoutParams(v.getLayoutParams());
        if (index == -1)
            parent.addView(rippleViewCreator, index);
        else
            parent.addView(rippleViewCreator);
        rippleViewCreator.addView(v);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
    }

    @Override
    protected void dispatchDraw(@NonNull Canvas canvas) {
        super.dispatchDraw(canvas);

        if (radius > 0 && radius < endRadius) {
            canvas.drawCircle(rippleX, rippleY, radius, paint);
            if (touchAction == MotionEvent.ACTION_UP)
                invalidate();
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return true;
    }

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        rippleX = event.getX();
        rippleY = event.getY();

        touchAction = event.getAction();
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP: {
                getParent().requestDisallowInterceptTouchEvent(false);

                radius = 1;
                endRadius = Math.max(Math.max(Math.max(width - rippleX, rippleX), rippleY), height - rippleY);
                speed = endRadius / duration * frameRate;
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (radius < endRadius) {
                            radius += speed;
                            paint.setAlpha(90 - (int) (radius / endRadius * 90));
                            handler.postDelayed(this, frameRate);
                        } else if (getChildAt(0) != null) {
                            getChildAt(0).performClick();
                        }
                    }
                }, frameRate);
                break;
            }
            case MotionEvent.ACTION_CANCEL: {
                getParent().requestDisallowInterceptTouchEvent(false);
                break;
            }
            case MotionEvent.ACTION_DOWN: {
                getParent().requestDisallowInterceptTouchEvent(true);
                endRadius = Math.max(Math.max(Math.max(width - rippleX, rippleX), rippleY), height - rippleY);
                paint.setAlpha(90);
                radius = endRadius / 3;
                invalidate();
                return true;
            }
            case MotionEvent.ACTION_MOVE: {
                if (rippleX < 0 || rippleX > width || rippleY < 0 || rippleY > height) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                    touchAction = MotionEvent.ACTION_CANCEL;
                    break;
                } else {
                    invalidate();
                    return true;
                }
            }
        }
        invalidate();
        return false;
    }

    @Override
    public final void addView(@NonNull View child, int index, ViewGroup.LayoutParams params) {
        //limit one view
        if (getChildCount() > 0) {
            throw new IllegalStateException(this.getClass().toString() + " can only have one child.");
        }
        super.addView(child, index, params);
    }
}

任何人都可以向我解释这个涟漪效应代码究竟是如何工作的,因为我一直试图理解它至少一天,我仍然无法理解它是如何工作的。

准确我不明白:

public static void addRippleToView(View v) {
    ViewGroup parent = (ViewGroup) v.getParent();
    int index = -1;
    if (parent != null) {
        index = parent.indexOfChild(v);
        parent.removeView(v);
    }
    RippleViewCreator rippleViewCreator = new RippleViewCreator(v.getContext());
    rippleViewCreator.setLayoutParams(v.getLayoutParams());
    if (index == -1)
        parent.addView(rippleViewCreator, index);
    else
        parent.addView(rippleViewCreator);
    rippleViewCreator.addView(v);
}

1 个答案:

答案 0 :(得分:1)

我不知道如果这个类实际上有效,但如果确实,那就是这样的猜测:

这个自定义视图是FrameLayout的子类,这意味着它希望有一个子视图。所以你要在你的视图XML中声明它并给它一个子项放在它的边界内。

当它在onTouchEVent中收到触摸事件时,会绘制一个动画涟漪效果,该效果会出现在它包含的视图后面。

开发人员也可以告诉这个RippleViewCreator通过调用addRippleToView在它包含的特定视图后面呈现涟漪效果。看起来这个方法从视图层次结构中删除了目标子视图,将其包装在另一个RippleViewCreator中,并将 添加回自己的视图层次结构中以获得波纹动画。

疯狂的东西,伙计。