Mengimplementasikan Alur UI Adaptif

Bergantung pada layout yang saat ini ditampilkan aplikasi Anda, alur UI dapat berbeda-beda. Misalnya, jika aplikasi Anda ada dalam mode dua-panel, mengeklik sebuah item di panel kiri hanya akan menampilkan materi di panel kanan; jika dalam mode satu-panel, materi akan ditampilkan di panelnya sendiri (dalam aktivitas berbeda).

Menentukan Layout Saat Ini

Karena implementasi Anda pada setiap layout akan sedikit berbeda, salah satu hal pertama yang mungkin harus Anda tentukan adalah layout apa yang saat ini dilihat oleh pengguna. Misalnya, Anda mungkin ingin mengetahui apakah pengguna ada dalam mode "satu-panel" atau mode "dua-panel". Anda bisa melakukannya dengan melakukan kueri untuk mengetahui apakah tampilan itu ada dan terlihat:

public class NewsReaderActivity extends FragmentActivity {
    boolean mIsDualPane;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);

        View articleView = findViewById(R.id.article);
        mIsDualPane = articleView != null &&
                        articleView.getVisibility() == View.VISIBLE;
    }
}

Perhatikan, kode ini melakukan kueri untuk mengetahui apakah panel "article" tersedia atau tidak, yang jauh lebih fleksibel daripada melakukan kueri hard-coding untuk layout spesifik.

Contoh lain mengenai cara beradaptasi dengan keberadaan komponen berbeda adalah memeriksa komponen itu ada sebelum melakukan operasi pada komponen tersebut. Misalnya, dalam aplikasi contoh News Reader, ada tombol yang membuka menu, namun tombol itu hanya ada saat berjalan di versi sebelum Android 3.0 (karena fungsinya diambil alih oleh ActionBar pada API Level 11+). Jadi, untuk menambahkan event listener untuk tombol ini, Anda bisa:

Button catButton = (Button) findViewById(R.id.categorybutton);
OnClickListener listener = /* create your listener here */;
if (catButton != null) {
    catButton.setOnClickListener(listener);
}

Bereaksi Sesuai dengan Layout Saat Ini

Sebagian tindakan mungkin memiliki hasil berbeda bergantung pada layout saat ini. Misalnya, dalam contoh News Reader, mengeklik berita utama dari daftar berita utama akan membuka artikel dalam panel sisi kanan jika UI berada dalam mode dua-panel, namun akan meluncurkan aktivitas terpisah jika UI berada dalam mode satu-panel:

@Override
public void onHeadlineSelected(int index) {
    mArtIndex = index;
    if (mIsDualPane) {
        /* display article on the right pane */
        mArticleFragment.displayArticle(mCurrentCat.getArticle(index));
    } else {
        /* start a separate activity */
        Intent intent = new Intent(this, ArticleActivity.class);
        intent.putExtra("catIndex", mCatIndex);
        intent.putExtra("artIndex", index);
        startActivity(intent);
    }
}

Demikian pula jika aplikasi berada dalam mode dua-panel, aplikasi akan menyiapkan bilah aksi berisi tab untuk navigasi, sementara jika aplikasi berada dalam mode satu-panel, aplikasi akan menyiapkan navigasi dengan widget spinner. Jadi kode Anda juga harus memeriksa kasus mana yang sesuai:

final String CATEGORIES[] = { "Top Stories", "Politics", "Economy", "Technology" };

public void onCreate(Bundle savedInstanceState) {
    ....
    if (mIsDualPane) {
        /* use tabs for navigation */
        actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
        int i;
        for (i = 0; i < CATEGORIES.length; i++) {
            actionBar.addTab(actionBar.newTab().setText(
                CATEGORIES[i]).setTabListener(handler));
        }
        actionBar.setSelectedNavigationItem(selTab);
    }
    else {
        /* use list navigation (spinner) */
        actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_LIST);
        SpinnerAdapter adap = new ArrayAdapter(this,
                R.layout.headline_item, CATEGORIES);
        actionBar.setListNavigationCallbacks(adap, handler);
    }
}

