Android 模仿android5上的Reveal实现
来源:互联网 发布:美国大豆数据 编辑:程序博客网 时间:2024/05/11 05:39
模仿android5上的Reveal实现
大家应该都接触过安卓5吧,上面的按钮点击的时候会出现波纹的动画,那个是新出来的一个reveal,但是在低版本的系统上不支持,只能想办法自己实现了,同样也是看到了任老师的实现方法,自己分析了一会儿后,加上了点自己的见解,分享给大家,demo的源码我已经上传了.
实现的基本原理就是,自定义一个继承了LinearLayout的MyRevealLayout控件,重写这个控件里面的一些函数,只要是在这个Layout中的控件被点击了,遍历MyRevealLayout中的所有子控件,和点击的位置作比对,确定被点击的子控件后初始化各种参数,随后开始绘制水波.
package com.example.dada.testapplication;import java.util.ArrayList;import android.annotation.TargetApi;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.os.Build;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.widget.LinearLayout;public class MyRevealLayout extends LinearLayout implements Runnable { /** * 画笔工具 */ private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); /** * 用户点击处的坐标 */ private float mCenterX, mCenterY; /** * 获取自定义控件MyRevealLayout * 在屏幕上的位置 */ private int[] mLocation = new int[2]; /** * 重新绘制的时间 */ private int INVALIDATE_DURATION = 20; /** * 被点击的控件的参数 */ private int mTargetHeight, mTargetWidth; /** * mRevealRadius为初始的数值 * mRevealRadiusGap为每次重新绘制半径增加的值 * mMaxRadius为绘制的水波纹圆圈最大的半径 */ private int mRevealRadius = 0, mRevealRadiusGap, mMaxRadius; /** * 通过名字就可以看出来了把 * 在被选中的控件长宽中的最大值和最小值 */ private int mMinBetweenWidthAndHeight, mMaxBetweenWidthAndHeight; /** * 是否被按下 * 是否需要执行动画 */ private boolean mIsPressed; private boolean mShouldDoAnimation; /** * 这个就是在布局文件中被点击的控件了 */ private View mTargetView; /** * 松手的事件分发线程 */ private DispatchUpTouchEventRunnable mDispatchUpTouchEventRunnable = new DispatchUpTouchEventRunnable(); public MyRevealLayout(Context context) { super(context); init(); } public MyRevealLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) public MyRevealLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public 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(mLocation); } /** * 这个就是主要的绘制函数 * 在确定了被点击的控件之后 * 这个方法就会不断的被调用 * 直到水波纹的绘制完成 * * 这个的原理就是不断的改变绘制的水波纹圆圈半径的大小 * 同时也在判断是否需要继续绘制 * @param canvas */ @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (mTargetView == null || !mShouldDoAnimation || mTargetWidth <= 0) return; if (mRevealRadius > mMinBetweenWidthAndHeight / 2) mRevealRadius += mRevealRadiusGap * 4; else mRevealRadius += mRevealRadiusGap; int[] location = new int[2]; this.getLocationOnScreen(mLocation); mTargetView.getLocationOnScreen(location); int top = location[1] - mLocation[1]; int left = location[0] - mLocation[0]; int right = left + mTargetView.getMeasuredWidth(); int bottom = top + mTargetView.getMeasuredHeight(); canvas.save(); canvas.clipRect(left, top, right, bottom); canvas.drawCircle(mCenterX, mCenterY, mRevealRadius, mPaint); canvas.restore(); if (mRevealRadius <= mMaxRadius) postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom); else if (!mIsPressed) { mShouldDoAnimation = false; postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom); } } /** * 重写事件分发 * 分别对按下等动作做出不同的响应 * * * @param event * @return */ @Override public boolean dispatchTouchEvent(MotionEvent event) { int x = (int) event.getRawX(); int y = (int) event.getRawY(); int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: View targetView = getTargetView(this, x, y); if (targetView != null && targetView.isEnabled()) { mTargetView = targetView; initParametersForChild(event, targetView); postInvalidateDelayed(INVALIDATE_DURATION); } break; case MotionEvent.ACTION_UP: mIsPressed = false; postInvalidateDelayed(INVALIDATE_DURATION); mDispatchUpTouchEventRunnable.event = event; postDelayed(mDispatchUpTouchEventRunnable, 100); break; case MotionEvent.ACTION_CANCEL: mIsPressed = false; postInvalidateDelayed(INVALIDATE_DURATION); break; } return super.dispatchTouchEvent(event); } /** * 这个函数的作用正如它的名字一样 * 用于返回被点击的控件是哪一个 * * 其中调用了另外一个函数isTouchPointInView * 在后面会注释到 * @param view * @param x * @param y * @return */ public View getTargetView(View view, int x, int y) { View target = null; ArrayList<View> views = view.getTouchables(); for (View child : views) if (isTouchPointInView(child, x, y)) { target = child; break; } return target; } /** * 对比用户点击的坐标和传入的控件坐标 * 如果在控件内就返回true * 否则返回false * * @param child * @param x * @param y * @return */ public boolean isTouchPointInView(View child, int x, int y) { int[] location = new int[2]; child.getLocationOnScreen(location); int top = location[1]; int left = location[0]; int right = left + child.getMeasuredWidth(); int bottom = top + child.getMeasuredHeight(); if (child.isClickable() && y >= top && y <= bottom && x >= left && x <= right) return true; else return false; } /** * 初始化被点击控件的参数 * 确定开始绘制水波纹的各项参数 * 比如圆的半径,最大半径,半径增长数值等 * * 在dispatchTouchEvent()对DOWN事件响应的时候调用 * @param event * @param view */ public 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; mRevealRadiusGap = mMinBetweenWidthAndHeight / 20; mIsPressed = true; mShouldDoAnimation = true; int[] location = new int[2]; view.getLocationOnScreen(location); int left = location[0] - mLocation[0]; int mTransformedCenterX = (int) mCenterX - left; mMaxRadius = Math.max(mTransformedCenterX, mTargetWidth - mTransformedCenterX); int top = location[1]-mLocation[1]; int transformedCenterY = (int)mCenterY-top; mMaxRadius = Math.max(mMaxRadius,Math.max(transformedCenterY,mTargetHeight-transformedCenterY)); } @Override public void run() { super.performClick(); } @Override public boolean performClick() { postDelayed(this, 40); return true; } private class DispatchUpTouchEventRunnable implements Runnable { public MotionEvent event; @Override public void run() { if (mTargetView.isEnabled() && mTargetView.isClickable()) return; if (isTouchPointInView(mTargetView, (int) event.getRawX(), (int) event.getRawX())) mTargetView.performClick(); } }}
上面的代码就是自定义的控件MyRevealLayout.java,使用的时候只需要将它当作是一个LInearLayout一样的用就可以了,小达自己的理解都写在注释里面了,理解起来应该不是很困难把...~~~
0 0
- Android 模仿android5上的Reveal实现
- Android5.0新推出的Circular Reveal
- Reveal---iOS上的屠龙刀
- android5.x的 Ripple和Reveal动效
- Android5.0上WebView的android.content.res.Resources$NotFoundException
- Android Reveal Animation(揭露动画)实现
- Winform中模仿实现上一步、下一步的导航页面
- Winform中模仿实现上一步、下一步的导航页面
- android5 Ripple和Reveal动效
- Android 模仿RxAndroid实现监听
- android之模仿QQ登陆的布局实现
- Android模仿打字机效果的自定义View实现
- Android模仿打字机效果的自定义View实现
- Android学习之微信界面的模仿实现
- 模仿微信,android bottom navigation的实现
- #android 利用fragment实现模仿微信的实例
- 移动开发----Android模仿打字机效果的自定义View实现
- Reveal在真机和模拟器上的使用
- 一键生成IOS/ANDROID APP的各种规格图标
- 《UNIX环境高级编程》笔记:第4章 文件和目录
- struts2入门总结
- android下打造个性化的圆形进度条
- C++语言的词法和词法规则
- Android 模仿android5上的Reveal实现
- java web项目中配置spring mvc
- Android 编程下 Touch 事件的分发和消费机制
- 杭电2064
- Winform程序_石头剪刀布练习
- Qt 常用类 (16)—— QLineEdit
- Linux 常用技巧集锦
- unique paths II
- 在django里使用多个数据库