destek FragmentPagerAdapter eski parçalara başvuru tutar


109

EN SON BİLGİ:

Sorunumu, fragmentManager'ın eski parçaların örneklerini tutması ve görüntüleyicimin FragmentManager'ımla senkronize olmamasıyla ilgili bir sorun olarak daralttım. Bu soruna bakın ... http://code.google.com/p/android/issues/detail?id=19211#makechanges . Hala bunu nasıl çözeceğime dair hiçbir fikrim yok. Baska öneri...

Uzun süredir bu hatayı gidermeye çalıştım ve herhangi bir yardım çok takdir edilecektir. Şu şekilde parçaların listesini kabul eden bir FragmentPagerAdapter kullanıyorum:

List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, Fragment1.class.getName())); 
...
new PagerAdapter(getSupportFragmentManager(), fragments);

Uygulama standarttır. Fragments için ActionBarSherlock ve v4 hesaplanabilirlik kitaplığını kullanıyorum.

Benim sorunum, uygulamadan çıktıktan ve diğer birkaç uygulamayı açtıktan ve geri döndükten sonra parçaların FragmentActivity'ye (yani getActivity() == null) referanslarını kaybetmeleridir . Bunun neden olduğunu anlayamıyorum. Manuel olarak ayarlamayı denedim setRetainInstance(true);ama bu yardımcı olmuyor. Bunun FragmentActivity'm yok edildiğinde olduğunu anladım, ancak günlük mesajını almadan uygulamayı açarsam bu yine de oluyor. Herhangi bir fikir var mı?

@Override
protected void onDestroy(){
    Log.w(TAG, "DESTROYDESTROYDESTROYDESTROYDESTROYDESTROYDESTROY");
    super.onDestroy();
}

Adaptör:

public class PagerAdapter extends FragmentPagerAdapter {
    private List<Fragment> fragments;

    public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);

        this.fragments = fragments;

    }

    @Override
    public Fragment getItem(int position) {

        return this.fragments.get(position);

    }

    @Override
    public int getCount() {

        return this.fragments.size();

    }

}

Parçalarımdan biri soyuldu ama her şeyi sıyırdım ve hala çalışmıyor ...

public class MyFragment extends Fragment implements MyFragmentInterface, OnScrollListener {
...

@Override
public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    handler = new Handler();    
    setHasOptionsMenu(true);
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    Log.w(TAG,"ATTACHATTACHATTACHATTACHATTACH");
    context = activity;
    if(context== null){
        Log.e("IS NULL", "NULLNULLNULLNULLNULLNULLNULLNULLNULLNULLNULL");
    }else{
        Log.d("IS NOT NULL", "NOTNOTNOTNOTNOTNOTNOTNOT");
    }

}

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

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.my_fragment,container, false);

    return v;
}


@Override
public void onResume(){
    super.onResume();
}

private void callService(){
    // do not call another service is already running
    if(startLoad || !canSet) return;
    // set flag
    startLoad = true;
    canSet = false;
    // show the bottom spinner
    addFooter();
    Intent intent = new Intent(context, MyService.class);
    intent.putExtra(MyService.STATUS_RECEIVER, resultReceiver);
    context.startService(intent);
}

private ResultReceiver resultReceiver = new ResultReceiver(null) {
    @Override
    protected void onReceiveResult(int resultCode, final Bundle resultData) {
        boolean isSet = false;
        if(resultData!=null)
        if(resultData.containsKey(MyService.STATUS_FINISHED_GET)){
            if(resultData.getBoolean(MyService.STATUS_FINISHED_GET)){
                removeFooter();
                startLoad = false;
                isSet = true;
            }
        }

        switch(resultCode){
        case MyService.STATUS_FINISHED: 
            stopSpinning();
            break;
        case SyncService.STATUS_RUNNING:
            break;
        case SyncService.STATUS_ERROR:
            break;
        }
    }
};

public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    menu.clear();
    inflater.inflate(R.menu.activity, menu);
}

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

public void onScroll(AbsListView arg0, int firstVisible, int visibleCount, int totalCount) {
    boolean loadMore = /* maybe add a padding */
        firstVisible + visibleCount >= totalCount;

    boolean away = firstVisible+ visibleCount <= totalCount - visibleCount;

    if(away){
        // startLoad can now be set again
        canSet = true;
    }

    if(loadMore) 

}

