您的位置:首页 > 移动开发 > Android开发

Android中按钮的水波纹点击效果的实现

2015-02-08 11:53 246 查看
关于按钮水波纹的点击效果,这个是我在http://blog.csdn.net/singwhatiwanna/article/details/42614953这篇文章读到的。写得真心不错,我只是站在巨人的肩上而已。

我加了一些注释,以至于我们更好的理解这篇不错的文章

下面是主要源代码:

public class RevealLayout 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;
// 获取自定义控件MyRevealLayout 在屏幕上的位置
private int[] mLocationInScreen = new int[2];
// 是否执行动画
private boolean mShouldDoAnimation = false;
// 是否被按下
private boolean mIsPressed = false;
// 重新绘制的时间 单位毫秒
private int INVALIDATE_DURATION = 50;
// mTouchTarget指的是用户点击的那个view
private View mTouchTarget;
// 松手的事件分发线程
private DispatchUpTouchEventRunnable mDispatchUpTouchEventRunnable = new DispatchUpTouchEventRunnable();

public RevealLayout(Context context)
{
super(context);
init();
}

public RevealLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public RevealLayout(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
init();
}

private void init()
{
setWillNotDraw(false);
mPaint.setColor(getResources().getColor(R.color.reveal_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)
{
mCenterX = event.getX();
mCenterY = event.getY();
mTargetWidth = view.getMeasuredWidth();
mTargetHeight = view.getMeasuredHeight();
mMinBetweenWidthAndHeight = Math.min(mTargetWidth, mTargetHeight);
mMaxBetweenWidthAndHeight = Math.max(mTargetWidth, mTargetHeight);
mRevealRadius = 0;
mShouldDoAnimation = true;
mIsPressed = true;
mRevealRadiusGap = mMinBetweenWidthAndHeight / 8;

int[] location = new int[2];
view.getLocationOnScreen(location);
int left = location[0] - mLocationInScreen[0];
int transformedCenterX = (int) mCenterX - left;
mMaxRevealRadius = Math.max(transformedCenterX, mTargetWidth - transformedCenterX);
}

/**
* 绘制水波
*/
protected void dispatchDraw(Canvas canvas)
{
super.dispatchDraw(canvas);
if (!mShouldDoAnimation || mTargetWidth <= 0 || mTouchTarget == null)
{
return;
}

if (mRevealRadius > mMinBetweenWidthAndHeight / 2)
{
mRevealRadius += mRevealRadiusGap * 4;
} else
{
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(mCenterX, mCenterY, mRevealRadius, mPaint);
canvas.restore();

if (mRevealRadius <= mMaxRevealRadius)
{
postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);
} else if (!mIsPressed)
{
mShouldDoAnimation = false;
postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);
}
}

@Override
public boolean dispatchTouchEvent(MotionEvent 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);
mDispatchUpTouchEventRunnable.event = event;
postDelayed(mDispatchUpTouchEventRunnable, 40);
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();
}
}
};

}
注:

在Android中实现view的更新有中方法,一种是invalidate,另一种是postInvalidate,

其中:前者是在UI线程自身中使用,而后者在非UI线程中使用。

当invalidate方法被调用的时候,会告诉系统,当前的view发生改变,应该尽可能快的来进行重绘。并且运行在UI线程

当postInvalidate方法被调用的时候,将会发送消息到主线程,当主线程的消息队列轮询到当前消息的时候,这个方法才会被调用,

刷新界面并不能保证马上刷新。只是尽可能快的进行刷新。尤其是在postInvalidate方法中,这种情况会出现几率更大。

当我们想要使用这种效果时候,只需要将我的控件包裹在这个布局下面即可,如下:

<包名.RevealLayout
android:id="@+id/layout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >

<Button
android:id="@+id/button1"
android:layout_width="200dp"
android:layout_height="40dp"
android:enabled="true"
android:text="Button" />
</包名.RevealLayout>


这样我们就可以实现按钮的水波纹效果了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: