Пользовательская ViewGroup, последний дочерний элемент в строке Совпадение с родителем, чтобы заполнить все пустое пространство - PullRequest
0 голосов
/ 24 апреля 2018

Я пытаюсь создать пользовательский ViewGroup base на DavidPizarro / AutoLabelUI https://github.com/DavidPizarro/AutoLabelUI

Моя цель - создать страницу, которая выглядит следующим образом: enter image description here

но это самое близкое, что я получил:

enter image description here

Это важная часть моего кода:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int sizeWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
    int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);

    int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
    int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

    totalWidth = sizeWidth;

    int width = 0;
    int height = getPaddingTop() + getPaddingBottom();

    int lineWidth = 0;
    int lineHeight = 0;

    int childCount = getChildCount();


    for (int i = 0; i < childCount; i++) {

        View child = getChildAt(i);

// child.setMinimumWidth (1080);boolean lastChild = i == childCount - 1;

        if (child.getVisibility() == View.GONE) {

            if (lastChild) {
                width = Math.max(width, lineWidth);
                height += lineHeight;
            }

            continue;
        }


        Logger.Log("");

        //child.setMinimumWidth(300);
        measureChildWithMargins(child, widthMeasureSpec, lineWidth, heightMeasureSpec, height);

        LayoutParams lp = (LayoutParams) child.getLayoutParams();

        int childWidthMode = MeasureSpec.AT_MOST;
        int childWidthSize = sizeWidth;

        int childHeightMode = MeasureSpec.AT_MOST;
        int childHeightSize = sizeHeight;

        if (lp.width == LayoutParams.MATCH_PARENT) {
            childWidthMode = MeasureSpec.EXACTLY;
            childWidthSize -= lp.leftMargin + lp.rightMargin;
        } else if (lp.width >= 0) {
            childWidthMode = MeasureSpec.EXACTLY;
            childWidthSize = lp.width;
        }

        if (lp.height >= 0) {
            childHeightMode = MeasureSpec.EXACTLY;
            childHeightSize = lp.height;
        } else if (modeHeight == MeasureSpec.UNSPECIFIED) {
            childHeightMode = MeasureSpec.UNSPECIFIED;
            childHeightSize = 0;
        }

        Logger.Log("");

        child.measure(
                MeasureSpec.makeMeasureSpec(childWidthSize, childWidthMode),
                MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode)
        );

        int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;

        if (lineWidth + childWidth > sizeWidth) {
            // NEW LINE!!! this code means that the width of the child is bןgger then the space left!
            width = Math.max(width, lineWidth);
            lineWidth = childWidth;

            height += lineHeight;
            lineHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;

        } else {
            lineWidth += childWidth;
            lineHeight = Math.max(lineHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
        }

        if (lastChild) {
            width = Math.max(width, lineWidth);
            height += lineHeight;
        }

    }
    // end of loop.

    width += getPaddingLeft() + getPaddingRight();

    setMeasuredDimension(
            (modeWidth == MeasureSpec.EXACTLY) ? sizeWidth : width,
            (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight : height);
}