public void onScrollStateChanged(AbsListView arg0, int state) {
    switch(state){
    case OnScrollListener.SCROLL_STATE_FLING: 
        adapter.setLoad(false); 
        lastState = OnScrollListener.SCROLL_STATE_FLING;
        break;
    case OnScrollListener.SCROLL_STATE_IDLE: 
        adapter.setLoad(true);
        if(lastState == SCROLL_STATE_FLING){
            // load the images on screen
        }

        lastState = OnScrollListener.SCROLL_STATE_IDLE;
        break;
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
        adapter.setLoad(true);
        if(lastState == SCROLL_STATE_FLING){
            // load the images on screen
        }

        lastState = OnScrollListener.SCROLL_STATE_TOUCH_SCROLL;
        break;
    }
}

@Override
public void onDetach(){
    super.onDetach();
    if(this.adapter!=null)
        this.adapter.clearContext();

    Log.w(TAG, "DETACHEDDETACHEDDETACHEDDETACHEDDETACHEDDETACHED");
}

public void update(final int id, String name) {
    if(name!=null){
        getActivity().getSupportActionBar().setTitle(name);
    }

}

}

Güncelleme yöntemi, bir kullanıcı farklı bir parça ile etkileşim kurduğunda ve getActivity boş döndürdüğünde çağrılır. İşte diğer parçanın çağırdığı yöntem ...

((MyFragment) pagerAdapter.getItem(1)).update(id, name);

Uygulama yok edildiğinde ve ardından uygulamayı varsayılan parçaya kadar başlatmak yerine yeniden oluşturulduğunda uygulamanın başlatıldığına ve ardından görüntüleyicinin bilinen son sayfaya gittiğine inanıyorum. Bu garip görünüyor, uygulamanın sadece varsayılan parçaya yüklenmesi gerekmez mi?


27
Android'in Fragmanı berbat!
Hamidreza Sadegh

13
Tanrım Günlük mesajlarınızı seviyorum: D
oli.G

Yanıtlar:


120

Bir problemle karşılaşıyorsunuz çünkü fragmanlarınızı örneklendiriyor ve referansları dışında tutuyorsunuz. PagerAdapter.getItem bu referansları ViewPager'dan bağımsız olarak kullanmaya çalışıyorsunuz. Seraph'ın dediği gibi, belirli bir zamanda bir ViewPager'da bir parçanın somutlaştırıldığını / eklendiğini garanti ediyorsunuz - bu bir uygulama ayrıntısı olarak düşünülmelidir. ViewPager, sayfalarını tembelce yükler; varsayılan olarak yalnızca geçerli sayfayı ve soldaki ve sağdakileri yükler.

Uygulamanızı arka plana koyarsanız, parça yöneticisine eklenen parçalar otomatik olarak kaydedilir. Uygulamanız öldürülmüş olsa bile bu bilgiler, uygulamanızı yeniden başlattığınızda geri yüklenir.

Şimdi birkaç sayfa, A, B ve C Parçalarını görüntülediğinizi düşünün. Bunların parça yöneticisine eklendiğini biliyorsunuz. Çünkü kullanıyorsun FragmentPagerAdapterve kullanmıyorsunFragmentStatePagerAdapter için, diğer sayfalara kaydırdığınızda bu parçalar eklenmeye devam edecek (ancak potansiyel olarak ayrılacaktır).

Daha sonra uygulamanızı arka planda tuttuğunuzu ve ardından uygulandığınızı düşünün. Geri döndüğünüzde Android, parça yöneticisinde Parçalar A, B ve C'ye sahip olduğunuzu hatırlayacak ve böylece bunları sizin için yeniden oluşturacak ve sonra ekleyecektir. Ancak, parça yöneticisine eklenenler şu anda Faaliyetinizdeki parça listenizde bulunanlar DEĞİLDİR.

FragmentPagerAdapter, getPositiono belirli sayfa konumu için zaten bir parça eklenmişse çağırmayı denemeyecektir . Aslında, Android tarafından yeniden oluşturulan parça asla kaldırılmayacağı için, onu bir çağrı ile değiştirme umudunuz yok getPosition. Bununla ilgilenmek, sizin bilmediğiniz bir etiketle eklendiği için referans almak da oldukça zordur. Bu, tasarım gereğidir; görünüm çağrı cihazının yönettiği parçalarla uğraşmaktan vazgeçersiniz. Tüm eylemlerinizi bir parça içinde gerçekleştirmeli, faaliyet ile iletişim kurmalı ve gerekirse belirli bir sayfaya geçmeyi talep etmelisiniz.

Şimdi, eksik aktivite ile ilgili probleminize geri dönün. pagerAdapter.getItem(1)).update(id, name)Tüm bunlar olduktan sonra aramak size listenizdeki, henüz parça yöneticisine eklenmemiş parçayı döndürür ve bu nedenle bir Etkinlik referansına sahip olmayacaktır. Güncelleme yönteminizin bazı paylaşılan veri yapısını (muhtemelen etkinlik tarafından yönetilen) değiştirmesi gerektiğini ve ardından belirli bir sayfaya geçtiğinizde bu güncellenmiş verilere dayanarak kendisini çizebileceğini öneririm.


