Bagaimana cara mencegah bilah status dan bilah navigasi agar tidak hidup selama transisi animasi adegan aktivitas?

127

Pertama, latar belakang bilah status saya diatur ke coklat gelap, dan latar belakang bilah navigasi saya adalah hitam standar. Saya menggunakan tema Material ringan.

Saya memulai aktivitas baru menggunakan ActivityOptions.makeSceneTransitionAnimationtransisi default, dan saya perhatikan bahwa status dan bilah navigasi sebentar memudar menjadi putih dan kemudian kembali ke warna yang benar.

Menurut dokumentasi :

Untuk mendapatkan efek penuh dari transisi, Anda harus mengaktifkan transisi konten jendela pada aktivitas panggilan dan aktivitas yang dipanggil. Jika tidak, aktivitas panggilan akan memulai transisi keluar, tetapi kemudian Anda akan melihat transisi jendela (seperti skala atau pudar)

Saya menggunakan getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);kedua aktivitas untuk menelepon dan menelepon.

Demikian pula, jika saya mengubah transisi masuk ke slide, status dan navigasi bar secara singkat memiliki transisi slide dengan latar belakang putih.

Bagaimana cara mencegah bilah status dan bilah navigasi agar tidak hidup selama transisi animasi adegan aktivitas?

rlay3
sumber

Jawaban:

217

Ada dua pendekatan yang dapat Anda gunakan yang saya tahu untuk mencegah bilah navigasi / status dari animasi selama transisi:

Pendekatan # 1: Kecualikan bilah status dan bilah navigasi dari transisi keluar / masuk fade standar jendela

Alasan mengapa bilah navigasi / status memudar masuk dan keluar selama transisi adalah karena secara default semua tampilan yang tidak dibagikan (termasuk latar belakang navigasi / status bilah) akan memudar / masuk masing-masing dalam aktivitas panggilan / aktivitas yang dipanggil masing-masing begitu transisi dimulai . Anda dapat, bagaimanapun, dengan mudah menyiasatinya dengan mengecualikan latar belakang navigasi / status bar dari Fadetransisi keluar / masuk standar jendela . Cukup tambahkan kode berikut ke onCreate()metode Aktivitas Anda :

Transition fade = new Fade();
fade.excludeTarget(android.R.id.statusBarBackground, true);
fade.excludeTarget(android.R.id.navigationBarBackground, true);
getWindow().setExitTransition(fade);
getWindow().setEnterTransition(fade);

Transisi ini juga dapat dideklarasikan dalam tema aktivitas menggunakan XML (yaitu dalam res/transition/window_fade.xmlfile Anda sendiri ):

<?xml version="1.0" encoding="utf-8"?>
<fade xmlns:android="http://schemas.android.com/apk/res/android">
    <targets>
        <target android:excludeId="@android:id/statusBarBackground"/>
        <target android:excludeId="@android:id/navigationBarBackground"/>
    </targets>
</fade>

Pendekatan # 2: Tambahkan bilah status dan bilah navigasi sebagai elemen bersama

Pendekatan ini dibangun dari jawaban klmprt, yang hampir berhasil untuk saya ... walaupun saya masih perlu membuat beberapa modifikasi.

Dalam Aktivitas menelepon saya, saya menggunakan kode berikut untuk memulai Aktivitas:

View statusBar = findViewById(android.R.id.statusBarBackground);
View navigationBar = findViewById(android.R.id.navigationBarBackground);

List<Pair<View, String>> pairs = new ArrayList<>();
if (statusBar != null) {
  pairs.add(Pair.create(statusBar, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME));
}
if (navigationBar != null) {
  pairs.add(Pair.create(navigationBar, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));
}
pairs.add(Pair.create(mSharedElement, mSharedElement.getTransitionName()));

Bundle options = ActivityOptions.makeSceneTransitionAnimation(activity, 
        pairs.toArray(new Pair[pairs.size()])).toBundle();
startActivity(new Intent(context, NextActivity.class), options);

Sejauh ini pada dasarnya hal yang sama yang disarankan klmprt dalam jawabannya. Namun, saya juga perlu menambahkan kode berikut dalam onCreate()metode Aktivitas saya yang disebut untuk mencegah bilah status dan bilah navigasi dari "berkedip" selama transisi:

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

    // Postpone the transition until the window's decor view has
    // finished its layout.
    postponeEnterTransition();

    final View decor = getWindow().getDecorView();
    decor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            decor.getViewTreeObserver().removeOnPreDrawListener(this);
            startPostponedEnterTransition();
            return true;
        }
    });
}

Menambahkan bilah status dan latar belakang bilah navigasi sebagai elemen bersama akan memaksa mereka untuk digambar di atas transisi keluar / masuk fade default jendela, yang berarti bahwa mereka tidak akan pudar selama transisi. Diskusi lebih lanjut tentang pendekatan ini dapat ditemukan di pos Google+ ini .

