Ich habe die neue API für verschachtelte Fragmente verwendet , die Android in die Support-Bibliothek aufgenommen hat.
Das Problem, mit dem ich bei verschachtelten Fragmenten konfrontiert bin, besteht darin, dass die Methode des verschachtelten Fragments nicht aufgerufen wird, wenn ein verschachteltes Fragment (dh ein Fragment, das über die FragmentManager
Rückgabe von zu einem anderen Fragment hinzugefügt wurde getChildFragmentManager()
) aufgerufen wird. Es werden jedoch sowohl das übergeordnete Fragment als auch die Aktivität aufgerufen.startActivityForResult()
onActivityResult()
onActivityResult()
onActivityResult()
Ich weiß nicht, ob mir etwas an verschachtelten Fragmenten fehlt, aber ich habe das beschriebene Verhalten nicht erwartet. Unten finden Sie den Code, der dieses Problem reproduziert. Ich würde mich sehr freuen, wenn mich jemand in die richtige Richtung weisen und mir erklären kann, was ich falsch mache:
package com.example.nestedfragmentactivityresult;
import android.media.RingtoneManager;
import android.os.Bundle;
import android.content.Intent;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends FragmentActivity
{
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.getSupportFragmentManager()
.beginTransaction()
.add(android.R.id.content, new ContainerFragment())
.commit();
}
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
// This is called
Toast.makeText(getApplication(),
"Consumed by activity",
Toast.LENGTH_SHORT).show();
}
public static class ContainerFragment extends Fragment
{
public final View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState)
{
View result = inflater.inflate(R.layout.test_nested_fragment_container,
container,
false);
return result;
}
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
getChildFragmentManager().beginTransaction()
.add(R.id.content, new NestedFragment())
.commit();
}
public void onActivityResult(int requestCode,
int resultCode,
Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
// This is called
Toast.makeText(getActivity(),
"Consumed by parent fragment",
Toast.LENGTH_SHORT).show();
}
}
public static class NestedFragment extends Fragment
{
public final View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState)
{
Button button = new Button(getActivity());
button.setText("Click me!");
button.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
startActivityForResult(intent, 0);
}
});
return button;
}
public void onActivityResult(int requestCode,
int resultCode,
Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
// This is NOT called
Toast.makeText(getActivity(),
"Consumed by nested fragment",
Toast.LENGTH_SHORT).show();
}
}
}
test_nested_fragment_container.xml ist:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
quelle
Antworten:
Ja, das
onActivityResult()
verschachtelte Fragment wird auf diese Weise nicht aufgerufen.Die Aufrufsequenz von onActivityResult (in der Android-Supportbibliothek) lautet
Activity.dispatchActivityResult()
.FragmentActivity.onActivityResult()
.Fragment.onActivityResult()
.Im dritten Schritt befindet sich das Fragment im
FragmentMananger
übergeordneten ElementActivity
. In Ihrem Beispiel wird also das Containerfragment zum Versenden gefundenonActivityResult()
. Das verschachtelte Fragment könnte das Ereignis niemals empfangen.Ich denke, Sie müssen Ihren eigenen Versand implementieren
ContainerFragment.onActivityResult()
, das verschachtelte Fragment finden und das Ergebnis und die Daten an dieses übergeben.quelle
Ich habe dieses Problem mit dem folgenden Code gelöst (Unterstützungsbibliothek wird verwendet):
Überschreiben Sie onActivityResult im Containerfragment folgendermaßen:
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); List<Fragment> fragments = getChildFragmentManager().getFragments(); if (fragments != null) { for (Fragment fragment : fragments) { fragment.onActivityResult(requestCode, resultCode, data); } } }
Jetzt erhält das verschachtelte Fragment einen Aufruf der onActivityResult-Methode.
Wie Eric Brynsvold in einer ähnlichen Frage bemerkte , sollte das verschachtelte Fragment die Aktivität mit dem übergeordneten Fragment und nicht mit dem einfachen Aufruf startActivityForResult () starten. Beginnen Sie also im verschachtelten Fragment die Aktivität mit:
getParentFragment().startActivityForResult(intent, requestCode);
quelle
So habe ich es gelöst.
In Aktivität:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); List<Fragment> frags = getSupportFragmentManager().getFragments(); if (frags != null) { for (Fragment f : frags) { if (f != null) handleResult(f, requestCode, resultCode, data); } } } private void handleResult(Fragment frag, int requestCode, int resultCode, Intent data) { if (frag instanceof IHandleActivityResult) { // custom interface with no signitures frag.onActivityResult(requestCode, resultCode, data); } List<Fragment> frags = frag.getChildFragmentManager().getFragments(); if (frags != null) { for (Fragment f : frags) { if (f != null) handleResult(f, requestCode, resultCode, data); } } }
quelle
Für Androidx mit Navigationskomponenten unter Verwendung von NavHostFragment
Aktualisierte Antwort am 2. September 2019
Dieses Problem tritt wieder in Androidx auf, wenn Sie eine einzelne Aktivität verwenden und ein verschachteltes Fragment in der haben
NavHostFragment
,onActivityResult()
wird das untergeordnete Fragment von nicht aufgerufenNavHostFragment
.Um dies zu beheben, müssen Sie den Aufruf manuell an die
onActivityResult()
untergeordneten FragmenteonActivityResult()
der Hostaktivität weiterleiten.Hier erfahren Sie, wie Sie dies mit Kotlin-Code tun. Dies gehört
onActivityResult()
zu Ihrer Hauptaktivität, in der Folgendes ausgeführt wirdNavHostFragment
:override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) val navHostFragment = supportFragmentManager.findFragmentById(R.id.your_nav_host_fragment) val childFragments = navHostFragment?.childFragmentManager?.fragments childFragments?.forEach { it.onActivityResult(requestCode, resultCode, data) } }
Dadurch wird sichergestellt, dass die
onActivityResult()
untergeordneten Fragmente normal aufgerufen werden.Für Android Support Library
Alte Antwort
Dieses Problem wurde ab Android Support Library 23.2 behoben .
Fragment
In der Android Support Library 23.2+ müssen wir nichts mehr tun.onActivityResult()
wird nunFragment
wie erwartet im verschachtelten aufgerufen .Ich habe es gerade mit der Android Support Library 23.2.1 getestet und es funktioniert. Endlich kann ich saubereren Code haben!
Die Lösung besteht also darin, die neueste Android-Support-Bibliothek zu verwenden.
quelle
Ich habe die FragmentActivity-Quelle durchsucht. Ich finde diese Fakten.
Beobachten Sie den Code in FragmentActivity.
public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode) { if (requestCode == -1) { super.startActivityForResult(intent, -1); return; } if ((requestCode&0xffff0000) != 0) { throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); } super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff)); }
Wenn das Ergebnis zurück.FragmentActivity die Funktion onActivityResult überschreibt und prüft, ob das höhere 16-Bit des requestCode nicht 0 ist, übergeben Sie das onActivityResult an das untergeordnete Fragment.
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { mFragments.noteStateNotSaved(); int index = requestCode>>16; if (index != 0) { index--; final int activeFragmentsCount = mFragments.getActiveFragmentsCount(); if (activeFragmentsCount == 0 || index < 0 || index >= activeFragmentsCount) { Log.w(TAG, "Activity result fragment index out of range: 0x" + Integer.toHexString(requestCode)); return; } final List<Fragment> activeFragments = mFragments.getActiveFragments(new ArrayList<Fragment>(activeFragmentsCount)); Fragment frag = activeFragments.get(index); if (frag == null) { Log.w(TAG, "Activity result no fragment exists for index: 0x" + Integer.toHexString(requestCode)); } else { frag.onActivityResult(requestCode&0xffff, resultCode, data); } return; } super.onActivityResult(requestCode, resultCode, data); }
So löst FragmentActivity das Ereignis onActivityResult aus.
Wenn Sie eine fragmentActivity haben, enthält sie fragment1 und fragment1 enthält fragment2. OnActivityResult kann also nur an fragment1 übergeben werden.
Und dann finde ich einen Weg, um dieses Problem zu lösen. Erstellen Sie zuerst ein NestedFragment-Erweiterungsfragment, das die Funktion startActivityForResult überschreibt.
public abstract class NestedFragment extends Fragment { @Override public void startActivityForResult(Intent intent, int requestCode) { List<Fragment> fragments = getFragmentManager().getFragments(); int index = fragments.indexOf(this); if (index >= 0) { if (getParentFragment() != null) { requestCode = ((index + 1) << 8) + requestCode; getParentFragment().startActivityForResult(intent, requestCode); } else { super.startActivityForResult(intent, requestCode); } } }
}}
als MyFragment erstellen erweitern Fragment überschreiben onActivityResult Funktion.
public abstract class MyFragment extends Fragment { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { int index = requestCode >> 8; if (index > 0) { //from nested fragment index--; List<Fragment> fragments = getChildFragmentManager().getFragments(); if (fragments != null && fragments.size() > index) { fragments.get(index).onActivityResult(requestCode & 0x00ff, resultCode, data); } } }
}}
Das ist alles! Verwendung:
Warnung !!!!!!!! Der requestCode muss unter 512 (8 Bit) liegen, da wir den requestCode in drei Teilen verschüttet haben
16bit | 8bit | 8 Bit
Das höhere 16-Bit-Android, das bereits verwendet wird, und das mittlere 8-Bit-Format sollen Fragment1 dabei helfen, das Fragment2 in seinem fragmentManager-Array zu finden. Das niedrigste 8-Bit-Format ist der Ursprungsanforderungscode.
quelle
Für die Hauptaktivität schreiben Sie OnActivityForResult mit der Super-Klasse wie
@Override public void onActivityResult(int requestCode, int resultCode, Intent result) { super.onActivityResult(requestCode, resultCode, result); if (requestCode == Crop.REQUEST_PICK && resultCode == RESULT_OK) { beginCrop(result.getData()); } }
Für Aktivität Schreibe Absicht ohne getActivity () wie als
Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts")); pickContactIntent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers startActivityForResult(pickContactIntent, 103);
OnActivityResult für Fragment
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 101 && resultCode == getActivity().RESULT_OK && null != data) {
}}
quelle
Ich hatte das gleiche Problem! Ich löste es durch statische Verwendung
ChildFragment
in derParentFragment
, wie folgt aus :private static ChildFragment mChildFragment; protected void onActivityResult(int requestCode, int resultCode, Intent data) { mChildFragment.onActivityResult(int requestCode, int resultCode, Intent data); }
quelle