1
Çözümünüzü çok zarif olduğu ve muhtemelen kodumu yeniden düzenleyeceği için beğendim, ancak sizin de söylediğiniz gibi, mantık ve verilerin% 100'ünü parça içinde tutmak istedim. Çözümünüz hemen hemen tüm verileri FragmentActivity'de tutmayı ve ardından her Parçayı yalnızca görüntüleme Mantığını işlemek için kullanmayı gerektirir. Dediğim gibi bunu tercih ederim ama fragmanlar arasındaki etkileşim çok ağır ve yönetmesi can sıkıcı olabilir. Her neyse, detaylı açıklamanız için teşekkür ederiz. Bunu benden çok daha iyi açıkladın.
Maurycy

28
Kısa: Bağdaştırıcının dışındaki bir Parçaya asla referans
vermeyin

2
Peki, FragmentPagerAdapter örneğini oluşturmadıysa bir Activity örneği FragmentPagerAdapter örneğine nasıl erişir? Gelecekteki örnekler, FragmentPagerAdapter ve tüm parça örneklerini yeniden başlatmayacak mı? FragmentPagerAdapter, parçalar arasındaki iletişimi yönetmek için tüm parça arabirimlerini uygulamalı mı?
Eric H.

"Bunu ele almak da bir referans elde etmek oldukça zordur çünkü sizin bilmediğiniz bir etiketle eklenmiştir. Bu tasarım gereğidir; görüntüleme çağrı cihazının yönettiği parçalarla mesajlaşmaktan vazgeçersiniz ." yanlış. o arayarak başvuru almak çok kolaydır instantiateItemve aslında gerektiğini de bunu onCreateetkinlik. ayrıntılara buradan bakın: stackoverflow.com/questions/14035090/…
morgwai

Genel olarak, parçaları yüklemeden kendi başınıza örneklemek
sakıncalıdır

108

Benim için çalışan basit bir çözüm buldum.

Parça adaptörünüzün FragmentPagerAdapter yerine FragmentStatePagerAdapter genişlemesini sağlayın ve boş döndürmek için onSave yöntemini geçersiz kılın

@Override
public Parcelable saveState()
{
    return null;
}

Bu, android'in parçayı yeniden oluşturmasını engeller


Bir gün sonra başka ve daha iyi bir çözüm buldum.

setRetainInstance(true)Tüm parçalarınızı arayın ve referansları bir yere kaydedin. Bunu aktivitemdeki statik değişkende yaptım, çünkü tek görev olarak ilan edildi ve parçalar her zaman aynı kalabilir.

Bu şekilde android parçaları yeniden oluşturmaz, ancak aynı örnekleri kullanır.


4
Teşekkürler, Teşekkürler, Teşekkürler Çok teşekkür ederim, gerçekten teşekkür edecek sözüm yok Mik, son 10 gündür bu konuyu takip ediyordum ve pek çok

7
Parçalara ve / veya etkinliklere statik bir referansa sahip olmak, çok kolay bir şekilde bellek sızıntılarına neden olabileceğinden, yapılması çok riskli bir şeydir. Elbette, dikkatli olursanız, artık ihtiyaç duyulmadığında boş olarak ayarlayarak oldukça kolay bir şekilde başa çıkabilirsiniz.
android geliştiricisi

Bu benim için çalıştı. Parçalar ve ilgili görünümleri, uygulama çöktükten ve yeniden başlatıldıktan sonra bağlantılarını korur. Teşekkürler!
swebal

Ben kullanıyorum setRetainInstance(true)ile FragmentPagerAdapter. Hepsi iyi çalışıyor. Ancak cihazı döndürdüğümde, adaptör hala parçalara sahip ancak parçalar gösterilmiyor. Parçaların yaşam döngüsü yöntemleri de çağrılmaz. Biri yardım edebilir mi?
Jonas

1
günümü kurtardın !!! Şimdiye kadar 3 gündür bu hatayı arıyordum. Tek Görev Etkinliğinin içinde 2 parça bulunan ve "Etkinlikleri tutma" bayrağı etkinleştirilmiş bir Görüntüleyici'm vardı. Çok teşekkürler!!!!
matrix

29

Bu sorunu, fragmanlarıma FragmentPagerAdapter yerine doğrudan FragmentManager üzerinden erişerek çözdüm. Öncelikle FragmentPagerAdapter tarafından oluşturulan otomatik parçanın etiketini bulmam gerekiyor ...