Alex Lockwood
sumber
14
Adakah ide mengapa findViewById (android.R.id.navigationBarBackground) mengembalikan nol di Lollipop? Saya menggunakan appcompat-v7
radzio
3
Pendekatan # 2 berfungsi, tetapi masih ada flicker (dalam aktivitas) ketika transisi terjadi. bilah status dan bilah navigasi tidak berkedip lagi.
Akshat
16
@alex Ini bekerja hampir sempurna bagi saya sampai kami beralih ke perpustakaan dukungan baru dengan android.support.design.widget.TabLayout dan android.support.design.widget.AppBarLayout - sekarang kelap-kelipnya kembali
Noa Drach
6
android.R.id.navigationBarBackground dapat memberi Anda NPE di perangkat Samsung atau HTC karena mereka tidak memiliki bilah navigasi di layar. Lakukan cek nol sebelum menambahkannya sebagai elemen bersama
Boy
8
Saya mencoba solusi ini pada nexus 6 dengan android nougat. Tetapi kedua pendekatan itu tidak berhasil untuk saya.
Pardeep Kr
4

Mencegah transisi Aktivitas agar tidak mengganggu transisi elemen bersama:

Pada aktivitas yang keluar, panggil getWindow (). SetExitTransition (null);

Pada aktivitas yang masuk, panggil getWindow (). SetEnterTransition (null);

Dari https://stackoverflow.com/a/34907685/967131

Saya menduga ini mungkin memiliki efek samping, tetapi tidak tahu pasti. Itu mati sederhana dan bekerja sekalipun.

Cegah elemen tertentu agar tidak berkedip:

Saya mulai dengan jawaban Alex Lockwood dan melakukan sedikit percobaan untuk membuatnya bekerja. Inti dari hal itu benar, walaupun saya tidak memerlukan kode yang disarankannya untuk Aktivitas penerima, tetapi saya mengalami beberapa masalah dengan menyebutnya dalam Fragmen (bukan Aktivitas) dan dengan menetapkan bilah alat sebagai bilah tindakan.

Oh, soal Fragmen? Saya melihat banyak komentar yang mencoba mengambil referensi ke bilah status dan bilah navigasi adalah nol. Hal yang sama terjadi pada saya juga, sampai saya menyadari bahwa saya tidak akan menemukan yang ada di layout Fragment ... mereka berada di atas level itu. Karenanya, kode di bawah ini untuk mendapatkan tampilan dekorasi dari Aktivitas dan cari itu. Kemudian saya menemukan mereka tanpa masalah.

Pada akhirnya, saya mengembangkan metode utilitas ini:

public static Bundle transitionOptions(Activity activity, int transitionViewResId, int transitionNameResId) {
   if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
       return null;
   }

   View decorView = activity.getWindow().getDecorView();
   View statusBar = decorView.findViewById(android.R.id.statusBarBackground);
   View navigationBar = decorView.findViewById(android.R.id.navigationBarBackground);
   View appBarLayout = decorView.findViewById(**R.id.appbarlayout**);
   View transitionView = decorView.findViewById(transitionViewResId);
   String transitionName = activity.getString(transitionNameResId);

   List<Pair<View, String>> pairs = new ArrayList<>();
   pairs.add(Pair.create(statusBar, Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME));
   pairs.add(Pair.create(navigationBar, Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME));
   if (appBarLayout != null) {
       pairs.add(Pair.create(appBarLayout, activity.getString(**R.string.transition_appbarlayout**)));
   }
   pairs.add(Pair.create(transitionView, transitionName));
   //noinspection unchecked - we're not worried about the "unchecked" conversion of List<Pair> to Pair[] here
   return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, pairs.toArray(new Pair[pairs.size()]))
           .toBundle();
}

Catatan R.string.transition_appbarlayout dan R.id.appbarlayout . ID ini sewenang-wenang, asalkan cocok dengan apa yang digunakan kode Anda. Dalam XML saya, saya tata letak bilah tindakan khusus seperti itu (diedit ke bagian terpenting):

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout
    android:id="**@+id/appbarlayout**"
    android:transitionName="**@string/transition_appbarlayout**">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"/>
</android.support.design.widget.AppBarLayout>

Jika Anda tidak menggunakan Bilah Alat seperti ini, bagian itu dapat dihapus dari metode utilitas.

Maka Anda akan menyebutnya di Fragmen Anda seperti ini:

startActivity(intent, UIUtils.transitionOptions(getActivity(),
                        R.id.**my_view**,
                        R.string.**transition_my_view**));

Menggunakan nilai apa pun yang Anda inginkan, asalkan cocok dengan XML Anda.

Ini mencegah bilah status, bilah alat dan bilah navigasi (tombol back / home / apps terkini) berkedip selama transisi. Sisa dari transisi Aktivitas adalah normal.

Dalam kasus saya, tema aplikasi kami memiliki android:windowBackgroundwarna biru. Ini menyebabkan flash biru dalam transisi, yang cukup membuat frustrasi. Tetapi alih-alih melakukan perubahan yang memengaruhi seluruh aplikasi seperti itu, untuk saat ini saya akan menggunakan opsi pertama, cepat dan kotor.

