1. 程式人生 > >Android 點選單個按鈕或者其他的View,在view上出現水波紋效果,然後執行點選事件。

Android 點選單個按鈕或者其他的View,在view上出現水波紋效果,然後執行點選事件。

1.自定義一個佈局,得到點選的view,在view上繪畫水波紋,水波紋的圓心是view的中點,半徑為寬和高一半的最小值。

package com.example.waterview;



import java.util.ArrayList;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;


public class WaterLinearLayout extends LinearLayout implements Runnable{


private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);    
    // 被點選的控制元件的寬高    
    private int mTargetWidth;    
    private int mTargetHeight;    
    // 在被選中的控制元件長寬中的最大值和最小值    
//    private int mMinBetweenWidthAndHeight;    
//    private int mMaxBetweenWidthAndHeight;    
    
    // mMaxRadius為繪製的水波紋圓圈最大的半徑    
    private int mMaxRevealRadius;    
    // mRevealRadiusGap為每次重新繪製半徑增加的值    
    private int mRevealRadiusGap;    
    // mRevealRadius為初始的數值    
    private int mRevealRadius = 0;    
    // 使用者點選處的座標    
//    private float mCenterX;    
//    private float mCenterY;    
    // 獲取自定義控制元件WaterLinearLayout 在螢幕上的位置    
    private int[] mLocationInScreen = new int[2];    
    // 是否執行動畫    
    private boolean mShouldDoAnimation = false;    
    // 是否被按下    
//    private boolean mIsPressed = false;  
    private MotionEvent mchildEvent;
    // 重新繪製的時間 單位毫秒    
    private int INVALIDATE_DURATION = 80;    
    // mTouchTarget指的是使用者點選的那個view    
    private View mTouchTarget;    
    // 鬆手的事件分發執行緒    
    private DispatchUpTouchEventRunnable mDispatchUpTouchEventRunnable = new DispatchUpTouchEventRunnable();    
    
    public WaterLinearLayout(Context context)    
    {    
        super(context);    
        init();    
    }    
    
    public WaterLinearLayout(Context context, AttributeSet attrs)    
    {    
        super(context, attrs);    
        init();    
    }    
       
    public WaterLinearLayout(Context context, AttributeSet attrs, int defStyleAttr)    
    {    
        super(context, attrs, defStyleAttr);    
        init();    
    }    
    
    private void init()    
    {    
        setWillNotDraw(false);    
        mPaint.setColor(getResources().getColor(R.color.waterline_color));    
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
    this.getLocationOnScreen(mLocationInScreen);
    }
    
    private void initParametersForChild(MotionEvent event, View view)    
    {     
        mTargetWidth = view.getMeasuredWidth();    
        mTargetHeight = view.getMeasuredHeight();    
//        mMinBetweenWidthAndHeight = Math.min(mTargetWidth, mTargetHeight);      
        mRevealRadius = 0;    
        mShouldDoAnimation = true;    
//        mIsPressed = true;    
        mMaxRevealRadius = Math.min(mTargetHeight/2, mTargetWidth/2);   
        mRevealRadiusGap = mMaxRevealRadius / 32;  
    }   
    
    /**  
     * 繪製水波  
     */    
    protected void dispatchDraw(Canvas canvas)    
    {    
        super.dispatchDraw(canvas);    
        if (!mShouldDoAnimation || mTargetWidth <= 0 || mTouchTarget == null)    
        {    
            return;    
        }  
        
        
        mRevealRadius += mRevealRadiusGap;      
        this.getLocationOnScreen(mLocationInScreen);    
        int[] location = new int[2];    
        mTouchTarget.getLocationOnScreen(location);    
        // 獲得要繪製View的left, top, right, bottom值    
        int left = location[0] - mLocationInScreen[0];    
        int top = location[1] - mLocationInScreen[1];    
        int right = left + mTouchTarget.getMeasuredWidth();    
        int bottom = top + mTouchTarget.getMeasuredHeight();    
    
        canvas.save();    
        canvas.clipRect(left, top, right, bottom);    
        canvas.drawCircle(mTargetWidth/2+left, top+mTargetHeight/2, mRevealRadius, mPaint);    
        canvas.restore();    
    
        if (mRevealRadius <= mMaxRevealRadius)    
        {    
            postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);    
        } else{    
            mShouldDoAnimation = false;  
            postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);     
            mDispatchUpTouchEventRunnable.event = mchildEvent;  
postDelayed(mDispatchUpTouchEventRunnable, 50); 
        }    
    }    
    
    @Override    
    public boolean dispatchTouchEvent(MotionEvent event)    
    {    
    mchildEvent = event;
        // 獲得相對於螢幕的座標    
        int x = (int) event.getRawX();    
        int y = (int) event.getRawY();    
        // 獲得動作    
        int action = event.getAction();    
        if (action == MotionEvent.ACTION_DOWN)    
        {    
       
            View touchTarget = getTouchTarget(this, x, y);  
            if (touchTarget != null && touchTarget.isClickable() && touchTarget.isEnabled())    
            {    
                mTouchTarget = touchTarget;    
                initParametersForChild(event, touchTarget);    
                // 重新整理介面,延遲執行時間    
                postInvalidateDelayed(INVALIDATE_DURATION);    
            }    
        } else if (action == MotionEvent.ACTION_UP)    
        {    
//            mIsPressed = false;    
            postInvalidateDelayed(INVALIDATE_DURATION); 
//            Log.d("sdd", "mIsFinish = "+mIsFinish);
//            if (mIsFinish) {
//mDispatchUpTouchEventRunnable.event = event;  
//postDelayed(mDispatchUpTouchEventRunnable, 50); 
//}
            return true;    
        } else if (action == MotionEvent.ACTION_CANCEL)    
        {    
//            mIsPressed = false;    
            postInvalidateDelayed(INVALIDATE_DURATION);    
        }    
    
        return super.dispatchTouchEvent(event);    
    }    
    
    /**  
     * 遍歷view樹找到使用者所點選的那個view  
     *   
     * @param view  
     * @param x  
     * @param y  
     * @return  
     */    
    private View getTouchTarget(View view, int x, int y)    
    {    
        View target = null;    
        ArrayList<View> TouchableViews = view.getTouchables();    
        for (View child : TouchableViews)    
        {    
            if (isTouchPointInView(child, x, y))    
            {    
                target = child;    
                break;    
            }    
        }    
    
        return target;    
    }    
    
    /**  
     * 判斷事件的xy是否落在view的上下左右四個角之內  
     *   
     * @param view  
     * @param x  
     * @param y  
     * @return  
     */    
    private boolean isTouchPointInView(View view, int x, int y)    
    {    
        int[] location = new int[2];    
        view.getLocationOnScreen(location);    
        int left = location[0];    
        int top = location[1];    
        int right = left + view.getMeasuredWidth();    
        int bottom = top + view.getMeasuredHeight();    
        if (view.isClickable() && y >= top && y <= bottom && x >= left && x <= right)    
        {    
            return true;    
        }    
        return false;    
    }    
    
    // 使用程式碼主動去呼叫控制元件的點選事件(模擬人手去觸控控制元件)    
    @Override    
    public boolean performClick()    
    {    
        postDelayed(this, 400);    
        return true;    
    }    
    
    @Override    
    public void run()    
    {    
        super.performClick();    
    }    
    
    private class DispatchUpTouchEventRunnable implements Runnable    
    {    
        public MotionEvent event;    
    
        @Override    
        public void run()    
        {    
            if (mTouchTarget == null || !mTouchTarget.isEnabled())    
            {    
                return;    
            }    
    
            if (isTouchPointInView(mTouchTarget, (int) event.getRawX(), (int) event.getRawY()))    
            {    
                // 使用程式碼主動去呼叫控制元件的點選事件(模擬人手去觸控控制元件)    
                mTouchTarget.performClick();    
            }    
        }    
    };    

}

2.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" >


    <com.example.waterview.WaterLinearLayout
        android:layout_width="400dp"
        android:layout_height="500dp"
        android:padding="50dp"
        android:orientation="vertical" >


        <Button
            android:onClick="close"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:text="關閉" 
            android:background="@drawable/close_selector"/>
    </com.example.waterview.WaterLinearLayout>
</RelativeLayout>

3.按鈕的點選效果,圖片自行定義,close_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
      <item android:drawable="@drawable/image02" android:state_pressed="true"/>
 <item android:drawable="@drawable/gougou2" android:state_focused="false" android:state_pressed="false"/> 
</selector>

4.Activity主檔案

public class MainActivity extends Activity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}


public void close(View view){
//Toast.makeText(this, "關閉", 0).show();
Log.d("MainActivity", "close");
}
}