Bagaimana cara mengubah tampilan konten ketika tab lain dipilih? (Contoh Android HoneyCombGallery terkait)

Saya mencoba memodifikasi kode contoh HoneyCombGallery untuk mengubah tampilan yang ditampilkan ketika tab diubah. Pada contoh yang diberikan Google, fragmen yang ditampilkan selalu sama saat Anda berpindah tab. Hanya data yang ditampilkan yang berubah. Namun, yang ingin saya lakukan adalah mengubah fragmen dan kelas yang ditampilkan saat tab diubah.

Berikut kode yang telah saya modifikasi pada contoh HoneyCombGallery: (MainActivity.java)

public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
    int nTabSelected = tab.getPosition();
    switch (nTabSelected) {
    case 0:
        setContentView(R.layout.settings_fragment);
        break;
    case 1:
        setContentView(R.layout.main);
        break;
    }

segala sesuatu yang lain hampir sama. (perhatikan juga bahwa, seperti sebelumnya, MainActivity.onCreate memanggil setContentView(R.layout.main) - Saya belum pernah mendengar bahwa memanggil setContentView beberapa kali tidak diperbolehkan. Saya tetap bermaksud melakukannya di setiap klik tab)

Dan berikut adalah tata letak yang digelembungkan dalam kode (settings_fragment.xml)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/frags2">
    <fragment class="com.example.android.hcgallery.SettingsFragment"
            android:id="@+id/settings_fragment2"
            android:visibility="gone"
            android:layout_marginTop="?android:attr/actionBarSize"
            android:layout_width="@dimen/titles_size"
            android:layout_height="match_parent" />
</LinearLayout>

"Fragmen pengaturan" adalah kelas saya yang memperluas Fragmen: (SettingsFragment.java)

public class SettingsFragment extends Fragment {
    private View v;
    @Override
    public View onCreateView(final LayoutInflater inflater,
            final ViewGroup container, final Bundle savedInstanceState) {

        if (v != null)
            return v;
        v = inflater.inflate(R.layout.settings_layout, container, false);
        return v;
    }
}

Xmlnya juga sangat mudah (settings_layout.xml):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/settings_layout" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingTop="10dip">

    <TextView android:text="1.0.0" android:id="@+id/tv_version2"
        android:layout_marginTop="?android:attr/actionBarSize"
        android:textSize="15dp" android:textStyle="bold" android:typeface="sans"
        android:layout_height="wrap_content" android:layout_width="200dp"
        android:paddingTop="10dip" android:gravity="right"  >
    </TextView>
</RelativeLayout>

Ini tampak mudah dan ketika aplikasi dimulai, settings_fragment terlihat karena tab ke-0 dipilih secara default. Saat kita memilih tab berikutnya, saya berharap setContentView(R.layout.main) dijalankan dan saya akan melihat tata letak utama (ini sama seperti pada contoh Google: tidak ada modifikasi). Namun, yang saya dapatkan adalah pengecualian ini:

12-15 16:49:28.501: ERROR/AndroidRuntime(31485): FATAL EXCEPTION: main
12-15 16:49:28.501: ERROR/AndroidRuntime(31485): android.view.InflateException: Binary XML file line #23: Error inflating class fragment
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:688)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.view.LayoutInflater.rInflate(LayoutInflater.java:724)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.view.LayoutInflater.inflate(LayoutInflater.java:479)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.view.LayoutInflater.inflate(LayoutInflater.java:391)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.view.LayoutInflater.inflate(LayoutInflater.java:347)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:224)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.app.Activity.setContentView(Activity.java:1777)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at com.example.android.hcgallery.MainActivity.onTabSelected(MainActivity.java:109)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at com.android.internal.app.ActionBarImpl.selectTab(ActionBarImpl.java:462)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at com.android.internal.app.ActionBarImpl$TabImpl.select(ActionBarImpl.java:787)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at com.android.internal.widget.ActionBarView$TabClickListener.onClick(ActionBarView.java:950)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.view.View.performClick(View.java:3100)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.view.View$PerformClick.run(View.java:11644)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.os.Handler.handleCallback(Handler.java:587)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.os.Handler.dispatchMessage(Handler.java:92)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.os.Looper.loop(Looper.java:126)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.app.ActivityThread.main(ActivityThread.java:3997)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at java.lang.reflect.Method.invokeNative(Native Method)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at java.lang.reflect.Method.invoke(Method.java:491)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at dalvik.system.NativeStart.main(Native Method)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485): Caused by: java.lang.IllegalArgumentException: Binary XML file line #23: Duplicate id 0x7f09000a, tag null, or parent id 0x7f090009 with another fragment for com.example.android.hcgallery.TitlesFragment
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.app.Activity.onCreateView(Activity.java:4095)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:664)
12-15 16:49:28.501: ERROR/AndroidRuntime(31485):     ... 21 more

