使用先前启动的活动过渡恢复活动时,查看可见性状态丢失

时间:2018-06-21 13:08:49

标签: android android-layout android-activity

说明:

  • 我从活动A开始活动过渡,开始了活动B。

  • 启动新的活动B后,我更改了B中某些视图(View.GONE)的可见性状态。

问题是:

启动新的活动C并返回活动B(或在B中强制执行onPause)时,具有更改后的可见性状态的视图会再次出现,而无需通过代码或其他方式触摸视图。

以下视频在图片中解释了该问题:https://youtu.be/oqCZo5CSkQk

不使用过渡时,一切都会按预期进行。有谁知道如何在恢复Activity时如何防止视图状态丢失?我使用ActivityOptionsCompat错误吗?

我使用支持库:

'com.android.support:support-v4:27.1.1''com.android.support:appcompat-v7:27.1.1'

但是该问题还会出现在旧版本和不同手机制造商(Pixel,三星等)上。

这里是重现问题的代码:

布局

活动A:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical">

  <TextView
      style="@style/TextAppearance.AppCompat.Title"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="24dp"
      android:gravity="center"
      android:text="Activity A | this starts the transition to another activity"/>

  <android.support.v7.widget.AppCompatImageView
      android:id="@+id/imageToAnimate"
      android:layout_width="20dp"
      android:layout_height="20dp"
      app:srcCompat="@android:drawable/star_big_on"/>

  <Button
      android:id="@+id/start_next_activity"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="30dp"
      android:text="start Another Activity"/>

</LinearLayout>

活动B:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

  <TextView
      style="@style/TextAppearance.AppCompat.Title"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="24dp"
      android:gravity="center"
      android:text="Activity B | with progressbar"/>

  <android.support.v7.widget.AppCompatImageView
      android:id="@+id/imageToAnimate"
      android:layout_width="100dp"
      android:layout_height="100dp"
      android:transitionName="toAnimate"
      app:srcCompat="@android:drawable/star_big_on"/>

  <ProgressBar
      android:id="@+id/progress"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="24dp"
      android:visibility="gone"/>

  <TextView
      android:id="@+id/dismiss_text"
      style="@style/TextAppearance.AppCompat.Subhead"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginBottom="24dp"
      android:text="Also a text to dismiss"/>

  <Button
      android:id="@+id/show"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="SHOW"/>

  <Button
      android:id="@+id/hide_gone"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="SET GONE"/>

  <Button
      android:id="@+id/hide_invisible"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="SET INVISIBLE"/>

  <Button
      android:id="@+id/start_activity_c"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="START activity"/>


</LinearLayout>

活动C:

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

  <TextView
      style="@style/TextAppearance.AppCompat.Title"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Activity C"
      android:textSize="40sp"/>

</LinearLayout>

活动源代码

活动A:

public class DebugActivityA extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
        findViewById(R.id.start_next_activity).setOnClickListener(v -> startWithTransition());
    }

    private void startWithTransition() {
        ActivityOptionsCompat options = ActivityOptionsCompat
                .makeSceneTransitionAnimation(DebugActivityA.this,
                                              findViewById(R.id.imageToAnimate),
                                              "toAnimate");
        startActivity(new Intent(DebugActivityA.this, DebugActivityB.class), options.toBundle());
    }

}

活动B:

public class DebugActivityB extends AppCompatActivity {

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

        View progressbar = findViewById(R.id.progress);
        View dismissText = findViewById(R.id.dismiss_text);
        progressbar.setVisibility(View.VISIBLE);
        findViewById(R.id.show).setOnClickListener(v -> {
            progressbar.setVisibility(View.VISIBLE);
            dismissText.setVisibility(View.VISIBLE);
        });
        findViewById(R.id.hide_gone).setOnClickListener(v -> {
            progressbar.setVisibility(View.GONE);
            dismissText.setVisibility(View.GONE);
        });
        findViewById(R.id.hide_invisible).setOnClickListener(v -> {
            progressbar.setVisibility(View.INVISIBLE);
            dismissText.setVisibility(View.INVISIBLE);
        });
        findViewById(R.id.start_activity_c).setOnClickListener(this::startOtherActivity);
    }

    private void startOtherActivity(View view) {
        startActivity(new Intent(this, DebugActivityC.class));
    }

}

活动C:

public class DebugActivityC extends AppCompatActivity {

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

}

6 个答案:

答案 0 :(得分:0)

您可以在离开活动时使用onSaveInstanceState方法保存UI的状态,然后使用onRestoreInstanceState将其恢复到该状态。 将onSaveInstanceState添加到活动B:

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
  super.onSaveInstanceState(savedInstanceState);
  // Save UI state changes to the savedInstanceState.
  // This bundle will be passed to onCreate if the process is
  // killed and restarted.
  savedInstanceState.putInt("progressbarVisibility", progressbar.getVisibility());
  savedInstanceState.putInt("dismissTextVisibility", dismissText.getVisibility());
}

