须知少年凌云志,曾许人间第一流。这篇文章主要讲述android动画效果相关的知识,希望能为你提供帮助。
http://blog.csdn.net/L_wwbs/article/details/53408830
本篇通过自定义View模拟一个物理现象—
—
竖直平面内小球在最低点以一定初速度在重力作用下绕圆环做变速圆周运动的效果(从最低点减速到0时上升到最高点再加速到初始速度时回到最低点)。可以用于加载等待等场景,下面按照自定义View的步骤具体说明一下。
1、定义属性为了能在布局文件中使用我们的自定义控件,定制其属性,我们需要自定义一些控件的属性,以供更灵活的使用此控件。
【android动画效果】
[java]
view plain
copy
- < ?xml version="1.0" encoding="utf-8"?>
- < resources>
- < attr name="ringColor" format="color"> < /attr>
- < attr name="ringWidth" format="dimension"> < /attr>
- < attr name="globuleColor" format="color"> < /attr>
- < attr name="globuleRadius" format="dimension"> < /attr>
- < attr name="cycleTime" format="float"> < /attr>
- < declare-styleable name="AccelerateCircularView">
- < attr name="ringColor" />
- < attr name="ringWidth" />
- < attr name="globuleColor" />
- < attr name="globuleRadius" />
- < attr name="cycleTime" />
- < /declare-styleable>
- < /resources>
-
2、获取属性
自定义属性完成后,我们需要在自定义View的构造方法中逐一获取这些属性。[java]
view plain
copy
- public AccelerateCircularView(Context context) {
- this(context, null);
- }
- public AccelerateCircularView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public AccelerateCircularView(Context context, AttributeSet attrs,
- int defStyle) {
- super(context, attrs, defStyle);
- TypedArray attrsArray = context.getTheme().obtainStyledAttributes(
- attrs, R.styleable.AccelerateCircularView, defStyle, 0);
- mRingColor = attrsArray.getColor(
- R.styleable.AccelerateCircularView_ringColor, Color.GRAY);
- mGlobuleColor = attrsArray.getColor(
- R.styleable.AccelerateCircularView_globuleColor, Color.BLUE);
- mRingWidth = attrsArray.getDimension(
- R.styleable.AccelerateCircularView_ringWidth, TypedValue
- .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
- getResources().getDisplayMetrics()));
- mGlobuleRadius = attrsArray.getDimension(
- R.styleable.AccelerateCircularView_globuleRadius, TypedValue
- .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6,
- getResources().getDisplayMetrics()));
- mCycleTime = attrsArray.getFloat(
- R.styleable.AccelerateCircularView_cycleTime, 3000);
- attrsArray.recycle();
- mPaint = new Paint();
- }
2、重写onMeasure[java] view plain copy- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int mWidth , mHeight ;
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- if (widthMode == MeasureSpec.EXACTLY) {
- mWidth = widthSize;
- } else {
- mWidth = 169;
- if (widthMode == MeasureSpec.AT_MOST) {
- mWidth = Math.min(mWidth, widthSize);
- }
- }
- if (heightMode == MeasureSpec.EXACTLY) {
- mHeight = heightSize;
- } else {
- mHeight = 169;
- if (heightMode == MeasureSpec.AT_MOST) {
- mHeight = Math.min(mWidth, heightSize);
- }
- }
- setMeasuredDimension(mWidth, mHeight);
- }
这里主要是处理当VIew设置为“ wrap_content” 时需要自己给出测量结果,否则系统默认给我们测量的结果将是"match_parent"的大小。3、重写onDraw[java] view plain copy- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- int central = Math.min(getWidth(), getHeight()) / 2;
- mRingRadius = central - mGlobuleRadius;
- if (mGlobuleRadius < mRingWidth / 2) {// 小球嵌在环里
- mRingRadius = central - mRingWidth / 2;
- }
- mPaint.setStrokeWidth(mRingWidth);
- mPaint.setStyle(Style.STROKE);
- mPaint.setAntiAlias(true);
- mPaint.setColor(mRingColor);
- canvas.drawCircle(central, central, mRingRadius, mPaint); // 绘制圆环
- mPaint.setStyle(Style.FILL);
- mPaint.setAntiAlias(true);
- mPaint.setColor(mGlobuleColor);
- if (currentAngle == -1) {
- startCirMotion();
- }
- drawGlobule(canvas, central); // 绘制小球
- }
- /**
- * 绘制小球,起始位置为圆环最低点
- *
- * @param canvas
- * @param central
- */
- private void drawGlobule(Canvas canvas, float central) {
- float cx = central + (float) (mRingRadius * Math.cos(currentAngle));
- float cy = (float) (central + mRingRadius * Math.sin(currentAngle));
- canvas.drawCircle(cx, cy, mGlobuleRadius, mPaint);
- }
- /**
- * 旋转小球
- */
- private void startCirMotion() {
- ValueAnimator animator = ValueAnimator.ofFloat(90f, 450f); //起始位置在最低点
- animator.setDuration((long) mCycleTime).setRepeatCount(
- ValueAnimator.INFINITE);
- animator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- Float angle = (Float) animation.getAnimatedValue();
- currentAngle = angle * Math.PI / 180;
- invalidate();
- }
- });
- // animator.setInterpolator(new LinearInterpolator()); // 匀速旋转
- // 自定义开始减速到0后加速到初始值的Interpolator
- animator.setInterpolator(new TimeInterpolator() {
- @Override
- public float getInterpolation(float input) {
- float output;
- if (input < 0.5) {
- output = (float) Math.sin(input * Math.PI) / 2; // 先加速
- } else {
- output = 1 - (float) Math.sin(input * Math.PI) / 2; // 后减速,最高点(中间)速度为0
- }
- return output;
- }
- });
- animator.start();
- }
这里通过自定义Interpolator来实现对动画进度变化快慢的控制,动画设置的值为小球的当前角度。初值90° 保证小球从最低点开始运动。 http://blog.csdn.net/chjr1000/article/details/41823505
推荐阅读
- Android 代码混淆之proguard
- Android中的单元测试
- Android下的数据存储与访问权限
- Your ApplicationContext is unlikely to start due to a @ComponentScan of the default package
- android多线程
- Android入门准备工作
- insert /*+append*/为什么会提高性能
- 基于Android官方Paging Library的RecyclerView分页加载框架
- [Android]异常8-android.view.WindowManager$BadTokenException