// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.chrome.browser.ntp; import android.content.Context; import android.content.res.Resources; import android.util.AttributeSet; import android.view.View; import android.widget.FrameLayout; import org.chromium.base.ApiCompatibilityUtils; import org.chromium.chrome.R; import org.chromium.chrome.browser.util.MathUtils; /** * A layout that arranges most visited items in a grid. */ public class MostVisitedLayout extends FrameLayout { private static final int MAX_COLUMNS = 4; private int mVerticalSpacing; private int mExtraVerticalSpacing; private int mMinHorizontalSpacing; private int mMaxHorizontalSpacing; private int mMaxWidth; private int mMaxRows; /** * @param context The view context in which this item will be shown. * @param attrs The attributes of the XML tag that is inflating the view. */ public MostVisitedLayout(Context context, AttributeSet attrs) { super(context, attrs); Resources res = getResources(); mVerticalSpacing = res.getDimensionPixelOffset(R.dimen.most_visited_vertical_spacing); mMinHorizontalSpacing = res.getDimensionPixelOffset( R.dimen.most_visited_min_horizontal_spacing); mMaxHorizontalSpacing = res.getDimensionPixelOffset( R.dimen.most_visited_max_horizontal_spacing); mMaxWidth = res.getDimensionPixelOffset(R.dimen.most_visited_layout_max_width); } /** * Sets the maximum number of rows to display. Any items that don't fit within these rows will * be hidden. */ public void setMaxRows(int rows) { mMaxRows = rows; } /** * Sets the extra vertical spacing that must be used. It will be distributed evenly above each * row. */ public void setExtraVerticalSpacing(int spacing) { if (mExtraVerticalSpacing == spacing) { return; } mExtraVerticalSpacing = spacing; // Clear the measure cache for this view and make sure it will be remeasured. forceLayout(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int totalWidth = resolveSize(mMaxWidth, widthMeasureSpec); int childCount = getChildCount(); if (childCount == 0) { setMeasuredDimension(totalWidth, resolveSize(0, heightMeasureSpec)); return; } // Measure the children. for (int i = 0; i < childCount; i++) { measureChild(getChildAt(i), MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); } // Determine the number of columns that will fit. int gridWidth = totalWidth - ApiCompatibilityUtils.getPaddingStart(this) - ApiCompatibilityUtils.getPaddingEnd(this); int childHeight = getChildAt(0).getMeasuredHeight(); int childWidth = getChildAt(0).getMeasuredWidth(); int numColumns = MathUtils.clamp( (gridWidth + mMinHorizontalSpacing) / (childWidth + mMinHorizontalSpacing), 1, MAX_COLUMNS); // Ensure column spacing isn't greater than mMaxHorizontalSpacing. int gridWidthMinusColumns = Math.max(0, gridWidth - numColumns * childWidth); int gridSidePadding = gridWidthMinusColumns - mMaxHorizontalSpacing * (numColumns - 1); int gridStart = 0; float horizontalSpacing; if (gridSidePadding > 0) { horizontalSpacing = mMaxHorizontalSpacing; gridStart = gridSidePadding / 2; } else { horizontalSpacing = (float) gridWidthMinusColumns / Math.max(1, numColumns - 1); } // Limit the number of rows to mMaxRows. int visibleChildCount = Math.min(childCount, mMaxRows * numColumns); // Arrange the visible children in a grid. int numRows = (visibleChildCount + numColumns - 1) / numColumns; int paddingTop = getPaddingTop(); boolean isRtl = ApiCompatibilityUtils.isLayoutRtl(this); for (int i = 0; i < visibleChildCount; i++) { View child = getChildAt(i); child.setVisibility(View.VISIBLE); int row = i / numColumns; int column = i % numColumns; int verticalOffset = Math.round(mExtraVerticalSpacing * ((float) (row + 1) / numRows)); int childTop = row * (childHeight + mVerticalSpacing) + verticalOffset; int childStart = gridStart + Math.round(column * (childWidth + horizontalSpacing)); MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams(); layoutParams.setMargins(isRtl ? 0 : childStart, childTop, isRtl ? childStart : 0, 0); child.setLayoutParams(layoutParams); } // Hide any extra children in case there are more than needed for the maximum number of // rows. for (int i = visibleChildCount; i < childCount; i++) { getChildAt(i).setVisibility(View.GONE); } int totalHeight = paddingTop + getPaddingBottom() + numRows * childHeight + (numRows - 1) * mVerticalSpacing + mExtraVerticalSpacing; setMeasuredDimension(totalWidth, resolveSize(totalHeight, heightMeasureSpec)); } }