Sonuç için başlangıç ​​parçası gibi çalışan bir yöntem var mı?


95

Şu anda bir kaplamada bir parçam var. Bu, serviste oturum açmak içindir. Telefon uygulamasında, arayüzde göstermek istediğim adımların her biri kendi ekranları ve etkinlikleridir. Oturum açma işleminin 3 bölümü vardır ve her birinin startActivityForResult () ile çağrılan kendi etkinliği vardır.

Şimdi aynı şeyi parçalar ve bir kaplama kullanarak yapmak istiyorum. Yer paylaşımı, her etkinliğe karşılık gelen bir parça gösterecektir. Sorun, bu parçaların Honeycomb API'deki bir etkinlikte barındırılmasıdır. İlk parçayı çalıştırabilirim, ancak sonraActivityForResult () 'u başlatmam gerekiyor, ki bu mümkün değil. StartFragmentForResult () satırlarında yeni bir parçayı başlatabileceğim ve bittiğinde önceki parçaya bir sonuç döndürmesini sağlayan bir şey var mı?

Yanıtlar:


58

Tüm Parçalar Aktivitelerin içinde yaşar. Bir sonuç için bir Fragman başlatmak pek bir anlam ifade etmiyor, çünkü onu barındıran Aktivite ona her zaman erişebilir ve bunun tersi de geçerlidir. Parçanın bir sonucu iletmesi gerekiyorsa, Faaliyetine erişebilir ve sonucunu ayarlayıp bitirebilir. Tek bir Aktivitede Parçaların değiştirilmesi durumunda, Aktiviteye her iki Fragman tarafından da erişilebilir ve tüm mesajlarınız Aktivite üzerinden geçebilir.

Her zaman bir Parça ve Etkinliği arasında iletişiminiz olduğunu unutmayın. Bir sonuç için başlamak ve bir sonuçla bitirmek, Faaliyetler arasındaki iletişim mekanizmasıdır - Faaliyetler daha sonra gerekli bilgileri kendi Parçalarına devredebilir.


11
Ek olarak, bir parçayı diğerinden yüklerken, hedef parçayı ayarlayabilir ve isterseniz ana parçanın onActivityResultyöntemini geri çağırabilirsiniz .
PJL

4
Cevabınız eksik, parçalar, aktiviteler gibi bir yaşam döngüsü kullanıyor. Bu nedenle, argümanları yöntemler aracılığıyla geçiremeyiz, ancak değerleri geçirmek için demetleri kullanmalıyız. Parça bir yerde bir değer oluştursa bile, ne zaman bittiğini bilmemiz gerekir. Önceki parçalar başladığında / devam ettiğinde değeri almalı mı? Bu bir fikirdir. Ancak değeri saklamanın uygun bir yolu yoktur, parça birçok başka parça / faaliyet tarafından çağrılabilir.
Loenix

1
peki, çok daha fazla esneklik sağlayacağı için 'sonuç için parçaya başla' demek mantıklı olacaktır -> neden bir çocuk ebeveyni hakkında bilgi sahibi olsun ki? Parçanın, hangi aktivitede yaşadığına bakılmaksızın sadece işini yapması ve sonucu döndürmesi çok daha temiz olmazdı. Özellikle tek aktivite uygulamaları bağlamında.
daneejela

Ya potansiyel bellek sızıntıları?
daneejela

60

Dilerseniz Fragmentler arası iletişim için bazı yöntemler vardır,

setTargetFragment(Fragment fragment, int requestCode)
getTargetFragment()
getTargetRequestCode()

Bunları kullanarak geri arama yapabilirsiniz.

Fragment invoker = getTargetFragment();
if(invoker != null) {
    invoker.callPublicMethod();
}

Onaylamadım ama belki işe yarıyor. bu nedenle, 'nin kötüye kullanılması nedeniyle döngüsel başvurudan kaynaklanan bellek sızıntılarına dikkat etmelisiniz setTargetFragment().
nagoya0

3
Şimdiye kadarki en iyi çözüm! Bir Aktivite ve bildirmek için doğru parçayı bulmaya çalışmanın bir kabus olacağı bir yığın parçası olduğunda harika çalışıyor. Teşekkürler
Damien Praca

1
@ userSeven7s bu olmaz için çalışma yerine davayı ancak için çalışması gerekir gizlemek durumunda
Muhammed Babar

1
@ nagoya0, hedef parça için WeakReference kullanırsak daha iyi olur
quangson91

