web-dev-qa-db-ja.com

GridViewの行が重なっている:行の高さを最も高いアイテムに合わせる方法

この前の人 のように、GridViewアイテム間に不要なオーバーラップがあります:

GridView items overlapping

右端の列を除くすべての列のテキストに注意してください。

前の質問と違うのは、私が一定の行の高さを望まないということです。画面のスペースを効率的に使用するために、行の高さをに変えて、各行の最も高いコンテンツに対応させたい。

GridViewのソース (信頼できるコピーではありませんが、kernel.orgはまだダウンしています)を見ると、fillDown()およびmakeRow()で、最後に表示されたビューが「参照ビュー」であることがわかります。 :行の高さは、最も高いものではなく、そのビューの高さから設定されます。 これは、右端の列が大丈夫な理由を説明します。残念ながら、GridViewは継承によってこれを修正するための適切な設定ではありません。関連するすべてのフィールドとメソッドはプライベートです。

だから、私は「クローンと所有」という使い古された肥大化した道を取る前に、ここに欠けているトリックがありますか? TableLayout、しかし、それは私がnumColumns="auto_fit"を自分で実装することを要求します(たとえば、電話画面に1つの長い列だけが必要なため)、また、それは本来あるべきように感じるAdapterViewでもありません。

Edit:実際、ここではclone and ownは実用的ではありません。 GridViewは、親クラスと兄弟クラスのアクセスできない部分に依存し、少なくとも6000行のコード(AbsListView、AdapterViewなど)をインポートします。

43
Chris Boyle

私は非常に多くの研究を行いましたが、不完全な答えを見つけた、またはソリューションで何が起こっているのかを理解するのが困難でしたが、最終的に適切な説明に完全に適合する答えを見つけました。

私の問題は、gridviewアイテムを適切に高さに合わせることでした。この Grid-viewすべてのビューが同じ高さのとき、うまく機能しました。ただし、ビューの高さが異なる場合、グリッドは期待どおりに動作しません。ビューは互いに重なり、an-aesthetically心地よいグリッド。

ここ Solution XMLレイアウトでこのクラスを使用しました。

私はこのソリューションを使用しましたが、これは非常にうまく機能しています。ありがとうございます。--Abhishek Mittal

1
opalfire

Chrisからの情報に基づいて、他のGridViewアイテムの高さを決定するときに、ネイティブのGridViewで使用される参照ビューを利用して、この回避策を使用しました。

このGridViewItemContainerカスタムクラスを作成しました。

/**
 * This class makes sure that all items in a GridView row are of the same height.
 * (Could extend FrameLayout, LinearLayout etc as well, RelativeLayout was just my choice here)
 * @author Anton Spaans
 *
*/
public class GridViewItemContainer extends RelativeLayout {
private View[] viewsInRow;

public GridViewItemContainer(Context context) {
    super(context);
}

public GridViewItemContainer(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

public GridViewItemContainer(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public void setViewsInRow(View[] viewsInRow) {
    if  (viewsInRow != null) {
        if (this.viewsInRow == null) {
            this.viewsInRow = Arrays.copyOf(viewsInRow, viewsInRow.length);
        }
        else {
            System.arraycopy(viewsInRow, 0, this.viewsInRow, 0, viewsInRow.length);
        }
    }
    else if (this.viewsInRow != null){
        Arrays.fill(this.viewsInRow, null);
    }
}

@Override
protected LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}

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

    if (viewsInRow == null) {
        return;
    }

    int measuredHeight = getMeasuredHeight();
    int maxHeight      = measuredHeight;
    for (View siblingInRow : viewsInRow) {
        if  (siblingInRow != null) {
            maxHeight = Math.max(maxHeight, siblingInRow.getMeasuredHeight());
        }
    }

    if (maxHeight == measuredHeight) {
        return;
    }

    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    switch(heightMode) {
    case MeasureSpec.AT_MOST:
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(maxHeight, heightSize), MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        break;

    case MeasureSpec.EXACTLY:
        // No debate here. Final measuring already took place. That's it.
        break;

    case MeasureSpec.UNSPECIFIED:
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        break;

    }
}

アダプターのgetViewメソッドで、convertViewを新しいGridViewItemContainerの子としてラップするか、これをアイテムレイアウトの最上位XML要素にします。

        // convertView has been just been inflated or came from getView parameter.
        if (!(convertView instanceof GridViewItemContainer)) {
            ViewGroup container = new GridViewItemContainer(inflater.getContext());

            // If you have tags, move them to the new top element. E.g.:
            container.setTag(convertView.getTag());
            convertView.setTag(null);

            container.addView(convertView);
            convertView = container;
        }
        ...
        ...
        viewsInRow[position % numColumns] = convertView;
        GridViewItemContainer referenceView = (GridViewItemContainer)convertView;
        if ((position % numColumns == (numColumns-1)) || (position == getCount()-1)) {
            referenceView.setViewsInRow(viewsInRow);
        }
        else {
            referenceView.setViewsInRow(null);
        }

ここで、numColumnsはGridViewの列数で、「viewsInRow」は「position」が存在する現在の行のビューのリストです。

1

GridViewまたはListViewを RecyclerView に変換すると、この問題は発生しません。また、カスタムGridViewクラスを作成する必要はありません。

0
Mr-IDE