Android(安卓)自定义动画 单个View平面位移以及一组View轮回旋转(二)
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中的引用,因为只是针对这个动画的逻辑代码部分做一个尝试,所以关于自定义属性,等等没有去解决,这里只是提供一个思路供大家参考,有更好的点子,希望大家能积极评论,谢谢!
更多相关文章
- 腾讯T3大牛带你了解 2019 Android开发趋势及必备技术点!
- Android(安卓)仿今日头条频道管理(下)(GridView之间Item的移动和拖
- Android对移动计算的影响及产业变革
- 全能HOOK框架 JNI NATIVE JAVA ART DALVIK
- android 通过lint以及android-resource-remover清楚不用的资源以
- Android仿QQ圆形头像
- H5与原生IOS交互
- Android(安卓)和 PHP 之间进行数据加密传输
- Android下自定义IP控件