private String getFragmentTag(int pos){
    return "android:switcher:"+R.id.viewpager+":"+pos;
}

Sonra o parçaya bir referans alırım ve ihtiyacım olan şeyi yaparım ...

Fragment f = this.getSupportFragmentManager().findFragmentByTag(getFragmentTag(1));
((MyFragmentInterface) f).update(id, name);
viewPager.setCurrentItem(1, true);

Parçalarımın içinde setRetainInstance(false);, SavedInstanceState paketine manuel olarak değerler ekleyebilmek için ayarlıyorum.

@Override
public void onSaveInstanceState(Bundle outState) {
    if(this.my !=null)
        outState.putInt("myId", this.my.getId());

    super.onSaveInstanceState(outState);
}

ve sonra OnCreate'de bu anahtarı alırım ve gerektiğinde parçanın durumunu geri yüklerim. Çözmesi zor olan (en azından benim için) kolay bir çözüm.


Size benzer bir sorunum var, ancak açıklamanızı tam olarak anlamadım, daha fazla ayrıntı verebilir misiniz? Listede parçaları depolayan bir adaptöre de sahibim, ancak uygulamamı son uygulamalardan devam ettirdiğimde, bazı ayrılmış parçalar olduğu için uygulamalar çöküyor. Sorun burada stackoverflow.com/questions/11631408/…
Georgy Gobozov

3
Parça referansındaki 'benim' nedir?
Josh

Hepimiz bu çözümün iyi bir yaklaşım olmadığını biliyoruz, ancak FragmentByTagViewPager'da kullanmanın en kolay yolu bu .
Youngjae

iç etiket atama yöntemiyle uyumluluğa güvenmeden parçalara erişmenin bir yolu: stackoverflow.com/questions/14035090/…
morgwai

26

Global çalışma için test edilmiş çözüm.

getSupportFragmentManager()bazı zamanlarda boş referansı tutar ve View pager, aynı parçaya referans bulduğu için yeni oluşturmaz. Bu nedenle, bu kullanım getChildFragmentManager()sorunu basit bir şekilde çözer.

Bunu yapma:

new PagerAdapter(getSupportFragmentManager(), fragments);

Bunu yap:

new PagerAdapter(getChildFragmentManager() , fragments);


6
bu yalnızca, pagerAdapter'ı etkinliğinizin içinde değil de bir Fragment içinde barındırıyorsanız (ve somutlaştırıyorsanız) mümkündür. Bu durumda haklısınız, varsayılan olarak childFragmentManager'ı kullanmalısınız.
SupportFragmentManager'ı

Çok hassas. Hack kullanmadan sorunumu çözdüm. Teşekkürler! Kotlin versiyonum FragmentStatePagerAdapter(activity!!.supportFragmentManager)daha kolay bir görünüme dönüştü FragmentStatePagerAdapter(childFragmentManager):)
ecth

7

ViewPager'da parçalar arasında etkileşim kurmaya çalışmayın. Başka bir parçanın eklendiğini veya var olduğunu garanti edemezsiniz. İşlem çubuğu başlığını parçadan değiştirmek yerine, bunu aktivitenizden yapabilirsiniz. Bunun için standart arayüz modelini kullanın:

public interface UpdateCallback
{
    void update(String name);
}

public class MyActivity extends FragmentActivity implements UpdateCallback
{
    @Override
    public void update(String name)
    {
        getSupportActionBar().setTitle(name);
    }

}

public class MyFragment extends Fragment
{
    private UpdateCallback callback;

    @Override
    public void onAttach(SupportActivity activity)
    {
        super.onAttach(activity);
        callback = (UpdateCallback) activity;
    }

    @Override
    public void onDetach()
    {
        super.onDetach();
        callback = null;
    }

    public void updateActionbar(String name)
    {
        if(callback != null)
            callback.update(name);
    }
}

Elbette şimdi kodu ekleyeceğim ... Bu yöntemleri zaten kaydediyorum. OnDetach, fragmentActivity'de onStop çağrıldığında çağrılır. onAttach, FragmentActivity'nin onCreate öğesinden hemen önce çağrılır.
Maurycy

Hmm teşekkür ederim ama sorun hala devam ediyor. Bunun yerine adı bir geri arama olarak belirledim. Parçaya mantık koyamaz mıyım? Parçalarımın, faaliyete referans gerektiren hizmetleri ve diğer unsurları ateşlemesini sağlıyorum. Yaşadığım sorunu önlemek için tüm uygulama mantığımı ana etkinliğe taşımam gerekir. Bu biraz gereksiz görünüyor.
Maurycy

