7

I have followed so many answers from here.. but not a single one solving my issue .. that's why i am asking.

I want to save scroll position in a fragment. In So many articles they have suggested to follow

@Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
    }

and

@Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
    }

But this two method is not available in fragment.

My code:

private int mPositionIng = RecyclerView.NO_POSITION;

private String KEY_POSITION_ING = "KeyPositionIng";

in OnCreateView()

if (savedInstanceState != null) {

            if (savedInstanceState.containsKey(KEY_POSITION_ING)) {
                mPositionIng = savedInstanceState.getInt(KEY_POSITION_ING);
            }
}

Override Methods in fragment They are not same method as above. i don't know where i am doing wrong.

@Override
    public void onSaveInstanceState(Bundle outState) {

        int scrollPositionIng = mRecyclerViewIngredients.computeVerticalScrollOffset();

        mPositionIng = scrollPositionIng;

        outState.putInt(KEY_POSITION_ING, mPositionIng);
        super.onSaveInstanceState(outState);
    }

@Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {

        if (mPositionIng != RecyclerView.NO_POSITION) {

            mRecyclerViewIngredients.getLayoutManager().scrollToPosition(mPositionIng);
        }


        super.onViewStateRestored(savedInstanceState);
    }

I just need to save scroll position while the orientation changes .. Please help. Any Suggestion will be help full. Thanks......

Tapan Kumar Patro
  • 767
  • 1
  • 7
  • 18

3 Answers3

2

Update

Everything I wrote below is correct, but the reason it didn't work for you is that I didn't realize how your Activity's layout was structured. Here is your Activity's layout (slightly cleaned up):

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.tapan.recipemaster.activity.RecipeDetailActivity">
    
        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
    
                <FrameLayout
                    android:id="@+id/fl_fragment_detail"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:padding="@dimen/padding_10dp"/>
    
            </RelativeLayout>
    
        </ScrollView>
    
    </android.support.constraint.ConstraintLayout>

Meanwhile, this is your Fragment's layout (again, slightly cleaned up):

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.tapan.recipemaster.fragment.RecipeDetailFragment">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="@dimen/padding_10dp"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/colorAccent"
                android:orientation="vertical">

                <TextView
                    android:id="@+id/tv_ingredient"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textColor="#FFF"
                    android:textSize="@dimen/text_23sp"
                    android:text="Ingredients"/>

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/rv_ingredients"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/dimen_8dp"/>

            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <TextView
                    android:id="@+id/tv_step"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/dimen_8dp"
                    android:textColor="@color/colorPrimaryDark"
                    android:textSize="@dimen/text_23sp"
                    android:text="Steps"/>

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/rv_steps"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/dimen_8dp"/>

            </LinearLayout>

        </LinearLayout>

    </ScrollView>

</FrameLayout>

Both RecyclerViews have android:layout_height="wrap_content", which means they do not scroll. Rather, the ScrollView in your Activity is the view providing the scrolling behavior, so it is this view whose scroll position must be saved.

You can have the system do this for you by giving this ScrollView an id. Any id you want, as long as it's unique. You don't have to write any Java at all.

<ScrollView
    android:id="@+id/thisfixestheproblem"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

Make sure you're modifying the ScrollView in your activity's layout, not the one in your fragment's layout.

Original

None of the code you posted should be necessary to save your RecyclerView's scroll position on orientation change. As long as the RecyclerView has a unique ID in your layout, it will save the scroll position for you automatically.

Here is a very small sample app that shows automatic saving of scroll position, even with a dynamically added Fragment. As you can see, the only instance state I'm saving myself is whether the button to start the fragment should be visible.

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private Button button;

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean("buttonVisible", button.getVisibility() == View.VISIBLE);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                getSupportFragmentManager()
                        .beginTransaction()
                        .replace(R.id.content, new MyFragment())
                        .commit();

                button.setVisibility(View.GONE);
            }
        });

        if (savedInstanceState != null) {
            boolean buttonVisible = savedInstanceState.getBoolean("buttonVisible");
            button.setVisibility(buttonVisible ? View.VISIBLE : View.GONE);
        }
    }
}

MyFragment.java

public class MyFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.myfragment, container, false);

        RecyclerView recycler = (RecyclerView) root.findViewById(R.id.recycler);
        recycler.setAdapter(new MyAdapter());

        return root;
    }

    private static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {

        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            View itemView = inflater.inflate(R.layout.itemview, parent, false);
            return new MyViewHolder(itemView);
        }

        @Override
        public void onBindViewHolder(MyViewHolder holder, int position) {
            Drawable d = new ColorDrawable(Color.argb(0xff, 0, 0, position));
            ViewCompat.setBackground(holder.image, d);
            holder.text.setText("" + position);
        }

        @Override
        public int getItemCount() {
            return 256;
        }
    }

    private static class MyViewHolder extends RecyclerView.ViewHolder {

        final View image;
        final TextView text;

        MyViewHolder(View itemView) {
            super(itemView);

            this.image = itemView.findViewById(R.id.image);
            this.text = (TextView) itemView.findViewById(R.id.text);
        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="create fragment"/>

</FrameLayout>

myfragment.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/recycler"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutManager="android.support.v7.widget.LinearLayoutManager"
    tools:listitem="@layout/itemview"/>

itemview.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <View
        android:id="@+id/image"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_margin="12dp"
        tools:background="#333"/>

    <TextView
        android:id="@+id/text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        tools:text="text"/>

</LinearLayout>
Ben P.
  • 52,661
  • 6
  • 95
  • 123
  • Super easy one.... but the problem is i don't have button to open it from activity in oncreate method its transact directly/....in my case what can i do ? – Tapan Kumar Patro Jul 28 '17 at 02:07
  • Shouldn't make a difference whether you open it with a button or not. How **are** you including the fragment? Are you doing a fragment transaction in `onCreate()`? Are you including it in a `` tag in xml? Etc. – Ben P. Jul 28 '17 at 02:16
  • android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.add(R.id.fl_fragment_detail, DetailFragment).commit(); This is my code to start fragment inside oncreate of that activity – Tapan Kumar Patro Jul 28 '17 at 02:43
  • https://github.com/tapanpatro/RecipeListMaster/blob/master/app/src/main/java/com/tapan/recipemaster/activity/RecipeDetailActivity.java my link for that acrtivity – Tapan Kumar Patro Jul 28 '17 at 02:44
  • What happens if you only add the fragment when `savedInstanceState == null` ? – Ben P. Jul 28 '17 at 03:40
  • @TapanKumarPatro I have updated my answer after viewing your project on github – Ben P. Jul 28 '17 at 21:36
1

Let the Android OS handle that for you, in AndroidManifest.xml inside your wanted tag activity add:

android:configChanges="orientation|screenSize"
MohammadL
  • 2,398
  • 1
  • 19
  • 36
0

for future coders

I guess when you came back to your fragment you lost position this is because the values stored in the recycler view of the fragment is lost. If you keep code in start or other methods in fragment their scope is lost.

keep your code in On Create View in fragment because scope will not be lost it will work maximum times if not try by declaring all variables in class not in local method and initialize in oncreateview method. how to get context in on create view?? you can google