Destek kitaplığını kullanmadan Android 4.0, 4.1'de (<4.2) iç içe geçmiş parçalar için en iyi uygulama


115

4.0 ve 4.1 tabletler için bir uygulama yazıyorum, bunun için destek kitaplıklarını (gerekli değilse), ancak yalnızca 4.x api'yi kullanmak istemiyorum .

Yani hedef platformum şu şekilde çok iyi tanımlanmış:> = 4.0 ve <= 4.1

Uygulamanın çok bölmeli bir düzeni (biri solda küçük, sağda bir içerik parçası olmak üzere iki bölüm) ve sekmeli bir eylem çubuğu vardır.

Şuna benzer:

görüntü açıklamasını buraya girin

İşlem çubuğundaki bir sekmeye tıklamak 'dış' parçayı değiştirir ve iç parça iki iç içe bölümden oluşan bir parçadır (1. küçük sol liste parçası, 2. geniş içerik parçası).

Şimdi parçaları ve özellikle iç içe geçmiş parçaları değiştirmek için en iyi uygulamanın ne olduğunu merak ediyorum. ViewPager, destek kitaplığının bir parçasıdır, bu sınıf için yerel 4.x alternatifi yoktur. Benim açımdan 'kullanımdan kaldırılmış' gibi görünüyor. - http://developer.android.com/reference/android/support/v4/view/ViewPager.html

Ardından ChildFragmentManager, hangisinin uygun olacağı konusunda Android 4.2 sürüm notlarını okudum , ancak 4.0 ve 4.1'i hedefliyorum, bu yüzden bu da kullanılamaz.

ChildFragmentManager yalnızca 4.2'de mevcuttur

Maalesef, Android geliştirici kılavuzlarının tamamında bile, destek kitaplığı olmadan parça kullanımları için en iyi uygulamaları gösteren hemen hemen hiç iyi örnek yoktur; ve özellikle iç içe geçmiş parçalarla ilgili hiçbir şey.

Bu yüzden merak ediyorum: Destek kitaplığını ve beraberinde gelen her şeyi kullanmadan yuvalanmış parçalarla 4.1 uygulama yazmak mümkün değil mi? (Fragment yerine FragmentActivity kullanmanız gerekiyor mu?) Veya en iyi uygulama ne olurdu?


Şu anda geliştirmede yaşadığım sorun tam olarak şu ifadedir:

Android Destek Kitaplığı artık iç içe geçmiş parçaları da desteklediğinden, Android 1.6 ve sonraki sürümlerde yuvalanmış parça tasarımlarını uygulayabilirsiniz.

Not: Bir düzeni, bir <fragment>. İç içe geçmiş parçalar, yalnızca bir parçaya dinamik olarak eklendiğinde desteklenir.

İç içe geçmiş parçaları tanımladığım için XML'de şu gibi bir hataya neden olur:

Caused by: java.lang.IllegalArgumentException: Binary XML file line #15: Duplicate id 0x7f090009, tag frgCustomerList, or parent id 0x7f090008 with another fragment for de.xyz.is.android.fragment.CustomerListFragment_

Şu anda kendim için sonuca varıyorum: 4.1'de bile, 2.x platformunu hedeflemek istemediğimde bile, ekran görüntüsünde gösterildiği gibi iç içe geçmiş parçalar destek kitaplığı olmadan mümkün değil.

(Bu aslında bir sorudan çok bir wiki girişi olabilir, ama belki daha önce başkası yönetmiştir).

Güncelleme:

Yararlı bir cevap: Parçanın İçinde Parçacık


22
Üç seçeneğiniz vardır: 1. Yerel iç içe parçalarla yalnızca 4.2'yi hedefleyin. 2. Destek kitaplığındaki yuvalanmış parçalarla 4.x'i hedefleyin 3. Diğer platform hedef senaryoları için yuvalanmış parçalar kullanmayın. Bu, sorunuzu yanıtlamalıdır. Xml düzeninde gömülü iç içe geçmiş parça kullanamazsınız, hepsi koda eklenmelidir. Destek kitaplığı olmadan parça kullanımları için en iyi uygulamaları gösteren hemen hemen hiç iyi örnek yoktur - destek parçası çerçevesi yerel olanı kopyalar, bu nedenle herhangi bir örnek her iki şekilde de çalışmalıdır.
Luksprog

