无法在 OnPageChangeCallBack 中从 ViewPager2 获取 ScrollView

can't get ScrollView from ViewPager2 in OnPageChangeCallBack

我有一个包含 ScrollView 的 ViewPager2。
问题是,当我尝试在 onPageSelected() 中获取当前页面的 ScrollView 时,它不起作用。 在这里,当用户返回查看所选页面时,我想将以前的 scrollY 设置为 ScrollView。 (因为在此之前由于某种原因重置了 scrollY) 我的代码如下。

ViewPagerAdapter.java(已编辑)

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);
        }
    }

[修改]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(已编辑)

<?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>

非常感谢任何建议。感谢您阅读这么长的问题。

onPageSelected 将比选定的页面绘制机制更早调用,在任何 onCreateViewHolderonBindViewHolder 之前。我建议你以某种方式将你的数据“注入”到你的“页面”中(它是 View 还是整个 Fragment?这有很大的不同)并且在你的情况下将这个滚动位置设置在 onBindViewHolder.这将使 RecyclerView 的渲染甚至在正确的滚动位置绘制第一帧。你的方式,即使你会等待一段时间 RecyclerView 绘图,也会使 onBindViewHolder 将在 0 滚动上绘制第一帧,你的方法将在下一帧滚动一点 - 这将像一些一样可见闪烁或快速自动滚动(我认为这种行为不是故意的)

编辑 - 添加正确的方法来为您的 Adapter 设置滚动,甚至在 super 调用

之前
@Override
public void onPageSelected(int position) {
    int positionY[] = getScrollFromViewModel();
    adapter.setScrollForPosition(positionY[position]);
    super.onPageSelected(position);
    ...

适配器

private final HashMap<Integer, Integer> scrollYHistory = new HashMap<>();

public void setScrollForPosition(int position, int scrollY){
    scrollYHistory.put(position, scrollY);
}

使用 onBindViewHolder 中存储的 scrollY 值并清除 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);
    ...

还删除了适配器构造函数和内部变量中对 MainActivity main 的不必要引用,它已经获得了 Context 引用并且不需要(不应该)知道哪个 Activity 是创建它

编辑 - 扩展评论:

与其直接在 onBindViewHolder 中调用 scrollView.setScrollY,不如先设置 TextView,然后等待渲染,然后滚动到正确的位置

@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);
        }
    });
}

多亏了 snachmsm,我才知道 onPageSelected 会比 ViewPager2 绘图更早被调用。出于某种原因,我使用 mPager2.postDelayed().
找到了我的解决方案 添加这些后,我这里没有空的 ScrollView。

        mPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            ...

            //Here I added onPageScrollStateChanged() and postDelayed() in it.
            @Override
            public void onPageScrollStateChanged(int state){
                if(state == ViewPager2.SCROLL_STATE_SETTLING){
                    mPager2.postDelayed(new Runnable() {   
                        
                        @Override
                        public void run() {
                            int position = mPager2.getCurrentItem();
                            int y = scrollModel.getPreviousScrollY(position);

                            ScrollView sv = findScrollView();
                            sv.scrollTo(0, y);
                        }
                    }, 50);
                }
            }
        });
    }