Kesalahannya tampaknya adalah "Android.view.InflateException: Baris file XML biner #23: Kesalahan menggembungkan fragmen kelas"

Apa artinya ini? Bisakah saya melakukan setContentView untuk mengubah fragmen dan tata letak sesuai keinginan ketika tab berbeda dipilih? Jika ini bukan cara yang tepat untuk memiliki tata letak berbeda untuk tab berbeda, apa cara yang tepat untuk melakukannya?

Saya telah mengunggah seluruh kode sumber zip ke HoneycombGallery_Modified.zip - https://docs.google.com/open?id=0B0FHtQOpOYx6YWFiMDcxYmEtOGFkMy00NWU2LTk2MzMtZWY0YzdmZWUzMDU0 (Klik "File->Unduh asli" jika Anda ingin mengunduh seluruh file zip)


person Community    schedule 15.12.2011    source sumber


Jawaban (3)


Anda harus membaca Dokumen Pengembang tentang Fragmen - dan melihat Melakukan Transaksi Fragmen.

person kaspermoerch    schedule 15.12.2011
comment
Saya telah membaca panduan fragmen.. namun, mengapa setContentView() tidak berfungsi? Ini berfungsi pertama kali ketika tab ke-0 dipilih. Mengapa gagal untuk kedua kalinya ketika tab berikutnya dipilih? Bisakah kita melakukan setContentView pada file XML yang sama untuk kedua kalinya? - person ; 15.12.2011
comment
Android Docs sepertinya tidak secara eksplisit mengatakan bahwa Anda tidak dapat menelepon setContentView dua kali dalam Activity. Namun saya akan menahan diri untuk tidak melakukan hal itu mungkin juga akan membuat kelas Activity Anda menjadi gumpalan. Maka Fragments adalah jalan yang lebih baik untuk diambil. - person kaspermoerch; 15.12.2011
comment
Ok terima kasih. Jika saya menggunakan fragmen, saya mengalami masalah ini: Saat tablet diputar, hal berikut terjadi: MainActivity dibuat, tab dipilih lagi dan Android juga membuat ulang Fragmen saya. Namun logika MainActivity dan tabSelected juga mencoba membuat ulang fragmen yang sama seperti saat ap dijalankan untuk pertama kalinya. Saya dapat memastikan bahwa saya tidak membuat di MainActivity onCreate dengan melihat file yang disimpanInstanceState. Tapi pada akhirnya kita akan membuatnya di onTabSelected. Jadi setelah rotasi kita akan membuat fragmen dua kali. Apakah ada cara untuk menghindari hal ini? - person ; 15.12.2011
comment
Saya akhirnya menggunakan Fragmen dan memperbaiki masalah ini, jadi tandai orang pertama yang meminta saya menggunakan fragmen sebagai jawaban yang benar. - person ; 08.01.2012

Saya berhasil mencapai apa yang Anda butuhkan.

Situasi saya: Saya memiliki bilah tindakan dengan banyak tab dan tab ini memerlukan tata letak berbeda agar dapat ditampilkan dengan benar. Saya juga menggunakan ActionBarSherlock untuk kompatibilitas ke belakang tetapi menurut saya prinsip yang sama dapat dilakukan dengan kelas asli.

Saya memiliki 2 jenis tab:

  • Tab untuk menampilkan daftar dengan sebuah fragmen untuk menampilkan konten item yang dipilih dalam daftar (itulah contoh khas dari fragmen)
  • Tab untuk menampilkan webView sederhana

Begini cara saya melakukannya, saya memiliki kelas bernama ActionBarActivity yang menginisialisasi tab dan berisi TabListener.

Inilah ActionBarActivity saya:

public class ActionBarActivity
        extends SherlockFragmentActivity
{
    /**
    * The current layoutId on the screen
    */
    private int layoutId;
    protected final int LAYOUT_LIST = R.layout.list_container;
    protected final int LAYOUT_WEBVIEW = R.layout.webview_container;

    @Override
    public void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
        ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode( ActionBar.NAVIGATION_MODE_TABS );
        getSupportActionBar().removeAllTabs();
        // we pass the layout to use for each tab
        addTab("Tab 1", LAYOUT_LIST, ListFragment.class);
        addTab("Tab 2", LAYOUT_WEBVIEW, WebViewFragment.class);

        // in case of screen orientation
        if ( savedInstanceState != null ) {
            actionBar.setSelectedNavigationItem( savedInstanceState.getInt( "tab", 0 ) );
        }
        // initial setting of the content 
        updateContentView( actionBar.getSelectedTab() );

    }

    private void addTab( String title, int contentLayoutId, Class<? extends Fragment> tabClass )
    {
        Tab t = getSupportActionBar().newTab();
        t.setText( tabTitle );
        t.setTag( contentLayoutId );
        t.setTabListener( new TabListener( this, tabTitle, activity, null ) );
        getSupportActionBar().addTab( t );
    }

    // that's how I deal to replace the layout used to display the tab's content 
    protected void updateContentView( Tab t )
    {
        FragmentManager fgtManager = getSupportFragmentManager();
        int tabLayoutId = ( Integer ) t.getTag();

        if ( tabLayoutId == LAYOUT_WEBVIEW ) {

            Fragment details = fgtManager.findFragmentById( R.id.detail_fragment);

            if(details != null && details.isInLayout()){
                FragmentTransaction transact = fgtManager.beginTransaction();
                transact.remove( details );
                transact.commit();
                details = null;
            }
        }

        if ( this.layoutId != tabLayoutId ) {
            layoutId = tabLayoutId;
            setContentView( layoutId );
        }
    }

    @Override
    protected void onSaveInstanceState( Bundle outState )
    {
        super.onSaveInstanceState( outState );
        outState.putInt( "tab", getSupportActionBar().getSelectedNavigationIndex() );
    }

    // see APIDemos.app.FragmentTabs for more details 
    public static class TabListener
            implements ActionBar.TabListener
    {
        private SherlockFragmentActivity mActivity;

        public void onTabSelected( Tab tab, FragmentTransaction ft )
        {
            // When we select a tab we udpate the content of the ActionBarActivity 
            ((ActionBarActivity)mActivity).updateContentView( tab );

            mFragment = mActivity.getSupportFragmentManager().findFragmentByTag( mTag );
            if ( mFragment == null ) {
                mFragment = Fragment.instantiate( mActivity, mClass.getName(), mArgs );
                ft.add( R.id.content_fragment, mFragment, mTag );
            } else {
                ft.attach( mFragment );
            }
        }
    }
}

Dan dua tata letak saya:

tata letak/list_container.xml:

<?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:orientation="horizontal">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="0.6"
        android:id="@+id/content_fragment"/>
    <fragment
        android:id="@+id/detail_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="0.4"
        class="my.webview.WebViewFragment">
    </fragment>
</LinearLayout>

tata letak/tampilan web_container.xml:

<?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:orientation="vertical">
        <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:id="@+id/content_fragment"/>
</LinearLayout>    

PS: ini kontribusi pertama saya, jadi saya senang mendapat kritik.

PPS: Saya orang Prancis jadi saya mungkin lupa beberapa kesalahan bahasa Inggris, maaf.

person PierreB    schedule 17.08.2012

menurut saya Anda tidak perlu menyetelContentView lagi, ini hanya dapat disetel satu kali. Atur fragmen yang sesuai ke dalam wadah fragmen menggunakan fragmentTransaction.replace()

person Rajdeep Dua    schedule 15.12.2011
comment
Seperti yang saya sebutkan di komentar sebelumnya, pada rotasi tablet, fragmentTransaction.replace akan dipanggil (seperti onTabSelected akan dipanggil) dan kemudian Android juga akan membuat ulang fragmen saya. Jadi fragmen saya akan dibuat dua kali. Bisakah saya menghindarinya? - person ; 15.12.2011