史上难得的自定义效果——屏幕中随机出来十个头像不重叠

项目需求:要求在屏幕上显示十个头像,并且不重叠。

先给大家看一下效果:


史上难得的自定义效果——屏幕中随机出来十个头像不重叠


技术分析: 很明显这次头像的显示不是列表,所以不能用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;
}