I have a ViewPager2 contains ScrollView.
What the problem is, when I try to get ScrollView of current page in onPageSelected(), it doesn't work.
Here I'd like to set previous scrollY to the ScrollView when user back to see selected page. (because scrollY is reset for some reason before that)
My code is below.
ViewPagerAdapter.java (edited)
public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.ViewHolder> {
private LayoutInflater mInflater;
private List<String> mText;
private ViewPager2 pager2;
MainActivity main;
public ViewPagerAdapter(Context context, List<String> data, ViewPager2 pager2, MainActivity main){
this.mInflater = LayoutInflater.from(context);
this.mText = data;
this.pager2 = pager2;
this.main = main;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View view = mInflater.inflate(R.layout.tab_scroll_item, parent, false);
return new ViewPagerAdapter.ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewPagerAdapter.ViewHolder holder, int position){
holder.scrollView.setTag("scv_tab" + position);
holder.scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
@Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if(scrollY != 0) {
main.setScrollY(scrollY, getPosition());
}
System.out.println("onScrollChanged : " + scrollY);
}
});
holder.textView.setEnabled(false);
holder.textView.setEnabled(true);
holder.textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, applyTextSize());
holder.textView.setText(mText.get(position));
holder.textView.setTag("tv_tab" + position);
}
@Override
public int getItemCount(){
return mText.size();
}
protected int getPosition(){
return pager2.getCurrentItem();
}
public class ViewHolder extends RecyclerView.ViewHolder{
ScrollView scrollView;
TextView textView;
public ViewHolder(View itemView){
super(itemView);
scrollView = itemView.findViewById(R.id.tab_scroll);
textView = itemView.findViewById(R.id.tab_textview);
}
}
[REVICED]onBindViewHolder
@Override
public void onBindViewHolder(final ViewPagerAdapter.ViewHolder holder, final int position){
holder.scrollView.setTag("scv_tab" + position);
holder.scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
@Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if(scrollY != 0) {
main.storeScrollY(scrollY, position);
}
System.out.println("onScrollChanged : " + scrollY);
}
});
int y = main.retrieveScrollY(position);
holder.scrollView.setScrollY(y);
holder.textView.setEnabled(false);
holder.textView.setEnabled(true);
holder.textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, applyTextSize());
holder.textView.setText(mText.get(position));
holder.textView.setTag("tv_tab" + position);
}
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
...
mPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
if(!searchView.isIconified()){
searchView.setIconified(true);
}
if(highlightModel.getHighlitedOrNot(position)){
searchText.deleteTextHighlight(position);
highlightModel.setHighlitedOrNot(position, false);
}
int positionY[] = getScrollFromViewModel();
ScrollView sv = findScrollView(); // HERE IS THE PROBLEM
if(sv != null) {
sv.setScrollY(positionY[position]);
}else{
System.out.println("sv is null"); // ALWAYS SHOWS NULL
}
}
});
...
searchView = (SearchView) findViewById(R.id.searchview);
searchView.setOnSearchClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
fab.setVisibility(View.VISIBLE);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int actualResult = searchText.scrollToHighlightedWord(findTextView()
, findScrollView() // here findScrollView() works perfectly.
, searchResultIndex);
if(actualResult == (searchResultIndex + 1)) {
++searchResultIndex;
}else if(actualResult == searchResultIndex){
showToastAtMain("last word");
}else{
forOnClose();
}
}
});
}
});
private ScrollView findScrollView(){
ScrollView sv = mPager2.findViewWithTag("scv_tab" + mPager2.getCurrentItem());
return sv;
}
tab_scroll_item.xml (edited)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="@+id/tab1_layout">
<ScrollView
android:id="@+id/tab_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tab_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:lineSpacingExtra="7dp"
android:textIsSelectable="true"
android:paddingStart="9dp"
android:paddingTop="9dp"
android:paddingEnd="9dp"/>
</ScrollView>
</LinearLayout>
Any advice is truly appreciated. Thank you for reading this long question.
onPageSelected
will be called way earlier than selected page drawing mechanism, before any onCreateViewHolder
and onBindViewHolder
. I would advise you to "incject" somehow your data into your "page" (is it View
or whole Fragment
? that makes big difference) and in your case set this scroll position inside onBindViewHolder
. This will make rendering of RecyclerView
draw even first frame in correct scrolled position. Your way, even when you will wait some time for RecyclerView
drawing, will make that onBindViewHolder
will draw first frame on 0 scroll and your method will scroll a bit in next frames - this will be visible like some blink or fast-autoscroll (I think this behavior isn't intended)
edit - add proper method for setting scroll for your Adapter
even before super
call
@Override
public void onPageSelected(int position) {
int positionY[] = getScrollFromViewModel();
adapter.setScrollForPosition(positionY[position]);
super.onPageSelected(position);
...
for adapter
private final HashMap<Integer, Integer> scrollYHistory = new HashMap<>();
public void setScrollForPosition(int position, int scrollY){
scrollYHistory.put(position, scrollY);
}
use stored scrollY value in onBindViewHolder
and clean entry in your HashMap
@Override
public void onBindViewHolder(final ViewPagerAdapter.ViewHolder holder, final int position){
Integer scrollY = scrollYHistory.remove(position);
if (scrollY == null) scrollYa = 0; // may be null if not stored yet!
holder.scrollView.setScrollY(scrollY);
...
also remove unnecessary reference to MainActivity main
in adapter's constructor and variable inside, it already gets Context
reference and doesn't (shouldn't) need to know which Activity
is creating it
edit - expanding comment:
instead of calling scrollView.setScrollY
straight inside onBindViewHolder
you should set up your TextView
at first, then wait for rendering it and then scrolling to proper position
@Override
public void onBindViewHolder(final ViewPagerAdapter.ViewHolder holder, final int position){
...
holder.textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, applyTextSize());
holder.textView.setText(mText.get(position));
...
holder.textView.post(new Runnable(){
// this will be called after nearest drawing
@Override
public void run() {
Integer scrollY = scrollYHistory.remove(position);
if (scrollY == null) scrollYa = 0; // may be null if not stored yet!
holder.scrollView.setScrollY(scrollY);
}
});
}