安卓自定义控件Android技术android技术

自定义加载动画(仿58同城)

2021-08-20  本文已影响0人  lostmypieces

1.demo简介

本demo实现的是加载中间过渡动画,主要涉及自定义控件以及属性动画的使用,本代码没有引用任何资源,一切效果都是通过自定义实现的,所以可以直接搬到自己的本地使用。

2.逻辑分析

1.自定义三个图形,并绘制,设置更新数据接口
2.将上一步自定义的图形引入到自定义布局中,并且根据逻辑设置三个动画:图形变化,图形旋转,底部阴影变化
3.将自定义好的布局添加进自己需要的布局中使用

3.代码实现

MainActivity.class(没做任何处理)

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

ShapSwitch.class(自定义图形类)

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

public class ShapSwitch extends View {

    private Path path;
    private Paint mpaint;
    private Shap mCurrtenShap = Shap.CIRCLE;

    enum Shap {
        CIRCLE, SQUARE, TRIGON;
    }

    public ShapSwitch(Context context) {
        this(context, null);
    }

    public ShapSwitch(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ShapSwitch(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public ShapSwitch(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(Math.min(width, height), Math.min(width, height));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        switchShap(canvas);
    }

    private void switchShap(Canvas canvas) {
        switch (mCurrtenShap) {
            case CIRCLE:
                mpaint = new Paint();
                mpaint.setAntiAlias(true);
                mpaint.setColor(Color.YELLOW);
                canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2, mpaint);
                mCurrtenShap = Shap.SQUARE;
                break;
            case SQUARE:
                mpaint = new Paint();
                mpaint.setAntiAlias(true);
                mpaint.setColor(Color.RED);
                Rect rect = new Rect(0, 0, getWidth(), getHeight());
                canvas.drawRect(rect, mpaint);
                mCurrtenShap = Shap.TRIGON;
                break;
            case TRIGON:
                mpaint.setAntiAlias(true);
                mpaint.setColor(Color.BLUE);
                mCurrtenShap = Shap.CIRCLE;
                if (path == null) {
                    path = new Path();
                }
                path.moveTo(getWidth() / 2, 0);
                path.lineTo(0, (float) (getWidth() / 2 * Math.sqrt(3)));
                path.lineTo(getWidth(), (float) (getWidth() / 2 * Math.sqrt(3)));
                path.close();
                canvas.drawPath(path, mpaint);
                break;
        }

    }

    public Shap getmCurrtenShap() {
        return mCurrtenShap;
    }

    public void exchange() {
        switch (mCurrtenShap) {
            case CIRCLE:
                mCurrtenShap = Shap.SQUARE;
                break;
            case SQUARE:
                mCurrtenShap = Shap.TRIGON;
                break;
            case TRIGON:
                mCurrtenShap = Shap.CIRCLE;
                break;
        }
        invalidate();
    }
}

LodingShapLayout.class(自定义布局类,实现属性动画)

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;

import androidx.annotation.Nullable;

/**
 * 1.将布局加载进来
 * 2.绘制三个动画
 * 3.将下落和阴影缩放进行绑定
 * 4.设置监听,当落地时进行形状变化以及开始旋转
 * 5.设置差值器
 */
public class LodingShapLayout extends LinearLayout {

    private ShapSwitch shapLoding;
    private View shadowLoding;
    private boolean isBottom = true;
    private final long DURING_TIME = 500;
    ObjectAnimator shapAnimator;
    ObjectAnimator shadowAnimator;
    ObjectAnimator rotationshapAnimator;
    AnimatorSet animatorSet;

    public LodingShapLayout(Context context) {
        this(context, null);
    }

    public LodingShapLayout(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LodingShapLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public LodingShapLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initShap();
    }

    /**
     * @return void
     * @description 初始化数据
     * @time 2021/8/20 10:37
     */
    private void initShap() {
        inflate(getContext(), R.layout.loding_shap, this);
        shadowLoding = findViewById(R.id.shadowLoding);
        shapLoding = findViewById(R.id.shapLoding);
        animatorSet = new AnimatorSet();
        startAnimation();
    }

    private void startAnimation() {
        fallAnimation();
        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                if (isBottom){
                    shapLoding.exchange();
                    rotateAnimation();
                    isBottom = false;
                    upAnimation();
                }else {
                    isBottom = true;
                    fallAnimation();
                }
            }
        });

    }

    /**
     * @description 下落动画
     * @time 2021/8/20 11:20
     */
    private void fallAnimation(){
        shapAnimator = ObjectAnimator.ofFloat(shapLoding, "translationY", 0,dipToPx(40));
        shadowAnimator = ObjectAnimator.ofFloat(shadowLoding, "scaleX", 0.5f, 1f);
        animatorSet.playTogether(shadowAnimator, shapAnimator);
        animatorSet.setDuration(DURING_TIME);
        animatorSet.setInterpolator(new AccelerateInterpolator());
        animatorSet.start();
    }

    /**
     * @description 上升动画
     * @time 2021/8/20 11:20
     */
    private void upAnimation(){
        shapAnimator = ObjectAnimator.ofFloat(shapLoding, "translationY", dipToPx(40),0);
        shadowAnimator = ObjectAnimator.ofFloat(shadowLoding, "scaleX", 1f, 0.5f);
        animatorSet.playTogether(shadowAnimator, shapAnimator);
        animatorSet.setDuration(DURING_TIME);
        animatorSet.setInterpolator(new DecelerateInterpolator());
        animatorSet.start();
    }

    /**
     * @description 图形旋转
     * @time 2021/8/20 12:17
     */
    private void rotateAnimation(){
        switch (shapLoding.getmCurrtenShap()){
            case CIRCLE:
                break;
            case SQUARE:
                rotationshapAnimator = ObjectAnimator.ofFloat(shapLoding,"rotation",0,180);
                rotationshapAnimator.setDuration(DURING_TIME*2);
                rotationshapAnimator.start();
                break;
            case TRIGON:
                rotationshapAnimator = ObjectAnimator.ofFloat(shapLoding,"rotation",0,240);
                rotationshapAnimator.setDuration(DURING_TIME*2);
                rotationshapAnimator.start();
                break;
        }

    }

    private int dipToPx(int dip){
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dip,getResources().getDisplayMetrics());
    }
}

loding_shap.xml(动画加载xml文件)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    tools:ignore="MissingDefaultResource">

    <com.incall.apps.shapwhitch.ShapSwitch
        android:id="@+id/shapLoding"
        android:layout_width="25dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="40dp"
        />

    <View
        android:id="@+id/shadowLoding"
        android:layout_width="25dp"
        android:layout_height="1dp"
        android:background="@drawable/shadow"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="玩命加载中..."
        android:textSize="15dp"/>

</LinearLayout>

MainActivity.xml(引入动画布局文件)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.incall.apps.shapwhitch.LodingShapLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />


</LinearLayout>

效果图

loding.gif
上一篇下一篇

猜你喜欢

热点阅读