Tamam, bu yüzden bunu tamamen test ettim ve tüm kodumu yorumladım ... başlığı değiştirmek için geri arama dışında. Uygulama ilk başlatıldığında ve o ekrana gittiğimde başlık adı değiştirilir np. Birkaç uygulama yükledikten sonra geri döndüğünüzde başlık artık değişmiyor. Görünüşe göre geri arama eski bir etkinlik örneğinde alınıyor. Başka bir açıklama düşünemiyorum
Maurycy

bunu fragmentManager ve bu sorunla ilgili bir sorun haline getirdim ... code.google.com/p/android/issues/detail?id=19211 . Bunu nasıl çözeceğime dair hâlâ hiçbir fikrim yok
Maurycy

5

Görüntüleyiciyi yok ettiğinizde parçaları kaldırabilirsiniz, benim durumumda onları parçamdan çıkardım onDestroyView():

@Override
public void onDestroyView() {

    if (getChildFragmentManager().getFragments() != null) {
        for (Fragment fragment : getChildFragmentManager().getFragments()) {
            getChildFragmentManager().beginTransaction().remove(fragment).commitAllowingStateLoss();
        }
    }

    super.onDestroyView();
}

Teşekkürler, çözümünüz işe yarıyor. Ayrıca adaptöre (değil ) ViewPagerdayalı olması gerekir . Ayrıca başka bir varyant da çalışır: adaptör oluşturmadan önce alt parçaları kullanmayın , ancak kaldırın . childFragmentManagerfragmentManageronDestroyViewViewPager
CoolMind

4

Birkaç saat benzer bir sorunu aradıktan sonra, başka bir çözümün olduğunu düşünüyorum. Bu en azından benim için çalıştı ve sadece birkaç satırı değiştirmem gerekiyor.

Yaşadığım sorun bu, iki Parçalı bir FragmentStatePagerAdapter kullanan bir görünüm çağrı cihazına sahip bir etkinliğim var. Etkinliği yok edilmeye zorlayana (geliştirici seçenekleri) veya ekranı döndürene kadar her şey yolunda gidiyor. GetItem yöntemi içinde oluşturulduktan sonra iki parçaya bir başvuru tutuyorum.

Bu noktada etkinlik yeniden oluşturulacak ve bu noktada her şey yolunda gidiyor, ancak getItem yeniden çağrılmadığı için fragmanlarıma referansı kaybettim.

FragmentStatePagerAdapter içinde bu sorunu bu şekilde çözdüm:

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object aux = super.instantiateItem(container, position);

        //Update the references to the Fragments we have on the view pager
        if(position==0){
            fragTabOne = (FragOffersList)aux;
        }
        else{
            fragTabTwo = (FragOffersList) aux;
        }

        return aux;
    }

Bağdaştırıcının zaten dahili olarak bir referansı varsa, getItem'de tekrar bir çağrı almayacaksınız ve bunu değiştirmemelisiniz. Bunun yerine, parçaların her biri için çağrılacak olan bu diğer yöntem instantiateItem () 'e bakarak, kullanılan parçayı elde edebilirsiniz.

Umarım bu herkese yardımcı olur.


Ya çok sayıda parçanız varsa? gerçekten her biri için bir referans kaydetmeyi tercih eder miydiniz? Hafıza için kötü değil mi?
android geliştiricisi

Bu tamamen neyi başarmaya çalıştığınıza bağlı. Normalde bu görüntüleme çağrı cihazlarında bir seferde 3'ten fazla parça tutmazsınız. Ortadaki ve her iki taraftaki. İstediğim şey için parçalara gerçekten bir gönderme yapmam gerekiyor, ama sadece ikisiyle uğraşmam gerektiğini biliyorum.
Lancelot

0

FragmentManager, onResume () yöntemi çağrılır çağrılmaz Fragmentlerinizi sizin için geri yüklemeyi halledeceğinden, parçanın aktiviteye çağrılmasına ve kendisini bir listeye ekledim. Benim örneğimde bunların hepsini PagerAdapter uygulamamda saklıyorum. Her parça konumunu bilir çünkü yaratılıştaki parça argümanlarına eklenir. Artık ne zaman belirli bir dizindeki bir parçayı değiştirmem gerekirse yapmam gereken tek şey adaptörümdeki listeyi kullanmak.

Aşağıda, odak noktasına hareket ederken parçayı büyütecek ve odak dışına çıktıkça küçültecek özel bir ViewPager için Adaptör örneği verilmiştir. Burada sahip olduğum Adaptör ve Parça sınıflarının yanı sıra, ihtiyacınız olan tek şey ana aktivitenin adaptör değişkenine başvurabilmesi ve ayarlanmasıdır.

