Cách tạo bố cục hai khung cho máy tính bảng hoặc khi xoay ngang thiết bị di động (dual pane layout in Android) sử dụng Fragment trong Android (Phần 1)

Đăng bởi: Admin | Lượt xem: 1119 | Chuyên mục: Android

Trong hướng dẫn này, bạn sẽ tìm hiểu cách tạo bố cục hai khung trong Android (dual pane layout in Android). Thiết kế này giúp ứng dụng trên điện thoại hoặc máy tính bảng nhìn sẽ đẹp mắt hơn, một trong những mẫu thiết kế thường được sử dụng khi trên máy tính bảng là two pane.


Trong hướng dẫn này, ta sẽ code một ứng dụng có 2 giao diện khác nhau trên 2 thiết bị khác nhau là thiết bị di động và máy tính bảng. Mã hoàn chỉnh có sẵn trên GitHub Repo này 

- Giao diện trên thiết bị di động

Giao diện trên thiết bị di động

- Giao diện trên máy tính bảng

Giao diện trên máy tính bảng

Bạn sẽ phát triển một ứng dụng mô phỏng cài đặt có 3 tùy chọn “Network”, “Storage” and “Display”. Trên điện thoại bình thường, bạn sẽ hiển thị danh sách các tùy chọn này, khi click vào từng tùy chọn sẽ hiển thị nội dung chi tiết cho tùy chọn đó. 

Trên máy tính bảng, bạn sẽ hiển thị bố cục hai khung trong đó khi nhấp vào tùy chọn trong khung bên trái sẽ cập nhật nội dung trong khung bên phải.

Đầu tiên bạn sẽ phát triển bố cục cho thiết bị di động và sau đó bạn sẽ điều chỉnh nó để thích ứng trên máy tính bảng.

1. Thiết lập

Tạo một dự án Android Studio mới. Bây giờ bạn có file MainActivity.java và activity_main.xml

main_activity.xml 

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

</FrameLayout>

2. Tạo layout chứa các tùy chọn

Tạo một tệp mới có tên fragment_sinstall_options_view.xml . Đây sẽ là tệp layout của chúng ta cho 3 tùy chọn cài đặt.

<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <LinearLayout
            android:id="@+id/displayOption"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?selectableItemBackground"
            android:clickable="true"
            android:focusable="true"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:padding="16dp">

            <ImageView
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:src="@mipmap/ic_launcher" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="16dp"
                android:layout_marginStart="16dp"
                android:text="@string/display"
                android:textSize="20sp" />

        </LinearLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="0.5dp"
            android:background="@android:color/darker_gray" />

        <LinearLayout
            android:id="@+id/storageOption"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?selectableItemBackground"
            android:clickable="true"
            android:focusable="true"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:padding="16dp">

            <ImageView
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:src="@mipmap/ic_launcher" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="16dp"
                android:layout_marginStart="16dp"
                android:text="@string/storage"
                android:textSize="20sp" />

        </LinearLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="0.5dp"
            android:background="@android:color/darker_gray" />


        <LinearLayout
            android:id="@+id/networkOption"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?selectableItemBackground"
            android:clickable="true"
            android:focusable="true"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:padding="16dp">

            <ImageView
                android:layout_width="40dp"
                android:layout_height="40dp"
                android:src="@mipmap/ic_launcher" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="16dp"
                android:layout_marginStart="16dp"
                android:text="@string/network"
                android:textSize="20sp" />

        </LinearLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="0.5dp"
            android:background="@android:color/darker_gray" />

    </LinearLayout>

</ScrollView>

Bây giờ hãy tạo một lớp Fragment có tên là SetOptionsFragment 

public class SettingOptionsFragment extends Fragment {

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_settings_options_view, container, false);

        return rootView;
    }
}

Trong MainActivity

public class MainActivity extends AppCompatActivity {
    private FragmentManager fragmentManager;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fragmentManager = getSupportFragmentManager();

        if (savedInstanceState == null) {
            fragmentManager.beginTransaction()
                    .add(R.id.container, new SettingOptionsFragment())
                    .commit();
        }
    }
}

Bây giờ nếu bạn chạy ứng dụng, nó sẽ trông như thế này.

3. Tạo layout chi tiết

Tạo một Activity mới có tên SettingsDetailActivity. Dưới đây là layout của SettingsDetailActivity.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/settingsContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SettingDetailsActivity">

</FrameLayout>

Bây giờ hãy tạo ba fragment cho mỗi tùy chọn “network”, “display” and “storage”.

Network Settings

fragment_network_sinstall.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"
    android:padding="16dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/network"
        android:textSize="40sp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/network_description"/>

</LinearLayout>

NetworkSettingsFragment

public class NetworkSettingsFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_network_settings, container, false);

        return rootView;
    }
}

Display Settings

fragment_display_settings.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"
    android:padding="16dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/display"
        android:textSize="40sp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/display_description"/>

</LinearLayout>

DisplaySettingsFragment

public class DisplaySettingsFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_display_settings, container, false);

        return rootView;
    }
}

Storage Settings

fragment_storage_settings.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"
    android:padding="16dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/storage"
        android:textSize="40sp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/storage_description"/>

</LinearLayout>

StorageSettingsFragment

public class StorageSettingsFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_storage_settings, container, false);

        return rootView;
    }
}

Giờ ta cần xác định người dùng click tùy chọn nào để hiển thị fragment tương ứng

4. Fragment- Activity

