Android 自定义动画 单个View平面位移以及一组View轮回旋转(二)

Android 自定义动画 单个View平面位移以及一组View轮回旋转(一)

 

这一篇文章主要讲到的是那个循环动画,好了先把动画的样子奉上,请各位大佬轻喷:

Android 自定义动画 单个View平面位移以及一组View轮回旋转(二)_第1张图片

关于这个动画,与遇上一个动画的联系就是,使用同样的方法去绘制的小方块,为了避免大家翻看过于麻烦,在这里再给大家展示出来,Been类:


/**
 * @author: jjf
 * @date: 2019/5/10
 * @describe:View 参数
 */
public class BlockBeen {

    //初始View位置
    private int viewAddressX = 100;
    private int viewAddressY = 200;
    //View 大小
    private int viewWide = 100;
    private int viewHeight = 100;
    //画view的画笔仓库
    private  Paint viewPain ;
    //view仓库
    private RectF rectF;
    //地址状态 0:第一排 1:牧默认位置(中间一排) 2:下边一排
    private int addressState;


    public int getAddressState() {
        return addressState;
    }

    public void setAddressState(int addressState) {
        this.addressState = addressState;
    }

    public int getViewAddressX() {
        return viewAddressX;
    }

    public void setViewAddressX(int viewAddressX) {
        this.viewAddressX = viewAddressX;
    }

    public int getViewAddressY() {
        return viewAddressY;
    }

    public void setViewAddressY(int viewAddressY) {

        this.viewAddressY = viewAddressY;
    }
    String TAG="MoveBlockView";
    public int getViewWide() {
        return viewWide;
    }

    public void setViewWide(int viewWide) {
        this.viewWide = viewWide;
    }

    public int getViewHeight() {
        return viewHeight;
    }

    public void setViewHeight(int viewHeight) {
        this.viewHeight = viewHeight;
    }

    public Paint getViewPain() {
        return viewPain;
    }

    public void setViewPain(Paint viewPain) {
        this.viewPain = viewPain;
    }

    public RectF getRectF() {
        return rectF;
    }

    public void setRectF(RectF rectF) {
        this.rectF = rectF;
    }
}

还有一个填充数据并绘制的方法:

 /**
     * 画View
     *
     * @param canvas
     * @param position 第几个view
     */
    public void drawBlock(Canvas canvas, int position) {//viewAddressY+ position * (viewWide+padding)+viewHeight
        BlockBeen blockBeen;
        if (position >= blockBeens.size()) {
            blockBeen = new BlockBeen();
            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);//创建画笔
            paint.setColor(getResources().getColor(R.color.colorPrimaryDark));//添加画笔颜色
            blockBeen.setViewPain(paint);
        } else {
            blockBeen = blockBeens.get(position);
        }
        if (position == 0) {
            blockBeen.setViewAddressX(viewAddressX);
            blockBeen.setViewAddressY(viewAddressY - padding - blockBeen.getViewHeight());
        } else {
            blockBeen.setViewAddressX(viewAddressX + (position - 1) * (viewWide + padding));
            blockBeen.setViewAddressY(viewAddressY);
        }

        blockBeen.setViewWide(viewWide);
        blockBeen.setViewHeight(viewHeight);
        RectF rectF;
        if (blockBeen.getRectF() == null) {
            rectF = new RectF(blockBeen.getViewAddressX(), blockBeen.getViewAddressY(),
                    blockBeen.getViewAddressX() + blockBeen.getViewWide(),
                    blockBeen.getViewAddressY() + blockBeen.getViewHeight());//先画一个矩形
            blockBeen.setRectF(rectF);
        } else {
            rectF = blockBeen.getRectF();
            rectF.set(blockBeens.get(position).getViewAddressX()
                    , blockBeens.get(position).getViewAddressY(), blockBeens.get(position).getViewAddressX() + blockBeens.get(position).getViewWide()
                    , blockBeens.get(position).getViewAddressY() + blockBeens.get(position).getViewHeight());
        }
        if (!blockBeens.contains(blockBeen)) {
            blockBeens.add(blockBeen);
        }
        canvas.drawRoundRect(rectF, 30, 30, blockBeen.getViewPain());//根据提供的矩形为四个角画弧线,(其中的数字:第一个表示X轴方向大小,第二个Y轴方向大小。可以改成其他的,你可以自己体验),最后添加画笔。
        //绘制View
        invalidate();
    }

为了与上一篇文章的代码混淆,在这里画一条分割线表示以上代码与上一篇文章相同:

