Scroll state of recyclerview inside ViewPager Tab Fragment not saved when device orientation is handled manually using onConfigurationChanged()

戏子无情 提交于 2019-12-25 18:58:07

问题


What I want to achieve:

FragmentOne is under Tab1 and FragmentTwoPortrait is under Tab2. I want to save the state of the recyclerview of FragmentTwoPortrait, then restore the fragment state along with the last scroll position of the recyclerview. Similar operation is intended when I rotate the device from landscape to portrait orientation.

In my posted code here, Fragment transaction through the instance FragmentTwoLandscape is done inside a FrameLayout at the beginning to create the initial state of FragmentTwo for landscape orientation. Later on, the view of last saved state of FragmentTwoPortrait is tried to be inflated. The FrameLayout is hidden in portrait orientation, and will be visible only when the device is in Landscape orientation.

Due to certain reasons, I have to handle the device orientation change manually through overriding onConfigurationChanged() from inside the parent activity. Parent activity's life-cycle states need to be handled accordingly if needed.

I have tried out so many solutions over the last few days. I have applied solutions provided on stackoverflow and also tried some of my own. But nothing seems to work! I think I have made a silly mistake somewhere for the code not to return the desired behavior.

P.S: Please keep in mind while proposing a solution: 1. The tab fragments are inside a viewpager 2. FragmentTwo is not allowed to call onSaveInstanceState() [there are obvious reasons behind that] 3. I cannot preserve the header value of the StickyHeaderRecyclerView library (https://github.com/TellH/RecyclerStickyHeaderView) using basic recycler view methods.

activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.sakib.viewpagerfragmentrestoration.Activity.MainActivity">

<FrameLayout
    android:background="@android:color/white"
    android:id="@+id/fragment_container"
    android:layout_width="480dp"
    android:layout_height="match_parent"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true"
    android:visibility="gone"/>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_toStartOf="@+id/fragment_container"
    android:layout_toLeftOf="@+id/fragment_container"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="match_parent"
        android:layout_height="200dp" />

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

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            android:minHeight="?attr/actionBarSize"
            app:tabGravity="fill"
            app:tabMode="fixed"
            app:tabSelectedTextColor="@color/colorAccent"
            app:tabTextColor="#FFF" />


        <com.example.sakib.viewpagerfragmentrestoration.ViewPager.NoScrollViewPager
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </LinearLayout>


</LinearLayout>

</RelativeLayout>

MainActivity.java:

public class MainActivity extends AppCompatActivity {

NoScrollViewPager viewPager;
FragmentOne fragmentOne;
FragmentTwo fragmentTwoPortrait, fragmentTwoLandscape;
ViewPagerAdapter adapter;
TabLayout tabLayout;
FrameLayout fragmentTwoLandscapeContainer;

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

    viewPager = (NoScrollViewPager) findViewById(R.id.pager);
    tabLayout = (TabLayout) findViewById(R.id.tabs);
    fragmentTwoLandscapeContainer = (FrameLayout) findViewById(R.id.fragment_container);

    setViewPagerAdapter();
    loadFragmentForLandscape();
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        fragmentTwoLandscape.adjustScrollPosition(fragmentTwoPortrait.getRecyclerView());
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else {
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
        fragmentTwoPortrait.adjustScrollPosition(fragmentTwoLandscape.getRecyclerView());
    }

    adapter.notifyDataSetChanged();
    setVisibility(newConfig);
}

private void setVisibility(Configuration newConfig){
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        fragmentTwoLandscapeContainer.setVisibility(View.VISIBLE);
    } else {
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
        fragmentTwoLandscapeContainer.setVisibility(View.GONE);
    }
}


private void setViewPagerAdapter() {

    fragmentOne = new FragmentOne();
    fragmentTwoPortrait = new FragmentTwo();

    //Add Fragments to adapter one by one
    adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFragment(fragmentOne, "FRAG1");
    adapter.addFragment(fragmentTwoPortrait, "FRAG2");
    viewPager.setAdapter(adapter);
    tabLayout.setupWithViewPager(viewPager);

    if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
        viewPager.setCurrentItem(1);
    }
}


