屬性動畫—仿58載入效果
阿新 • • 發佈:2018-12-16
實現思路:
1、自定義view繪製圓、正方形、三角形;
2、將繪製好的view新增到佈局容器中;
3、新增相應的動畫效果;
自定義view,重寫onMeasure()方法進行測量,重寫onDraw()方法進行繪製;
public class ShapeView extends View {
private Shape mCurrentShape = Shape.Circle;
private Paint mPaint;
private Path mPath;
//圓形顏色
private int mCircleColor = Color.BLUE;
//正方形顏色
private int mSquareColor = Color.RED;
//三角形顏色
private int mTriangleColor = Color.GREEN;
public ShapeView(Context context) {
this(context, null);
}
public ShapeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ShapeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ShapeView);
mCircleColor = array.getColor(R.styleable.ShapeView_circleColor, mCircleColor);
mSquareColor = array.getColor(R.styleable.ShapeView_squareColor, mSquareColor);
mTriangleColor = array.getColor(R.styleable.ShapeView_triangleColor, mTriangleColor);
array.recycle();
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
//寬高不一致時區最小值
width = Math.min(width, height);
height = Math.min(width, height);
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (mCurrentShape) {
case Circle:
//畫圓形
int center = getWidth() / 2;
mPaint.setColor(mCircleColor);
canvas.drawCircle(center, center, center, mPaint);
break;
case Square:
//畫正方形
mPaint.setColor(mSquareColor);
canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
break;
case Triangle:
//畫三角形 繪製路線
mPaint.setColor(mTriangleColor);
if (mPath == null) {
mPath = new Path();
mPath.moveTo(getWidth() / 2, 0);
mPath.lineTo(0, (float) (getWidth() / 2 * Math.sqrt(3)));
mPath.lineTo(getWidth(), (float) (getWidth() / 2 * Math.sqrt(3)));
// path.lineTo(getWidth()/2,0);
//將繪製的路徑閉合
mPath.close();
}
canvas.drawPath(mPath, mPaint);
break;
}
}
/**
* 改變當前繪製的狀態
*/
public void exchange() {
switch (mCurrentShape) {
case Circle:
mCurrentShape = Shape.Square;
break;
case Square:
mCurrentShape = Shape.Triangle;
break;
case Triangle:
mCurrentShape = Shape.Circle;
break;
}
//進行繪製
invalidate();
}
public enum Shape {
Circle, Square, Triangle
}
public Shape getmCurrentShape() {
return mCurrentShape;
}
}
在onDraw()方法中呼叫canvas.drawCircle();繪製圓,canvas.drawRect();繪製正方形,對於三角形的繪製canvas類並沒有提供相應的api可以進行呼叫,需要用到Path進行繪製,三角形本來就是三條線段連線起來的圖形,使用Path進行線段繪製,然後再將其閉合就可以實現了;
view已經繪製好了,接下來是將其新增到自定義容器中;
/**
* Created by Administrator on 2018/1/18.
* 仿58載入資料動畫效果
*/
public class LoadingView extends LinearLayout {
private ShapeView mShapeView;//形狀
private View mShadowView;//陰影
private int mTranslationDistance;
//動畫執行的時間
private final long ANIMATOR_DURATION = 650;
//是否停止動畫
private boolean mIsStopAnimator = false;
public LoadingView(Context context) {
this(context, null);
}
public LoadingView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initLayout();
}
/**
* 初始化載入佈局
*/
private void initLayout() {
//1.載入寫好的ui_loading_view佈局
//1.1例項化view
View loadView = inflate(getContext(), R.layout.ui_loading_view, null);
//1.2新增到該view中
addView(loadView);
mShapeView = (ShapeView) loadView.findViewById(R.id.shape_view);
mShadowView = loadView.findViewById(R.id.shadow_view);
LinearLayout.LayoutParams params = (LayoutParams) mShapeView.getLayoutParams();
mTranslationDistance = params.bottomMargin - dip2px(5);
post(new Runnable() {
@Override
public void run() {
startFallAnimator();
}
});
}
private int dip2px(int dip) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
/**
* 開始下落動畫
*/
private void startFallAnimator() {
if (mIsStopAnimator) {
return;
}
//下落位移動畫
ObjectAnimator tanslation = ObjectAnimator.ofFloat(mShapeView, "translationY", 0, mTranslationDistance);
//設定插值器
tanslation.setInterpolator(new AccelerateInterpolator());
tanslation.setDuration(ANIMATOR_DURATION);
//配合陰影縮小
ObjectAnimator shadowAniator = ObjectAnimator.ofFloat(mShadowView, "scaleX", 1f, 0.3f);
shadowAniator.setDuration(ANIMATOR_DURATION);
AnimatorSet set = new AnimatorSet();
//一起執行動畫
set.playTogether(tanslation, shadowAniator);
//下落後監聽動畫執行完畢
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
//開始改變形狀
mShapeView.exchange();
//上拋
startUpAnimator();
}
});
set.start();
}
/**
* 執行上拋動畫
*/
private void startUpAnimator() {
if (mIsStopAnimator) {
return;
}
//上拋位移動畫
ObjectAnimator tanslation = ObjectAnimator.ofFloat(mShapeView, "translationY", mTranslationDistance, 0);
//設定插值器
tanslation.setInterpolator(new DecelerateInterpolator());
tanslation.setDuration(ANIMATOR_DURATION);
//配合陰影放大
ObjectAnimator shadowAniator = ObjectAnimator.ofFloat(mShadowView, "scaleX", 0.3f, 1f);
shadowAniator.setDuration(ANIMATOR_DURATION);
AnimatorSet set = new AnimatorSet();
//一起執行動畫
set.playTogether(tanslation, shadowAniator);
//上拋後監聽動畫執行完畢
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
//下落
startFallAnimator();
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
startRotationAnimator();
}
});
set.start();
}
/**
* 上拋的時候需要旋轉
*/
private void startRotationAnimator() {
if (mIsStopAnimator) {
return;
}
ObjectAnimator rotationTanslation = null;
switch (mShapeView.getmCurrentShape()) {
case Circle:
case Square:
//180
rotationTanslation = ObjectAnimator.ofFloat(mShapeView, "rotation", 0, 120);
break;
case Triangle:
rotationTanslation = ObjectAnimator.ofFloat(mShapeView, "rotation", 0, -60);
break;
}
rotationTanslation.setDuration(ANIMATOR_DURATION);
rotationTanslation.setInterpolator(new DecelerateInterpolator());
rotationTanslation.start();
}
@Override
public void setVisibility(int visibility) {
super.setVisibility(View.INVISIBLE);//不要再去擺放和計算,少走一些系統的原始碼
//清除掉動畫
mShapeView.clearAnimation();
mShadowView.clearAnimation();
//把LoadingView從父佈局移除
ViewGroup parent = (ViewGroup) getParent();
if (parent != null) {
parent.removeView(this);//從父佈局移除
//移除自己裡面的所有View
removeAllViews();
}
mIsStopAnimator = true;
}
}
這裡是採用xml佈局解析的方式新增進來的;
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<com.progressbardemo.ShapeView
android:id="@+id/shape_view"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_horizontal"
app:circleColor="@color/circle"
app:squareColor="@color/rect"
app:triangleColor="@color/triangle"
android:layout_marginBottom="82dp"
android:layout_marginTop="10dp"/>
<View
android:id="@+id/shadow_view"
android:layout_width="25dp"
android:layout_height="3dp"
android:background="@drawable/loading_shadow_bg" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="玩命載入中..."
android:layout_marginTop="5dp"
android:textSize="15sp"
android:textColor="@android:color/black"/>
</LinearLayout>
包括位移、縮放、旋轉動畫也是在這裡完成的;這樣就ok了。