OnGlobalLayoutListener в ListView
Я хочу иметь возможность расширять строки в моем ListView с помощью анимации. Поэтому мне нужно знать высоту расширяемого представления. Я использую это в методе getView()
моего ArrayAdapter
:
mDetailsView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mDetailsView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
onClickListener.setHeight(mDetailsView.getHeight());
mDetailsView.setVisibility(View.GONE);
}
});
Это отлично работает для строк, которые отображаются на экране при запуске, но когда я прокручиваю вниз, метод onGlobalLayout
не вызывается для строк, которые сначала не были видны.
Как получить высоту для этих строк?
Ответы
Ответ 1
Возможно, что реализации ListView
не выполняют весь макет, поэтому ViewTreeObserver
никогда не видит макет в процессе.
Если у меня нет определенного телефона, я не знаю, вы можете использовать метод post
в представлениях для выполнения runnable, когда они находятся в поле зрения.
mDetailsView.post(new Runnable() {
@Override
public void run() {
onClickListener.setHeight(mDetailsView.getHeight());
mDetailsView.setVisibility(View.GONE);
}
});
EDIT:
Я не знаю, как выкладывается весь метод getView()
. Проблема, если я должен был догадаться, это ListView
просто не запрашивает макет. Вместо этого он выполняет самую работу для каждого вида, чтобы ускорить процесс. Вы можете попробовать следующее:
public void getView(int position, View convertView, ViewGroup parent) {
/*
* set your view up
*/
mDetailsView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mDetailsView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
onClickListener.setHeight(mDetailsView.getHeight());
mDetailsView.setVisibility(View.GONE);
}
});
notifyDataSetInvalidated(); // Notify the ListView() and any other listeners that your views are invalid.
return view;
}
Забастовкa >
Новое редактирование: использование notifyDataSetInvalidated()
обычно является плохой идеей и особенно если используется в getView()
.
Чтобы предварительно измерить макет, вы должны взять его параметры макета или дать ему новые, если они не существуют.
LayoutParams params = newView.getLayoutParams();
if (params == null) {
params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
final int widthSpec = MeasureSpec.makeMeasureSpec(parent.getWidth(), MeasureSpec.UNSPECIFIED);
final int heightSpec = MeasureSpec.makeMeasureSpec(parent.getHeight(), MeasureSpec.UNSPECIFIED);
newView.measure(widthSpec, heightSpec);
Ответ 2
В ListView или RecyclerView вместо использования OnGlobalLayoutListener
я всегда использую OnPreDrawListener
. Этот обратный вызов запускается также для невидимых строк при запуске. Из официальной документации:
В этот момент все представления в дереве были измерены и заданы рамкой.
Ответ 3
Хорошо, похоже, что у меня была аналогичная проблема.
По какой-то причине, если ListView потерял convertView в середине, то при раздувании представления заново он не вызывал его Global Layout Listener, если он был установлен.
В этом обходном пути я придумал:
public class MyViewThatIsInList extends View {
public MyViewThatIsInList (Context context) {
super(context);
ensureInited();
}
public MyViewThatIsInList (Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyViewThatIsInList (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyViewThatIsInList , 0, 0);
//...
ensureInited();
}
boolean isViewInited = false;
private void init() {
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//my custom helper method
Utils.safeRemoveLayoutListener(getViewTreeObserver(), this);
initLogic();
}
});
}
private void initLogic() {
//...
isViewInited = true;
}
private void ensureInited() {
init();
postDelayed(new Runnable() {
@Override
public void run() {
if (!isViewInited) {
initLogic();
}
}
}, 500);
}
}
Ответ 4
В ListView или RecyclerView вместо использования OnGlobalLayoutListener мы всегда используем OnPreDrawListener. Этот обратный вызов запускается также для невидимых строк при запуске. Из официальной документации:
private void makeTextViewResizable (окончательный текст TextView, окончательный int maxLine, final String expandText, окончательный логический видMore) {
try {
if (tv.getTag() == null) {
tv.setTag(tv.getText());
}
//OnGlobalLayoutListener
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
ViewTreeObserver obs = tv.getViewTreeObserver();
// obs.removeGlobalOnLayoutListener((ViewTreeObserver.OnGlobalLayoutListener) mActivity);
obs.removeOnPreDrawListener(this);
if (maxLine == 0) {
int lineEndIndex = tv.getLayout().getLineEnd(0);
String text = tv.getText().subSequence(0, lineEndIndex - expandText.length() + 1) + " " + expandText;
tv.setText(text);
tv.setMovementMethod(LinkMovementMethod.getInstance());
tv.setText(
addClickablePartTextViewResizable(Html.fromHtml(tv.getText().toString()), tv, expandText,
viewMore), TextView.BufferType.SPANNABLE);
} else if (maxLine > 0 && tv.getLineCount() >= maxLine) {
int lineEndIndex = tv.getLayout().getLineEnd(maxLine - 1);
String text = tv.getText().subSequence(0, lineEndIndex - expandText.length() + 1) + " " + expandText;
tv.setText(text);
tv.setMovementMethod(LinkMovementMethod.getInstance());
tv.setText(
addClickablePartTextViewResizable(Html.fromHtml(tv.getText().toString()), tv, expandText,
viewMore), TextView.BufferType.SPANNABLE);
} else {
int lineEndIndex = tv.getLayout().getLineEnd(tv.getLayout().getLineCount() - 1);
String text = tv.getText().subSequence(0, lineEndIndex) + " " + expandText;
tv.setText(text);
tv.setMovementMethod(LinkMovementMethod.getInstance());
tv.setText(
addClickablePartTextViewResizable(Html.fromHtml(tv.getText().toString()), tv, expandText,
viewMore), TextView.BufferType.SPANNABLE);
}
return true;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}