@Luksprog Yorumlarınız için teşekkürler. Çözüm 2'yi tercih ediyorum ve çerçeveler destek kitaplığında iyi çalışıyor, ancak ActionBar'daki Sekmeler işe yaramıyor - afaik, ActionBarSherlock'u kullanmam gerekecek, ancak sekmeler ActionBar'da değil, yalnızca altında ( t 4.x için gerekli). Ve ActionBar.TabListener, destek kitaplığından değil, yalnızca android.app.Fragment'tan Fragments'ı destekler.
Mathias Conradt

2
Galaxy sekmesindeki Kişiler uygulamasına aşina değilim, ancak her zaman özel bir uygulama ile karşılaşabileceğinizi unutmayın ActionBar(Samsung tarafından evde oluşturulmuştur). ActionBarSherlock'a daha yakından bakın, yer varsa ActionBar'da sekmeleri vardır.
Luksprog

4
@Luksprog Verilecek tek cevabı zaten verdiğinize inanıyorum, uygun bir cevap olarak bu kadar nazik davranır mısınız?
Warpzit

1
@Pork Sorunun ana nedeni şudur: destek kitaplığını ve diğer tüm görünüm öğelerini kullanmak zorunda kalmadan iç içe geçmiş parçalar için herhangi bir geçici çözüm var mı? Yani, destek kitaplığına geçersem, Fragment yerine FragmentActivity kullanacağım. Ama Parçayı kullanmak istiyorum, tek istediğim İç İçe Parçaların yerine geçmesi , ancak tüm v4 bileşenlerinin değil. Yani diğer açık kaynak kütüphaneleri vb. Aracılığıyla. Örneğin yukarıdaki ekran görüntüsü 4.0'da çalışıyor ve ABS, SupportLib veya başka bir şey kullanıp kullanmadıklarını merak ediyorum.
Mathias Conradt

Yanıtlar:


60

Sınırlamalar

Yani parçaları başka bir parçanın içine yerleştirmek, hangi sürümünü FragmentManagerkullanırsanız kullanın , xml ile mümkün değildir .

Yani kod yoluyla parçalar eklemeniz gerekiyor, bu bir sorun gibi görünebilir, ancak uzun vadede mizanpajlarınızı süper esnek hale getirir.

Yani kullanmadan yuva yapmak getChildFragmentManger? Bunun özü childFragmentManager, önceki parça işlemi bitene kadar yüklemeyi ertelemesidir. Ve tabii ki sadece 4.2'de veya destek kitaplığında doğal olarak destekleniyordu.

ChildManager Olmadan Yerleştirme - Çözüm

Çözüm Elbette! Bunu uzun zamandır yapıyorum ( ViewPagerilan edildiğinden beri ).

Aşağıya bakınız; Bu, Fragmentyüklemeyi geciktiren bir işlemdir, böylece Fragmentiçine yüklenebilir.

Oldukça basit, Handlergerçekten kullanışlı bir sınıftır, etkili bir şekilde işleyici, geçerli parça işlemi tamamlandıktan sonra ana iş parçacığı üzerinde yürütmek için bir alan bekler (parçalar, ana iş parçacığı üzerinde çalıştırdıkları kullanıcı arayüzüne müdahale ettikçe).

// Remember this is an example, you will need to modify to work with your code
private final Handler handler = new Handler();
private Runnable runPager;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    return inflater.inflate(R.layout.frag_layout, container, false);
}

@Override
public void onActivityCreated(Bundle savedInstanceState)
{
    super.onActivityCreated(savedInstanceState);
    runPager = new Runnable() {

        @Override
        public void run()
        {
          getFragmentManager().beginTransaction().addFragment(R.id.frag_container, MyFragment.newInstance()).commit();
        }
    };
    handler.post(runPager);
}

/**
 * @see android.support.v4.app.Fragment#onPause()
 */
@Override
public void onPause()
{
    super.onPause();
    handler.removeCallbacks(runPager);
}

Bunun 'en iyi uygulama' olduğunu düşünmüyorum, ancak bu hack'i kullanan canlı uygulamalarım var ve henüz bununla ilgili herhangi bir sorun yaşamıyorum.

Bu yöntemi görüntüleme çağrı cihazlarını yerleştirmek için de kullanıyorum - https://gist.github.com/chrisjenx/3405429


İç içe geçmiş parçaları nasıl işliyorsunuz?
pablisco

Bu çalışmayı görebilmemin tek yolu, bir CustomLayoutInflater kullanmaktır, fragmentöğeye rastladığınızda , süper uygulamayı geçersiz kılar ve kendiniz ayrıştırmaya / şişirmeye çalışırsınız. Ama bu ÇOK çaba gerektirecek, Bir StackOverflow sorusunun kapsamı dışında.
Chris.Jenkins

Merhaba, bu konuda bana yardımcı olabilecek biri var mı? Gerçekten sıkıştım .. stackoverflow.com/questions/32240138/…
Nicks

2

API 17 öncesi bunu yapmanın en iyi yolu bunu hiç yapmamaktır. Bu davranışı uygulamaya çalışmak sorunlara neden olacaktır. Ancak bu, mevcut API 14 kullanılarak ikna edici bir şekilde sahte olamayacağı anlamına gelmez. Yaptığım şey şuydu:

1 - parçalar arasındaki iletişime bakın http://developer.android.com/training/basics/fragments/communicating.html

2 - layout xml FrameLayout'unuzu mevcut Parçanızdan Activity layout'a taşıyın ve 0 yükseklik vererek gizleyin:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent">
<FrameLayout android:id="@+id/content"
          android:layout_width="300dp"
          android:layout_height="match_parent" />


<FrameLayout android:id="@+id/lstResults"
             android:layout_width="300dp"
             android:layout_height="0dp"
             android:layout_below="@+id/content"
             tools:layout="@layout/treeview_list_content"/>


<FrameLayout android:id="@+id/anomalies_fragment"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
        android:layout_toRightOf="@+id/content" />

3 - Arayüzü üst Parçada uygulayın

    OnListener mCallback;

// Container Activity must implement this interface
public interface OnListener 
{
    public void onDoSomethingToInitChildFrame(/*parameters*/);
    public void showResults();
    public void hideResults();
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    // This makes sure that the container activity has implemented
    // the callback interface. If not, it throws an exception
    try {
        mCallback = (OnFilterAppliedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement OnListener");
    }
}

@Override
public void onActivityCreated(Bundle savedInstanceState) 
{
    super.onActivityCreated(savedInstanceState);

    mCallback.showResults();
}

@Override
public void onPause()
{
    super.onPause();

    mCallback.hideResults();
}

public void onClickButton(View view)
{
    // do click action here

    mCallback.onDoSomethingToInitChildFrame(/*parameters*/);
}

4 - Arayüzü ana Faaliyette uygulayın

public class YourActivity, Activity'i genişletir yourParentFragment.OnListener {

public void onDoSomethingToInitChildFrame(/*parameters*/)
{
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    Fragment childFragment = getFragmentManager().findFragmentByTag("Results");
    if(childFragment == null)
    {
        childFragment = new yourChildFragment(/*parameters*/);
        ft.add(R.id.lstResults, childFragment, "Results");
    }
    else
    {
        ft.detach(childFragment);

        ((yourChildFragment)childFragment).ResetContent(/*parameters*/);

        ft.attach(childFragment);
    }
    ft.commit();

    showResultsPane();
}

public void showResults()
{
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    Fragment childFragment = getFragmentManager().findFragmentByTag("Results");
    if(childFragment != null)
        ft.attach(childFragment);
    ft.commit();

    showResultsPane();
}

public void showResultsPane()
{
    //resize the elements to show the results pane
    findViewById(R.id.content).getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
    findViewById(R.id.lstResults).getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
}

public void hideResults()
{
    //resize the elements to hide the results pane
    findViewById(R.id.content).getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
    findViewById(R.id.lstResults).getLayoutParams().height = 0;

    FragmentTransaction ft = getFragmentManager().beginTransaction();
    Fragment childFragment = getFragmentManager().findFragmentByTag("Results");
    if(childFragment != null)
        ft.detach(childFragment);
    ft.commit();
}

}

5 - Tadını çıkarın, bu yöntemle, API 17 öncesi ortamda getChildFragmentManager () işleviyle aynı akışkan işlevselliğini elde edersiniz. Çocuk parçasının artık ana parçanın çocuğu olmadığını fark etmiş olabileceğiniz gibi, şimdi etkinliğin bir çocuğu, bu gerçekten önlenemez.


1

TabHost nedeniyle destek kitaplığının kullanımıyla ilgili komplikasyonları olan NavigationDrawer, TabHost ve ViewPager'in bir kombinasyonu nedeniyle bu sorunla uğraşmak zorunda kaldım. Ayrıca, JellyBean 4.1'in minimum API'sini de desteklemem gerekiyordu, bu nedenle getChildFragmentManager ile iç içe geçmiş parçaları kullanmak bir seçenek değildi.

Yani benim sorunum damıtılabilir ...

TabHost (üst düzey için)
+ ViewPager (üst düzey sekmeli parçalardan yalnızca biri için)
= Yuvalanmış Parçalara ihtiyaç (JellyBean 4.1 desteklemeyecektir)

Çözümüm, parçaları gerçekten iç içe geçirmeden iç içe geçmiş parçaların yanılsamasını yaratmaktı. Bunu, ana etkinliğin, görünürlüğü layout_weight 0 ile 1 arasında değiştirilerek yönetilen iki kardeş Görünümü yönetmek için TabHost VE ViewPager kullanmasını sağlayarak yaptım.

//Hide the fragment used by TabHost by setting height and weight to 0
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 0);
mTabHostedView.setLayoutParams(lp);
//Show the fragment used by ViewPager by setting height to 0 but weight to 1
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1);
mPagedView.setLayoutParams(lp);

Bu, ilgili düzen ağırlıklarını manuel olarak yönettiğim sürece sahte "Yuvalanmış Parçam" ın bağımsız bir görünüm olarak çalışmasına etkili bir şekilde izin verdi.

İşte benim activity_main.xml'im:

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.ringofblades.stackoverflow.app.MainActivity">

    <TabHost
        android:id="@android:id/tabhost"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <FrameLayout android:id="@android:id/tabcontent"
                android:background="@drawable/background_image"
                android:layout_width="match_parent"
                android:layout_weight="0.5"
                android:layout_height="0dp"/>
            <android.support.v4.view.ViewPager
                xmlns:tools="http://schemas.android.com/tools"
                android:id="@+id/pager"
                android:background="@drawable/background_image"
                android:layout_width="match_parent"
                android:layout_weight="0.5"
                android:layout_height="0dp"
                tools:context="com.ringofblades.stackoverflow.app.MainActivity">
                <FrameLayout
                    android:id="@+id/container"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
            </android.support.v4.view.ViewPager>
            <TabWidget android:id="@android:id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </TabHost>

    <fragment android:id="@+id/navigation_drawer"
        android:layout_width="@dimen/navigation_drawer_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:name="com.ringofblades.stackoverflow.app.NavigationDrawerFragment"
        tools:layout="@layout/fragment_navigation_drawer" />
</android.support.v4.widget.DrawerLayout>

"@ + İd / pager" ve "@ + id / container" öğelerinin 'android: layout_weight = "0.5" ve' android: layout_height = "0dp" 'olan kardeşler olduğunu unutmayın. Bu, herhangi bir ekran boyutu için önizleyicide görebilmem içindir. Her halükarda, ağırlıkları çalışma sırasında kod içinde değiştirilecektir.


Merhaba, Neden Tabs ile ActionBar yerine TabHost'u kullanmayı seçtiğinizi merak ediyorum? Ben de TabHost'tan sadece ActionBar'a geçtim ve kodum daha temiz ve daha kompakt hale geldi ...
IgorGanapolsky

Hatırladığım kadarıyla, ActionBar'da sekmeleri kullanmanın bir dezavantajı, bunları otomatik olarak bir dönen açılır menü (küçük bir ekran olması durumunda) olarak göstermeye karar vermesi ve bu benim için iyi değildi. Ama% 100 emin değilim.
WindRider

@Igor, burada bir yerde sekme SOkullanmanın iyi olmadığını okudum çünkü sekmeleri otomatik olarak çekmecenizin görünümüne yerleştirecektir. Maalesef bunu destekleyecek bağlantım yok. ActionBarNavigation Drawer
Azurespot


1
Vay, @Igor bağlantısı için teşekkürler! Bunu kesinlikle kontrol edeceğim. Hala yeni başlayan biriyim, bu yüzden Android ile öğreneceğim milyonlarca şey var, ancak bu bir mücevher gibi görünüyor! Tekrar teşekkürler.
Azurespot

1

@ Chris.Jenkins cevabına dayanarak, bu benim için iyi çalışan çözüm, yaşam döngüsü olayları sırasında (IllegalStateExceptions atma eğilimi olan) parçaları kaldırmak için iyi çalışıyor. Bu, İşleyici yaklaşımının ve bir Activity.isFinishing () kontrolünün bir kombinasyonunu kullanır (aksi takdirde, "Bu eylem onSaveInstanceState'ten sonra gerçekleştirilemez) için bir hata atar.

import android.app.Activity;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;

public abstract class BaseFragment extends Fragment {
    private final Handler handler = new Handler();

    /**
     * Removes the {@link Fragment} using {@link #getFragmentManager()}, wrapped in a {@link Handler} to
     * compensate for illegal states.
     *
     * @param fragment The {@link Fragment} to schedule for removal.
     */
    protected void removeFragment(@Nullable final Fragment fragment) {
        if (fragment == null) return;

        final Activity activity = getActivity();
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (activity != null && !activity.isFinishing()) {
                    getFragmentManager().beginTransaction()
                            .remove(fragment)
                            .commitAllowingStateLoss();
                }
            }
        });
    }

    /**
     * Removes each {@link Fragment} using {@link #getFragmentManager()}, wrapped in a {@link Handler} to
     * compensate for illegal states.
     *
     * @param fragments The {@link Fragment}s to schedule for removal.
     */
    protected void removeFragments(final Fragment... fragments) {
        final FragmentManager fragmentManager = getFragmentManager();
        final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        for (Fragment fragment : fragments) {
            if (fragment != null) {
                fragmentTransaction.remove(fragment);
            }
        }

        final Activity activity = getActivity();
        handler.post(new Runnable() {
            @Override
            public void run() {
                if (activity != null && !activity.isFinishing()) {
                    fragmentTransaction.commitAllowingStateLoss();
                }
            }
        });
    }
}

Kullanımı:

class MyFragment extends Fragment {
    @Override
    public void onDestroyView() {
        removeFragments(mFragment1, mFragment2, mFragment3);
        super.onDestroyView();
    }
}

1

OP'nin Destek Kitaplığı'nı kullanmasını engelleyen özel koşulları olsa da çoğu kişi onu kullanmalıdır. Android belgeleri bunu önermektedir ve uygulamanızı mümkün olan en geniş kitleye sunacaktır.

Gelen burada dolgun cevap ben destek kütüphane ile iç içe parçaları nasıl kullanılacağını gösteren bir örnek yaptı.

görüntü açıklamasını buraya girin


Ben OP idim. Destek kitaplığını kullanmamanın nedeni, kullanılan donanımın> = 4.0 ve <= 4.1 olarak açıkça tanımlandığı bir şirket içi uygulaması olmasıydı. Geniş bir kitleye ulaşmaya gerek yoktu, iç personeldi ve uygulamayı şirket dışında kullanma niyeti yoktu. Destek kitaplığının tek nedeni geriye dönük uyumlu olmaktır - ancak destek kitaplığıyla yapabileceğiniz her şeyi onsuz "doğal" olarak başarabilmeniz beklenir. Neden "yerel" daha yüksek bir sürüm, bir destek kitaplığından daha az özelliğe sahip olsun ki, yalnızca amaç aşağı doğru uyumlu olmaktır.
Mathias Conradt

1
Yine de elbette destek kütüphanesini kullanabilirsin ve ben de kullanabilirdim. Sadece Google'ın neden YALNIZCA destek kitaplığında özellikler sunduğunu ancak dışarıda olmadığını veya neden onu destek kitaplığı olarak adlandırdıklarını ve en iyi uygulama buysa, o zaman onu genel standart haline getirmediklerini anlamadım. Destek kitaplığıyla ilgili güzel bir makale: martiancraft.com/blog/2015/06/android-support-library
Mathias Conradt

@Mathias, güzel makale.
Suragch
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.