adaptör

public class GrowPagerAdapter extends FragmentPagerAdapter implements OnPageChangeListener, OnScrollChangedListener {

public final String TAG = this.getClass().getSimpleName();

private final int COUNT = 4;

public static final float BASE_SIZE = 0.8f;
public static final float BASE_ALPHA = 0.8f;

private int mCurrentPage = 0;
private boolean mScrollingLeft;

private List<SummaryTabletFragment> mFragments;

public int getCurrentPage() {
    return mCurrentPage;
}

public void addFragment(SummaryTabletFragment fragment) {
    mFragments.add(fragment.getPosition(), fragment);
}

public GrowPagerAdapter(FragmentManager fm) {
    super(fm);

    mFragments = new ArrayList<SummaryTabletFragment>();
}

@Override
public int getCount() {
    return COUNT;
}

@Override
public Fragment getItem(int position) {
    return SummaryTabletFragment.newInstance(position);
}

@Override
public void onPageScrollStateChanged(int state) {}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    adjustSize(position, positionOffset);
}

@Override
public void onPageSelected(int position) {
    mCurrentPage = position;
}

/**
 * Used to adjust the size of each view in the viewpager as the user
 * scrolls.  This provides the effect of children scaling down as they
 * are moved out and back to full size as they come into focus.
 * 
 * @param position
 * @param percent
 */
private void adjustSize(int position, float percent) {

    position += (mScrollingLeft ? 1 : 0);
    int secondary = position + (mScrollingLeft ? -1 : 1);
    int tertiary = position + (mScrollingLeft ? 1 : -1);

    float scaleUp = mScrollingLeft ? percent : 1.0f - percent;
    float scaleDown = mScrollingLeft ? 1.0f - percent : percent;

    float percentOut = scaleUp > BASE_ALPHA ? BASE_ALPHA : scaleUp;
    float percentIn = scaleDown > BASE_ALPHA ? BASE_ALPHA : scaleDown;

    if (scaleUp < BASE_SIZE)
        scaleUp = BASE_SIZE;

    if (scaleDown < BASE_SIZE)
        scaleDown = BASE_SIZE;

    // Adjust the fragments that are, or will be, on screen
    SummaryTabletFragment current = (position < mFragments.size()) ? mFragments.get(position) : null;
    SummaryTabletFragment next = (secondary < mFragments.size() && secondary > -1) ? mFragments.get(secondary) : null;
    SummaryTabletFragment afterNext = (tertiary < mFragments.size() && tertiary > -1) ? mFragments.get(tertiary) : null;

    if (current != null && next != null) {

        // Apply the adjustments to each fragment
        current.transitionFragment(percentIn, scaleUp);
        next.transitionFragment(percentOut, scaleDown);

        if (afterNext != null) {
            afterNext.transitionFragment(BASE_ALPHA, BASE_SIZE);
        }
    }
}

@Override
public void onScrollChanged(int l, int t, int oldl, int oldt) {

    // Keep track of which direction we are scrolling
    mScrollingLeft = (oldl - l) < 0;
}
}

fragman

public class SummaryTabletFragment extends BaseTabletFragment {

public final String TAG = this.getClass().getSimpleName();

private final float SCALE_SIZE = 0.8f;

private RelativeLayout mBackground, mCover;
private TextView mTitle;
private VerticalTextView mLeft, mRight;

private String mTitleText;
private Integer mColor;

private boolean mInit = false;
private Float mScale, mPercent;

private GrowPagerAdapter mAdapter;
private int mCurrentPosition = 0;

public String getTitleText() {
    return mTitleText;
}

public void setTitleText(String titleText) {
    this.mTitleText = titleText;
}

public static SummaryTabletFragment newInstance(int position) {

    SummaryTabletFragment fragment = new SummaryTabletFragment();
    fragment.setRetainInstance(true);

    Bundle args = new Bundle();
    args.putInt("position", position);
    fragment.setArguments(args);

    return fragment;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);

    mRoot = inflater.inflate(R.layout.tablet_dummy_view, null);

    setupViews();
    configureView();

    return mRoot;
}

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

    if (savedInstanceState != null) {
        mColor = savedInstanceState.getInt("color", Color.BLACK);
    }

    configureView();
}

@Override
public void onSaveInstanceState(Bundle outState)  {

    outState.putInt("color", mColor);

    super.onSaveInstanceState(outState);
}

@Override
public int getPosition() {
    return getArguments().getInt("position", -1);
}

@Override
public void setPosition(int position) {
    getArguments().putInt("position", position);
}