/**
 * This method will place each label next to another. If there is not enough
 * space for the next label, it will be added in a new new line.
 * <p>
 */
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {

    mLines.clear();
    mLineHeights.clear();
    mLineMargins.clear();

    int width = getWidth();

    int lineWidth = 0;
    int lineHeight = 0;
    lineViews.clear();

    float horizontalGravityFactor = 0;

    for (int i = 0; i < getChildCount(); i++) {


        View child = getChildAt(i);

        if (child.getVisibility() == View.GONE) {
            continue;
        }

        LayoutParams lp = (LayoutParams) child.getLayoutParams();

        int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
        int childHeight = child.getMeasuredHeight() + lp.bottomMargin + lp.topMargin;

        if (lineWidth + childWidth > width) {
            mLineHeights.add(lineHeight);
            mLines.add(lineViews);
            mLineMargins.add((int) ((width - lineWidth) * horizontalGravityFactor) + getPaddingLeft());

            lineHeight = 0;
            lineWidth = 0;
            lineViews = new ArrayList<>();
        }

        lineWidth += childWidth;
        lineHeight = Math.max(lineHeight, childHeight);
        lineViews.add(child);
    }

    // END OF LOOP.

    mLineHeights.add(lineHeight);
    mLines.add(lineViews);
    mLineMargins.add((int) ((width - lineWidth) * horizontalGravityFactor) + getPaddingLeft());

    int verticalGravityMargin = 0;

    int numLines = mLines.size();

    int left;
    int top = getPaddingTop();

    // now we are looping threw the number of row in total - mLines.size(), and we want to measure each tag!
    for (int i = 0; i < numLines; i++) {

        lineHeight = mLineHeights.get(i);
        // lineViews = number of lines for child!
        lineViews = mLines.get(i);
        left = mLineMargins.get(i);

        int children = lineViews.size();

        for (int j = 0; j < children; j++) {

            View child = lineViews.get(j);

            if (child.getVisibility() == View.GONE) {
                continue;
            }

            LayoutParams lp = (LayoutParams) child.getLayoutParams();

            // if height is match_parent we need to remeasure child to line height
            if (lp.height == LayoutParams.MATCH_PARENT) {
                int childWidthMode = MeasureSpec.AT_MOST;
                int childWidthSize = lineWidth;

                if (lp.width == LayoutParams.MATCH_PARENT) {
                    childWidthMode = MeasureSpec.EXACTLY;
                } else if (lp.width >= 0) {
                    childWidthMode = MeasureSpec.EXACTLY;
                    childWidthSize = lp.width;
                }

                child.measure(
                        MeasureSpec.makeMeasureSpec(childWidthSize, childWidthMode),
                        MeasureSpec.makeMeasureSpec(lineHeight - lp.topMargin - lp.bottomMargin,
                                MeasureSpec.EXACTLY)
                );
            }

            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            int gravityMargin = 0;

            if (Gravity.isVertical(lp.gravity)) {
                switch (lp.gravity) {
                    case Gravity.TOP:
                    default:
                        break;
                    case Gravity.CENTER_VERTICAL:
                    case Gravity.CENTER:
                        gravityMargin = (lineHeight - childHeight - lp.topMargin - lp.bottomMargin) / 2;
                        break;
                    case Gravity.BOTTOM:
                        gravityMargin = lineHeight - childHeight - lp.topMargin - lp.bottomMargin;
                        break;
                }
            }

            child.layout(left + lp.leftMargin,
                    top + lp.topMargin + gravityMargin + verticalGravityMargin,
                    left + childWidth + lp.leftMargin,
                    top + childHeight + lp.topMargin + gravityMargin + verticalGravityMargin);



            left += childWidth + lp.leftMargin + lp.rightMargin;

        }
        // end of loop

        top += lineHeight;
    }
    // end of loop

}

Буду признателен, если вы покажете мне, что мне нужно сделать, чтобы изменить размер дочернего элемента, чтобы он растягивался до заполнения пустого поля.пространство.

1 Ответ

0 голосов
/ 24 апреля 2018

Честно говоря, я не знаю, что делал весь этот код, который вы опубликовали выше, слишком долго, слишком мало смысла, даже не знаю, как он работал.

Из любопытства, я собираюсьПопробуйте решить эту проблему прямо здесь, я надеюсь, что это работает.

Сначала метод onMeasure():

// maximum available width for children
int maxW = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();

// you want all children to be as wide as they want, but not wider than the view group
int widthMS = MeasureSpec.makeMeasureSpec(w, MeasureSpec.AT_MOST);
// you probably want them all to have the same height.
// maybe define it in xml attrs?
int heightMS = MeasureSpec.makeMeasureSpec(rowHeight, MeasureSpec.EXACTLY);