-------------------------------------------我是分割线---------哦略略-------------------------------------------------------

 

然后循环动画的的核心代码,让他动起来:

 public void moveRectf(Canvas canvas, BlockBeen blockBeen) {
        blockBeen.getRectF().set(blockBeen.getViewAddressX()
                , blockBeen.getViewAddressY(), blockBeen.getViewAddressX() + blockBeen.getViewWide()
                , blockBeen.getViewHeight() + blockBeen.getViewAddressY() + mSpeedX);
        //根据提供的矩形为四个角画弧线,(其中的数字:第一个表示X轴方向大小,第二个Y轴方向大小。可以改成其他的,你可以自己体验),最后添加画笔。
        canvas.drawRoundRect(blockBeen.getRectF(), 30, 30, blockBeen.getViewPain());
        setViewState(index);
        setViewState(nextIndex);
    }

        看到这里 是不是有点儿怀疑,则呢吗可能这么少,嗯 是的。不可能这么少,只是这部分代码是关键代码,多次引用,我就把他摘出来单独放一起,然后下面的代码将会是这个动画的数据操作部分,

        我先给大家说一下我的实现思路,这样对于代码会更好地理解:

        首先我们把这个动画看成三行区域,上、中、下三行,小方块默认是在第二行,也就是中间行,向下移动,,当Y轴坐标超过中间行向下移动开始,默认这个小方块的状态在下行,向上移动时也是一样的道理,路过中间行开始向上移动时,说明小方块的移动状态是在上行,这是区分小方块移动位置的一个方法;

        还有一个问题是 小方块是循环移动的,也就是有想做一定的时候,也有向右移动的时候,对于处理数据来说,无非就是在X坐标上的 加减,但是如何知道应该去加还是应该去减呢? 大家可以自己想想这块的逻辑,说不定有更好的方法,如果不想动脑子的话,就直接看看我的方法喽,,让我们先看单个方块的半个循环步骤中做了什么:

 

我在这里是使用标记法来计算它的方向的,index是当前小方块的角标。 nextIndex 是下一个小方块的脚标,向左移动时,前面的角标比后面的角标要小 ,向右移动时 前面的要比后面的脚标要大

boolean directionToRight = index < nextIndex;//判断运动方向

代码中。View的起步,也就是第一个方块,初始位置是在第一行,也就是上行,第一个动作是与第二个小方块同时向下移动,第一个小方块移动到中行时,第二个正好也会移动到下行底部,然后第二个小方块向左移动,移动到第三个小方块的下面时(X轴位置相同的地方),开始对index、nextIndex重新赋值

index = nextIndex;
nextIndex = directionToRight ? (nextIndex + 1) : (nextIndex - 1);

关于第二行代码nextIndex的赋值,大家暂时可以当作nextIndex = directionToRight ? (nextIndex + 1) : (nextIndex - 1);

nextIndex =  nextIndex + 1;

因为是向右移动 所以 nextIndex+=1;

以上是半个步骤的操作思路,如果以上理解了,那么下面的代码看起来会很轻松,如果上面的不理解,可能。。。。。。