Bu işe setTargetFragmentyarasa da , şu anda kullanımdan kaldırılmıştır. Bkz setResultListeneriçinde stackoverflow.com/a/61881149/2914140 burada.
CoolMind

11

Aynı ViewModel'i parçalar arasında paylaşabiliriz

SharedViewModel

import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.ViewModel

class SharedViewModel : ViewModel() {

    val stringData: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }

}

FirstFragment

import android.arch.lifecycle.Observer
import android.os.Bundle
import android.arch.lifecycle.ViewModelProviders
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

class FirstFragment : Fragment() {

    private lateinit var sharedViewModel: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activity?.run {
            sharedViewModel = ViewModelProviders.of(activity).get(SharedViewModel::class.java)
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        sharedViewModel.stringData.observe(this, Observer { dateString ->
            // get the changed String
        })

    }

}

SecondFragment

import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGrou

class SecondFragment : Fragment() {

    private lateinit var sharedViewModel: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activity?.run {
            sharedViewModel = ViewModelProviders.of(activity).get(SharedViewModel::class.java)
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        changeString()
    }

    private fun changeString() {
        sharedViewModel.stringData.value = "Test"
    }

}

3
Merhaba Levon, yukarıdaki kodun aynı görünüm modeli örneğini paylaşmayacağını düşünüyorum. Bu (parçayı) ViewModelProviders.of (this) .get (SharedViewModel :: class.java) için iletiyorsunuz. Bu, parçalar için iki ayrı örnek oluşturacaktır. ViewModelProviders.of (activity) .get (SharedViewModel :: class.java)
Shailendra Patil

@ShailendraPatil Güzel yakala, şimdi düzelteceğim.
Levon Petrosyan

8

Son zamanlarda Google, parça sonuçları için merkezi bir depo görevi görmesini mümkün FragmentManagerkılan yeni bir yetenek ekledi FragmentManager. Verileri Fragments arasında kolayca ileri geri aktarabiliriz.

Başlangıç ​​parçası.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Use the Kotlin extension in the fragment-ktx artifact
    setResultListener("requestKey") { key, bundle ->
        // We use a String here, but any type that can be put in a Bundle is supported
        val result = bundle.getString("bundleKey")
        // Do something with the result...
    }
}

Sonucu geri istediğimiz bir fragman.

button.setOnClickListener {
    val result = "result"
    // Use the Kotlin extension in the fragment-ktx artifact
    setResult("requestKey", bundleOf("bundleKey" to result))
}

Snippet, Google'ın resmi belgelerinden alınmıştır. https://developer.android.com/training/basics/fragments/pass-data-between#kotlin

Bu cevabın yazıldığı tarihte, bu özellik hala alphadurumdadır. Bu bağımlılığı kullanarak deneyebilirsiniz.

androidx.fragment:fragment:1.3.0-alpha05

4

2 sentim.

Gizle ve göster / ekle (mevcut / yeni) kullanarak eski bir parçayı yenisiyle değiştirerek parçalar arasında geçiş yapıyorum. Yani bu cevap, benim yaptığım gibi parçaları kullanan geliştiriciler içindir.

Daha sonra onHiddenChangedeski parçanın yenisinden geri döndüğünü bilmek için yöntemi kullanıyorum . Aşağıdaki koda bakın.

Yeni parçadan ayrılmadan önce, eski parça tarafından sorgulanacak global bir parametrede bir sonuç belirledim. Bu çok saf bir çözüm.

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    if (hidden) return;
    Result result = Result.getAndReset();
    if (result == Result.Refresh) {
        refresh();
    }
}

public enum Result {
    Refresh;

    private static Result RESULT;

    public static void set(Result result) {
        if (RESULT == Refresh) {
            // Refresh already requested - no point in setting anything else;
            return;
        }
        RESULT = result;
    }

    public static Result getAndReset() {
        Result result = RESULT;
        RESULT = null;
        return result;
    }
}

Nedir getAndReset()yöntemi?
EpicPandaForce

onResume()İkincisi kovulduğunda ilk fragman da çağrılmıyor mu?
PJ_Finnegan

2

Parçanızda getActivity () 'yi çağırabilirsiniz. Bu, parçayı oluşturan aktiviteye erişmenizi sağlayacaktır. Oradan, değerleri ayarlamak veya değerleri geçirmek için özelleştirme yönteminizi çağırabilirsiniz.


1

Sonuçlar için parçaları başlatmanıza izin veren bir Android kitaplığı - FlowR vardır.

