studio - fragments android tutorial
onActivityResult no se llama en Fragmento (30)
Un punto que nadie ha
mention
que asegúrese de que el modo de inicio de la Actividad del Host no se debe configurar ensingleInstance
osingleTask
.
onActivityResult no funcionará si su modo de inicio está establecido en SingleInstance o SingleTask. o llamas a tu actividad usando estos IntentFilters
standard
modo de lanzamiento standard
o singleTop
funcionará bien.
La actividad que alberga este fragmento tiene su onActivityResult
cuando la actividad de la cámara regresa.
Mi fragmento inicia una actividad para obtener un resultado con la intención enviada para que la cámara tome una fotografía. La aplicación de imagen se carga bien, toma una imagen y regresa. El onActivityResult
sin embargo nunca se golpea. He establecido puntos de interrupción, pero nada se activa. ¿Puede un fragmento tener onActivityResult
? Yo creo que sí, ya que es una función proporcionada. ¿Por qué no se está activando esto?
ImageView myImage = (ImageView)inflatedView.findViewById(R.id.image);
myImage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, 1888);
}
});
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if( requestCode == 1888 ) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
((ImageView)inflatedView.findViewById(R.id.image)).setImageBitmap(photo);
}
}
PARA FRAGMENTOS ANULADOS (por ejemplo, cuando se utiliza un ViewPager)
En tu actividad principal:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
En su fragmento principal de nivel superior (fragmento ViewPager):
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
YourFragment frag = (YourFragment) getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());
frag.yourMethod(data); // Method for callback in YourFragment
super.onActivityResult(requestCode, resultCode, data);
}
En YourFragment (fragmento anidado):
public void yourMethod(Intent data){
// Do whatever you want with your data
}
Simplemente puede anular BaseActivity
onActivityResult
en el fragmentobaseActivity.startActivityForResult
.En BaseActivity, agregue la interfaz y anule onActivityResult.
private OnBaseActivityResult baseActivityResult; public static final int BASE_RESULT_RCODE = 111; public interface OnBaseActivityResult{ void onBaseActivityResult(int requestCode, int resultCode, Intent data); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(getBaseActivityResult() !=null && requestCode == BASE_RESULT_RCODE){ getBaseActivityResult().onBaseActivityResult(requestCode, resultCode, data); setBaseActivityResult(null); }
On Fragment implementa
OnBaseActivityResult
@Override public void onBaseActivityResult(int requestCode, int resultCode, Intent data) { Log.d("RQ","OnBaseActivityResult"); if (data != null) { Log.d("RQ","OnBaseActivityResult + Data"); Bundle arguments = data.getExtras(); } }
Esta solución hará el truco.
Acabo de hacer un método de solución:
public static Fragment _tempFragment = null;
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(_tempFragment != null)
_tempFragment.onActivityResult(requestCode, resultCode, data);
}
En tu Fragmento, antes de startActivityResult, establece
MainActivity._tempFragment = this;
Después de onActivityResult <- en Fragmento
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Do your job
{
}
MainActivity._tempFragment = null;
}
Como mencionó Ollie C, hay un error activo para la biblioteca de soporte que usa valores devueltos para onActivityResult cuando está usando fragmentos anidados. Acabo de golpearlo :-(.
Creo que getActivity().startActivityForResult(intent,111);
. Debe llamar a startActivityForResult(intent,111);
.
Dentro de tu fragmento, llama
this.startActivityForResult(intent, REQUEST_CODE);
Cuando this
se refiere al fragmento. De lo contrario, haz lo que dijo @Clevester:
Fragment fragment = this;
....
fragment.startActivityForResult(intent, REQUEST_CODE);
También tuve que llamar
super.onActivityResult(requestCode, resultCode, data);
en la actividad de los padres, onActivityResult
para que funcione.
(Adapté esta respuesta de la respuesta de @Clevester).
En breve,
En fragmento, declare Fragment fragment = this
;
después de eso use fragment.startActivityForResult
.
El resultado volverá en activityResult.
En caso de que no sepa fragmentos en su actividad, simplemente enumérelos todos y envíe los argumentos de los resultados de la actividad:
// In your activity
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
En mi caso, se trata de un error de Android ( http://technet.weblineindia.com/mobile/onactivityresult-not-getting-called-in-nested-fragments-android/ ), si utiliza FragmentActivity
compatible, debe usar getSupportFragmentManager
en getSupportFragmentManager
lugar. de getChildFragmentManager
:
List<Fragment> fragments = getSupportFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
if(fragment instanceof UserProfileFragment) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
}
Este es uno de los temas más populares. Podemos encontrar un montón de hilo con respecto a este problema. Pero ninguno de ellos es útil para MÍ.
Así que he resuelto este problema utilizando esta solución.
Primero entendamos por qué esto está sucediendo.
Podemos llamar a startActivityForResult
directamente desde Fragment, pero en realidad todos los mecánicos están controlados por Activity.
Una vez que llame a startActivityForResult
desde un Fragmento, requestCode se cambiará para adjuntar la identidad del Fragmento al código. Eso permitirá a Activity rastrear quién envía esta solicitud una vez que se recibe el resultado.
Una vez que se haya vuelto a navegar la actividad, el resultado se enviará a Activity''s OnActivityResult con el requestCode modificado que se descodificará a requestCode original + identidad del fragmento. Después de eso, la Actividad enviará el Resultado de la Actividad a ese Fragmento a través de onActivityResult. Y todo está hecho.
El problema es:
La actividad podría enviar el resultado solo al Fragmento que se ha adjuntado directamente a la Actividad pero no al anidado. Esa es la razón por la cual onActivityResult de fragmento anidado nunca se llamaría sin importar qué.
Solución:
1) Inicia la Intención en tu Fragmento por el siguiente código:
/** Pass your fragment reference **/
frag.startActivityForResult(intent, REQUEST_CODE); // REQUEST_CODE = 12345
2) Ahora en su actividad de padres anular ** onActivityResult()
: **
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
Tienes que llamar a esto en la actividad de los padres para que funcione.
3) En tu llamada de fragmento:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
}
}
Eso es. Con esta solución, podría aplicarse a cualquier fragmento individual, ya sea que esté anidado o no. Y sí, también cubre todo el caso! Además, los códigos también son bonitos y limpios.
Estoy teniendo este mismo problema con ChildFragmentManager
. El administrador no pasará el resultado al fragmento anidado, debe hacerlo manualmente en su fragmento base.
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
Fragment fragment = (Fragment) getChildFragmentManager().findFragmentByTag(childTag);
if (fragment != null) {
fragment.onActivityResult(requestCode, resultCode, intent);
}
}
La actividad de alojamiento anula onActivityResult()
, pero no realizó una llamada a super.onActivityResult()
para los códigos de resultados no super.onActivityResult()
. Aparentemente, aunque el fragmento es el que realiza la llamada startActivityForResult()
, la actividad obtiene la primera oportunidad de manejar el resultado. Esto tiene sentido cuando se considera la modularidad de los fragmentos. Una vez que implementé super.onActivityResult()
para todos los resultados no super.onActivityResult()
, el fragmento tuvo una oportunidad de manejar el resultado.
Y también de la respuesta @siqing:
Para obtener el resultado en su fragmento, asegúrese de llamar a startActivityForResult(intent,111);
en lugar de getActivity().startActivityForResult(intent,111);
dentro de tu fragmento.
La mayoría de estas respuestas siguen diciendo que debe llamar a super.onActivityResult(...)
en su Activity
host para su Fragment
. Pero eso no parecía estar funcionando para mí.
Por lo tanto, en su Activity
host, debe llamar a su Fragments
onActivityResult(...)
lugar. Aquí hay un ejemplo.
public class HostActivity extends Activity {
private MyFragment myFragment;
protected void onActivityResult(...) {
super.onActivityResult(...);
this.myFragment.onActivityResult(...);
}
}
En algún momento de su HostActivity
, deberá asignar a this.myFragment
el Fragment
que está utilizando. O use FragmentManager
para obtener el Fragment
lugar de mantener una referencia a él en su HostActivity
. Además, compruebe si hay un null
antes de intentar llamar a this.myFragment.onActivityResult(...);
.
Mi problema fue con la actividad del host. Lo encontré con un conjunto de android:launchMode="standard"
Lo android:launchMode="standard"
temporalmente y funcionó!
PARA MUCHOS FRAGMENTOS ANULADOS (por ejemplo, cuando se utiliza un ViewPager en un fragmento)
En tu actividad principal :
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
En tu fragmento:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
for (Fragment fragment : getChildFragmentManager().getFragments()) {
fragment.onActivityResult(requestCode, resultCode, data);
}
}
En tu fragmento anidado
Actividad de llamada
getParentFragment().startActivityForResult(intent, uniqueInstanceInt);
uniqueInstanceInt : reemplácelo con un int que sea único entre los fragmentos anidados para evitar que otro fragmento trate la respuesta.
Recibir respuesta
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == uniqueInstanceInt ) {
// TODO your code
}
}
Atención
Es necesario utilizar un número entre 0 y 65536 en uniqueInstanceInt para evitar el error "Solo se pueden usar 16 bits más bajos para requestCode".
Puedo agregar dos consejos si alguien todavía no puede hacerlo. En el archivo Manifest.xml, asegúrese de que la actividad de hospedaje no haya finalizado cuando se devuelva la llamada y que la actividad a comenzar tenga el modo de inicio como estándar. Vea los detalles a continuación:
Para la actividad de Hosting, establezca la propiedad sin historial como falsa si tiene
android:noHistory="false"
Para que se inicie la actividad, configure el modo de inicio como estándar si tiene
android:launchMode="standard"
Si el problema anterior se enfrenta al inicio de sesión de Facebook , puede usar el código siguiente en una actividad principal de su fragmento como:
Fragment fragment = getFragmentManager().findFragmentById(android.R.id.tabcontent);
fragment.onActivityResult(requestCode, resultCode, data);
O:
Fragment fragment = getFragmentManager().findFragmentById("fragment id here");
fragment.onActivityResult(requestCode, resultCode, data);
Y añada la siguiente llamada en su fragmento ...
callbackManager.onActivityResult(requestCode, resultCode, data);
Si está utilizando fragmentos anidados, esto también funciona:
getParentFragment().startActivityForResult(intent, RequestCode);
Además de esto, debe llamar a super.onActivityResult
de la actividad principal y completar el método onActivityResult
del fragmento.
Si hay problemas con el método onActivityResult
que está dentro de la clase de fragmento y desea actualizar algo que también está dentro de la clase de fragmento, use:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == Activity.RESULT_OK)
{
// If the user had agreed to enabling Bluetooth,
// populate the ListView with all the paired devices.
this.arrayDevice = new ArrayAdapter<String>(this.getContext(), R.layout.device_item);
for(BluetoothDevice bd : this.btService.btAdapater.getBondedDevices())
{
this.arrayDevice.add(bd.getAddress());
this.btDeviceList.setAdapter(this.arrayDevice);
}
}
super.onActivityResult(requestCode, resultCode, data);
}
Solo agrega la this.variable
como se muestra en el código anterior. De lo contrario, se llamará al método dentro de la actividad principal y la variable no se actualizará de la instancia actual.
También lo probé poniendo este bloque de código en MainActivity
, reemplazándolo con la clase HomeFragment
y teniendo las variables estáticas. Obtuve resultados como esperaba.
Entonces, si desea que la clase de fragmento tenga su propia implementación de onActivityResult
, el ejemplo de código anterior es la respuesta.
Simplemente use el siguiente código para el fragmento.
@Override
public void onOtherButtonClick(ActionSheet actionSheet, int index) {
if (index == 1)
{
Intent intent = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent,
"Select Picture"), 1);
}
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == 1) {
if (requestCode == 1) {
Uri selectedImageUri = data.getData();
//selectedImagePath = getPath(selectedImageUri);
}
}
}
onActivityResult llamará sin llamar a su padre.
Solución 1:
Llame a startActivityForResult(intent, REQUEST_CODE);
en lugar de getActivity().startActivityForResult(intent, REQUEST_CODE);
.
Solución 2:
When startActivityForResult(intent, REQUEST_CODE);
se llama onActivityResult(requestCode,resultcode,intent)
la actividad onActivityResult(requestCode,resultcode,intent)
se invoca, y luego se pueden llamar fragments onActivityResult()
desde aquí, pasando el requestCode, resultCode and intent
.
También enfrenté el mismo problema una vez que cambié este bloque de código fuera de un Fragmento a una Clase de Utilidad , con parentActivity
pasado como argumento ,
Intent intent = new Intent(parentActivity, CameraCaptureActivity.class);
parentActivity.startActivityForResult(intent,requestCode);
Entonces no estaba obteniendo ningún valor en el método onActivityResult
de ese Fragmento . Después, cambié el argumento a Fragmento , por lo que la definición revisada de método parecía
Intent intent = new Intent(fragment.getContext(), CameraCaptureActivity.class);
fragment.startActivityForResult(intent,requestCode);
Después de eso, pude obtener valor en onActivityResult
en el Fragmento
También me encontré con este problema en un fragmento. Y llamé a startActivityForResult
en un DialogFragment
.
Pero ahora este problema se ha resuelto:
FragmentClassname.this.startActivityForResult
.
Tengo una fuerte sospecha de que todas las respuestas aquí no son más que hacks. Los he probado todos y muchos otros, pero sin ninguna conclusión confiable, ya que siempre hay algún tipo de problema estúpido. Por mi parte, no puedo confiar en resultados inconsistentes. Si miras la documentación oficial de la API de Android para Fragmentos, verás que Google establece claramente lo siguiente:
Llame a startActivityForResult (Intent, int) desde la actividad que contiene el fragmento.
Ver: API de fragmento de Android
Por lo tanto, parece que el enfoque más correcto y confiable sería llamar a startActivityForResult () desde la actividad de alojamiento y también manejar el onActivityResult () resultante desde allí.
Una de las formas más simples es comenzar una actividad desde su fragmento.
startActivity(ActivityName);
Luego, agregue lo que llama startActivityForResult(intent,"1");
de su actividad y agregue onActivityResult
en su actividad
startActivityForResult(intent,"1");
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.dualPane);
fragment.onActivityResult(requestCode, resultCode, data);
// perform other activity result like fetching from Uris or handling cursors
finish(); // finish the activity will get to back to your fragment.
}
FragmentActivity
reemplaza requestCode
por uno modificado. Después de eso, cuando se onActivityResult()
, FragmentActivity
analiza los 16 bits más altos y restaura el índice del Fragmento original. Mira este esquema:
Si tiene algunos fragmentos en el nivel raíz, no hay problemas. Pero si tiene fragmentos anidados , por ejemplo Fragment
con algunas pestañas dentro de ViewPager
, garantiza que se enfrentará a un problema (o ya lo enfrentó).
Porque solo un índice se almacena dentro de requestCode
. Ese es el índice de Fragment
dentro de su FragmentManager
. Cuando estamos utilizando fragmentos anidados, hay FragmentManager
secundarios, que tienen su propia lista de Fragmentos. Por lo tanto, es necesario guardar toda la cadena de índices, a partir de FragmentManager
raíz.
¿Cómo resolvemos este problema? Hay una solución de solución común en este post .
Opción 1:
Si llama a startActivityForResult()
desde el fragmento, debería llamar a startActivityForResult()
, no a getActivity().startActivityForResult()
, ya que dará como resultado el fragmento onActivityResult ().
Si no está seguro de dónde está llamando en startActivityForResult()
y cómo llamará a los métodos.
Opcion 2:
Dado que la Actividad obtiene el resultado de onActivityResult()
, deberá anular el onActivityResult()
la actividad y llamar a super.onActivityResult()
para propagarse al fragmento respectivo para los códigos de resultados no super.onActivityResult()
o para todos.
Si las dos opciones anteriores no funcionan, consulte la opción 3, ya que definitivamente funcionará.
Opción 3:
Una llamada explícita desde el fragmento a la función onActivityResult es la siguiente.
En la clase de actividad principal, anule el método onActivityResult () e incluso anule el mismo en la clase Fragment y llame al siguiente código.
En la clase padre:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.dualPane);
fragment.onActivityResult(requestCode, resultCode, data);
}
En la clase infantil:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// In fragment class callback
}
Su código tiene un fragmento anidado. Llamar a super.onActivityForResult no funciona
No desea modificar todas las actividades desde las que se puede llamar a su fragmento ni hacer una solución para llamar a cada fragmento en la cadena de fragmentos.
Aquí está una de las muchas soluciones de trabajo. cree un fragmento sobre la marcha y conéctelo directamente a la actividad con el administrador de fragmentos de soporte. Luego llame a startActivityForResult desde el fragmento recién creado.
private void get_UserEmail() {
if (view == null) {
return;
}
((TextView) view.findViewById(R.id.tvApplicationUserName))
.setText("Searching device for user accounts...");
final FragmentManager fragManager = getActivity().getSupportFragmentManager();
Fragment f = new Fragment() {
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
startActivityForResult(AccountPicker.newChooseAccountIntent(null, null,
new String[]{"com.google"}, false, null, null, null, null), REQUEST_CODE_PICK_ACCOUNT);
}
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == REQUEST_CODE_PICK_ACCOUNT) {
String mEmail = "";
if (resultCode == Activity.RESULT_OK) {
if (data.hasExtra(AccountManager.KEY_ACCOUNT_NAME)) {
mEmail = data
.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
}
}
if (mActivity != null) {
GoPreferences.putString(mActivity, SettingApplication.USER_EMAIL, mEmail);
}
doUser();
}
super.onActivityResult(requestCode, resultCode, data);
fragManager.beginTransaction().remove(this).commit();
}
};
FragmentTransaction fragmentTransaction = fragManager
.beginTransaction();
fragmentTransaction.add(f, "xx" + REQUEST_CODE_PICK_ACCOUNT);
fragmentTransaction.commit();
}
public class takeimage extends Fragment {
private Uri mImageCaptureUri;
private static final int PICK_FROM_CAMERA = 1;
private static final int PICK_FROM_FILE = 2;
private String mPath;
private ImageView mImageView;
Bitmap bitmap = null;
View view;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.activity_send_image, container, false);
final String[] items = new String[] { "From Camera", "From SD Card" };
mImageView = (ImageView)view.findViewById(R.id.iv_pic);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.select_dialog_item, items);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Select Image");
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if (item == 0) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = new File(Environment.getExternalStorageDirectory(), "tmp_avatar_"
+ String.valueOf(System.currentTimeMillis())
+ ".jpg");
mImageCaptureUri = Uri.fromFile(file);
try {
intent.putExtra(
android.provider.MediaStore.EXTRA_OUTPUT,
mImageCaptureUri);
intent.putExtra("return-data", true);
getActivity().startActivityForResult(intent,
PICK_FROM_CAMERA);
} catch (Exception e) {
e.printStackTrace();
}
dialog.cancel();
} else {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
getActivity().startActivityForResult(
Intent.createChooser(intent,
"Complete action using"), PICK_FROM_FILE);
}
}
});
final AlertDialog dialog = builder.create();
Button show = (Button) view.findViewById(R.id.btn_choose);
show.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Switch the tab content to display the list view.
dialog.show();
}
});
return view;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK)
return;
if (requestCode == PICK_FROM_FILE) {
mImageCaptureUri = data.getData();
// mPath = getRealPathFromURI(mImageCaptureUri); //from Gallery
if (mPath == null)
mPath = mImageCaptureUri.getPath(); // from File Manager
if (mPath != null)
bitmap = BitmapFactory.decodeFile(mPath);
} else {
mPath = mImageCaptureUri.getPath();
bitmap = BitmapFactory.decodeFile(mPath);
}
mImageView.setImageBitmap(bitmap);
}
public String getRealPathFromURI(Uri contentUri) {
String [] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = managedQuery(contentUri, proj, null, null,null);
if (cursor == null) return null;
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
}