Chad Schultz
sumber
3

Anda harus membagikannya ActivityOptions.makeSceneTransitionAnimation.

Misalnya:

ActivityOptions.makeSceneTransitionAnimation(... Pair.create(activity.findViewById(android.R.id.window_status_bar), Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME) 

(permisi psuedo; saya tidak memiliki nilai android.R.id persis)

Anda dapat menjalankan transisi yang sesuai setelah membagikan tampilan.

klmprt
sumber
Apakah ini berhasil untuk Anda? Saya mencoba ini dan bilah status dan bilah navigasi masih berkedip putih selama transisi.
rlay3
Ya, itu berhasil untuk saya. Apakah Anda yakin tidak ada tampilan lain yang mungkin tumpang tindih untuk sementara waktu? Sudahkah Anda mencoba menyiapkan transisi aktivitas sederhana di lingkungan 'kotak pasir' untuk mengisolasi masalah?
klmprt
@klmprt Saya pikir Anda juga perlu menunda transisi masuk untuk membuatnya berfungsi ... Saya juga tidak dapat mencegah bilah status / navigasi dari animasi hanya dengan berbagi tampilan. Saya kira Anda perlu menunggu tampilan dekorasi jendela untuk menyelesaikan tata letaknya sebelum Anda membiarkan transisi masuk dimulai.
Alex Lockwood
@klmprt Namun jawaban saya masih belum membahas adalah bagaimana mencegah warna latar belakang Action Bar dari animasi selama transisi. Jika kedua Activity berbagi warna latar belakang Bilah Tindakan yang sama, maka warna latar bilah tindakan akan tampak bernyawa ketika bilah tindakan Aktivitas secara bertahap memudar dan bilah tindakan Aktivitas dengan lembut memudar. Apakah Anda tahu cara menyiasatinya isu?
Alex Lockwood
@ KLMPRT Sebenarnya, saya pikir ada solusi yang lebih baik. Anda cukup mengecualikan latar belakang bilah navigasi / status sebagai target dalam transisi keluar / masuk fade standar jendela. Lihat jawaban saya yang diperbarui untuk detailnya.
Alex Lockwood
3

Sejauh yang saya mengerti ini disebabkan oleh tumpang tindih kegiatan transisi. Untuk mengatasi masalah ini, saya telah menggunakan nilai-nilai berikut dalam onCreate()metode kedua aktivitas:

getWindow().setAllowEnterTransitionOverlap(false);
getWindow().setAllowReturnTransitionOverlap(false);
Randeep
sumber
3

Saya baru saja mengalami masalah yang sama ini, dan jawabannya tampaknya tidak ada bagian penting dari teka-teki. Ingat bahwa pada transisi elemen bersama, semuanya terjadi di Aktivitas Tujuan .

Untuk menghapus efek flashing, cukup tambahkan berikut ini ke aktivitas yang dipanggil:

Fade fade = new Fade();
fade.excludeTarget(android.R.id.statusBarBackground, true);
fade.excludeTarget(android.R.id.navigationBarBackground, true);

getWindow().setEnterTransition(fade);
getWindow().setExitTransition(fade);

Ini harus menyelesaikan masalah Anda!

LukeWaggoner
sumber
Hai. Bukankah ini sama dengan Pendekatan # 1 di jawaban teratas?
rlay3
@ rlay3 Tidak sepenuhnya. Sejauh yang saya tahu, dia tidak pernah menyebutkan fakta bahwa ini hanya perlu ditetapkan dalam kegiatan tujuan.
LukeWaggoner
hey @LukeWaggoner, bisakah Anda membantu saya: stackoverflow.com/questions/50189286/…
blackHawk
Anda harus mengecualikan bilah status dan bilah navigasi di kedua aktivitas, penelepon dan yang dipanggil.
Giovanni Di Gregorio
1

getWindow().setEnterTransition(null); pada transisi Memasuki dihapus overlay putih untuk saya.

Dominic Davies
sumber
0

Inilah cara saya melakukannya. Saya berbagi Status Bardan Navigation Bardalam SharedElementTransitionbersama dengan ImageView:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  View imageView = view.findViewById(R.id.iv);
  Resources resources = view.getResources();
  imageView.setTransitionName(resources.getString(R.string.transition_image_thumbnail));

  Pair<View, String> p1 = Pair.create(imageView, resources.getString(R.string.transition_image_thumbnail));

  Window window = getActivity().getWindow();

  View navigationBar = getActivity().findViewById(android.R.id.navigationBarBackground);
  View statusBar = getActivity().findViewById(android.R.id.statusBarBackground);

  Pair<View, String> p2 = Pair.create(statusBar, statusBar.getTransitionName());
  Pair<View, String> p3 = Pair.create(navigationBar, navigationBar.getTransitionName());

  ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
          p1, p2, p3);

  ActivityCompat.startActivity(getActivity(), intent, options.toBundle());
} else {
  startActivity(intent);
}
toobsco42
sumber