然后添加活动B的修改onCreate方法:

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

    View progressbar = findViewById(R.id.progress);
    View dismissText = findViewById(R.id.dismiss_text);
    if (savedInstanceState != null){
        dismissText.setVisibility(savedInstanceState.getInt("dismissTextVisibility", View.VISIBLE));
        progressbar.setVisibility(savedInstanceState.getInt("progressbarVisibility", View.VISIBLE)); 
    }
    // The rest of your code.
}

答案 1 :(得分:0)

为此可能需要解决。

与其更改onCreate中的视图可见性(只有在新创建Activity时才调用它),而是尝试根据{{1}中的标志来操纵onResume中的Views的可见性。 }}本身。

  

使用Activity B甚至基于简单的布尔标志

现在,当SharedPreference恢复可见时,将基于Activity B中的这些标志本身重新应用相同的状态。

如果使用onResume,则在推送新的SharedPreference的同时更新新值。

Activity C中,检查值并更改目标视图。 一旦onResume从后堆栈中删除,即({{1}中的Activity B

,清除标志

答案 2 :(得分:0)

  

它可能是共享过渡库问题,也可能不是,我来了   使用以下方法来解决可见性问题。差不多花了一天!

让我在这里分享我的看法。

如果您更改共享过渡视图的可见性状态,则不会出现此问题。

如果您有这样的要求,要在使用共享过渡时更改某些视图的可见性,请将它们设置为共享视图。

ViewCompat.setTransitionName(<view>, <uniqueString>);

此处共享修改后的文件,我已验证并解决了我们的问题

活动A

public class ActivityA extends AppCompatActivity {

    private View startNextActivity;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
        startNextActivity = findViewById(R.id.start_next_activity);
        startNextActivity.setOnClickListener(v -> startWithTransition());
    }

    private void startWithTransition() {
        ActivityOptionsCompat options = ActivityOptionsCompat
                .makeSceneTransitionAnimation(this,
                        new Pair<View, String>(startNextActivity, ActivityB.VIEW_NAME_IMAGEVIEW)
                        new Pair<View, String>(startNextActivity, ActivityB.VIEW_NAME_PROGRESSBAR),
                        new Pair<View, String>(startNextActivity, ActivityB.VIEW_NAME_DETAIL_TEXTVIEW));
        startActivity(new Intent(ActivityA.this, ActivityB.class), options.toBundle());
    }

}

活动B

public class ActivityB extends AppCompatActivity {

    public static final String VIEW_NAME_PROGRESSBAR = "progressbar";
    public static final String VIEW_NAME_IMAGEVIEW = "toAnimate";
    public static final String VIEW_NAME_DETAIL_TEXTVIEW = "detailTextView";

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

        View progressbar = findViewById(R.id.progress);
        View dismissText = findViewById(R.id.dismiss_text);
        View animateView = findViewById(R.id.imageToAnimate);

        // Key lines to fix our issue
        ViewCompat.setTransitionName(animateView, VIEW_NAME_IMAGEVIEW);
        ViewCompat.setTransitionName(progressbar, VIEW_NAME_PROGRESSBAR);
        ViewCompat.setTransitionName(dismissText, VIEW_NAME_DETAIL_TEXTVIEW);

        progressbar.setVisibility(View.VISIBLE);
        findViewById(R.id.show).setOnClickListener(v -> {
            progressbar.setVisibility(View.VISIBLE);
            dismissText.setVisibility(View.VISIBLE);
        });
        findViewById(R.id.hide_gone).setOnClickListener(v -> {
            progressbar.setVisibility(View.GONE);
            dismissText.setVisibility(View.GONE);
        });
        findViewById(R.id.hide_invisible).setOnClickListener(v -> {
            progressbar.setVisibility(View.INVISIBLE);
            dismissText.setVisibility(View.INVISIBLE);
        });
        findViewById(R.id.start_activity_c).setOnClickListener(this::startOtherActivity);
    }

    private void startOtherActivity(View view) {
        startActivity(new Intent(this, ActivityC.class));
    }

}

希望它可以在某种程度上帮助您。

祝你有美好的一天!

答案 3 :(得分:0)

是。 现在可以使用 onSaveInstanceState onRestoreInstanceState 了。

您可以将可见性状态保存在onSaveInstanceState 状态中,并恢复 onRestoreInstanceState 。活动始终会调用完美运行的onSaveInstanceState和onRestoreInstanceState。

快乐编码!

答案 4 :(得分:0)

当您开始活动C并返回时,活动B的XML将被重绘,因此将使用默认值VIEW.Visible。随后,视图再次弹出(显示)。

您需要以某种方式保存视图的状态。试试:

  • savedInstanceState或
  • SharedPreferences

答案 5 :(得分:0)

给我这个老问题的反馈。

我向 google 提交了错误单:https://issuetracker.google.com/issues/112158868,但他们无法修复此问题,因为他们优先处理其他错误。

我的解决方案是切换到带有喷气背包导航 (https://developer.android.com/jetpack/compose/navigation) 的单一活动概念。

所以我现在用片段做过渡。

感谢您的回复。