HorizontalScrollView: auto-scroll to the end when adding new views?

I have a HorizontalScrollView containing a LinearLayout. On the screen, I have a button that will add new views to LinearLayout at runtime, and I would like the scroll to scroll to the end of the list when adding a new view.

Almost everything works for me - except that it always scrolls one view to the last view. It seems that it scrolls without a preliminary calculation of the inclusion of a new view.

In my application, I use a custom View object, but I made a small test application that uses ImageView and has the same symptom. I tried various things, such as requestLayout () in both Layout and ScrollView, I tried scrollTo (Integer.MAX_VALUE) and it scrolled in netherverse :) Am I upsetting the UI thread or something like that?

  • 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 format:

  <?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> 
+49
android user-interface horizontal-scrolling
Jan 18 2018-11-11T00:
source share
9 answers

I think there is a time problem. The layout does not execute when adding a view. It is requested and executed after some time. When you call fullScroll immediately after adding a view, the width of the linearlayout function does not have the ability to expand.

Try replacing:

 s.fullScroll(HorizontalScrollView.FOCUS_RIGHT); 

from:

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

A short delay should give the system enough time to calculate.

PS It is enough to simply postpone scrolling until the current iteration of the user interface loop. I have not tested this theory, but if it is correct, it would be enough to do the following:

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

I like it better because introducing arbitrary delay seems hacked to me (although that was my suggestion).

+125
Jan 18 '11 at 4:44
source share

I agree with @monxalo and @ granko87 that the best approach is to listen, not to assume that the layout will be complete if we allow some amount of time.

In the case, like me, you do not need to use your own subclass of HorizontalScrollView, you can just add OnLayoutChangeListener to make everything simple:

 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); } }); 
+15
Mar 03 '15 at 19:36
source share

Another suggestion, as this question really helped me :).

You can place the listener when the view has completed its layout phase, and right after fullScroll is executed, although you need to extend the class for this.

I did this only because I wanted to go to the section immediately after onCreate () to avoid flickering from the start point to the scroll point.

Something like:

 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(); } } 
+12
Mar 16 2018-11-11T00:
source share

Use the View smoothScrollTo Method

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

You need to run runnable so that you take the time to build to host the new View

+4
Feb 04 '13 at 11:22
source share

This is what I did.

 //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); } }); } 
+3
Feb 08 '15 at 15:01
source share

Use the code below to scroll horizontally ( Scroll Right }

 view.post(new Runnable() { @Override public void run() { ((HorizontalScrollView) Iview .findViewById(R.id.hr_scroll)) .fullScroll(HorizontalScrollView.FOCUS_RIGHT); } }); 
+2
Aug 24 '14 at 0:03
source share

I think the best approach is the one sent by monxalo because you cannot know how long it will take until the layout is complete. I tried and it works fine. The only difference is that I added only one line to my own horizontal scroll view:

 @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); } 
+1
May 15 '13 at 12:17
source share

The View child cannot be immediately added to the ViewGroup . Therefore, you must publish a Runnable that will run after other pending tasks are completed.

So, after adding View , use:

 horizontalScrollView.post(new Runnable() { @Override public void run() { horizontalScrollView.fullScroll(View.FOCUS_RIGHT); } }); 
0
Mar 06 '18 at 7:25
source share

This is my way for 3 ImageViews in 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
May 13 '19 at 20:01
source share



All Articles