Chúng tôi đã phải thực hiện chính xác hành vi tương tự mà bạn mô tả cho một ứng dụng gần đây. Các màn hình và dòng chảy chung của ứng dụng đã được xác định nên chúng tôi phải gắn bó với nó (đó là một bản sao ứng dụng iOS ...). May mắn thay, chúng tôi đã tìm cách loại bỏ các nút quay lại trên màn hình :)
Chúng tôi đã hack giải pháp bằng cách sử dụng hỗn hợp TabActivity, FragmentActivities (chúng tôi đang sử dụng thư viện hỗ trợ cho các mảnh) và Fragment. Nhìn lại, tôi khá chắc chắn rằng đó không phải là quyết định kiến trúc tốt nhất, nhưng chúng tôi đã xoay sở để khiến mọi thứ hoạt động. Nếu tôi phải làm lại, có lẽ tôi sẽ thử thực hiện một giải pháp dựa trên hoạt động nhiều hơn (không có đoạn) hoặc thử và chỉ có một Hoạt động cho các tab và để tất cả phần còn lại là lượt xem (mà tôi thấy là nhiều hơn tái sử dụng hơn các hoạt động tổng thể).
Vì vậy, các yêu cầu là phải có một số tab và màn hình có thể lồng trong mỗi tab:
tab 1
screen 1 -> screen 2 -> screen 3
tab 2
screen 4
tab 3
screen 5 -> 6
Vân vân...
Vì vậy, nói: người dùng bắt đầu trong tab 1, điều hướng từ màn hình 1 đến màn hình 2 rồi đến màn hình 3, sau đó anh ta chuyển sang tab 3 và điều hướng từ màn hình 4 đến 6; nếu chuyển trở lại tab 1, anh ta sẽ nhìn thấy màn hình 3 lần nữa và nếu anh ta nhấn Back, anh ta sẽ quay lại màn hình 2; Quay lại lần nữa và anh ta ở màn hình 1; chuyển sang tab 3 và anh ta lại ở màn hình 6.
Hoạt động chính trong ứng dụng là MainTabActivity, mở rộng TabActivity. Mỗi tab được liên kết với một hoạt động, giả sử ActivityInTab1, 2 và 3. Và sau đó mỗi màn hình sẽ là một đoạn:
MainTabActivity
ActivityInTab1
Fragment1 -> Fragment2 -> Fragment3
ActivityInTab2
Fragment4
ActivityInTab3
Fragment5 -> Fragment6
Mỗi ActivityInTab chỉ giữ một đoạn duy nhất tại một thời điểm và biết cách thay thế một đoạn này cho một đoạn khác (khá giống với Actvitygroup). Điều thú vị là nó khá dễ dàng để sắp xếp các ngăn xếp riêng biệt cho mỗi tab theo cách này.
Chức năng cho mỗi ActivityInTab khá giống nhau: biết cách điều hướng từ đoạn này sang đoạn khác và duy trì ngăn xếp phía sau, vì vậy chúng tôi đặt nó vào một lớp cơ sở. Hãy gọi nó đơn giản là ActivityInTab:
abstract class ActivityInTab extends FragmentActivity { // FragmentActivity is just Activity for the support library.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_in_tab);
}
/**
* Navigates to a new fragment, which is added in the fragment container
* view.
*
* @param newFragment
*/
protected void navigateTo(Fragment newFragment) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, newFragment);
// Add this transaction to the back stack, so when the user presses back,
// it rollbacks.
ft.addToBackStack(null);
ft.commit();
}
}
Activity_in_tab.xml chỉ là thế này:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:isScrollContainer="true">
</RelativeLayout>
Như bạn có thể thấy, bố cục khung nhìn cho mỗi tab là như nhau. Đó là bởi vì nó chỉ là một FrameLayout được gọi là nội dung sẽ chứa từng phân đoạn. Các mảnh vỡ là những mảnh có mỗi màn hình.
Chỉ để nhận điểm thưởng, chúng tôi cũng đã thêm một số mã nhỏ để hiển thị hộp thoại xác nhận khi người dùng nhấn Back và không còn đoạn nào để quay lại:
// In ActivityInTab.java...
@Override
public void onBackPressed() {
FragmentManager manager = getSupportFragmentManager();
if (manager.getBackStackEntryCount() > 0) {
// If there are back-stack entries, leave the FragmentActivity
// implementation take care of them.
super.onBackPressed();
} else {
// Otherwise, ask user if he wants to leave :)
showExitDialog();
}
}
Đó là khá nhiều thiết lập. Như bạn có thể thấy, mỗi FragmentActivity (hoặc chỉ đơn giản là Hoạt động trong Android> 3) đang chăm sóc tất cả các bản sao lưu với FragmentManager của chính nó.
Một hoạt động như ActivityInTab1 sẽ rất đơn giản, nó sẽ chỉ hiển thị đoạn đầu tiên (tức là màn hình):
public class ActivityInTab1 extends ActivityInTab {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
navigateTo(new Fragment1());
}
}
Sau đó, nếu một mảnh cần điều hướng đến một mảnh khác, nó phải thực hiện một vài thao tác khó chịu ... nhưng điều đó không tệ:
// In Fragment1.java for example...
// Need to navigate to Fragment2.
((ActivityIntab) getActivity()).navigateTo(new Fragment2());
Vì vậy, đó là khá nhiều đó. Tôi khá chắc chắn rằng đây không phải là một giải pháp rất kinh điển (và hầu như chắc chắn không tốt lắm), vì vậy tôi muốn hỏi các nhà phát triển Android dày dạn về cách tiếp cận tốt hơn để đạt được chức năng này và nếu đây không phải là "cách nó thực hiện" trong Android, tôi đánh giá cao nếu bạn có thể chỉ cho tôi một số liên kết hoặc tài liệu nào giải thích đó là các cách Android tiếp cận này (tab, màn hình lồng nhau trong các tab, vv). Hãy xé ra câu trả lời này trong các ý kiến :)
Như một dấu hiệu cho thấy giải pháp này không tốt lắm là gần đây tôi đã phải thêm một số chức năng điều hướng vào ứng dụng. Một số nút kỳ quái sẽ đưa người dùng từ tab này sang tab khác và vào màn hình lồng nhau. Làm điều đó theo lập trình là một nỗi đau ở mông, bởi vì ai biết vấn đề và xử lý khi nào các mảnh và hoạt động thực sự được khởi tạo và khởi tạo. Tôi nghĩ mọi chuyện sẽ dễ dàng hơn nhiều nếu những màn hình và tab đó chỉ là Lượt xem thực sự.
Cuối cùng, nếu bạn cần tồn tại thay đổi định hướng, điều quan trọng là các mảnh của bạn được tạo bằng setArgument / getArgument. Nếu bạn đặt các biến đối tượng trong các hàm tạo của đoạn của bạn, bạn sẽ bị vặn. Nhưng may mắn thay, điều đó thực sự dễ sửa: chỉ cần lưu mọi thứ trong setArgument trong hàm tạo và sau đó lấy những thứ đó bằng getArgument trong onCreate để sử dụng chúng.