private void loadFragmentForLandscape() {
    fragmentTwoLandscape = new FragmentTwo();

    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    ft.replace(R.id.fragment_container, fragmentTwoLandscape);
    ft.commit();
}


// Adapter for the viewpager using FragmentPagerAdapter
class ViewPagerAdapter extends FragmentPagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();


    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getCount() {
        return mFragmentList.size();
    }

    public void addFragment(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mFragmentTitleList.get(position);
    }
  }
}

FragmentTwo.java:

public class FragmentTwo extends Fragment {
private RecyclerView rv;
private StickyHeaderViewAdapter adapter;
private Random random = new Random(System.currentTimeMillis());

public FragmentTwo() {
    // Required empty public constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_two, container, false);
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    // add your code here which executes after the execution of onCreateView() method.

    initView(view);
    initData();
}

private void initView(View view) {
    rv = (RecyclerView) view.findViewById(R.id.recyclerView);
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
    rv.setLayoutManager(linearLayoutManager);
}


private void initData() {
    Gson gson = new Gson();
    Result result = gson.fromJson(User.dataSource, Result.class);
    List<User> userList = result.getItems();
    Collections.sort(userList, new Comparator<User>() {
        @Override
        public int compare(User o1, User o2) {
            return o1.getLogin().compareToIgnoreCase(o2.getLogin());
        }
    });
    List<DataBean> userListBak = new ArrayList<>();
    String currentPrefix = userList.get(0).getLogin().substring(0, 1).toUpperCase();
    userListBak.add(new ItemHeader(currentPrefix));
    for (User user : userList) {
        if (currentPrefix.compareToIgnoreCase(user.getLogin().substring(0, 1)) == 0)
            userListBak.add(user);
        else {
            currentPrefix = user.getLogin().substring(0, 1).toUpperCase();
            userListBak.add(new ItemHeader(currentPrefix));
            userListBak.add(user);
        }
    }
    adapter = new StickyHeaderViewAdapter(userListBak)
            .RegisterItemType(new UserItemViewBinder())
            .RegisterItemType(new ItemHeaderViewBinder());
    rv.setAdapter(adapter);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.menu_main, menu);
    super.onCreateOptionsMenu(menu,inflater);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_add_view:
            User user = new User("Sticky View", 123, "https://avatars.githubusercontent.com/u/15800681?v=3");
            user.setShouldSticky(random.nextBoolean());
            adapter.append(user);
            break;
        case R.id.action_clear_all:
            adapter.clear(rv);
            break;
        default:
            break;
    }
    return super.onOptionsItemSelected(item);
}


public void adjustScrollPosition(RecyclerView recyclerView) {
    if(recyclerView == null) {
        return;
    }
    LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
    int position = layoutManager.findFirstVisibleItemPosition();
    int positionOffset = (recyclerView.getChildAt(0) == null) ? 0 : (recyclerView.getChildAt(0).getTop() - recyclerView.getChildAt(0).getPaddingTop());

    LinearLayoutManager targetLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
    targetLayoutManager.scrollToPositionWithOffset(position, positionOffset);
}

public RecyclerView getRecyclerView() {
    if(rv == null) {
        return null;
    }
    return rv;
}
}

回答1:


First of all get current visible position of your RecyclerView by adding addOnScrollListener() as follows :

private int position=0;
myRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == RecyclerView.SCROLL_STATE_IDLE){
                    position = ((LinearLayoutManager)recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
                }
            }
        });

Now save the current visible position in onSaveInstanceState() method as follows :

@Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("visiblePosition", position);
    }

Then in Your onActivityCreated() get current visible position from savedInstanceState and set scroll position as follows:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
        if(savedInstanceState.getInt("visiblePosition")>0)
        myFriendsRecyclerView.scrollToPosition(savedInstanceState.getInt("visiblePosition"));
    }

I hope its work for you thank you




回答2:


If you want same behaviour in both portrait and landscape mode then try not to save any configuration

In AndroidManifest for this particular activity

android:configChanges="keyboardHidden|orientation"


来源:https://stackoverflow.com/questions/51354479/scroll-state-of-recyclerview-inside-viewpager-tab-fragment-not-saved-when-device

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!