程序员人生 网站导航

Android Ripple 按钮水波纹效果(二)优化

栏目:综合技术时间:2015-05-07 09:49:20

上1篇中我们讲了自定义ripple 水波纹效果,先来回顾1下效果吧!
这里写图片描述

看了以后感觉没什么问题,我1开始也觉得很满意了,那好,我们拿Android 5.0自带的效果来对照1下
这里写图片描述

发现了不同的地方没?点击中间的时候是看不出甚么区分,但是点击两边的时候,就很明显了,我们自定义的效果,波纹向两边同速度的分散,所以就会出现,如果点击点不在中心的时候,距离短的1边波纹先到达,而距离长的1边后到达,不能同时到达边沿!而系统自带的则不存在这类情况,所以这是1个优化点;另外一个优化点是:我们自定义的效果,在波纹全部覆盖以后,按钮的选中效果没了。

有两处需要优化的
1、实现不论是否点击中间点都能实现波纹同步到达边沿
2、当手指未松开时,选中效果不消失

实现:
第2点好实现,我们主要讲1下第1点,
第1点我们视察系统的效果,看似两边速度不1致致使的,其实我们知道实现原理的话,很容易想到,它是不断改变圆的圆心来实现,我们上1篇中的实现方法是圆形固定,就是在我们手指按下的位置,而不断改变半径来实现,
很明显,这里也需要改变半径来实现,我记得我们上1篇中半径的最大值是需要计算,而这类效果是不需要计算的,由于其最大值是固定的,就是按钮对角线的1半!
肯定了半径的最大值,我们还需要肯定圆心X、Y的偏移量,相当于步长吧,其圆心从按下的点到按钮正中间的时间因该是和半径从0到最大值的时间保持1致,所以我们可以通过1下代码来获得圆心的偏移量和最大半径。

/*最大半径*/ mRadius = (float) Math.sqrt(mRect.width() / 2 * mRect.width() / 2 + mRect.height() / 2 * mRect.height() / 2); /*半径的偏移量*/ mStepRadius = mRadius / mCycle; /*圆心X的偏移量*/ mStepOriginX = (mRect.width() / 2 - mInitX) / mCycle; /*圆心Y的偏移量*/ mStepOriginY = (mRect.height() / 2 - mInitY) / mCycle;

全部实现代码

package eyeclip.myapplication; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.LinearLayout; /** * Created by moon.zhong on 2015/4/27. */ public class RippleLinearLayout extends LinearLayout { /*起始点*/ private int mInitX; private int mInitY; private float mCurrentX; private float mCurrentY; /*高度和宽度*/ private int mWidth; private int mHeight; /*绘制的半径*/ private float mRadius; private float mStepRadius; private float mStepOriginX; private float mStepOriginY; private float mDrawRadius; private boolean mDrawFinish; private final int DURATION = 150; private final int FREQUENCY = 10; private float mCycle; private final Rect mRect = new Rect(); private boolean mPressUp = false; private Paint mRevealPaint = new Paint(Paint.ANTI_ALIAS_FLAG); public RippleLinearLayout(Context context) { super(context); initView(context); } public RippleLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public RippleLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } private void initView(Context context) { mRevealPaint.setColor(0x25000000); mCycle = DURATION / FREQUENCY; final float density = getResources().getDisplayMetrics().density ; mCycle = (density*mCycle); mDrawFinish = true; } @Override protected void onDraw(Canvas canvas) { if (mDrawFinish) { super.onDraw(canvas); return; } canvas.drawColor(0x15000000); super.onDraw(canvas); if (mStepRadius == 0) { return; } mDrawRadius = mDrawRadius + mStepRadius; mCurrentX = mCurrentX + mStepOriginX; mCurrentY = mCurrentY + mStepOriginY; if (mDrawRadius > mRadius) { mDrawRadius = 0; canvas.drawCircle(mRect.width() / 2, mRect.height() / 2, mRadius, mRevealPaint); mDrawFinish = true; if (mPressUp) invalidate(); return; } canvas.drawCircle(mCurrentX, mCurrentY, mDrawRadius, mRevealPaint); ViewCompat.postInvalidateOnAnimation(this); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); } private void updateDrawData() { // int radiusLeftTop = (int) Math.sqrt((mRect.left - mInitX) * (mRect.left - mInitX) + // (mRect.top - mInitY) * (mRect.top - mInitY)); // int radiusRightTop = (int) Math.sqrt((mRect.right - mInitX) * (mRect.right - mInitX) + // (mRect.top - mInitY) * (mRect.top - mInitY)); // int radiusLeftBottom = (int) Math.sqrt((mRect.left - mInitX) * (mRect.left - mInitX) + // (mRect.bottom - mInitY) * (mRect.bottom - mInitY)); // int radiusRightBottom = (int) Math.sqrt((mRect.right - mInitX) * (mRect.right - mInitX) + // (mRect.bottom - mInitY) * (mRect.bottom - mInitY)); // mRadius = getMax(radiusLeftTop, radiusRightTop, radiusLeftBottom, radiusRightBottom); /*最大半径*/ mRadius = (float) Math.sqrt(mRect.width() / 2 * mRect.width() / 2 + mRect.height() / 2 * mRect.height() / 2); ; /*半径的偏移量*/ mStepRadius = mRadius / mCycle; /*圆心X的偏移量*/ mStepOriginX = (mRect.width() / 2 - mInitX) / mCycle; /*圆心Y的偏移量*/ mStepOriginY = (mRect.height() / 2 - mInitY) / mCycle; mCurrentX = mInitX; mCurrentY = mInitY; } @Override public boolean onTouchEvent(MotionEvent event) { final int action = MotionEventCompat.getActionMasked(event); switch (action) { case MotionEvent.ACTION_DOWN: { mPressUp = false; mDrawFinish = false; int index = MotionEventCompat.getActionIndex(event); int eventId = MotionEventCompat.getPointerId(event, index); if (eventId != -1) { mInitX = (int) MotionEventCompat.getX(event, index); mInitY = (int) MotionEventCompat.getY(event, index); updateDrawData(); invalidate(); } break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: mStepRadius = (int) (5 * mStepRadius); mStepOriginX = (int) (5 * mStepOriginX); mStepOriginY = (int) (5 * mStepOriginY); mPressUp = true; invalidate(); break; } return super.onTouchEvent(event); } private int getMax(int... radius) { if (radius.length == 0) { return 0; } int max = radius[0]; for (int m : radius) { if (m > max) { max = m; } } return max; } @Override public boolean performClick() { postDelayed(new Runnable() { @Override public void run() { RippleLinearLayout.super.performClick(); } }, 150); return true; } }

效果图对照
这里写图片描述

这篇主要是对上1篇的内容进行优化,固然你觉得不优化也行!

demo下载

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