Sonuç için bir fragman başlatılıyor.

Flowr.open(RequestFragment.class)
    .displayFragmentForResults(getFragmentId(), REQUEST_CODE);

İşleme, çağıran parçayla sonuçlanır.

@Override
protected void onFragmentResults(int requestCode, int resultCode, Bundle data) {
    super.onFragmentResults(requestCode, resultCode, data);

    if (requestCode == REQUEST_CODE) {
        if (resultCode == Activity.RESULT_OK) {
            demoTextView.setText("Result OK");
        } else {
            demoTextView.setText("Result CANCELED");
        }
    }
}

Sonucu Parçada ayarlama.

Flowr.closeWithResults(getResultsResponse(resultCode, resultData));

1

Arayüzler (ve Kotlin) kullanan bir çözüm. Temel fikir, bir geri arama arabirimi tanımlamak, bunu faaliyetinize uygulamak ve ardından onu parçanızdan çağırmaktır.

İlk önce bir arayüz oluşturun ActionHandler:

interface ActionHandler {
    fun handleAction(actionCode: String, result: Int)
}

Sonra, bunu çocuğunuzdan arayın (bu durumda, parçanız):

companion object {
    const val FRAGMENT_A_CLOSED = "com.example.fragment_a_closed"
}

fun closeFragment() {
    try {
        (activity as ActionHandler).handleAction(FRAGMENT_A_CLOSED, 1234)
    } catch (e: ClassCastException) {
        Timber.e("Calling activity can't get callback!")
    }
    dismiss()
}

Son olarak, geri aramayı almak için bunu ebeveyninizde uygulayın (bu durumda, Faaliyetiniz):

class MainActivity: ActionHandler { 
    override fun handleAction(actionCode: String, result: Int) {
        when {
            actionCode == FragmentA.FRAGMENT_A_CLOSED -> {
                doSomething(result)
            }
            actionCode == FragmentB.FRAGMENT_B_CLOSED -> {
                doSomethingElse(result)
            }
            actionCode == FragmentC.FRAGMENT_C_CLOSED -> {
                doAnotherThing(result)
            }
        }
    }

0

Verileri geri göndermenin en kolay yolu setArgument () yöntemidir. Örneğin, fragment3'ü çağıran fragment2'yi çağıran fragment1'e sahipsiniz, fragment1 -> framgnet2 -> fargment3

Parça 1

public void navigateToFragment2() {
    if (fragmentManager == null) return;

    Fragment2 fragment = Fragment2.newInstance();
    String tag = "Fragment 2 here";
    fragmentManager.beginTransaction()
            .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
            .add(R.id.flContent, fragment, tag)
            .addToBackStack(null)
            .commitAllowingStateLoss();
}

Fragment2'de her zamanki gibi fragment3 diyoruz

private void navigateToFragment3() {
    if (fragmentManager == null) return;
    Fragment3 fragment = new Fragment3();
    fragmentManager.beginTransaction()
            .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
            .replace(R.id.flContent, fragment, tag)
            .addToBackStack(null)
            .commit();
}

Parça3'teki görevimizi bitirdiğimizde şimdi şöyle diyoruz:

FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
if (fragmentManager == null) return;
fragmentManager.popBackStack();
Bundle bundle = new Bundle();
bundle.putString("bundle_filter", "data");
fragmentManager.findFragmentByTag("Fragment 2 here").setArguments(bundle);

Şimdi fragment2'de kolayca argümanlar çağırabiliriz

@Override
public void onResume() {
    super.onResume();
    Bundle rgs = getArguments();
    if (args != null) 
        String data = rgs.getString("bundle_filter");
}

Buna dikkat edin, bazı durumlarda işe yarayabilir, ancak parçanızın orijinal argümanları varsa (bu örnekte 'fragman 2'), bu, parçayı oluşturmak için kullanılan orijinal argümanları geçersiz kılar ve eğer aşağıdaki durumlarda tutarsız bir duruma yol açabilir parça yok edilir ve yeniden yaratılır.
Juan

0

Mimarinize bağlı olarak yapabileceğiniz başka bir şey, parçalar arasında paylaşılan bir ViewModel kullanmaktır. Bu yüzden benim durumumda FragmentA bir form ve FragmentB kullanıcının bir öğeyi arayabileceği ve seçebileceği, ViewModel'de depolayabileceği bir öğe seçim görünümüdür. Sonra FragmentA'ya geri döndüğümde, bilgiler zaten saklanıyor!

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.