【问题标题】:Android: strange issues in my app with 1 fragment after device rotationAndroid:设备旋转后我的应用中出现 1 个片段的奇怪问题
【发布时间】:2016-10-02 15:24:54
【问题描述】:

我的应用由 1 个片段组成。这个片段包含一个带有大量条目的回收器视图。

症状是设备旋转后似乎无法将回收站视图滚动到保存的位置。无论我做什么,结果都没有滚动。所以,UI中发生了奇怪的事情。

EDIT(揭示根本原因)...

在我的活动中,我有这个代码......

if (findViewById(R.id.fragment_container) != null) {
    FragmentCategoryChecklist f2 = new FragmentCategoryChecklist();
    Bundle b = new Bundle();
    b.putString("contents", "Category Fragment");
    f2.setArguments(b);        
    getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, f2).commit();
 }

这是错误的!请参阅下面的代码。

使用此代码旋转设备时,每次都会创建一个片段。这是错误的,因为 Android 已经重新创建(保存的)片段。

这样做的结果是代码用实例数据创建了一个片段,然后立即销毁,然后在没有任何实例数据的情况下重新创建。没有保存的实例数据意味着没有滚动到以前保存的位置。

正确的代码如下所示。对不起,我的问题只是一个症状,并没有显示根本原因。

Fragment f = getSupportFragmentManager().findFragmentById( R.id.fragment_container);
if( f == null) {
    if (findViewById(R.id.fragment_container) != null) {
        FragmentCategoryChecklist f2 = new FragmentCategoryChecklist();
        Bundle b = new Bundle();
        b.putString("contents", "Category Fragment");
        f2.setArguments(b); 
        getSupportFragmentManager().
            beginTransaction().
            replace(R.id.fragment_container, f2).
            commit();
    }
}

【问题讨论】:

  • 我认为您可以尝试使用 UI 处理程序。将Runnable 发布到 UI 消息队列并尝试在那里findLastCompletelyVisibleItemPosition。这将保证这个 Runnable 将在生命周期回调之后执行。如果这不起作用,请在此处发布一些代码,因为很难找到问题。
  • 你能详细说明你的答案吗?
  • 我没有发布这个作为答案,因为我不能 100% 确定它会起作用,所以我请你试试。 @bpr10 的解决方案有效吗?
  • 如果你没有明白我的意思,试试这样:uiHandler.post(new Runnable() { @Override public void run() { ((LinearLayoutManager) recyclerview.getLayoutManager()).findLastCompletelyVisibleItemPosition()); // this shouldn't return -1 }})。其中uiHandler 是附加到UI 线程的Handler 对象。
  • 我已将我的代码添加到问题中。您将看到选项 1 ... 在 findLastCompletelyVisibleItemPosition() 上返回 -1。您的选项 2 我也添加了,再次返回 -1。

标签: android android-recyclerview


【解决方案1】:

这是一种骇人听闻的方法,但您可以设置一个线程来定期检查您的RecyclerView

final LinearLayoutManager manager = (LinearLayoutManager) recyclerview.getLayoutManager();
new Thread(){
    @Override
    public void run(){
        try {
            while(true){
                Thread.sleep(543);
                if(manager.findLastCompletelyVisibleItemPosition() != -1){
                   //Return to the UI thread and continue your work
                   //Try runOnUiThread(Runnable)
                   break;
                }
            }
        } catch (Exception ex) {
            //Handle thread interruption, etc
        }
    }
}.start();

肯定有更好的不涉及线程的方法来解决这个问题。设置有限数量的试验可能会有所帮助,这样您就不会有一个永远运行的杂散线程。

【讨论】:

  • 为你的答案加了 1 个。
【解决方案2】:

我相信您可以从 first 和 lastVisibleItem 位置获得布局渲染完成的回调。这个answer 以最好的方式解释了这一点。希望这可以帮助。

private boolean canScroll = false;
final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false) {
            @Override 
            public void onLayoutChildren(final Recycler recycler, final State state) {
                super.onLayoutChildren(recycler, state);
                final int firstVisibleItemPosition = findFirstVisibleItemPosition();
                if (firstVisibleItemPosition != 0) {
                    // this avoids trying to handle un-needed calls 
                    if (firstVisibleItemPosition == -1)
                        //not initialized, or no items shown.Can't 
                        canScroll = false;
                        return; 
                } 
                final int lastVisibleItemPosition = findLastVisibleItemPosition();
                canScroll = (lastVisibleItemPosition - firstVisibleItemPosition) > 0
            } 
        }; 

然后你可以使用canScroll变量来决定是否滚动。

【讨论】:

  • 唉,这行不通。它只是提到(Toast 消息)lastVisibleItemPosition 就像 9。我仍然无法滚动到某个位置。所以在 canScroll == true 后面的滚动是无效的。 ((LinearLayoutManager) recyclerview.getLayoutManager()).scrollToPositionWithOffset(jumpToCurrentSelection, 20);
  • 为您的答案加了 1 个。
【解决方案3】:

问题的根本原因显示在问题的编辑部分之后。

设备旋转后正确启动片段的解决方案是:

Fragment f = getSupportFragmentManager().findFragmentById( R.id.fragment_container);
if( f == null) {
    if (findViewById(R.id.fragment_container) != null) {
        FragmentCategoryChecklist f2 = new FragmentCategoryChecklist();
        Bundle b = new Bundle();
        b.putString("contents", "Category Fragment");
        f2.setArguments(b);    getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, f2).commit();
    }
}

仅当它不存在时,这将在设备旋转后创建一个新片段。

原来的问题呢?

解决的很简单,就是加个...

((LinearLayoutManager) recyclerview.getLayoutManager()).scrollToPositionWithOffset( jumpToCurrentSelection, 20);

【讨论】:

    【解决方案4】:

    在片段上添加:

    @Override
    protected Parcelable onSaveInstanceState() {
       Bundle bundle = new Bundle();
       bundle.putParcelable(SAVED_LAYOUT_MANAGER,   recyclerView.getLayoutManager().onSaveInstanceState());
       return bundle;
    }
    

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            layoutManagerSavedState = ((Bundle) state).getParcelable(SAVED_LAYOUT_MANAGER);
        }
        super.onRestoreInstanceState(state);
    }
    

    并使用以下方法恢复您的 Recycler View 数据:

    public void setItems(List objects) {
        adapter.setItems(objects);
        restoreLayoutManagerPosition();
    }
    private void restoreLayoutManagerPosition() {
        if (layoutManagerSavedState != null) {
            recyclerView.getLayoutManager().onRestoreInstanceState(layoutManagerSavedState);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-27
      相关资源
      最近更新 更多