您的位置:首页 > 其它

按钮点击水波纹效果

2016-01-07 10:50 225 查看
水波纹的出现给我们的错觉是直接将波纹绘制在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;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: