史上难得的自定义效果——屏幕中随机出来十个头像不重叠
项目需求:要求在屏幕上显示十个头像,并且不重叠。
先给大家看一下效果:
技术分析: 很明显这次头像的显示不是列表,所以不能用RecyclerView这样的控件实现了。所以只能自己动手来打造一个控件了——自定义Layout
思路 : 1、创建MyViewLayout 继承 LinearLayout
2、重写onMeasure方法 —— 获取控件的宽和高
3、重写onLayout方法 (重点) —— 用来摆放子View的位置(头像的位置)
4、摆放子View的方法: —— X、Y随机一点当做子View的左上顶点,然后通过得到子View的宽和高去找到子View的右下点(通过子View.layout(左上右下) 方法设置子View的位置)
核心算法 : 计算摆放任意头像不重叠的思路
1、新建一个集合List<Point>,用来存储不重叠的坐标。(摆放第一个View把坐标放到list中,摆放第二个要跟集合里面比较有没有重叠,有重新找坐标比较,没有继续
把第二个放到list中,然后摆放第三个比较,一次类推)。
2、重叠的条件 : (Math.abs(x2 - x1) <= measuredWidth && Math.abs(y2 - y1) <= measuredHeight) 表示重叠下面就是代码:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); /** * 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式 */ sizeWidth = MeasureSpec.getSize(widthMeasureSpec); sizeHeight = MeasureSpec.getSize(heightMeasureSpec); // 计算出所有的childView的宽和高 measureChildren(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //得到子View总的个数 int childCount = getChildCount(); //循坏设置每一个子View的位置 for (int i = 0; i < childCount; i++) { childAt = getChildAt(i); measuredWidth = childAt.getMeasuredWidth(); measuredHeight = childAt.getMeasuredHeight(); //取 X Y坐标 setXY(i); } }
private void setXY(int i) { //随机取两个数 作为每一个子View的左上角坐标 最大为父控件的X Y 最小为0 //random.nextInt(max) % (max - min + 1) + min; Random random = new Random(); int x = random.nextInt(sizeWidth - measuredWidth) % ((sizeWidth - measuredWidth) - 1 + 1) + 1; int y = random.nextInt(sizeHeight - measuredHeight) % ((sizeHeight - measuredHeight) - 1 + 1) + 1; point1 = new Point(x, y); if (i > 0) { // >0的数据 while (twototwo(point1)) { //判断有没满足重叠的部分,如果有就返回true 重新创建点 去比较 int x2 = random.nextInt(sizeWidth - measuredWidth) % ((sizeWidth - measuredWidth) - 1 + 1) + 1; int y2 = random.nextInt(sizeHeight - measuredHeight) % ((sizeHeight - measuredHeight) - 1 + 1) + 1; point1 = new Point(x2, y2); } } //不确定原因 会走两次onLayout方法,也就是会加载这里两次,所以加一个判断 如果存放正确的点数集合跟所有的view个数相等就返回回去, //说明肯定第一次已经放好了 if (list.size() == getChildCount()) { return; } //把没有重合的点放到集合数据中去 list.add(point1); //摆放每一个view的位置 childAt.layout(point1.x, point1.y, point1.x + measuredWidth, point1.y + measuredHeight); }
private boolean twototwo(Point point) { for (int i = 0; i < list.size(); i++) { Point poition = list.get(i); int x1 = poition.x; int y1 = poition.y; //没有重合的部分 int x2 = point.x; //新的 int y2 = point.y; if (Math.abs(x2 - x1) <= measuredWidth && Math.abs(y2 - y1) <= measuredHeight) {//唯一一个重叠的条件 return true; } } return false; }
如果想做一些操作:比如选中哪一个(不用担心已经写好了。下面代码)
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //按下 int rawY = (int) event.getY(); int rawX = (int) event.getX(); //比较按下的这是不是包含在每一个控件之内 int compare = compare(rawY, rawX); //如果大于0 说明点击到了每一个图片 if (compare >= 0) { callback.click(compare); } break; case MotionEvent.ACTION_MOVE: //移动 break; case MotionEvent.ACTION_UP://松手 break; } return super.onTouchEvent(event); }
/** * @param rawY 按下的Y坐标 * @param rawX 按下的X坐标 * @return */ public int compare(int rawY, int rawX) { for (int i = 0; i < list.size(); i++) { //list为存放点的集合 Point point = list.get(i); int y = point.y; int x = point.x; //判断条件 if (y <= rawY && rawY <= y + measuredHeight && x <= rawX && rawX <= x + measuredWidth) { return i; } } return -1; }