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

 

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

关于这个动画,与遇上一个动画的联系就是,使用同样的方法去绘制的小方块,为了避免大家翻看过于麻烦,在这里再给大家展示出来,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中的引用,因为只是针对这个动画的逻辑代码部分做一个尝试,所以关于自定义属性,等等没有去解决,这里只是提供一个思路供大家参考,有更好的点子,希望大家能积极评论,谢谢!

更多相关文章

  1. 腾讯T3大牛带你了解 2019 Android开发趋势及必备技术点!
  2. Android(安卓)仿今日头条频道管理(下)(GridView之间Item的移动和拖
  3. Android对移动计算的影响及产业变革
  4. 全能HOOK框架 JNI NATIVE JAVA ART DALVIK
  5. android 通过lint以及android-resource-remover清楚不用的资源以
  6. Android仿QQ圆形头像
  7. H5与原生IOS交互
  8. Android(安卓)和 PHP 之间进行数据加密传输
  9. Android下自定义IP控件

随机推荐

  1. Android(安卓)M 指纹框架
  2. Linux下Android开发平台的搭建
  3. 2011.07.08(5)——— android shortcut
  4. android shape用法(xml文件)
  5. 从零开始学android开发-adt-bundle-eclip
  6. Android编译过程详解(一)
  7. Android截取开机关机事件
  8. android 调用js中的方法
  9. Android(安卓)控件的可见,不可见,隐藏的
  10. Android(安卓)应用启动闪白一下处理方法