public void onResume() {
    super.onResume();

    mAdapter = mActivity.getPagerAdapter();
    mAdapter.addFragment(this);
    mCurrentPosition = mAdapter.getCurrentPage();

    if ((getPosition() == (mCurrentPosition + 1) || getPosition() == (mCurrentPosition - 1)) && !mInit) {
        mInit = true;
        transitionFragment(GrowPagerAdapter.BASE_ALPHA, GrowPagerAdapter.BASE_SIZE);
        return;
    }

    if (getPosition() == mCurrentPosition && !mInit) {
        mInit = true;
        transitionFragment(0.00f, 1.0f);
    }
}

private void setupViews() {

    mCover = (RelativeLayout) mRoot.findViewById(R.id.cover);
    mLeft = (VerticalTextView) mRoot.findViewById(R.id.title_left);
    mRight = (VerticalTextView) mRoot.findViewById(R.id.title_right);
    mBackground = (RelativeLayout) mRoot.findViewById(R.id.root);
    mTitle = (TextView) mRoot.findViewById(R.id.title);
}

private void configureView() {

    Fonts.applyPrimaryBoldFont(mLeft, 15);
    Fonts.applyPrimaryBoldFont(mRight, 15);

    float[] size = UiUtils.getScreenMeasurements(mActivity);
    int width = (int) (size[0] * SCALE_SIZE);
    int height = (int) (size[1] * SCALE_SIZE);

    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
    mBackground.setLayoutParams(params);

    if (mScale != null)
        transitionFragment(mPercent, mScale);

    setRandomBackground();

    setTitleText("Fragment " + getPosition());

    mTitle.setText(getTitleText().toUpperCase());
    mLeft.setText(getTitleText().toUpperCase());
    mRight.setText(getTitleText().toUpperCase());

    mLeft.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            mActivity.showNextPage();
        }
    });

    mRight.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            mActivity.showPrevPage();
        }
    });
}

private void setRandomBackground() {

    if (mColor == null) {
        Random r = new Random();
        mColor = Color.rgb(r.nextInt(255), r.nextInt(255), r.nextInt(255));
    }

    mBackground.setBackgroundColor(mColor);
}

public void transitionFragment(float percent, float scale) {

    this.mScale = scale;
    this.mPercent = percent;

    if (getView() != null && mCover != null) {

        getView().setScaleX(scale);
        getView().setScaleY(scale);

        mCover.setAlpha(percent);
        mCover.setVisibility((percent <= 0.05f) ? View.GONE : View.VISIBLE);
    }
}

@Override
public String getFragmentTitle() {
    return null;
}
}

0

Çözümüm: Hemen hemen her Görünümü olarak ayarladım static. Artık uygulamam mükemmel etkileşim kuruyor. Statik metotları her yerden çağırabilmek belki iyi bir stil değil, ama neden işe yaramayan bir kodla oynayalım ki? Burada SO'da çok sayıda soru ve cevaplarını okudum ve hiçbir çözüm (benim için) başarı getirmedi.

Belleği sızdırabileceğini ve yığın yığınını israf edebileceğini biliyorum ve kodum diğer projelere uymayacak, ancak bundan korkmuyorum - uygulamayı farklı cihazlarda ve koşullarda test ettim, hiç sorun yok, Android Platform bunu halledebilecek gibi görünüyor. Kullanıcı arayüzü her saniye yenilenir ve hatta bir S2 ICS (4.0.3) cihazında uygulama binlerce coğrafi işaretleyiciyi işleyebilir.


0

Aynı sorunla karşılaştım ama ViewPager'ım kullanarak bir adaptör oluşturan ve ayarlayan bir TopFragment içindeydi setAdapter(new FragmentPagerAdapter(getChildFragmentManager())).

onAttachFragment(Fragment childFragment)TopFragment'da şu şekilde geçersiz kılarak bu sorunu çözdüm:

@Override
public void onAttachFragment(Fragment childFragment) {
    if (childFragment instanceof OnboardingDiamondsFragment) {
        mChildFragment = (ChildFragment) childFragment;
    }

    super.onAttachFragment(childFragment);
}

Zaten bilindiği gibi (yukarıdaki yanıtlara bakın), childFragmentManager kendini yeniden oluşturduğunda, aynı zamanda viewPager'ın içindeki parçaları da oluşturur.
Önemli olan, bundan sonra onAttachFragment'ı çağırması ve şimdi yeniden oluşturulmuş yeni parçaya bir referansımız olması!

Umarım bu benim gibi bu eski Q'yu alan herkese yardımcı olur :)


0

SparceArray'deki parçaları kaydederek sorunu çözdüm:

public abstract class SaveFragmentsPagerAdapter extends FragmentPagerAdapter {

    SparseArray<Fragment> fragments = new SparseArray<>();

    public SaveFragmentsPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        fragments.append(position, fragment);
        return fragment;
    }

    @Nullable
    public Fragment getFragmentByPosition(int position){
        return fragments.get(position);
    }

}

0

Bilmeniz için ...

Bu sınıflarla ilgili sıkıntılara ek olarak, paylaşmaya değer oldukça ilginç bir hata var.

Bir öğe ağacında gezinmek için bir ViewPager kullanıyorum (bir öğe seçin ve görünüm sayfalayıcısı sağa kaydırmayı canlandırıyor ve sonraki dal beliriyor, geri dönüyor ve ViewPager, önceki düğüme dönmek için ters yönde kayıyor) .

Sorun, FragmentStatePagerAdapter sonundaki parçaları itip açtığımda ortaya çıkıyor. Öğelerin değiştiğini fark edecek kadar akıllı ve öğe değiştiğinde bir parça oluşturup değiştirecek kadar akıllı. Ancak, parça durumunu atacak kadar akıllı değil veya adaptör boyutu değiştiğinde dahili olarak kaydedilen parça durumlarını kırpacak kadar akıllı değil. Bu nedenle, bir öğeyi açıp yenisini sonuna ittiğinizde, yeni öğenin parçası, eski öğe için parçanın kaydedilmiş durumunu alır ve bu da kodumda mutlak hasara neden olur. Parçalarım internetten yeniden alınması için çok fazla çalışma gerektirebilecek veriler taşıyor, bu yüzden durumu kaydetmemek gerçekten bir seçenek değildi.

Temiz bir çözümüm yok. Bunun gibi bir şey kullandım:

  public void onSaveInstanceState(Bundle outState) {
    IFragmentListener listener = (IFragmentListener)getActivity();
    if (listener!= null)
    {
        if (!listener.isStillInTheAdapter(this.getAdapterItem()))
        {
            return; // return empty state.
        }

    }
    super.onSaveInstanceState(outState);

    // normal saving of state for flips and 
    // paging out of the activity follows
    ....
  }

Kusurlu bir çözüm çünkü yeni parça örneği hala kaydedilmiş bir Durum Paketi alıyor, ancak en azından eski verileri taşımıyor.


0

İnsanlar yorumları okuma eğiliminde olmadıkları için, burada yazdıklarımı çoğunlukla tekrarlayan bir cevap burada. :

Sorunun temel nedeni, android sisteminin getItemgerçekte görüntülenen parçaları elde etmek için çağırmaması , ancak instantiateItem. Bu yöntem ilk olarak içindeki belirli bir sekme için bir parça örneğini aramaya ve yeniden kullanmaya çalışır FragmentManager. Yalnızca bu arama başarısız olursa (bu, yalnızca FragmentManageryeni oluşturulduğunda ilk kez gerçekleşir ) o getItemzaman çağrılır. Örneğin bir kullanıcı cihazını her döndürdüğünde parçaların (ağır olabilen) yeniden yaratılmaması aşikâr nedenlerden dolayıdır.
Bunu çözmek Fragment.instantiateiçin, aktivitenizde fragmanlar oluşturmak yerine , bunu yapmalısınız pagerAdapter.instantiateItemve tüm bu çağrılar , kendi kurucuları kullanılarak fragmanların gerçekten yaratıldığı yer olmalıdır.startUpdate/finishUpdate sırasıyla parça işlemi başlatan / kesinleştiren yöntem çağrıları .getItem

List<Fragment> fragments = new Vector<Fragment>();

@Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.myLayout);
        ViewPager viewPager = (ViewPager) findViewById(R.id.myViewPager);
        MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);
        ((TabLayout) findViewById(R.id.tabs)).setupWithViewPager(viewPager);

        adapter.startUpdate(viewPager);
        fragments.add(adapter.instantiateItem(viewPager, 0));
        fragments.add(adapter.instantiateItem(viewPager, 1));
        // and so on if you have more tabs...
        adapter.finishUpdate(viewPager);
}

class MyPagerAdapter extends FragmentPagerAdapter {

        public MyPagerAdapter(FragmentManager manager) {super(manager);}

        @Override public int getCount() {return 2;}

        @Override public Fragment getItem(int position) {
            if (position == 0) return new Fragment0();
            if (position == 1) return new Fragment1();
            return null;  // or throw some exception
        }

        @Override public CharSequence getPageTitle(int position) {
            if (position == 0) return getString(R.string.tab0);
            if (position == 1) return getString(R.string.tab1);
            return null;  // or throw some exception
        }
}
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.