HorizontalScrollView: автоматическая прокрутка до конца при добавлении новых видов? - PullRequest
49 голосов
/ 18 января 2011

У меня есть HorizontalScrollView, содержащий LinearLayout. На экране у меня есть кнопка, которая добавляет новые представления к LinearLayout во время выполнения, и я хотел бы, чтобы представление прокрутки прокручивалось до конца списка при добавлении нового представления.

У меня почти все работает - за исключением того, что он всегда прокручивает один вид за исключением последнего. Похоже, что это прокрутка без предварительного расчета включения нового представления.

В моем приложении я использую пользовательский объект View, но я создал небольшое тестовое приложение, которое использует ImageView и имеет тот же симптом. Я пробовал разные вещи, такие как requestLayout () как в Layout, так и в ScrollView, я пробовал scrollTo (Integer.MAX_VALUE), и он прокручивался в нижний угол :).

  • Rick

======

    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>

Ответы [ 9 ]

126 голосов
/ 18 января 2011

Я думаю, что есть проблема времени.Макет не выполняется при добавлении представления.Это запрошено и сделано через некоторое время.Когда вы вызываете fullScroll сразу после добавления представления, ширина линейного макета не имеет возможности увеличить.

Попробуйте заменить:

s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);

на:

s.postDelayed(new Runnable() {
    public void run() {
        s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
    }
}, 100L);

Короткая задержка должна дать системе достаточно времени для установки.

PS Может быть достаточно просто отложить прокрутку до окончания текущей итерации цикла пользовательского интерфейса.Я не проверял эту теорию, но если она верна, было бы достаточно сделать следующее:

s.post(new Runnable() {
    public void run() {
        s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
    }
});

Мне это нравится больше, потому что введение произвольной задержки кажется мне хакерским (хотя это было мое предложение)).

15 голосов
/ 03 марта 2015

Я согласен с @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);
    }
});
12 голосов
/ 16 марта 2011

Просто еще одно предположение, так как этот вопрос мне очень помог:).

Вы можете поместить слушателя, когда представление завершит фазу макета, и сразу после выполнения 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 голосов
/ 04 февраля 2013

Используйте метод View smoothScrollTo

postDelayed(new Runnable() {
    public void run() {
       scrollView.smoothScrollTo(newView.getLeft(), newView.getTop());
 }
 }, 100L);

. Вам нужно вставить исполняемый файл, чтобы дать время процессу макета для позиционирования нового View

.
3 голосов
/ 08 февраля 2015

Это то, что я сделал.

//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);
            }
     });
   }
2 голосов
/ 24 августа 2014

Используйте код ниже для горизонтальной прокрутки { Прокрутка вправо }

view.post(new Runnable() {
                @Override
                public void run() {
                    ((HorizontalScrollView) Iview
                            .findViewById(R.id.hr_scroll))
                            .fullScroll(HorizontalScrollView.FOCUS_RIGHT);
                }
            });
1 голос
/ 15 мая 2013

Я думаю, что лучший подход - тот, который опубликовал 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);
}
0 голосов
/ 13 мая 2019

Это мой способ для 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)
0 голосов
/ 06 марта 2018

Ребенок View не может быть немедленно добавлен в ViewGroup. Поэтому необходимо опубликовать Runnable, который будет запущен после выполнения других ожидающих задач.

Итак, после добавления View используйте:

horizontalScrollView.post(new Runnable() {
    @Override
    public void run() {
        horizontalScrollView.fullScroll(View.FOCUS_RIGHT);
    }
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...