Вы смотрели на исходный код класса Spinner? Я только что сделал. Вот что я нашел (API 27 Sources):
Прядильщик использует ListView
внутренне (первый LOL), поддержанный DropdownPopup
(частный класс):
private class DropdownPopup extends ListPopupWindow implements SpinnerPopup {
Прежде чем смотреть на это, посмотрите на ListPopupWindow
, потому что у него много информации о проблемах, с которыми приходится сталкиваться. Это большой класс, но среди этих вещей вы можете увидеть:
private int mDropDownHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
private int mDropDownWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
private int mDropDownHorizontalOffset;
private int mDropDownVerticalOffset;
Похоже, что DropDown - по умолчанию - оборачивает содержимое на основе базового класса, однако DropDownPopup, который управляет (и содержит адаптер со всеми элементами в счетчике), также имеет метод void computeContentWidth() {
.
Этот метод вызывается из метода show()
, поэтому перед показом всплывающего окна это вычисление происходит каждый раз.
Я думаю, что вот часть ответа, который вы ищете:
void computeContentWidth() {
final Drawable background = getBackground();
int hOffset = 0;
if (background != null) {
background.getPadding(mTempRect);
hOffset = isLayoutRtl() ? mTempRect.right : -mTempRect.left;
} else {
mTempRect.left = mTempRect.right = 0;
}
final int spinnerPaddingLeft = Spinner.this.getPaddingLeft();
final int spinnerPaddingRight = Spinner.this.getPaddingRight();
final int spinnerWidth = Spinner.this.getWidth();
if (mDropDownWidth == WRAP_CONTENT) {
int contentWidth = measureContentWidth(
(SpinnerAdapter) mAdapter, getBackground());
final int contentWidthLimit = mContext.getResources()
.getDisplayMetrics().widthPixels - mTempRect.left - mTempRect.right;
if (contentWidth > contentWidthLimit) {
contentWidth = contentWidthLimit;
}
setContentWidth(Math.max(
contentWidth, spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight));
} else if (mDropDownWidth == MATCH_PARENT) {
setContentWidth(spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight);
} else {
setContentWidth(mDropDownWidth);
}
if (isLayoutRtl()) {
hOffset += spinnerWidth - spinnerPaddingRight - getWidth();
} else {
hOffset += spinnerPaddingLeft;
}
setHorizontalOffset(hOffset);
}
Вы можете захотеть отладить и установить точки останова, чтобы посмотреть, что это за значения и что они значат.
Другая часть - это метод setContentWidth()
. Этот метод из ListPopupWindow
и выглядит так:
/**
* Sets the width of the popup window by the size of its content. The final width may be
* larger to accommodate styled window dressing.
*
* @param width Desired width of content in pixels.
*/
public void setContentWidth(int width) {
Drawable popupBackground = mPopup.getBackground();
if (popupBackground != null) {
popupBackground.getPadding(mTempRect);
mDropDownWidth = mTempRect.left + mTempRect.right + width;
} else {
setWidth(width);
}
}
И setWidth
(также в этом классе) все, что он делает:
/**
* Sets the width of the popup window in pixels. Can also be {@link #MATCH_PARENT}
* or {@link #WRAP_CONTENT}.
*
* @param width Width of the popup window.
*/
public void setWidth(int width) {
mDropDownWidth = width;
}
Этот mDropDownWidth
, кажется, используется повсеместно, но также заставил меня найти этот другой метод в ListPopupWindow ...
/**
* Sets the width of the popup window by the size of its content. The final width may be
* larger to accommodate styled window dressing.
*
* @param width Desired width of content in pixels.
*/
public void setContentWidth(int width) {
Drawable popupBackground = mPopup.getBackground();
if (popupBackground != null) {
popupBackground.getPadding(mTempRect);
mDropDownWidth = mTempRect.left + mTempRect.right + width;
} else {
setWidth(width);
}
}
Итак, у вас это есть, необходима дополнительная логика, включая "оформление витрин" (?)
Я согласен, что Spinner - это плохо спроектированный класс (точнее, с устаревшим дизайном) и, тем более, с именем (на этом вводе-выводе Google в 2019 году они фактически объяснили в одной из сессий, почему название «Spinner» подсказка: это из 1-го андроидного прототипа). Изучив весь этот код, потребуется несколько часов, чтобы выяснить, что вертушка пытается сделать и как она работает, но путешествие не будет приятным.
Удачи.
Я повторю свой совет использовать ConstraintLayout, с которым, как вы сказали, вы знакомы; по крайней мере, сбросить вес.
Рассматривая, как это работает (ListView !!!), вычисление веса гарантирует 2-й этап измерения / разметки, который не только чрезвычайно неэффективен и не нужен, но также может вызывать проблемы с внутренним адаптером данных, которым управляет эта DropDown, поэтому "список" отображается.
В конечном счете, другой класс также вовлечен, все это представлено в PopupView
. PopupViews - это то, что вы видите, например, когда открываете элемент меню, и их иногда очень сложно настроить, в зависимости от того, что вы хотите сделать.
Почему Google выбрал этот подход в то время, я не знаю, но это, безусловно, требует обновления, и Material Design пока не принес много пользы в этом отношении, так как он всегда будет неполным или в альфа-состоянии. год позади всего остального.