Menggunakan Kembali Fragmen dalam Aktivitas Lainnya

Pola berulang dalam mendesain untuk beberapa layar memiliki bagian dari antarmuka yang diimplementasikan sebagai panel pada beberapa konfigurasi layar dan sebagai aktivitas terpisah pada konfigurasi lainnya. Misalnya, dalam contoh News Reader, teks artikel berita disajikan di panel sisi kanan pada layar besar, namun berupa aktivitas terpisah pada layar yang lebih kecil.

Dalam kasus seperti ini, Anda biasanya bisa menghindari duplikasi kode dengan menggunakan kembali subkelas Fragment yang sama di sejumlah aktivitas. Misalnya, ArticleFragment digunakan dalam layout dua panel:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="400dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>

Dan digunakan kembali (tanpa layout) dalam layout aktivitas untuk layar yang lebih kecil (ArticleActivity):

ArticleFragment frag = new ArticleFragment();
getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();

Biasanya, efeknya yang sama dengan mendeklarasikan fragmen dalam layout XML, namun dalam kasus ini, layout XML tidak diperlukan karena fragmen artikel merupakan komponen satu-satunya dalam aktivitas ini.

Satu hal yang sangat penting untuk diingat saat mendesain fragmen Anda adalah jangan membuat ikatan kuat ke aktivitas tertentu. Anda biasanya bisa melakukannya dengan mendefinisikan antarmuka yang mengabstraksikan semua cara yang digunakan fragmen untuk berinteraksi dengan aktivitas host, kemudian aktivitas host mengimplementasikan antarmuka itu:

Misalnya, HeadlinesFragment aplikasi News Reader persis melakukan hal seperti itu:

public class HeadlinesFragment extends ListFragment {
    ...
    OnHeadlineSelectedListener mHeadlineSelectedListener = null;

    /* Must be implemented by host activity */
    public interface OnHeadlineSelectedListener {
        public void onHeadlineSelected(int index);
    }
    ...

    public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener listener) {
        mHeadlineSelectedListener = listener;
    }
}

Kemudian, bila pengguna memilih berita utama, fragmen akan memberi tahu listener yang ditetapkan oleh aktivitas host (bertolak belakang dengan memberitahukan aktivitas hardcode tertentu):

public class HeadlinesFragment extends ListFragment {
    ...
    @Override
    public void onItemClick(AdapterView<?> parent,
                            View view, int position, long id) {
        if (null != mHeadlineSelectedListener) {
            mHeadlineSelectedListener.onHeadlineSelected(position);
        }
    }
    ...
}

Teknik ini didiskusikan lebih jauh dalam panduan pada Mendukung Tablet dan Handset.

Menangani Perubahan Konfigurasi Layar

Jika Anda menggunakan aktivitas terpisah untuk mengimplementasikan bagian terpisah dari antarmuka, Anda harus ingat bahwa aktivitas itu mungkin perlu bereaksi terhadap perubahan konfigurasi tertentu (seperti perubahan rotasi) agar antarmuka Anda tetap konsisten.

Misalnya, pada tablet 7" umumnya yang menjalankan Android 3.0 atau yang lebih tinggi, contoh News Reader menggunakan aktivitas terpisah untuk menampilkan artikel berita saat berjalan dalam mode potret, namun menggunakan layout dua-panel saat berada dalam mode lanskap.

Ini berarti bahwa bila pengguna berada dalam mode potret dan aktivitas untuk melihat artikel ada pada layar, Anda perlu mendeteksi apakah orientasi berubah menjadi lanskap dan selanjutnya bereaksi dengan menghentikan aktivitas serta kembali ke aktivitas utama agar materi bisa ditampilkan dalam layout dua-panel:

public class ArticleActivity extends FragmentActivity {
    int mCatIndex, mArtIndex;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCatIndex = getIntent().getExtras().getInt("catIndex", 0);
        mArtIndex = getIntent().getExtras().getInt("artIndex", 0);

        // If should be in two-pane mode, finish to return to main activity
        if (getResources().getBoolean(R.bool.has_two_panes)) {
            finish();
            return;
        }
        ...
}