İki bölmeli düzen oluşturma

"Oluştur" yöntemini deneyin
Jetpack Compose, Android için önerilen kullanıcı arayüzü araç setidir. Compose'da düzenlerle nasıl çalışacağınızı öğrenin.

Uygulamanızdaki her ekran duyarlı olmalı ve mevcut alana uyum sağlamalıdır. Duyarlı kullanıcı arayüzüyle Tek bölmeyi sağlayan ConstraintLayout pek çok boyuta yaklaşabilir, ancak daha büyük cihazlarda parçalara bölünebilir. bölmeyi bölmenize yardımcı olur. Örneğin, ekranda karşınıza bir öğe listesi.

İlgili içeriği oluşturmak için kullanılan SlidingPaneLayout bileşeni, daha büyük cihazlarda iki bölmenin yan yana gösterilmesini destekler ve aynı anda yalnızca bir bölmeyi gösterecek şekilde otomatik olarak uyarlanırken daha küçük cihazlarla uyumlu bir şekilde çalışır.

Cihaza özgü yardım için bkz. ekran uyumluluğuna genel bakış başlıklı makaleyi inceleyin.

Kurulum

SlidingPaneLayout kullanmak için uygulamanızın build.gradle dosyası:

Modern

dependencies {
    implementation "androidx.slidingpanelayout:slidingpanelayout:1.2.0"
}

Kotlin

dependencies {
    implementation("androidx.slidingpanelayout:slidingpanelayout:1.2.0")
}

XML düzeni yapılandırması

SlidingPaneLayout, üstte kullanılmak üzere yatay, iki bölmeli bir düzen sağlar. bir kullanıcı arayüzü seviyesidir. Bu düzen, ilk bölmeyi içerik listesi veya tarayıcı olarak kullanır. birincil ayrıntı görünümüne tabidir.

SlidingPaneLayout örneği gösteren bir resim
Şekil 1. Şununla oluşturulan bir düzen örneği: SlidingPaneLayout

SlidingPaneLayout, gösterilip gösterilmeyeceğini belirlemek için iki bölmenin genişliğini kullanır yan yana görebilirsiniz. Örneğin, liste bölmesinde bir 200 dp boyutunda ve ayrıntı bölmesi için 400 dp gerekir; daha sonra SlidingPaneLayout, gösterildiği sürece iki bölmeyi otomatik olarak yan yana gösterir en az 600 dp genişliğe sahip.

Birleştirilmiş genişlikleri SlidingPaneLayout Bu durumda, alt görüntülemeler mevcut SlidingPaneLayout genişliğindedir. Kullanıcı, ekranın en üstündeki görünümü kenarlarından geriye doğru sürükleyerek de şekli oluşturabilirsiniz.

Görünümler çakışmıyorsa SlidingPaneLayout, düzenin kullanımını destekler Kalan alanın nasıl bölüneceğini tanımlamak için alt görüntülemelerde layout_weight parametresi tamamlanmasından kısa bir süre sonra sunulur. Bu parametre yalnızca genişlikle ilgilidir.

Ekranında her iki görünümü yan yana gösterecek alan bulunan katlanabilir cihazlarda ise SlidingPaneLayout iki bölmenin boyutunu otomatik olarak ayarlar. üst üste binen kat veya menteşenin her iki tarafına yerleştirilmiş olmalıdır. Burada Bu durumda, ayarlanan genişlikler, her bir yüzeyin her birinde olması gereken minimum genişlik olarak tarafından yapılır. Bunu sağlamak için yeterli alan yoksa bir boyut seçerseniz SlidingPaneLayout, tekrar çakışan görünümlere geçer.

Örnek: SlidingPaneLayout Bunun nedeni: RecyclerView ve bir FragmentContainerView birincil ayrıntı görünümü olarak görüntüleyin:

<!-- two_pane.xml -->
<androidx.slidingpanelayout.widget.SlidingPaneLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/sliding_pane_layout"
   android:layout_width="match_parent"
   android:layout_height="match_parent">

   <!-- The first child view becomes the left pane. When the combined needed
        width, expressed using android:layout_width, doesn't fit on-screen at
        once, the right pane is permitted to overlap the left. -->

   <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/list_pane"
             android:layout_width="280dp"
             android:layout_height="match_parent"
             android:layout_gravity="start"/>

   <!-- The second child becomes the right (content) pane. In this example,
        android:layout_weight is used to expand this detail pane to consume
        leftover available space when the entire window is wide enough to fit
        the left and right pane.-->
   <androidx.fragment.app.FragmentContainerView
       android:id="@+id/detail_container"
       android:layout_width="300dp"
       android:layout_weight="1"
       android:layout_height="match_parent"
       android:background="#ff333333"
       android:name="com.example.SelectAnItemFragment" />
</androidx.slidingpanelayout.widget.SlidingPaneLayout>

Bu örnekte, FragmentContainerView üzerindeki android:name özelliği ilk parçayı ayrıntı penceresine ekleyerek, cihazlar, uygulama ilk kez başlatıldığında boş bir bölme görmüyor.

