HorizontalScrollView: авто-прокрутка до конца при добавлении новых просмотров?
У меня есть HorizontalScrollView, содержащий LinearLayout. На экране у меня есть кнопка, которая добавит новые представления в LinearLayout во время выполнения, и я бы хотел, чтобы прокрутка просматривала прокрутку до конца списка при добавлении нового представления.
У меня почти все работает - за исключением того, что он всегда прокручивает один вид до последнего представления. Кажется, что он прокручивается без предварительного расчета включения нового представления.
В моем приложении я использую пользовательский объект View, но я сделал небольшое тестовое приложение, которое использует ImageView и имеет тот же самый симптом. Я пробовал различные вещи, такие как requestLayout() как в Layout, так и в ScrollView, я пробовал scrollTo (Integer.MAX_VALUE), и он прокручивался в netherverse:) Я нарушаю проблему с потоком пользовательского интерфейса или что-то в этом роде?
======
public class Main extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button b = (Button) findViewById(R.id.addButton);
b.setOnClickListener(new AddListener());
add();
}
private void add() {
LinearLayout l = (LinearLayout) findViewById(R.id.list);
HorizontalScrollView s =
(HorizontalScrollView) findViewById(R.id.scroller);
ImageView i = new ImageView(getApplicationContext());
i.setImageResource(android.R.drawable.btn_star_big_on);
l.addView(i);
s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}
private class AddListener implements View.OnClickListener {
@Override
public void onClick(View v) {
add();
}
}
}
Формат XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<HorizontalScrollView
android:id="@+id/scroller"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbarSize="50px">
<LinearLayout
android:id="@+id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4px"/>
</HorizontalScrollView>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center">
<Button
android:id="@+id/addButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingLeft="80px"
android:paddingRight="80px"
android:paddingTop="40px"
android:paddingBottom="40px"
android:text="Add"/>
</LinearLayout>
</LinearLayout>
Ответы
Ответ 1
Я думаю, что есть проблема времени. Макет не выполняется при добавлении представления. Он запрашивается и выполняется через некоторое время. Когда вы вызываете fullScroll сразу после добавления представления, ширина функции linearlayout не имеет возможности расширяться.
Попробуйте заменить:
s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
с:
s.postDelayed(new Runnable() {
public void run() {
s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}
}, 100L);
Кратковременная задержка должна дать системе достаточно времени для расчета.
P.S. Достаточно просто отложить прокрутку до текущей итерации петли пользовательского интерфейса. Я не тестировал эту теорию, но если это правильно, достаточно было бы сделать следующее:
s.post(new Runnable() {
public void run() {
s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}
});
Мне нравится это лучше, потому что введение произвольной задержки кажется мне взломанным (хотя это было мое предложение).
Ответ 2
Я согласен с @monxalo и @granko87 в том, что лучший подход заключается в прослушивании, а не в предположении, что макет будет завершен, если мы допустим некоторое количество времени.
В случае, как и мне, вам не нужно использовать собственный подкласс HorizontalScrollView, вы можете просто добавить OnLayoutChangeListener, чтобы все было просто:
mTagsScroller.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
mTagsScroller.removeOnLayoutChangeListener(this);
mTagsScroller.fullScroll(View.FOCUS_RIGHT);
}
});
Ответ 3
Еще одно предложение, так как этот вопрос мне очень помог:).
Вы можете поместить слушателя, когда представление закончило свою фазу макета, и сразу после выполнения fullScroll, хотя вам нужно расширить класс для этого.
Я сделал это только потому, что мне захотелось перейти к разделу сразу после onCreate(), чтобы избежать мерцания от начальной точки до точки прокрутки.
Что-то вроде:
public class PagerView extends HorizontalScrollView {
private OnLayoutListener mListener;
///...
private interface OnLayoutListener {
void onLayout();
}
public void fullScrollOnLayout(final int direction) {
mListener = new OnLayoutListener() {
@Override
public void onLayout() {
fullScroll(direction)
mListener = null;
}
};
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if(mListener != null)
mListener.onLayout();
}
}
Ответ 4
Используйте метод View smoothScrollTo
postDelayed(new Runnable() {
public void run() {
scrollView.smoothScrollTo(newView.getLeft(), newView.getTop());
}
}, 100L);
Вам нужно запустить runnable, чтобы вы выделили время для компоновки, чтобы разместить новый View
Ответ 5
Это то, что я сделал.
//Observe for a layout change
ViewTreeObserver viewTreeObserver = yourLayout.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
yourHorizontalScrollView.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}
});
}
Ответ 6
Используйте приведенный ниже код для горизонтального прокрутки ( Прокрутить вправо}
view.post(new Runnable() {
@Override
public void run() {
((HorizontalScrollView) Iview
.findViewById(R.id.hr_scroll))
.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}
});
Ответ 7
Я думаю, что лучший подход - это тот, который был отправлен monxalo, потому что вы не можете знать, сколько времени потребуется, пока макет не будет выполнен. Я попробовал, и он работает отлично. Единственное различие заключается в том, что я добавил только одну строку в свой собственный горизонтальный вид прокрутки:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
this.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}
Ответ 8
Ребенок View
не может быть немедленно добавлено в ViewGroup
. Поэтому необходимо опубликовать Runnable
который будет запускаться после выполнения других ожидающих задач.
Итак, после добавления View
, используйте:
horizontalScrollView.post(new Runnable() {
@Override
public void run() {
horizontalScrollView.fullScroll(View.FOCUS_RIGHT);
}
});
Ответ 9
Это мой способ для 3 ImageViews в kotlin:
var p1 = true; var p2 = false; var p3 = false
val x = -55
val handler = Handler()
handler.postDelayed(object : Runnable {
override fun run() {
if (p1){
mainView.picsScrollViewID.smoothScrollTo(mainView.bigPic1ID.x.toInt() + x, 0)
p1 = false
p2 = true
p3 = false
}
else if(p2){
mainView.picsScrollViewID.smoothScrollTo(mainView.bigPic2ID.x.toInt() + x, 0)
p1 = false
p2 = false
p3 = true
}
else if(p3){
mainView.picsScrollViewID.smoothScrollTo(mainView.bigPic3ID.x.toInt() + x, 0)
p1 = true
p2 = false
p3 = false
}
handler.postDelayed(this, 2000)
}
}, 1400)