1. 程式人生 > >按鈕點選水波紋效果

按鈕點選水波紋效果

水波紋的出現給我們的錯覺是直接將波紋繪製在button上面的,但是這樣能做到嗎?首先button自己有background和src,如果把半透明的水波紋當作background或者src繪製到button上面,肯定是會損失button原有的樣式的。可能有朋友猜想那就把水波紋繪製在螢幕上唄,恭喜這位朋友答對了,至少我是這麼幹的,具體思路就是,我們自己實現一個layout,在layout中捕捉事件,並對事件進行相應的處理,在down事件中尋找當前使用者點選的是哪個view,找出view所在的矩形區域,將一個透明的圓環繪製到這個矩形區域。

實現思路

1、自己實現一個layout:
2、重寫layout的dispatchTouchEvent方法,在down事件中找出被點選的view;
3、接著找出view所在的矩形區域,因為要將波紋繪製到該區域;
4、矩形區域找到之後,這個區域就是我們要繪製的博波紋所在地,上面也說過了,波紋其實就是圓環,繪製圓的畫是需要知道圓心座標和圓的半徑,圓心座標肯定就是down時候的x和y了,然後計算半徑
5、半徑算出來了,雖說圓心就是down時的x和y,但是有個地方還是需要注意的,在繪製圓環的時候提供的圓心座標的x和y是在本文中是相對於layout的,所以在計算y的時候是需要進行一定處理的:
6、圓心座標和半徑都計算好了,記下來就可以繪製圓形波紋了,那麼在哪裡繪製這個波紋比較合適呢?有朋友立馬就說肯定是在onDraw方法裡面繪製了,那麼恭喜你在我看來你是答錯了,我們的layout中是很有很多childview的,而layout是個viewGroup,viewGroup在繪製的時候,是先繪製自身的背景,再繪製自身,再繪製childview,如果在onDraw中繪製波紋,也就意味者後面繪製出來的childView會將我們的波紋遮蓋,所以我們就應該等到childview繪製完畢後再來繪製波紋,這樣可以保證childview在最頂層。

自定義控制元件程式碼:

package com.example.viewresult;