Quay lại MainActivity và SettingsOptionsFragment . Trong SetupOptionsFragment, bạn phải xác định tùy chọn nào mà người dùng đã click vào và thông báo cho MainActivity. Bạn sẽ làm điều này bằng cách sử dụng callbacks.

Cập nhật SettingOptionsFragment của bạn bằng cách thêm interface OnOptionClickListener có chứa phương thức onOptionSelected (String) .

public class SettingOptionsFragment extends Fragment {

    interface OnOptionClickListener {
        void onOptionSelected(String option);
    }

    private OnOptionClickListener mCallback;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_settings_options_view, container, false);

        return rootView;
    }
}

MainActivity sẽ implement interface này.

public class MainActivity extends AppCompatActivity implements SettingOptionsFragment.OnOptionClickListener {

    private FragmentManager fragmentManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fragmentManager = getSupportFragmentManager();

        if (savedInstanceState == null) {
            fragmentManager.beginTransaction()
                    .add(R.id.container, new SettingOptionsFragment())
                    .commit();
        }
    }

    @Override
    public void onOptionSelected(String option) {

    }
}

Bây giờ, SettingOptionsFragment cần phải có một instance cho MainActivity thực hiện OnOptionClickListener. Bạn có thể lấy Activity trong phương thức onAttach của fragment.

public class SettingOptionsFragment extends Fragment {

    interface OnOptionClickListener {
        void onOptionSelected(String option);
    }

    private OnOptionClickListener mCallback;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        try {
            mCallback = (OnOptionClickListener) context;
        } catch (Exception e) {
            throw new ClassCastException(context.toString() + " must implement SettingOptionsFragment.OnOptionClickListener");
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_settings_options_view, container, false);

        return rootView;
    }
}

Cho phép thêm click listener vào các tùy chọn và thông báo cho Activity về lần click.

public class SettingOptionsFragment extends Fragment {

    interface OnOptionClickListener {
        void onOptionSelected(String option);
    }

    private OnOptionClickListener mCallback;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        try {
            mCallback = (OnOptionClickListener) context;
        } catch (Exception e) {
            throw new ClassCastException(context.toString() + " must implement SettingOptionsFragment.OnOptionClickListener");
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_settings_options_view, container, false);

        LinearLayout mNetworkOption = rootView.findViewById(R.id.networkOption);
        LinearLayout mStorageOption = rootView.findViewById(R.id.storageOption);
        LinearLayout mDisplayOption = rootView.findViewById(R.id.displayOption);

        mNetworkOption.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCallback.onOptionSelected("network");
            }
        });

        mStorageOption.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCallback.onOptionSelected("storage");
            }
        });

        mDisplayOption.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCallback.onOptionSelected("display");
            }
        });

        return rootView;
    }
}

Trong MainActivity, tạo một Intent để chuyển đến SetupDetailsActivity.

MainActivity

public class SettingOptionsFragment extends Fragment {

    interface OnOptionClickListener {
        void onOptionSelected(String option);
    }

    private OnOptionClickListener mCallback;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        try {
            mCallback = (OnOptionClickListener) context;
        } catch (Exception e) {
            throw new ClassCastException(context.toString() + " must implement SettingOptionsFragment.OnOptionClickListener");
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_settings_options_view, container, false);

        LinearLayout mNetworkOption = rootView.findViewById(R.id.networkOption);
        LinearLayout mStorageOption = rootView.findViewById(R.id.storageOption);
        LinearLayout mDisplayOption = rootView.findViewById(R.id.displayOption);

        mNetworkOption.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCallback.onOptionSelected("network");
            }
        });

        mStorageOption.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCallback.onOptionSelected("storage");
            }
        });

        mDisplayOption.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCallback.onOptionSelected("display");
            }
        });

        return rootView;
    }
}

Cập nhật SettingDetailsActivity để load fragment.

public class SettingDetailsActivity extends AppCompatActivity {

    public static final String EXTRA_SETTING_OPTION = "option";

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

        String option = getIntent().getStringExtra(EXTRA_SETTING_OPTION);
        FragmentManager fragmentManager = getSupportFragmentManager();

        if (option == null) {
            finish();
            return;
        }

        switch (option) {
            case "network": {
                fragmentManager.beginTransaction()
                        .add(R.id.settingsContainer, new NetworkSettingsFragment())
                        .commit();
                break;
            }
            case "display": {
                fragmentManager.beginTransaction()
                        .add(R.id.settingsContainer, new DisplaySettingsFragment())
                        .commit();
                break;
            }
            case "storage": {
                fragmentManager.beginTransaction()
                        .add(R.id.settingsContainer, new StorageSettingsFragment())
                        .commit();
                break;
            }
        }
    }
}

Bây giờ khởi chạy ứng dụng và chọn một tùy chọn để mở activity chi tiết.

Ở phần sau ta sẽ tìm hiểu cách thiết lập layout 2 ngăn tương thích với từng thiết bị

vncoder logo

Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!



Khóa học liên quan

Khóa học: Android

Học Kotlin cơ bản
Số bài học:
Lượt xem: 17037
Đăng bởi: Admin
Chuyên mục: Android

Học lập trình Flutter cơ bản
Số bài học:
Lượt xem: 54958
Đăng bởi: Admin
Chuyên mục: Android

Lập trình Android cơ bản
Số bài học:
Lượt xem: 22143
Đăng bởi: Admin
Chuyên mục: Android