// remaining width of current row, initially equals to w
int remW = w;
// index of first child in the current row
int rowFirstChild = 0;
// row count, used to calculate the view group's height
int rows = 0;

int i = 0;
while (i < getChildCount()) {
    Child ch = getChildAt(i);
    if (ch.getVisibility() != View.GONE) {
        // ask how big does it want to be
        child.measure(widthMS, heightMS);

        int chWidth = ch.getMeasuredWidth();

        // if it fits
        if (remW - chWidth >= 0) {
            // update remW, then go to next child
            remW -= chWidth;
            i++;
        } 
        // if it does not fit
        else {
            // if it's not the first child in the row
            if (i > rowFirstChild) {
                stretchViews(rowFirstChild, i - 1, maxW, remW, heightMS);

                // this will be the first child in the next row and take the space from
                // that row's remW
                rowFirstChild = i;
                remW = maxW - chWidth;
            }
            // if it's the first and only child on this row
            else {
                // it should just take the full width and give first spot in the next
                // row to the next child, and reset remW
                rowFirstChild = i + 1;
                remW = maxW;
            }
            // both of these scenarios result in the addition of a new row
            rows++;
        }
    }
}

// finally, we want to take as much width as we can, and as minimum height as we can
int finalW = MeasureSpec.getSize(widthMeasureSpec);
int finalH = Math.min(
    rows * rowHeight + getPaddingTop() + getPaddingBottom(),
    MeasureSpec.getSize(heightMeasureSpec)
);
setMeasuredDimensions(finalW, finalH);

// note that this measurement ignores height measure spec and automatically takes the
// minimum height, and ignores the height measure spec of its children as well and
// just gives them height from its rowHeight attr.
// you are free to change this setting.

Теперь, onLayout() метод.Здесь, поскольку мы уже присвоили измерения нашим детям, они все настроены, мы просто должны их выложить, а не много работы:

// maximum usable width in any row
int maxW = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();

// left starting point
int left = getPaddingLeft();
// top starting point
int top = getPaddingTop();

int i = 0;
while (i < getChildCount()) {
    Child ch = getChildAt(i);
    if (ch.getVisibility() != GONE) {
        int chWidth = ch.getMeasuredWidth();
        ch.layout(
            left,
            top,
            left + chWidth,
            top + rowHeight
        );

        // if this child is the last in row (-1 for error tolerance when rounding ints)
        if (left + chWidth >= maxW - 1) {
            // reset'left' back to its starting point
            left = getPaddingLeft();
            // move 'top' down by 'rowHeight'
            top += rowHeight;
        }
        // if it wasn't the last child in the row
        else {
            // then simply advance the 'left' position
            left += chWidth;
        }
    }
}

// Note that this method only supports LTR layout.
// You can make minor changes to add support for RTL.
// Also, margins between children are ignored, spend some time on that if you want

Дайте мне знать, если это работает

Обновление:

Неважно, я все равно просто сделаю метод растяжения:

/**
 * Stretches children of this view group horizontally, and proportionally to their widths
 * to span the entire row width
 *
 * @param firstIndex index of first child to stretch (inclusive)
 * @param lastIndex  index of last child to stretch (inclusive)
 * @param fullWidth  the width to cover (max available width for row children)
 * @param remWidth   remaining width to cover
 * @param heightMS   the hight measure spec to reuse on these views
 */
private void stretchViews(int firstIndex, int lastIndex, int fullWdith, int 
    remWidth, int heightMS) {
    for (int i = firstIndex; i <= lastIndex; i++) {
        Child ch = getChildAt(i);
        if (ch.getVisibility() != GONE) {
            int measuredW = ch.getMeasuredWidth();
            int widthMS = MeasureSpec.makeMeasureSpec(
                measuredW + remWidth * measuredW / fullWidth, MeasureSpec.EXACTLY);
            ch.measure(widthMS, heightMS); 
        }
    }
}
...