上核心代码:

 //循环移动
    private void moveCirclePath(Canvas canvas) {
        if(mSpeedY>padding||mSpeedX>padding){
            Toast.makeText(context,"移动单位 mSpeedY、mSpeedX 不可以大与每个View之间的间距padding",Toast.LENGTH_SHORT).show();
            return ;
        }


        boolean directionToRight = index < nextIndex;//判断运动方向
        //根据运行方向判断,当前角标是否在当前方向上的边界(是否第一位或者最后一位脚标)

        switch (setViewState(index)) {
            case UP:
                Log.i(TAG, "UP");
                blockBeens.get(index).setViewAddressY(blockBeens.get(index).getViewAddressY() + mSpeedY);
                blockBeens.get(nextIndex).setViewAddressY(blockBeens.get(nextIndex).getViewAddressY() + mSpeedY);
                for (int i = 0; i < blockBeens.size(); i++) {
                    moveRectf(canvas, blockBeens.get(i));
                }
                invalidate();
                break;
            case CENTER:
                Log.i(TAG, "CENTER");
                boolean isInIndexBoundary = directionToRight ? blockBeens.size() > nextIndex + 1 : 0 < nextIndex;
                if (isInIndexBoundary) {
                    //根据移动方向,判断当前移动是否到达预期位置
                    boolean IsMoveToCriticalPoint = directionToRight ?
                            (blockBeens.get(nextIndex).getViewAddressX() < blockBeens.get(nextIndex + 1).getViewAddressX())
                            : (blockBeens.get(nextIndex).getViewAddressX() > blockBeens.get(nextIndex - 1).getViewAddressX());

                    if (IsMoveToCriticalPoint) {
                        int addressX = directionToRight ? blockBeens.get(nextIndex).getViewAddressX() + mSpeedX : blockBeens.get(nextIndex).getViewAddressX() - mSpeedX;
                        blockBeens.get(nextIndex).setViewAddressX(addressX);
                        for (int i = 0; i < blockBeens.size(); i++) {
                            moveRectf(canvas, blockBeens.get(i));
                        }
                        invalidate();
                    } else {
                        index = nextIndex;
                        nextIndex = directionToRight ? (nextIndex + 1) : (nextIndex - 1);
                        moveCirclePath(canvas);
                    }
                } else {
                    BlockBeen indexBlockBeen = blockBeens.get(index);
                    blockBeens.remove(indexBlockBeen);
                    int insertIndex = directionToRight ? blockBeens.size() : 0;//当数据运行到集合的结尾或者头部的时候,需要按照图形位置,进行数据交换位置
                    blockBeens.add(insertIndex, indexBlockBeen);
                    index = nextIndex;
                    nextIndex = directionToRight ? (nextIndex -= 1) : (nextIndex += 1);
                    moveCirclePath(canvas);
                }

                break;
            case BELOW:
                Log.i(TAG, "BELOW");
                blockBeens.get(index).setViewAddressY(blockBeens.get(index).getViewAddressY() - mSpeedY);
                blockBeens.get(nextIndex).setViewAddressY(blockBeens.get(nextIndex).getViewAddressY() - mSpeedY);
                for (int i = 0; i < blockBeens.size(); i++) {
                    moveRectf(canvas, blockBeens.get(i));
                }
                invalidate();
                break;
            default:
                Toast.makeText(context, "View 位置状态错误", Toast.LENGTH_SHORT).show();
        }


    }

上面的代码主要是对数据的操作部分,运行状态的核心是这个。上中下三行。针对正在运行的小方块,分成上中下三部分去处理,这样的话, 一切的逻辑都将明朗。

好了,在这里我把完整的代码粘贴出来,里面包括上一个动画的代码:

package com.jjf.blockmoveforcirclepath.customview;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.widget.Toast;

import com.jjf.blockmoveforcirclepath.BlockBeen;
import com.jjf.blockmoveforcirclepath.R;

import java.util.ArrayList;

/**
 * @author: jjf
 * @date: 2019/5/10
 * @describe: 移动动画
 */
public class MoveBlockView extends View {
    //每个方块间距
    private int padding = 20;
    //初始View位置
    private int viewAddressX = 100;
    private int viewAddressY = 200;
    //View 大小
    private int viewWide = 100;
    private int viewHeight = 100;
    private int count = 8;//view数量(小方块的数量)
    //view仓库
    private ArrayList blockBeens = new ArrayList<>();

    public static final int MAX_SIZE = 140;
    private int mCoordX = 0;
    private int mCoordY = 0;
    private int mRealSize = 140;

    //移动间距必须小于padding
    private final int mSpeedX = 5;// View每次位移的距离单位(值越大 速度越快)
    private int mSpeedY = 5;
    private Context context;

    //三种状态
    private final int UP = 0;
    private final int CENTER = 1;
    private final int BELOW = 2;

    /**
     * moveto 动画
     */
    private boolean goRight = true;
    private boolean goDown = true;

    int parentHeight = 0;
    int parentWight = 0;

    public MoveBlockView(Context context) {
        super(context);
        this.context = context;
    }

    public MoveBlockView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        parentHeight = bottom - top;
        parentWight = right - left;
    }

    boolean bo = true;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //初始化
        if (bo) {
            for (int i = 0; i < count; i++) {
                drawBlock(canvas, i);
                //单词初始化核心代码
                if (i == 0) {
                    blockBeens.get(i).setAddressState(0);
                } else {
                    blockBeens.get(i).setAddressState(1);
                }
            }
            bo = false;
        }
        moveCirclePath(canvas);
//        moveTo(0, 5, 5, canvas);

    }

    //设置View状态
    public int setViewState(int index) {
        if (blockBeens.get(index).getViewAddressY() == viewAddressY) {
            blockBeens.get(index).setAddressState(CENTER);
            return CENTER;
        } else if (blockBeens.get(index).getViewAddressY() <= viewAddressY + blockBeens.get(index).getViewHeight() + padding
                && blockBeens.get(index).getViewAddressY() > viewAddressY) {
            blockBeens.get(index).setAddressState(BELOW);
            return BELOW;
        } else if (blockBeens.get(index).getViewAddressY() < viewAddressY
                && blockBeens.get(index).getViewAddressY() >= viewAddressY - padding - blockBeens.get(index).getViewHeight()) {
            blockBeens.get(index).setAddressState(UP);
            return UP;
        } else {
            return -1;
        }
    }

    String TAG = "MoveBlockView";

    /**
     * 移动小方块
     * @param position 第几个View移动
     * @param goX  在X轴上移动的幅度
     * @param goY  在Y轴上移动的幅度
     * @param canvas 画布
     */
    private void moveTo(int position, int goX, int goY, Canvas canvas) {

        // check the borders, and set the direction if a border has reached
        if (blockBeens.get(position).getViewAddressX() > (parentWight - MAX_SIZE)) {
            goRight = false;
        }

        if (blockBeens.get(position).getViewAddressX() <= 0) {
            goRight = true;
        }

        if (blockBeens.get(position).getViewAddressY() > (parentHeight - MAX_SIZE)) {
            goDown = false;
        }
        if (blockBeens.get(position).getViewAddressY() <= 0) {
            goDown = true;
        }
        // move the x and y
        if (goRight) {
            blockBeens.get(position).setViewAddressX(blockBeens.get(position).getViewAddressX() + goX);
        } else {
            blockBeens.get(position).setViewAddressX(blockBeens.get(position).getViewAddressX() - goX);
        }
        if (goDown) {
            blockBeens.get(position).setViewAddressY(blockBeens.get(position).getViewAddressY() + goY);
        } else {
            blockBeens.get(position).setViewAddressY(blockBeens.get(position).getViewAddressY() - goY);
        }
        blockBeens.get(position).getRectF().set(blockBeens.get(position).getViewAddressX()
                , blockBeens.get(position).getViewAddressY(), blockBeens.get(position).getViewAddressX() + blockBeens.get(position).getViewWide()
                , blockBeens.get(position).getViewAddressY() + blockBeens.get(position).getViewHeight());
        canvas.drawRoundRect(blockBeens.get(position).getRectF(), 30, 30, blockBeens.get(position).getViewPain());
        invalidate();
    }

    int index = 0;//当前角标
    int nextIndex = 1;//下一个View角标

    public void moveRectf(Canvas canvas, BlockBeen blockBeen) {
        blockBeen.getRectF().set(blockBeen.getViewAddressX()
                , blockBeen.getViewAddressY(), blockBeen.getViewAddressX() + blockBeen.getViewWide()
                , blockBeen.getViewHeight() + blockBeen.getViewAddressY() + mSpeedX);
        //根据提供的矩形为四个角画弧线,(其中的数字:第一个表示X轴方向大小,第二个Y轴方向大小。可以改成其他的,你可以自己体验),最后添加画笔。
        canvas.drawRoundRect(blockBeen.getRectF(), 30, 30, blockBeen.getViewPain());
        setViewState(index);
        setViewState(nextIndex);
    }

    //循环移动
    private void moveCirclePath(Canvas canvas) {
        if(mSpeedY>padding||mSpeedX>padding){
            Toast.makeText(context,"移动单位 mSpeedY、mSpeedX 不可以大与每个View之间的间距padding",Toast.LENGTH_SHORT).show();
            return ;
        }


        boolean directionToRight = index < nextIndex;//判断运动方向


        switch (setViewState(index)) {
            case UP:
                Log.i(TAG, "UP");
                blockBeens.get(index).setViewAddressY(blockBeens.get(index).getViewAddressY() + mSpeedY);
                blockBeens.get(nextIndex).setViewAddressY(blockBeens.get(nextIndex).getViewAddressY() + mSpeedY);
                for (int i = 0; i < blockBeens.size(); i++) {
                    moveRectf(canvas, blockBeens.get(i));
                }
                invalidate();
                break;
            case CENTER:
                Log.i(TAG, "CENTER");
                //根据运行方向判断,当前角标是否在当前方向上的边界(是否第一位或者最后一位脚标)
                boolean isInIndexBoundary = directionToRight ? blockBeens.size() > nextIndex + 1 : 0 < nextIndex;
                if (isInIndexBoundary) {
                    //根据移动方向,判断当前移动是否到达预期位置
                    boolean IsMoveToCriticalPoint = directionToRight ?
                            (blockBeens.get(nextIndex).getViewAddressX() < blockBeens.get(nextIndex + 1).getViewAddressX())
                            : (blockBeens.get(nextIndex).getViewAddressX() > blockBeens.get(nextIndex - 1).getViewAddressX());

                    if (IsMoveToCriticalPoint) {
                        int addressX = directionToRight ? blockBeens.get(nextIndex).getViewAddressX() + mSpeedX : blockBeens.get(nextIndex).getViewAddressX() - mSpeedX;
                        blockBeens.get(nextIndex).setViewAddressX(addressX);
                        for (int i = 0; i < blockBeens.size(); i++) {
                            moveRectf(canvas, blockBeens.get(i));
                        }
                        invalidate();
                    } else {
                        index = nextIndex;
                        nextIndex = directionToRight ? (nextIndex + 1) : (nextIndex - 1);
                        moveCirclePath(canvas);
                    }
                } else {
                    BlockBeen indexBlockBeen = blockBeens.get(index);
                    blockBeens.remove(indexBlockBeen);
                    int insertIndex = directionToRight ? blockBeens.size() : 0;//当数据运行到集合的结尾或者头部的时候,需要按照图形位置,进行数据交换位置
                    blockBeens.add(insertIndex, indexBlockBeen);
                    index = nextIndex;
                    nextIndex = directionToRight ? (nextIndex -= 1) : (nextIndex += 1);
                    moveCirclePath(canvas);
                }

                break;
            case BELOW:
                Log.i(TAG, "BELOW");
                blockBeens.get(index).setViewAddressY(blockBeens.get(index).getViewAddressY() - mSpeedY);
                blockBeens.get(nextIndex).setViewAddressY(blockBeens.get(nextIndex).getViewAddressY() - mSpeedY);
                for (int i = 0; i < blockBeens.size(); i++) {
                    moveRectf(canvas, blockBeens.get(i));
                }
                invalidate();
                break;
            default:
                Toast.makeText(context, "View 位置状态错误", Toast.LENGTH_SHORT).show();
        }


    }

    /**
     * 画View
     *
     * @param canvas
     * @param position 第几个view
     */
    public void drawBlock(Canvas canvas, int position) {//viewAddressY+ position * (viewWide+padding)+viewHeight
        BlockBeen blockBeen;
        if (position >= blockBeens.size()) {
            blockBeen = new BlockBeen();
            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);//创建画笔
            paint.setColor(getResources().getColor(R.color.colorPrimaryDark));//添加画笔颜色
            blockBeen.setViewPain(paint);
        } else {
            blockBeen = blockBeens.get(position);
        }
        if (position == 0) {
            blockBeen.setViewAddressX(viewAddressX);
            blockBeen.setViewAddressY(viewAddressY - padding - blockBeen.getViewHeight());
        } else {
            blockBeen.setViewAddressX(viewAddressX + (position - 1) * (viewWide + padding));
            blockBeen.setViewAddressY(viewAddressY);
        }

        blockBeen.setViewWide(viewWide);
        blockBeen.setViewHeight(viewHeight);
        RectF rectF;
        if (blockBeen.getRectF() == null) {
            rectF = new RectF(blockBeen.getViewAddressX(), blockBeen.getViewAddressY(),
                    blockBeen.getViewAddressX() + blockBeen.getViewWide(),
                    blockBeen.getViewAddressY() + blockBeen.getViewHeight());//先画一个矩形
            blockBeen.setRectF(rectF);
        } else {
            rectF = blockBeen.getRectF();
            rectF.set(blockBeens.get(position).getViewAddressX()
                    , blockBeens.get(position).getViewAddressY(), blockBeens.get(position).getViewAddressX() + blockBeens.get(position).getViewWide()
                    , blockBeens.get(position).getViewAddressY() + blockBeens.get(position).getViewHeight());
        }
        if (!blockBeens.contains(blockBeen)) {
            blockBeens.add(blockBeen);
        }
        canvas.drawRoundRect(rectF, 30, 30, blockBeen.getViewPain());//根据提供的矩形为四个角画弧线,(其中的数字:第一个表示X轴方向大小,第二个Y轴方向大小。可以改成其他的,你可以自己体验),最后添加画笔。
        //绘制View
        invalidate();
    }
}

关于在xml中的引用,因为只是针对这个动画的逻辑代码部分做一个尝试,所以关于自定义属性,等等没有去解决,这里只是提供一个思路供大家参考,有更好的点子,希望大家能积极评论,谢谢!

你可能感兴趣的:(自定义View,Android,动画,自定义控件,Android,绘制,动画,代码Demo,Android,Studio,java语言,自定义控件,Android,控件使用,自定义动画)