Android仿58同城Loading View
oint1572
9年前
来自: http://blog.csdn.net//lcq5211314123/article/details/49101935
今天打开58同城App看到他们的网络加载Loading做的很有新意,是一个三角形,圆形,正方形不同的运动并且切换,这个效果不说有多难,至少很有创意,就着手模仿了一下,先看下效果图:
58的更加复杂,在形状运动的过程还一直不停的旋转,旋转的坐标计算太复杂, 还没有搞定,先把这个半成品开源出来。
思路:1.先画一个静态的形状和下面的阴影。 2.通过ValueAnimator不停的改变纵坐标,在动画的监听中拿到 当前的坐标值,重新invalidate()此时的View。 3.在向下的动画执行完毕后,更新Shape。然后让动画不停的运动即可。
package view; /** * @description 仿58加载View * @author rzq * @date 2015年10月10日 */ public class MyLoadingView extends View { /** * 公共变量 */ private Context mContext; private Resources mResource; private Paint mPaint; private Paint mOvalPaint; /** */ private int mRadius; private int mDistance; private int mOvalTop; private int mOvalHeight; private int mOvalWidth; /** * 所有形状的中心位置 */ private int mCenterX, mCenterY; private int currentCenterY; private Animator mRotationAnim; private ValueAnimator mLineAnimDown; private ValueAnimator mLineAnimUp; private Shape shape; public MyLoadingView(Context context) { this(context, null); } public MyLoadingView(Context context, AttributeSet attrs) { super(context); this.mContext = context; this.mResource = context.getResources(); init(); } private void init() { mRadius = 10; mDistance = 50; mOvalHeight = 3; mOvalWidth = 10; mOvalTop = 25; mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Style.FILL); mPaint.setColor(mResource.getColor(R.color.rect)); mOvalPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mOvalPaint.setStyle(Style.FILL); mOvalPaint.setColor(mResource.getColor(R.color.color_666666)); setupAnimations(); shape = Shape.RECT; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { /** * 宽度最长是矩形的宽,高度最高是矩形的高加上distance */ int width = (mRadius * 2); int height = (mDistance + mRadius * 2 + mOvalTop + mOvalHeight); setMeasuredDimension(width, height); currentCenterY = mCenterX = mCenterY = mRadius; } @Override protected void onDraw(Canvas canvas) { switch (shape) { case RECT: drawRect(canvas); break; case TRAIL: drawTrail(canvas); break; case CIRCLE: drawCircle(canvas); break; } drawOval(canvas); } /** * 更具中心点的改变,绘制矩形 */ private void drawRect(Canvas canvas) { canvas.drawRect(mCenterX - mRadius, currentCenterY - mRadius, mCenterX + mRadius, currentCenterY + mRadius, mPaint); } private void drawTrail(Canvas canvas) { Path path = new Path(); int leftX = 0; int leftY = currentCenterY + mRadius; int middleX = mCenterX; int middleY = currentCenterY - mRadius; int rightX = mCenterX + mRadius; int rightY = currentCenterY + mRadius; path.moveTo(leftX, leftY); path.lineTo(middleX, middleY); path.lineTo(rightX, rightY); path.close(); canvas.drawPath(path, mPaint); } private void drawCircle(Canvas canvas) { canvas.drawCircle(mCenterX, currentCenterY, mRadius, mPaint); } private void drawOval(Canvas canvas) { float factory = ((mDistance + mRadius) - currentCenterY) / (float) mDistance; RectF rectF = new RectF(mCenterX - mOvalWidth * factory, mDistance + mRadius * 2 + mOvalTop, mCenterX + mOvalWidth * factory, mDistance + mRadius * 2 + mOvalTop + mOvalHeight); canvas.drawOval(rectF, mOvalPaint); } private void setupAnimations() { mRotationAnim = ValueAnimator.ofInt(0, 180); mLineAnimDown = ValueAnimator.ofInt(mCenterY + mRadius, mDistance); mLineAnimDown.setInterpolator(new AccelerateInterpolator(1.2f)); mLineAnimDown.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { currentCenterY = (Integer) valueAnimator.getAnimatedValue(); invalidate(); } }); mLineAnimUp = ValueAnimator.ofInt(mDistance, mCenterY + mRadius); mLineAnimUp.setInterpolator(new DecelerateInterpolator(1.2f)); mLineAnimUp.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { currentCenterY = (Integer) valueAnimator.getAnimatedValue(); invalidate(); } }); mLineAnimDown.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (shape == Shape.RECT) { mPaint.setColor(mResource.getColor(R.color.triangle)); shape = Shape.TRAIL; } else { if (shape == Shape.TRAIL) { mPaint.setColor(mResource.getColor(R.color.circle)); shape = Shape.CIRCLE; } else { mPaint.setColor(mResource.getColor(R.color.rect)); shape = Shape.RECT; } } } }); final AnimatorSet set = new AnimatorSet(); set.addListener(new AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { set.start(); } @Override public void onAnimationCancel(Animator animation) { } }); set.playSequentially(mLineAnimDown, mLineAnimUp); set.setDuration(300); set.setStartDelay(100); set.start(); } private enum Shape { RECT, TRAIL, CIRCLE; } } 用法:直接在布局中引入即可。 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res /android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white" > <view.MyLoadingView android:id="@+id/loadView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /> </RelativeLayout>
之前看到一个开源View整个动画都是用ViewAnimator衔接起来的,感觉这种方式非常的好,我也尝试的用这种方式实现。
扩张:自定义 View中我为方便将一些属性等的都直接指定了,更好的方式是以自定义属性的方式使用更好。