import java.util.ArrayList;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import
android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; /** * * 1. 如何得知使用者點選了哪個元素 2. 如何取得被點選元素的資訊 3. 如何通過layout進行重繪繪製水波紋 4. 如果延遲up事件的分發 * */ @SuppressLint("NewApi") public class CustomViewResult extends LinearLayout
{
private View mTargetTouchView; private Paint mHalfTransPaint; private Paint mTransPaint; private float[] mDownPositon;// 手指點選的座標,也就是圓環的中心點 private float rawRadius;// 原始的圓環半徑 private float drawedRadius;// 正在慢慢繪製的圓環半徑 private float drawingRadiusDegrees = 10;// 慢慢繪製圓環的時候,半徑的遞增百分比 private static final long INVALID_DURATION = 30; private static postUpEventDelayed delayedRunnable; public void init() { setOrientation(VERTICAL);// 設定方向 mHalfTransPaint = new Paint(); mHalfTransPaint.setColor(Color.parseColor("#55ffffff")); mHalfTransPaint.setAntiAlias(true); mTransPaint = new Paint(); mTransPaint.setColor(Color.parseColor("#00ffffff")); mTransPaint.setAntiAlias(true);// 抗鋸齒 mDownPositon = new float[2]; delayedRunnable = new postUpEventDelayed(); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { mTargetTouchView = null; drawedRadius = 0; float x = ev.getRawX(); float y = ev.getRawY(); mTargetTouchView = findTargetView(x, y, this); if (mTargetTouchView != null) { Button msg = (Button) mTargetTouchView; RectF targetTouchRectF = getViewRectF(mTargetTouchView); mDownPositon = getCircleCenterPostion(x, y); // 所要繪製的圓環的中心點 float circleCenterX = mDownPositon[0]; float circleCenterY = mDownPositon[1]; /** * 圓環的半徑: 圓環的中心點圓心當然是點選的那個點,但是半徑是變化的 * 圓心可能落在mTargetTouchView區域的任意個方位之內,所以要想圓環繪製覆蓋整個mTargetTouchView * 則radius的取值為圓心的橫座標到mTargetTouchView的四個點的距離中的最大值 */ float left = circleCenterX - targetTouchRectF.left; float right = targetTouchRectF.right - circleCenterX; float top = circleCenterY - targetTouchRectF.top; float bottom = targetTouchRectF.bottom - circleCenterY; // 計算出最大的值則為半徑 rawRadius = Math.max(bottom, Math.max(Math.max(left, right), top)); // Android中實現view的更新有兩組方法,一組是invalidate,另一組是postInvalidate, // 其中前者是在UI執行緒自身中使用,而後者在非UI執行緒中使用。 // postInvalidate還支援延遲重新整理 postInvalidateDelayed(INVALID_DURATION); } } else if (ev.getAction() == MotionEvent.ACTION_UP) { // 需要讓波紋繪製完畢後再執行在up中執行的方法 // if(drawedRadius==0){ // return false; // } // long totalTime = (long) (INVALID_DURATION * // (drawingRadiusDegrees+5)); // // 離波紋結束的時間 // long time = (long) (totalTime - drawedRadius*totalTime / // rawRadius); delayedRunnable.event = ev; return true; } return super.dispatchTouchEvent(ev); } class postUpEventDelayed implements Runnable { private MotionEvent event; @Override public void run() { if (mTargetTouchView != null && mTargetTouchView.isClickable() && event != null) { mTargetTouchView.dispatchTouchEvent(event); } } } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); /** * 繪製完子元素後開始繪製波紋 */ if (mTargetTouchView != null) { RectF clipRectF = clipRectF(mTargetTouchView); canvas.save(); // 為了不讓繪製的圓環超出所要繪製的範圍 canvas.clipRect(clipRectF); if (drawedRadius < rawRadius) { drawedRadius += rawRadius / drawingRadiusDegrees; canvas.drawCircle(mDownPositon[0], mDownPositon[1], drawedRadius, mHalfTransPaint); postInvalidateDelayed(INVALID_DURATION); } else { canvas.drawCircle(mDownPositon[0], mDownPositon[1], rawRadius, mTransPaint); post(delayedRunnable); } canvas.restore(); } } /** * 獲取圓環的中心座標 */ public float[] getCircleCenterPostion(float x, float y) { int[] location = new int[2]; float[] mDownPositon = new float[2]; getLocationOnScreen(location);// 獲取當前控制元件在螢幕中的絕對位置 mDownPositon[0] = x; mDownPositon[1] = y - location[1]; return mDownPositon; } /** * 獲取要剪下的區域 * * @param mTargetTouchView * @return */ public RectF clipRectF(View mTargetTouchView) { RectF clipRectF = getViewRectF(mTargetTouchView); int[] location = new int[2]; getLocationOnScreen(location); clipRectF.top -= location[1]; clipRectF.bottom -= location[1]; return clipRectF; } /** * 尋找目標view * * @param x * @param y * @param anchorView * 從哪個view開始往下尋找 * @return */ public View findTargetView(float x, float y, View anchorView) { ArrayList<View> touchablesView = anchorView.getTouchables(); View targetView = null; for (View child : touchablesView) { // 1、精度不一樣,Rect是使用int型別作為數值,RectF是使用float型別作為數值 // 2、兩個型別提供的方法也不是完全一致 RectF rectF = getViewRectF(child); if (rectF.contains(x, y) && child.isClickable()) { // 這說明被點選的view找到了 targetView = child; break; } } return targetView; } public RectF getViewRectF(View view) { int[] location = new int[2]; // View.getLocationInWindow(int[] location) // 一個控制元件在其父視窗中的座標位置 // View.getLocationOnScreen(int[] location) // 一個控制元件在其整個螢幕上的座標位置 view.getLocationOnScreen(location); int childLeft = location[0]; int childTop = location[1]; int childRight = childLeft + view.getMeasuredWidth(); int childBottom = childTop + view.getMeasuredHeight(); return new RectF(childLeft, childTop, childRight, childBottom); } public CustomViewResult(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public CustomViewResult(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomViewResult(Context context) { this(context, null, 0); } }

自定義控制元件的使用:

1.佈局

<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"
    tools:context=".MainActivity" >
<com.example.viewresult.CustomViewResult 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    <Button 
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#ff0000"
        />
     <Button 
        android:id="@+id/button2"
        android:layout_width="180dp"
        android:layout_height="wrap_content"
        android:background="#ff00ff"
        android:layout_marginLeft="50dp"
        android:layout_marginTop="10dp"
        />
      <Button 
        android:id="@+id/button3"
        android:layout_width="220dp"
        android:layout_height="wrap_content"
        android:background="#000000"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="10dp"
        />
</com.example.viewresult.CustomViewResult>
</RelativeLayout>

2.活動類

package com.example.viewresult;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.button1).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                Toast.makeText(getApplicationContext(), "點選", 0).show();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}