Mes Mes - 12 days ago 7
Android Question

scrollTo() for horizontalScrollView not working

I inflate another layout to appear below some view in my current layout.

This is done like this:

LayoutInflater vi = (LayoutInflater) getActivity().getApplicationContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rootView = vi.inflate(R.layout.horizontal_scroll_view, null);

horizontalScrollView = (HorizontalScrollView) rootView.findViewById(R.id.hsv_suggestions_scroll_view);
LinearLayout suggestionsContainer = (LinearLayout) horizontalScrollView.findViewById(R.id.ll_suggestions_container);


and I can confirm that it appears in the right place since I add some Views in it after a while and they all appear.

The layout I inflate is :

<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/hsv_suggestions_scroll_view"
android:scrollbars="none" android:layout_gravity="center_horizontal"
android:paddingTop="16dp" android:fillViewport="true"
android:layout_width="match_parent" android:layout_height="wrap_content">

<LinearLayout
android:id="@+id/ll_suggestions_container"
android:gravity="center_horizontal" android:orientation="horizontal"
android:layout_width="wrap_content" android:layout_height="wrap_content">

</LinearLayout>




just a
HorizontalScrollView
with a
LinearLayout
as a child.
The Views I add later are all of them
TextView
s.

Now after a user action (write some text on an editText) I'm trying to scroll to that View and highlight it. Highlight works. What does not work is scroll.

I have tried :

horizontalScrollView.postDelayed(new Runnable() {
@Override
public void run() {

horizontalScrollView.smoothScrollTo(scrollTo, 0);
}
}, 300);


where variable
scrollTo
is what I get when I apply
getLeft()
to the View I wanna scroll to. I can confirm that it takes various values.

Anyone can help me with that ?

Answer

Switch to using a RecyclerView with a LinearLayoutManager with orientation set to Horizontal. Like this:

scroll_view.xml

<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scrollView"
    android:scrollbars="none"
    android:layout_gravity="center_horizontal"
    android:paddingTop="16dp"
    android:fillViewport="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layoutManager="android.support.v7.widget.LinearLayoutManager"
    android:orientation="horizontal">
    <LinearLayout
        android:id="@+id/scrollContainer"
        android:gravity="center_horizontal"
        android:orientation="horizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</android.support.v7.widget.RecyclerView>

Note: the layoutManager tag is required here for the layout to inflate. After it's inflated, we're going to set it programatically as well because otherwise we'll get an exception because the RecyclerView basically disposes of it before it's done with it.

OptionAdapter.java

public class OptionAdapter extends RecyclerView.Adapter<OptionAdapter.OptionHolder> {

    private Context context;
    private LayoutInflater inflater;

    private ArrayList<String> options;

    public OptionAdapter(Context context) {
        this.context = context;
        this.inflater = LayoutInflater.from(context);

        options = new ArrayList<>();
    }

    @Override
    public OptionHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new OptionHolder(inflater.inflate(R.layout.textview, parent, false));
    }

    @Override
    public void onBindViewHolder(OptionHolder holder, int position) {
        String option = options.get(position);

        ((TextView) holder.itemView).setText(option);
    }

    @Override
    public int getItemCount() {
        return options != null ? options.size() : 0;
    }

    public ArrayList<String> getOptions() {
        return options;
    }

    public void addOption(String option, Integer index) {
        if (index != null && index <= options.size()) {
            options.add(index, option);
        } else {
            options.add(option);
        }

        notifyDataSetChanged();
    }

    public class OptionHolder extends RecyclerView.ViewHolder {

        public OptionHolder(View itemView) {
            super(itemView);
        }
    }
}

text_view.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_marginLeft="8dp"
    android:layout_marginRight="8dp"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="match_parent" />

Setting everything up

final LayoutInflater inflater = LayoutInflater.from(this);
final RecyclerView scrollView = (RecyclerView) inflater.inflate(R.layout.scroll_view, container, false);
scrollView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));

final OptionAdapter adapter = new OptionAdapter(this);
scrollView.setAdapter(adapter);

container here is whatever view is going to be holding the RecyclerView. In my code, I've got it in a LinearLayout.

Adding a view to the list

adapter.addOption("The Added One", null);

Or if you want to add it to a specific position in the list.

adapter.addOption("The Added One", position);

Scrolling to a specific position

scrollView.smoothScrollToPosition(position);

Scrolling to a specific item in the list

scrollView.smoothScrollToPosition(adapter.getOptions().indexOf("ItemText"));

Hope it works for you!