Ayrıntı bölmesini programatik olarak değiştirme

Yukarıdaki XML örneğinde, RecyclerView sayfasındaki bir öğeye dokunulduğunda ayrıntı bölmesinde bir değişikliği tetikler. Parça kullanırken bunun için FragmentTransaction öğesini çağırarak open() yeni görünür parçaya geçmek için SlidingPaneLayout üzerinde:

Kotlin

// A method on the Fragment that owns the SlidingPaneLayout,called by the
// adapter when an item is selected.
fun openDetails(itemId: Int) {
    childFragmentManager.commit {
        setReorderingAllowed(true)
        replace<ItemFragment>(R.id.detail_container,
            bundleOf("itemId" to itemId))
        // If it's already open and the detail pane is visible, crossfade
        // between the fragments.
        if (binding.slidingPaneLayout.isOpen) {
            setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
        }
    }
    binding.slidingPaneLayout.open()
}

Java

// A method on the Fragment that owns the SlidingPaneLayout, called by the
// adapter when an item is selected.
void openDetails(int itemId) {
    Bundle arguments = new Bundle();
    arguments.putInt("itemId", itemId);
    FragmentTransaction ft = getChildFragmentManager().beginTransaction()
            .setReorderingAllowed(true)
            .replace(R.id.detail_container, ItemFragment.class, arguments);
    // If it's already open and the detail pane is visible, crossfade
    // between the fragments.
    if (binding.getSlidingPaneLayout().isOpen()) {
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    }
    ft.commit();
    binding.getSlidingPaneLayout().open();
}

Bu kod özellikle addToBackStack() FragmentTransaction üzerinde. Bu, ayrıntılarda arka yığın oluşturmaktan kaçınır bölmesini de kullanabilirsiniz.

Bu sayfadaki örneklerde doğrudan SlidingPaneLayout kullanılmıştır ve parça işlemlerini manuel olarak yönetmenizi sağlar. Ancak Gezinme bileşeni, varsayılan olarak iki bölmeli düzende AbstractListDetailFragment, Listenizi yönetmek için arka planda SlidingPaneLayout kullanan bir API sınıfı ve ayrıntı bölmelerini genişletin.

Bu, XML düzeni yapılandırmanızı basitleştirmenize olanak tanır. Açıkça belirtmek yerine bir SlidingPaneLayout ve her iki bölmenizi de bildirdiğinizde, düzeniniz için yalnızca bir AbstractListDetailFragment cihazınızı tutmak için FragmentContainerView uygulama:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/two_pane_container"
        <!-- The name of your AbstractListDetailFragment implementation.-->
        android:name="com.example.testapp.TwoPaneFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        <!-- The navigation graph for your detail pane.-->
        app:navGraph="@navigation/two_pane_navigation" />
</FrameLayout>

Uygulama onCreateListPaneView() ve onListPaneViewCreated() kullanarak liste bölmeniz için özel bir görünüm oluşturabilirsiniz. Ayrıntı bölmesi için AbstractListDetailFragment, bir NavHostFragment Bu, bir navigasyon yönergesi tanımlayabileceğiniz grafik hedeflerin sayısını veya listesini görüntüler. Daha sonra, NavController ayrıntı bölmesini kullanarak, bağımsız gezinme grafiğindeki hedeflerin arasında

Kotlin

fun openDetails(itemId: Int) {
    val navController = navHostFragment.navController
    navController.navigate(
        // Assume the itemId is the android:id of a destination in the graph.
        itemId,
        null,
        NavOptions.Builder()
            // Pop all destinations off the back stack.
            .setPopUpTo(navController.graph.startDestination, true)
            .apply {
                // If it's already open and the detail pane is visible,
                // crossfade between the destinations.
                if (binding.slidingPaneLayout.isOpen) {
                    setEnterAnim(R.animator.nav_default_enter_anim)
                    setExitAnim(R.animator.nav_default_exit_anim)
                }
            }
            .build()
    )
    binding.slidingPaneLayout.open()
}

Java

void openDetails(int itemId) {
    NavController navController = navHostFragment.getNavController();
    NavOptions.Builder builder = new NavOptions.Builder()
            // Pop all destinations off the back stack.
            .setPopUpTo(navController.getGraph().getStartDestination(), true);
    // If it's already open and the detail pane is visible, crossfade between
    // the destinations.
    if (binding.getSlidingPaneLayout().isOpen()) {
        builder.setEnterAnim(R.animator.nav_default_enter_anim)
                .setExitAnim(R.animator.nav_default_exit_anim);
    }
    navController.navigate(
        // Assume the itemId is the android:id of a destination in the graph.
        itemId,
        null,
        builder.build()
    );
    binding.getSlidingPaneLayout().open();
}

Ayrıntı bölmesinin gezinme grafiğindeki hedefler şu hedeflerde bulunmamalıdır: uygulama genelindeki herhangi bir gezinme grafiği. Ancak ayrıntılardaki derin bağlantılar bölmenin gezinme grafiği, SlidingPaneLayout Bu, harici derin bağlantıların ilk olarak gezinmesini sağlar SlidingPaneLayout hedefine gidip doğru ayrıntıya gidin bölme hedefini.

Bkz. TwoPaneFragment örneği iki bölmeli düzenin tam olarak uygulanması için Gezinme bileşenini kullanır.

Sistemin geri düğmesiyle entegrasyon

Liste ve ayrıntı bölmelerinin çakıştığı daha küçük cihazlarda, sistemin geri düğmesi, kullanıcıyı ayrıntı penceresinden liste bölmesine geri götürür. Bunu yap özel geri ödeme sağlayarak gezinme ve bir OnBackPressedCallback - SlidingPaneLayout öğesinin mevcut durumu:

Kotlin

class TwoPaneOnBackPressedCallback(
    private val slidingPaneLayout: SlidingPaneLayout
) : OnBackPressedCallback(
    // Set the default 'enabled' state to true only if it is slidable, such as
    // when the panes overlap, and open, such as when the detail pane is
    // visible.
    slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen
), SlidingPaneLayout.PanelSlideListener {

    init {
        slidingPaneLayout.addPanelSlideListener(this)
    }

    override fun handleOnBackPressed() {
        // Return to the list pane when the system back button is tapped.
        slidingPaneLayout.closePane()
    }

    override fun onPanelSlide(panel: View, slideOffset: Float) { }

    override fun onPanelOpened(panel: View) {
        // Intercept the system back button when the detail pane becomes
        // visible.
        isEnabled = true
    }

    override fun onPanelClosed(panel: View) {
        // Disable intercepting the system back button when the user returns to
        // the list pane.
        isEnabled = false
    }
}

Java

class TwoPaneOnBackPressedCallback extends OnBackPressedCallback
        implements SlidingPaneLayout.PanelSlideListener {

    private final SlidingPaneLayout mSlidingPaneLayout;

    TwoPaneOnBackPressedCallback(@NonNull SlidingPaneLayout slidingPaneLayout) {
        // Set the default 'enabled' state to true only if it is slideable, such
        // as when the panes overlap, and open, such as when the detail pane is
        // visible.
        super(slidingPaneLayout.isSlideable() && slidingPaneLayout.isOpen());
        mSlidingPaneLayout = slidingPaneLayout;
        slidingPaneLayout.addPanelSlideListener(this);
    }

    @Override
    public void handleOnBackPressed() {
        // Return to the list pane when the system back button is tapped.
        mSlidingPaneLayout.closePane();
    }

    @Override
    public void onPanelSlide(@NonNull View panel, float slideOffset) { }

    @Override
    public void onPanelOpened(@NonNull View panel) {
        // Intercept the system back button when the detail pane becomes
        // visible.
        setEnabled(true);
    }

    @Override
    public void onPanelClosed(@NonNull View panel) {
        // Disable intercepting the system back button when the user returns to
        // the list pane.
        setEnabled(false);
    }
}

Geri çağırmayı OnBackPressedDispatcher kullanarak addCallback():

Kotlin

class TwoPaneFragment : Fragment(R.layout.two_pane) {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val binding = TwoPaneBinding.bind(view)

        // Connect the SlidingPaneLayout to the system back button.
        requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner,
            TwoPaneOnBackPressedCallback(binding.slidingPaneLayout))

        // Set up the RecyclerView adapter.
    }
}

Java

class TwoPaneFragment extends Fragment {

    public TwoPaneFragment() {
        super(R.layout.two_pane);
    }

    @Override
    public void onViewCreated(@NonNull View view,
             @Nullable Bundle savedInstanceState) {
        TwoPaneBinding binding = TwoPaneBinding.bind(view);

        // Connect the SlidingPaneLayout to the system back button.
        requireActivity().getOnBackPressedDispatcher().addCallback(
            getViewLifecycleOwner(),
            new TwoPaneOnBackPressedCallback(binding.getSlidingPaneLayout()));

        // Set up the RecyclerView adapter.
    }
}

Kilit modu

SlidingPaneLayout her zaman open() adlı kullanıcıyı manuel olarak aramanızı ve close() Bölme, telefonlarda liste ile ayrıntı bölmeleri arasında geçiş yapar. Bu yöntemlerde görünür olduğunda ve çakışmadığında etkisini gösterir.

Liste ve ayrıntı bölmeleri çakıştığında, kullanıcılar kaydırarak Varsayılan olarak, hareket kullanılmadığında bile iki bölme arasında serbestçe geçiş yapılabilir. gezinme başlıklı makaleyi inceleyin. Kaydırma yönünü kontrol edebilirsiniz SlidingPaneLayout cihazının kilit modunu ayarlayarak:

Kotlin

binding.slidingPaneLayout.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED

Java

binding.getSlidingPaneLayout().setLockMode(SlidingPaneLayout.LOCK_MODE_LOCKED);

Daha fazla bilgi

Farklı form faktörleri için düzenler tasarlama hakkında daha fazla bilgi edinmek üzere şu dokümanları bulabilirsiniz:

Ek kaynaklar