上一篇博客已经实现了一个固定view来展示碰撞算法—简单碰撞算法及其demo(屏幕气泡原理),今天做了一个相对负责的ViewGroup,即放在其中的每个view都可以自动去”飘动”!为其子view实现碰撞算法!
下面先看一下实现后的效果!
原理分析:
1.需自定义一个ViewGroup来存放所有的子view,但是每个子view的移动角度移动速度移动边界等信息是不同的,所以需要自定义一个helper类来存储每一个子view的移动范围,速度大小和速度方向以及位置信息!
2.根据子view的个数,在ViewGroup中新建helper对象.
3.不断更新每个子view的位置信息,在ViewGroup的onlayout中不断更新位置
代码实现过程:
1.新建一个ViewGroup来存放所有的子view
package com.example.automoveviewgroup;
import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.View;
import android.widget.RelativeLayout;
@SuppressLint("DrawAllocation")
public class AutoMoveViewGroup extends RelativeLayout {
private List<AutoMoveHelper> mAutoMoveHelpers;// 用于存放每一个子view对应的helper类
public AutoMoveViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public AutoMoveViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoMoveViewGroup(Context context) {
this(context, null);
}
private void init() {
mAutoMoveHelpers = new ArrayList<>();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int wideMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width, height;
if (wideMode == MeasureSpec.AT_MOST) {
width = 600;
} else {
width = MeasureSpec.getSize(widthMeasureSpec);
}
if (heightMode == MeasureSpec.AT_MOST) {
height = 600;
} else {
height = MeasureSpec.getSize(heightMeasureSpec);
}
setMeasuredDimension(width, height);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
requestLayout();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
int childCount = getChildCount();
if (mAutoMoveHelpers.size() != childCount) {
mAutoMoveHelpers.clear();
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
mAutoMoveHelpers.add(new AutoMoveHelper(l, t, r - view.getWidth(), b - view.getHeight(), 0, null));
}
}
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
AutoMoveHelper helper = mAutoMoveHelpers.get(i);
Point point = helper.getPoint();
view.layout(point.x, point.y, point.x + view.getWidth(), point.y + view.getHeight());
helper.updatePoint();
}
}
}
代码很简单,新建了一个list用于存放每一个子view对应的helper对象,然后在onMeasure中设置ViewGroup的大小(当宽高值为wrap_content时设置对应值为600像素),在onlayout中,①判断子view的个数是否和helper的数量一致,如果不一致则根据子view的个数新建helper并存储在list对象中.②根据子view对应的helper中的位置信息point来确定当前子view的v位置,取完位置信息后更新下一次的位置信息
2.新建helper类,存储子view对应的信息
package com.example.automoveviewgroup;
import java.util.Random;
import android.graphics.Point;
public class AutoMoveHelper {
private int left, top, right, bottom;// 该边界表示子view的左上角的边界值
private Point point;// 该值表示对应子view的当前的左上角的值
private double speedX, speedY;// 表示子view分别在X,Y轴上的移动速度(speed*100/s)
private boolean isXUp, isYUp;// 用来表示在X,Y轴上的移动方向
public AutoMoveHelper(int l, int t, int r, int b, int speed, Point p) {
Random random = new Random();
if (l > r || t > b) {
throw new RuntimeException("set wrong bounds");
}
this.left = l;
this.right = r;
this.top = t;
this.bottom = b;
/**
* 确定view的移动速度
*/
// 给x,y轴上的速度赋值
if (speed <= 0) {
speed = random.nextInt(3) + 1;
}
// 确定速度的角度
double nextDouble = random.nextDouble() * 50 + 20;//速度角度定在20-70度之前
this.speedX = speed * Math.cos(nextDouble * 2 * Math.PI / 360) + 1;
this.speedY = speed * Math.sin(nextDouble * 2 * Math.PI / 360) + 1;
if (p != null && p.x > left && p.x < r && p.y > t && p.y < b) {
this.point = p;
} else {
this.point = new Point();
point.x = random.nextInt(r - l) + l;
point.y = random.nextInt(b - t) + t;
}
int nextInt1 = random.nextInt(2);
if (nextInt1 == 0) {
isXUp = false;
} else {
isXUp = true;
}
int nextInt2 = random.nextInt(2);
if (nextInt2 == 0) {
isYUp = false;
} else {
isYUp = true;
}
}
public AutoMoveHelper() {
this(0, 0, 0, 0, 5, null);
}
public void setBounds(int l, int t, int r, int b) {
this.left = l;
this.right = r;
this.top = t;
this.bottom = b;
}
public int getLeft() {
return left;
}
public void setLeft(int left) {
this.left = left;
}
public int getTop() {
return top;
}
public void setTop(int top) {
this.top = top;
}
public int getRight() {
return right;
}
public void setRight(int right) {
this.right = right;
}
public int getBottom() {
return bottom;
}
public void setBottom(int bottom) {
this.bottom = bottom;
}
public Point getPoint() {
return point;
}
public void setPoint(Point point) {
this.point = point;
}
public double getSpeedX() {
return speedX;
}
public void setSpeedX(int speedX) {
this.speedX = speedX;
}
public double getSpeedY() {
return speedY;
}
public void setSpeedY(int speedY) {
this.speedY = speedY;
}
public boolean isXUp() {
return isXUp;
}
public void setXUp(boolean isXUp) {
this.isXUp = isXUp;
}
public boolean isYUp() {
return isYUp;
}
public void setYUp(boolean isYUp) {
this.isYUp = isYUp;
}
public void updatePoint() {//根据速度值更新下一次的位置信息
if (point.x <= left) {
isXUp = true;
} else if (point.x >= right) {
isXUp = false;
}
if (isXUp) {
point.x = (int) (point.x + speedX);
} else {
point.x = (int) (point.x - speedX);
}
// 判断y轴方向上的坐标
if (point.y <= top) {
isYUp = true;
} else if (point.y >= bottom) {
isYUp = false;
}
if (isYUp) {
point.y = (int) (point.y + speedY);
} else {
point.y = (int) (point.y - speedX);
}
}
}
该部分代码处理逻辑基本上跟上一篇博客内容相似,不做解释了,唯一不同这里对初始位置和速度信息做了一个随机生成
3.然后就可以使用我们做好的ViewGroup了
xml文件
<com.example.automoveviewgroup.AutoMoveViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
tools:context="com.example.automoveviewgroup.MainActivity"
tools:ignore="MergeRootFrame" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/darker_gray"
android:text="textview1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/darker_gray"
android:text="textview2" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ima1" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ima1" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ima2" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ima2" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ima3" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ima3" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ima4" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ima4" />
<ImageView
android:id="@+id/run"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/run" />
</com.example.automoveviewgroup.AutoMoveViewGroup>