自定义一个酷炫的提交完成按钮
来源:互联网 发布:linux find 所有目录 编辑:程序博客网 时间:2024/06/05 06:26
作者 夏至,欢迎转载,也请保留这段申明,谢谢
http://blog.csdn.net/u011418943/article/details/73555330
最近在学习自定义view,刚好有个需求是做一个点击完成的动画,所以就运用上了,效果如下:
把动画分解出来其实很简单,步骤如下:
- 把两边的圆角变成圆
- 把长椭圆变成缩小变成一个圆,文字渐变为无,可以用setalpha属性
- 圆从底部上升并变大,这没什么好说的,改变Y轴的值即可
- 打钩,这个也比较简单,用PathMeasure画勾就可以了
完成代码在底部,下面是简单分析:
1.1 圆角转圆
这个比较简单,首先我们先用 drawRoundRect 设置好圆角,想一下,从圆角到到圆,变化的是什么?没有就 drawRoundRect 的中间两个参数,只要把它变成圆的半径大小,即 height 的一半即可。
所以,动画如下:
//圆角变圆 mRectToRoundAnim = ValueAnimator.ofFloat(10,mRadius); mRectToRoundAnim.setDuration(500); mRectToRoundAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { mRectToRound = (float) valueAnimator.getAnimatedValue(); mStatus = 0; invalidate(); } });
获取到mRectToRound 的值,(好吧,这个命名有问题,自己去改吧);
1.2 长圆变圆
这个也是很简单,改变它的长度接口,那长度是多少? 我们是让它从两边移动过来的,所以就是 (width - height)/ 2;而在变的过程中,需要对文字渐变消失,可以使用 setalpha ,从255 到 0 即可。如下所示:
//长椭圆变圆动画,并让文字渐渐消失 mRoundToCircleAnim = ValueAnimator.ofFloat(0,(width-height)/2); mRoundToCircleAnim.setDuration(1000); mRoundToCircleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { mRoundToCircleOffert = (float) valueAnimator.getAnimatedValue(); int max = (width - height)/2 ; int alpha = (int) ((float)(max - mRoundToCircleOffert)/max * 255); mTextPaint.setAlpha(alpha); mStatus = 0; invalidate(); } });
1.3 圆上升
这个就更简单了,改变Y轴坐标就可以了。:
//小球上移 mCircleMoveAnim = ValueAnimator.ofFloat(0,300); mCircleMoveAnim.setInterpolator(new AccelerateDecelerateInterpolator()); mCircleMoveAnim.setDuration(500); mCircleMoveAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mMoveY = (float) animation.getAnimatedValue(); postInvalidate(); } });
1.4 小球变大
上面中,我们都是用drawRoundToRect 的画的,为了方便,我们改成 drawcircle 来绘制,这个时候就可以添加一个 status,用来选择哪个;
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); switch (mStatus){ case 0: //绘制椭圆等其他变化 drawoval(canvas); break; case 1: //绘制小球变大并打钩 drawCircle(canvas); break; } }
圆变大改变半径即可,有些不需要放大的,可以忽略这一步:
//圆形变大动画 mCircleScaleAnim = ValueAnimator.ofFloat(height/2,height*2); mCircleScaleAnim.setDuration(animTime); mCircleScaleAnim.setInterpolator(new LinearInterpolator()); mCircleScaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCircleWidth = (float) animation.getAnimatedValue(); mStatus = 1; postInvalidate(); } });
1.5 打钩
前面的都比较简单,可能就打钩这里比较难,但真的难吗?当然不是,打钩我们完全可以用 pathmeasure 这个方法可以轻松搞定,就是打钩的左边需要计算;所以,首先显示打钩:
//初始化打钩 private void initTick(){ mTickPaint = new Paint(); mTickPaint.setAntiAlias(true); mTickPaint.setColor(Color.WHITE); mTickPaint.setStyle(Paint.Style.STROKE); mTickPaint.setStrokeWidth(10); int x = width/2; //小球的中心坐标 x int y = -300; //小球的中心坐标 y Path path = new Path(); //画勾 path.moveTo(x-45,y-15); path.lineTo(x-15,y+20); path.lineTo(x+60,y-40); mTickPath = new Path(); mPathMeasure = new PathMeasure(path,false); //保存打钩的路径 mTickLength = mPathMeasure.getLength(); //获取打钩的总长度 }
然后我们在draw中这样写:
mTickPath.reset(); mTickPath.rLineTo(0,0); //防止硬件加速的bug float start = 0; float end = mTickLength * mTickDrawOffsert; mPathMeasure.getSegment(start,end,mTickPath,true); canvas.drawPath(mTickPath,mTickPaint);
其中mTickPath 通过 valueanimator 来获取:
// 打钩动画 mTickAnim = ValueAnimator.ofFloat(0,1); mTickAnim.setDuration(1000); mTickAnim.setInterpolator(new AccelerateInterpolator()); mTickAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mTickDrawOffsert = (float) animation.getAnimatedValue(); mStatus = 1; Log.d(TAG, "onAnimationUpdate: "+mTickDrawOffsert); postInvalidate(); } });
1.6 动画整合
上面中,我们用到很多动画,为了使它变得很流畅,那肯定得用 animatorset 啦,在点击的时候,开始我们的表演:
setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { AnimatorSet bound = new AnimatorSet(); bound.play(mRectToRoundAnim).with(mRoundToCircleAnim); AnimatorSet circle = new AnimatorSet(); circle.play(mCircleMoveAnim) .before(mCircleScaleAnim) .after(bound); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(mTickAnim).after(circle); animatorSet.start(); setClickable(false); } });
注意观察animatorset 的 play, with,befor,after 哦
这样就完成了所有动画了,其实也没啥好讲的,就一些基础动画;下面是完成代码;
xml :
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:clipChildren="false" android:clipToPadding="false" tools:context="com.toptech.tvhouse.MainActivity" > <com.toptech.tvhouse.view.CoolButton android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="call" app:text="一键加速" app:textsize="18sp" android:layout_alignParentBottom="true" android:layout_marginBottom="20dp" android:text="call phone"/></RelativeLayout>
文字和大小在 attrs.xml 中:
<declare-styleable name="CoolButton"> <attr name="text" format="string"/> <attr name="textsize" format="dimension"/> </declare-styleable>
java 完成代码
/** * Created by zhengshaorui on 2017/5/29. */public class CoolButton extends View { private static final String TAG = "zsr"; private Paint mPaint; //圆画笔 private Paint mTextPaint; //文字画笔 private Paint mTickPaint; //打钩画笔 private int width,height; private int mRadius; private float mRectToRound = 10; private float mRoundToCircleOffert = 0; private int mStatus;//根据不同类型draw不同动画 private String mButtonText = ""; private float mMoveY = 0; private float mCircleWidth; private Context mContext; private Path mTickPath = new Path(); private ValueAnimator mRectToRoundAnim,mRoundToCircleAnim; private ValueAnimator mCircleScaleAnim; private ValueAnimator mCircleMoveAnim; private PathMeasure mPathMeasure; private float mTickLength; private float mTickDrawOffsert; private ValueAnimator mTickAnim; public CoolButton(Context context) { this(context,null); } public CoolButton(Context context, AttributeSet attrs) { this(context, attrs,0); } public CoolButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //通过这个方法,将attrs中定义的declare-styleable 的所有属性的值存到typedarry里 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CoolButton); int textsize = 0; for (int i = 0; i < ta.length(); i++) { int attr = ta.getIndex(i); switch (attr) { case R.styleable.CoolButton_text: mButtonText = ta.getString(R.styleable.CoolButton_text); textsize = ta.getDimensionPixelSize(R.styleable.CoolButton_textsize,0); Log.d(TAG, "CoolButton: "+mButtonText); break; } } ta.recycle(); //调用完记得recycle掉,避免重复调用 mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(Color.BLUE); mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); mTextPaint.setColor(Color.WHITE); mTextPaint.setTextSize(textsize); mContext = context; setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { AnimatorSet bound = new AnimatorSet(); bound.play(mRectToRoundAnim).with(mRoundToCircleAnim); AnimatorSet circle = new AnimatorSet(); circle.play(mCircleMoveAnim) .before(mCircleScaleAnim) .after(bound); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(mTickAnim).after(circle); animatorSet.start(); setClickable(false); } }); } private void initTick(){ mTickPaint = new Paint(); mTickPaint.setAntiAlias(true); mTickPaint.setColor(Color.WHITE); mTickPaint.setStyle(Paint.Style.STROKE); mTickPaint.setStrokeWidth(10); int x = width/2; int y = -300; Path path = new Path(); //画勾 path.moveTo(x-45,y-15); path.lineTo(x-15,y+20); path.lineTo(x+60,y-40); mTickPath = new Path(); mPathMeasure = new PathMeasure(path,false); mTickLength = mPathMeasure.getLength(); } @Override public boolean isFocused() { return super.isFocused(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); switch (mStatus){ case 0: drawoval(canvas); break; case 1: drawCircle(canvas); break; } /*int x = width/2; int y = -300; canvas.drawCircle(x,y,height*2,mPaint); Path path = new Path(); //画勾 path.moveTo(x-50,y-15); path.lineTo(x-10,y+30); path.lineTo(x+60,y-40); canvas.drawPath(path,mTickPaint);*/ } /** * 画圆角以及上升过程 * @param canvas */ private void drawoval(Canvas canvas){ RectF rectF = new RectF(mRoundToCircleOffert,0 - mMoveY,width - mRoundToCircleOffert,height - mMoveY); canvas.drawRoundRect(rectF, mRectToRound, mRectToRound,mPaint); //绘制文字 float textwidth = mTextPaint.measureText(mButtonText); float x = (width - textwidth)/2; Paint.FontMetrics metrics = mTextPaint.getFontMetrics(); float textheight = (metrics.descent + metrics.ascent); float y = height/2 - textheight/2; canvas.drawText(mButtonText,x,y,mTextPaint); } /** * 小圆变大并打钩 * @param canvas */ private void drawCircle(Canvas canvas){ canvas.drawCircle((width)/2,-mMoveY,mCircleWidth,mPaint); mTickPath.reset(); mTickPath.rLineTo(0,0); //防止硬件加速的bug float start = 0; float end = mTickLength * mTickDrawOffsert; mPathMeasure.getSegment(start,end,mTickPath,true); canvas.drawPath(mTickPath,mTickPaint); } private void AnimInit(){ final int animTime = 500; //圆角变圆 mRectToRoundAnim = ValueAnimator.ofFloat(10,mRadius); mRectToRoundAnim.setDuration(500); mRectToRoundAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { mRectToRound = (float) valueAnimator.getAnimatedValue(); mStatus = 0; invalidate(); } }); //长椭圆变圆动画,并让文字渐渐消失 mRoundToCircleAnim = ValueAnimator.ofFloat(0,(width-height)/2); mRoundToCircleAnim.setDuration(1000); mRoundToCircleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { mRoundToCircleOffert = (float) valueAnimator.getAnimatedValue(); int max = (width - height)/2 ; int alpha = (int) ((float)(max - mRoundToCircleOffert)/max * 255); mTextPaint.setAlpha(alpha); mStatus = 0; invalidate(); } }); //小球上移 mCircleMoveAnim = ValueAnimator.ofFloat(0,300); mCircleMoveAnim.setInterpolator(new AccelerateDecelerateInterpolator()); mCircleMoveAnim.setDuration(500); mCircleMoveAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mMoveY = (float) animation.getAnimatedValue(); postInvalidate(); } }); //圆形变大动画 mCircleScaleAnim = ValueAnimator.ofFloat(height/2,height*2); mCircleScaleAnim.setDuration(animTime); mCircleScaleAnim.setInterpolator(new LinearInterpolator()); mCircleScaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCircleWidth = (float) animation.getAnimatedValue(); mStatus = 1; postInvalidate(); } }); // 打钩动画 mTickAnim = ValueAnimator.ofFloat(0,1); mTickAnim.setDuration(1000); mTickAnim.setInterpolator(new AccelerateInterpolator()); mTickAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mTickDrawOffsert = (float) animation.getAnimatedValue(); mStatus = 1; Log.d(TAG, "onAnimationUpdate: "+mTickDrawOffsert); postInvalidate(); } }); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { width = measureWidth(widthMeasureSpec); height = measureHeight(heightMeasureSpec); mRadius = height/2; //需要用到 width,height 都在这初始化 AnimInit(); initTick(); setMeasuredDimension(width, height); } //设置高的大小 private int measureHeight(int heightMeasureSpec) { // TODO Auto-generated method stub int result = 0; //获取模式和大小 int specMode = MeasureSpec.getMode(heightMeasureSpec); int specSize = MeasureSpec.getSize(heightMeasureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = 70; //如果是wrap_content ,给个初始值 if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } //设置宽的大小 private int measureWidth(int widthMeasureSpec) { // TODO Auto-generated method stub int result = 0; //获取模式和大小 int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = 70; //如果是wrap_content ,给个初始值 if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; }}
- 自定义一个酷炫的提交完成按钮
- Android自定义动画酷炫的提交按钮
- 表单的自定义按钮提交
- studio自定义SearchView的提交按钮图片
- Android自定义View--使用ViewAnimator实现一个提交按钮
- 一个页面有多个提交按钮的时候
- 一个jsp页面中多个提交按钮提交不同的页面
- 自定义键盘右上角完成按钮
- 图片和表单文字信息通过一个按钮来完成提交
- css 自定义全局的input样式(提交按钮,文本框)
- iOS模拟器键盘的下面添加一个完成按钮
- 自定义一个带删除按钮的ListView
- 一个FORM下 实现多个submit按钮的提交
- struts2中,一个表单里有多个提交按钮的处理办法
- 如何防止按钮重复提交的一个实践
- 让一个网页页面的提交按钮执行点击事件
- 生成一个带文本提示信息的图片提交按钮
- IOS 键盘右上角完成按钮自定义
- poj 1088 滑雪
- bean标签的解析及注册。
- Thinking in java-26 构造函数多态
- 请求
- 深度优先遍历的8个简单小例题
- 自定义一个酷炫的提交完成按钮
- LogSec日志大数据审计平台,企业信息安全管理人员不再“躺枪”
- 如何控制用户的输入
- hihocoder1496-高维前缀和|暴力
- Java泛型
- ssm(springmvc4+spring4+mybatis3)整合实战-个人博客系统-前端页面的开发
- 我的第一个技术博客
- Android 真机和VMware模拟机 ping通
- pygame开发2048游戏