Beralih antara gambar Laci Navigasi Android dan Karet Atas saat menggunakan fragmen

177

Saat menggunakan Navigasi Drawer, pengembang Android merekomendasikan bahwa di ActionBar "hanya layar yang diwakili dalam Navigasi Drawer yang benar-benar harus memiliki gambar Navigasi Drawer" dan bahwa "semua layar lain memiliki karat tradisional."

Lihat di sini untuk perincian: http://youtu.be/F5COhlbpIbY

Saya menggunakan satu aktivitas untuk mengontrol beberapa level fragmen dan dapat menampilkan gambar Drawer Navigasi untuk ditampilkan dan berfungsi di semua level.

Saat membuat fragmen level yang lebih rendah, saya dapat memanggil ActionBarDrawerToggle setDrawerIndicatorEnabled(false)untuk menyembunyikan gambar Laci Navigasi dan menampilkan tanda Up

LowerLevelFragment lowFrag = new LowerLevelFragment();

//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout, 
lowFrag, "lowerFrag").addToBackStack(null).commit();

Masalah yang saya alami adalah ketika saya menavigasi kembali ke fragmen tingkat atas yang masih ditampilkan oleh Karat Atas alih-alih gambar Navigasi Laci asli. Adakah saran tentang cara "menyegarkan" ActionBar pada fragmen tingkat atas untuk menampilkan kembali gambar Laci Navigasi?


Larutan

Saran Tom bekerja untukku. Inilah yang saya lakukan:

Aktifitas utama

Aktivitas ini mengontrol semua fragmen dalam aplikasi.

Saat menyiapkan fragmen baru untuk menggantikan yang lain, saya mengatur DrawerToggle setDrawerIndicatorEnabled(false)seperti ini:

LowerLevelFragment lowFrag = new LowerLevelFragment();

//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout,   
lowFrag).addToBackStack(null).commit();

Selanjutnya, onBackPressedsebagai ganti, saya mengembalikan yang di atas dengan mengatur DrawerToggle setDrawerIndicatorEnabled(true)seperti ini:

@Override
public void onBackPressed() {
    super.onBackPressed();
    // turn on the Navigation Drawer image; 
    // this is called in the LowerLevelFragments
    setDrawerIndicatorEnabled(true)
}

Di LowerLevelFragments

Dalam fragmen yang saya modifikasi onCreatedan onOptionsItemSelectedseperti ini:

Dalam onCreateditambahkan setHasOptionsMenu(true)untuk mengaktifkan konfigurasi menu opsi. Juga atur setDisplayHomeAsUpEnabled(true)untuk mengaktifkan < di bilah tindakan:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // needed to indicate that the fragment would 
    // like to add items to the Options Menu        
    setHasOptionsMenu(true);    
    // update the actionbar to show the up carat/affordance 
    getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}

Kemudian onOptionsItemSelectedsetiap kali < ditekan, panggilan onBackPressed()dari aktivitas untuk naik satu tingkat dalam hierarki dan menampilkan Navigasi Drawer Image:

@Override
public boolean onOptionsItemSelected(MenuItem item) {   
    // Get item selected and deal with it
    switch (item.getItemId()) {
        case android.R.id.home:
            //called when the up affordance/carat in actionbar is pressed
            getActivity().onBackPressed();
            return true;
         
    }
EvilAsh
sumber
2
Juga dalam metode onBackPressed () Anda dapat memeriksa berapa banyak entri yang ada di tumpukan belakang dengan metode getFragmentManager (). GetBackStackEntryCount () dan aktifkan indikator laci hanya jika hasilnya 0. Dalam hal itu tidak perlu mengaktifkan homeAsUpIndicator di setiap LowerLevelFragments.
pvshnik
2
Ini sangat berguna! Anda harus memindahkan "solusi" bagian dari posting Anda dan menjadikannya "jawaban" yang sebenarnya. Anda akan mendapatkan lebih banyak poin untuk upvote dan ini adalah jawaban
Oleksiy
Mengapa Anda mengganti fragmen di sini: .replace(R.id.frag_layout. Jika ini adalah satu tingkat hirarki lagi saya harapkan Anda .addke backstack.
JJD
5
Bro, bagaimana Anda mereferensikan bagian theDrawerToggle.setDrawerIndicatorEnabled(false);dalam fragmen? Saya pikir itu dideklarasikan di file kelas Kegiatan Utama. Saya tidak dapat menemukan cara untuk referensi ini. Ada petunjuk?
Skynet
1
Saat menggunakan bilah alat, saya harus mengganti opsi tampilan untuk tidak menggunakan rumah seperti pada saat itu. Kalau tidak, setDisplayOptions()metode di dalam ToolbarWidgetWrapper(dari paket android.support.v7.internal.widget internal) tidak akan membuat ulang ikon saat memasukkan fragmen yang sama untuk kedua kalinya. Hanya meninggalkan ini di sini ketika orang lain menemukan masalah ini juga.
Wolfram Rittmeyer

Jawaban:

29

Anda telah menulis bahwa, untuk mengimplementasikan fragmen level lebih rendah, Anda mengganti fragmen yang ada, sebagai lawan mengimplementasikan fragmen level bawah dalam aktivitas baru.

Saya akan berpikir bahwa Anda kemudian harus mengimplementasikan fungsionalitas kembali secara manual: ketika pengguna menekan kembali Anda memiliki kode yang muncul stack (misalnya dalam Activity::onBackPressedmenimpa). Jadi, di mana pun Anda melakukannya, Anda dapat membalikkannya setDrawerIndicatorEnabled.

Tom
sumber
Terima kasih Tom, itu berhasil! Saya telah memperbarui posting asli dengan solusi yang saya gunakan.
EvilAsh
6
Bagaimana cara merujuk theDrawerToggle di fragmen level yang lebih rendah? Itu telah didefinisikan dalam kegiatan Utama, tidak bisa mendapatkan kepalaku di sekitar ini!
Skynet
1
Anda bisa mendapatkan aktivitas dari fragmen. Jadi cukup tambahkan pengambil dalam aktivitas utama Anda untuk mengakses toggle drawer.
Raphael Royer-Rivard
83

Semudah 1-2-3.

Jika Anda ingin mencapai:

1) Indikator Laci - ketika tidak ada pecahan di Back Stack atau Drawer dibuka

2) Panah - ketika beberapa Fragmen berada di Back Stack

private FragmentManager.OnBackStackChangedListener
        mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
        syncActionBarArrowState();
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    getSupportActionBar().setDisplayShowHomeEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    mDrawerToggle = new ActionBarDrawerToggle(
            this,             
            mDrawerLayout,  
            R.drawable.ic_navigation_drawer, 
            0, 
            0  
    ) {

        public void onDrawerClosed(View view) {
            syncActionBarArrowState();
        }

        public void onDrawerOpened(View drawerView) {
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        }
    };

    mDrawerLayout.setDrawerListener(mDrawerToggle);
    getSupportFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener);
}

@Override
protected void onDestroy() {
    getSupportFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener);
    super.onDestroy();
}

private void syncActionBarArrowState() {
    int backStackEntryCount = 
        getSupportFragmentManager().getBackStackEntryCount();
    mDrawerToggle.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}

3) Kedua indikator bertindak sesuai dengan bentuknya

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (mDrawerToggle.isDrawerIndicatorEnabled() && 
        mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    } else if (item.getItemId() == android.R.id.home && 
               getSupportFragmentManager().popBackStackImmediate()) {
        return true;
    } else {
        return super.onOptionsItemSelected(item);
    }
}

PS Lihat Membuat Laci Navigasi di Android Pengembang tentang tips lain tentang perilaku indikator 3-baris.

riwnodennyk
sumber
3
Saya akhirnya pada sesuatu yang mirip dengan milik Anda. Saya pikir solusi ini (menggunakan BackStackChangedListener untuk beralih di antara apa yang ditampilkan) adalah yang paling elegan. Namun saya memiliki dua perubahan: 1) Saya tidak memanggil / mengubah indikator laci di panggilan onDrawerClosed / onDrawerOpened - tidak diperlukan, dan 2) Saya membiarkan Fragmen itu sendiri menangani navigasi Naik dengan membuat AbstractFragment yang semuanya mewarisi mana mengimplementasikan onOptionsItemSelected (..) dan yang selalu memanggil setHasOptionsMenu (true);
Espen Riskedal
2
Anda juga harus mengunci gerakan gesek untuk laci navigasi dalam mode panah: mDrawerLayout.setDrawerLockMode (DrawerLayout.LOCK_MODE_LOCKED_CLOSED); dan aktifkan lagi dalam mode indikator laci
artkoenig
5
Saya akhirnya melakukan semua ini dalam fragmen NavigationDrawer saya. Berfungsi seperti pesona, terima kasih.
frostymarvelous
1
setActionBarArrowDependingOnFragmentsBackStack()... nama yang panjang: P
S.Thiongane
2
Ini tidak menunjukkan tombol atas untuk saya ketika saya beralih dari fragmen A ke B dan menambahkan A ke backstack. :(
aandis
14

Saya telah menggunakan hal berikutnya:

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
            @Override
            public void onBackStackChanged() {
                if(getSupportFragmentManager().getBackStackEntryCount() > 0){
                    mDrawerToggle.setDrawerIndicatorEnabled(false);
                    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
                }
                else {
                    getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                    mDrawerToggle.setDrawerIndicatorEnabled(true);
                }
            }
        });
Yuriy Sych
sumber
1
TERIMA KASIH BANYAK INI adalah satu-satunya yang benar-benar berfungsi. Setelah saya menambahkan ini, drawerToggle.setToolbarNavigationClickListener(pendengar ini dipanggil saat mengklik panah
EpicPandaForce
Terima kasih @ Yuriy, ini membantu saya dalam menyelesaikan masalah saya
Muhammad Shoaib Murtaza
12

Jika tombol bilah tindakan atas Anda tidak berfungsi, jangan lupa menambahkan pendengar:

// Navigation back icon listener
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onBackPressed();
        }
});

Saya mengalami kesulitan menerapkan navigasi laci dengan tombol beranda, semuanya berfungsi kecuali tombol aksi.

Burrich
sumber
10

Cobalah menangani pemilihan item Beranda di MainActivity tergantung pada status DrawerToggle. Dengan cara ini Anda tidak perlu menambahkan kode yang sama ke setiap fragmen.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Only handle with DrawerToggle if the drawer indicator is enabled.
    if (mDrawerToggle.isDrawerIndicatorEnabled() &&
            mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle action buttons
    switch (item.getItemId()) {
        // Handle home button in non-drawer mode
        case android.R.id.home:
            onBackPressed();
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}
dzeikei
sumber
+1 solusi rapi. Agar jawaban Anda sepenuhnya dapat digunakan, Anda harus menambahkan cek di backstack. Ketika kosong maka secara otomatis mengatur indikator laci ke true.
HpTerm
@ HPTerm Saya menangani backstack onBackPressed()karena saya ingin perilaku yang sama untuk keduanya.
dzeikei
6

MENGIKUTI

Solusi yang diberikan oleh @dzeikei rapi, tetapi dapat diperpanjang, ketika menggunakan fragmen, untuk secara otomatis menangani pengaturan kembali indikator laci ketika backstack kosong.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Only handle with DrawerToggle if the drawer indicator is enabled.
    if (mDrawerToggle.isDrawerIndicatorEnabled() &&
            mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle action buttons
    switch (item.getItemId()) {
        // Handle home button in non-drawer mode
        case android.R.id.home:
            // Use getSupportFragmentManager() to support older devices
            FragmentManager fragmentManager = getFragmentManager();
            fragmentManager.popBackStack();
            // Make sure transactions are finished before reading backstack count
            fragmentManager.executePendingTransactions();
            if (fragmentManager.getBackStackEntryCount() < 1){
                mDrawerToggle.setDrawerIndicatorEnabled(true);  
            }
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}

EDIT

Untuk pertanyaan @JJD.

Fragmen-fragmen tersebut disimpan / dikelola dalam suatu kegiatan. Kode di atas ditulis sekali dalam aktivitas itu, tetapi hanya menangani tanda sisipan untukonOptionsItemSelected .

Di salah satu aplikasi saya, saya juga perlu menangani perilaku sisir atas ketika tombol kembali ditekan. Ini bisa ditangani dengan mengesampingkan onBackPressed.

@Override
public void onBackPressed() {
    // Use getSupportFragmentManager() to support older devices
    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.executePendingTransactions();
    if (fragmentManager.getBackStackEntryCount() < 1){
        super.onBackPressed();
    } else {
        fragmentManager.executePendingTransactions();
        fragmentManager.popBackStack();
        fragmentManager.executePendingTransactions();
        if (fragmentManager.getBackStackEntryCount() < 1){
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        }
    }
};

Perhatikan duplikasi kode antara onOptionsItemSelecteddan onBackPressedyang dapat dihindari dengan membuat metode dan memanggil metode itu di kedua tempat.

Juga perhatikan saya menambahkan dua kali lagi executePendingTransactionsyang dalam kasus saya diperlukan atau kalau tidak saya kadang-kadang memiliki perilaku aneh dari sisir atas.

HpTerm
sumber
Apakah ini kode lengkap yang perlu saya tambahkan untuk menerapkan perilaku caret Up atau Anda merujuk ke salah satu posting lain? Tolong jelaskan ini.
JJD
Terima kasih atas hasil editnya. Sebenarnya saya mempertahankan mDrawerToggledalam NavigationDrawerFragmentkelas. Untuk membuatnya bekerja saya harus juga beralih keadaan tombol home / indikator - lihat: NavigationDrawerFragment#toggleDrawerIndicator. Lebih lanjut, saya tidak yakin Anda perlu check in awal onOptionsItemSelected: Saya membatalkan komentar itu. - Contoh sederhana
JJD
Implementasi yang direvisi : Anda benar tentang check-in awal onOptionsItemSelected. Ini memastikan bahwa Navigasi Drawer masih terbuka di hierarki tingkat atas. Namun, saya memindahkan kode ke NavigationDrawerFragment#onOptionsItemSelected. Ini membantu saya untuk tidak mengekspos mDrawerToggleke MainActivity.
JJD
@ JJD Saya ingat berjuang sedikit memiliki segalanya bekerja untuk tanda atas untuk setiap tingkat hierarki. Selama itu bekerja untuk Anda juga tidak apa-apa. Tentu saja, seperti yang Anda katakan, Anda dapat memindahkan kode di tempat lain untuk tidak mengeksposnya.
HpTerm
2

Saya membuat antarmuka untuk aktivitas hosting untuk memperbarui status tampilan menu hamburger. Untuk fragmen tingkat atas, saya mengatur sakelar ke truedan untuk fragmen yang ingin saya tampilkan panah <saya menyetel sakelar ke false.

public class SomeFragment extends Fragment {

    public interface OnFragmentInteractionListener {
        public void showDrawerToggle(boolean showDrawerToggle);
    }

    private OnFragmentInteractionListener mListener;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            this.mListener = (OnFragmentInteractionListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        mListener.showDrawerToggle(false);
    }
}

Kemudian di Aktivitas saya ...

public class MainActivity extends Activity implements SomeFragment.OnFragmentInteractionListener {

    private ActionBarDrawerToggle mDrawerToggle;

    public void showDrawerToggle(boolean showDrawerIndicator) {
        mDrawerToggle.setDrawerIndicatorEnabled(showDrawerIndicator);
    }

}
Bill Mote
sumber
2

Ini jawaban bekerja tapi ada sedikit masalah dengan itu. Itu getSupportActionBar().setDisplayHomeAsUpEnabled(false)tidak dipanggil secara eksplisit dan itu menyebabkan ikon laci disembunyikan bahkan ketika tidak ada item di backstack sehingga mengubah setActionBarArrowDependingOnFragmentsBackStack()metode ini bekerja untuk saya.

private void setActionBarArrowDependingOnFragmentsBackStack() {
        int backStackEntryCount = getSupportFragmentManager()
                .getBackStackEntryCount();
        // If there are no items in the back stack
        if (backStackEntryCount == 0) {
            // Please make sure that UP CARAT is Hidden otherwise Drawer icon
            // wont display
            getSupportActionBar().setDisplayHomeAsUpEnabled(false);
            // Display the Drawer Icon
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        } else {
            // Show the Up carat
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            // Hide the Drawer Icon
            mDrawerToggle.setDrawerIndicatorEnabled(false);
        }

    }
Sheraz Ahmad Khilji
sumber
dalam kasus saya solusi rigth hanya menggunakan actionBarDrawerToggle.setDrawerIndicatorEnabled (getSupportFragmentManager (). getBackStackEntryCount () <0);
Gorets
1

Logikanya jelas. Tampilkan tombol kembali jika tumpukan back fragment jelas. Perlihatkan animasi materi hamburger-kembali jika tumpukan fragmen tidak jelas.

getSupportFragmentManager().addOnBackStackChangedListener(
    new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            syncActionBarArrowState();
        }
    }
);


private void syncActionBarArrowState() {
    int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
    mNavigationDrawerFragment.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}

//add these in Your NavigationDrawer fragment class

public void setDrawerIndicatorEnabled(boolean flag){
    ActionBar actionBar = getActionBar();
    if (!flag) {
        mDrawerToggle.setDrawerIndicatorEnabled(false);
        actionBar.setDisplayHomeAsUpEnabled(true);
        mDrawerToggle.setHomeAsUpIndicator(getColoredArrow());
    } else {
        mDrawerToggle.setDrawerIndicatorEnabled(true);
    }
    mDrawerToggle.syncState();
    getActivity().supportInvalidateOptionsMenu();
}

//download back button from this(https://www.google.com/design/icons/) website and add to your project

private Drawable getColoredArrow() {
    Drawable arrowDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_arrow_back_black_24dp);
    Drawable wrapped = DrawableCompat.wrap(arrowDrawable);

    if (arrowDrawable != null && wrapped != null) {
        // This should avoid tinting all the arrows
        arrowDrawable.mutate();
        DrawableCompat.setTint(wrapped, Color.GRAY);
    }
    return wrapped;
}
kml_ckr
sumber
1

Jika Anda melihat aplikasi GMAIL dan datang ke sini untuk mencari ikon carret / keterjangkauan ..

Saya akan meminta Anda untuk melakukan ini, tidak ada jawaban di atas yang jelas. saya dapat mengubah jawaban yang diterima.

  • NavigationDrawer -> Listview berisi subfragmen.


  • subfragmen akan terdaftar seperti ini

  • firstFragment == posisi 0 ---> ini akan memiliki subfragmen -> fragmen

  • secondFragment
  • ketigaFragment dan sebagainya ....

Di firstFragment Anda memiliki fragmen lain.

Sebut ini di DrawerActivity

getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if (getFragmentManager().getBackStackEntryCount() > 0) {
                mDrawerToggle.setDrawerIndicatorEnabled(false);
            } else {
                mDrawerToggle.setDrawerIndicatorEnabled(true);
            }
        }
    });

dan dalam fragmen

    setHasOptionsMenu(true);    

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Get item selected and deal with it
    switch (item.getItemId()) {
        case android.R.id.home:
            //called when the up affordance/carat in actionbar is pressed
            activity.onBackPressed();
            return true;
    }
    return false;
}

Pada metode aktivitas Drawer pada OnBackPress mengatur drawer toggle ke true untuk mengaktifkan kembali ikon daftar navigasi.

Terima kasih, Pusp

EngineSense
sumber
0

IMO, menggunakan onNavigateUp () (seperti yang ditunjukkan di sini ) dalam solusi riwnodennyk atau Tom lebih bersih dan tampaknya bekerja lebih baik. Cukup ganti kode onOptionsItemSelected dengan ini:

@Override
public boolean onSupportNavigateUp() {
    if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
        // handle up navigation
        return true;
    } else {
        return super.onSupportNavigateUp();
    }
}
0101100101
sumber