Passando dados entre um fragmento e sua atividade de contêiner

176

Como posso transmitir dados entre um fragmento e sua atividade de contêiner? Existe algo semelhante à passagem de dados entre as atividades por meio de intenções?

Eu li isso, mas não ajudou muito:
http://developer.android.com/guide/topics/fundamentals/fragments.html#CommunicatingWithActivity

tyb
fonte
Isso é insuficiente? Você pode enviar o que quiser, talvez possa explicar mais, o que você deseja realizar?
Marcin Milejski
No documento, não entendi como, por exemplo, eu passaria um valor ou uma sequência da atividade para o fragmento ou vice-versa. Graças
tyb
2
A melhor maneira de lidar com chamadas de forma assíncrona a partir de fragmentos e retornar dados a eles é implementar BroadcastReceivers nos dois lados. De qualquer forma, você deve torná-lo assíncrono se estiver trabalhando com um número não específico de fragmentos.
Marek Sebera
@punisher_malade Não, funciona para mim
Vadim Kotov

Respostas:

211

No seu fragmento você pode ligar getActivity().

Isso lhe dará acesso à atividade que criou o fragmento. A partir daí, obviamente, você pode chamar qualquer tipo de método de acesso que esteja na atividade.

por exemplo, para um método chamado getResult()em sua atividade:

((MyActivity) getActivity()).getResult();
usuario
fonte
86
Como você está acessando uma função na SUA Atividade (e não na atividade pai do Android), será necessário transmitir sua chamada getActivity (): ((MyActivity) getActivity ()). GetResult ();
21412 Nick
3
Como será possivelmente o método back, do fragmento à atividade?
Vasil Valchev
6
@VasilValchev, você pode criar uma interface e forçar a atividade a implementá-la e depois chamar um método de dentro do seu fragmento para passar os dados. Use o método onAttach para verificar se a atividade implementa a interface.
Ivan Nikolov
3
Excelente resposta de @IvanNikolov. Você pode encontrar uma explicação completa no Fragments Training Link
bogdan
8
Esta não é uma boa prática nem padrão. A resposta com interfaces é a correta.
moictab
303

Tente usar interfaces.

Qualquer fragmento que deve transmitir dados de volta à sua atividade de contenção deve declarar uma interface para manipular e transmitir os dados. Em seguida, verifique se a atividade que contém implementa essas interfaces. Por exemplo:

JAVA

No seu fragmento, declare a interface ...

public interface OnDataPass {
    public void onDataPass(String data);
}

Em seguida, conecte a implementação da interface da classe que contém ao fragmento no método onAttach, da seguinte maneira:

OnDataPass dataPasser;

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    dataPasser = (OnDataPass) context;
}

No seu fragmento, quando você precisar manipular a passagem de dados, basta chamá-lo no objeto dataPasser:

public void passData(String data) {
    dataPasser.onDataPass(data);
}

Finalmente, em sua atividade de contenção que implementa OnDataPass ...

@Override
public void onDataPass(String data) {
    Log.d("LOG","hello " + data);
}

KOTLIN

Etapa 1. Criar interface

interface OnDataPass {
    fun onDataPass(data: String)
}

Etapa 2. Em seguida, conecte a implementação da classe que contém a interface ao fragmento no método onAttach (YourFragment), da seguinte maneira:

lateinit var dataPasser: OnDataPass

override fun onAttach(context: Context) {
    super.onAttach(context)
    dataPasser = context as OnDataPass
}

Etapa 3. No seu fragmento, quando você precisar manipular a passagem de dados, basta chamá-lo no objeto dataPasser:

fun passData(data: String){
    dataPasser.onDataPass(data)
}

Etapa 4. Finalmente, em sua atividade implementa OnDataPass

class MyActivity : AppCompatActivity(), OnDataPass {}

override fun onDataPass(data: String) {
    Log.d("LOG","hello " + data)
}
Harlo Holmes
fonte
1
Obrigado boa resposta, ao ponto. Isso também é muito bem explicado aqui . O que eu não percebi é que você pode implementar várias interfaces, eu já estava implementando um ActionBar.TabListenere tive que adicionar uma interface adicional.
Eugene van der Merwe
5
Este é definitivamente o caminho a percorrer e, na minha opinião, o ÚNICO caminho a percorrer. E fornece interação bidirecional entre a atividade e o fragmento. Também permitirá um meio de comunicação entre fragmentos quando você tiver vários fragmentos em uma atividade, seja através de guias ou layout de várias fragmentos.
Christopher
1
Há algo que eu estou me perguntando ... Esta é a resposta oficial e, no site oficial do desenvolvedor, eles dizem que este é o caminho certo para comunicar frag-act-frag, mas por que é possível fazê-lo através do casting getActivity ( )?
Unmultimedio 16/05
3
Por que alguém precisa de uma interface extra para fazer isso? Por que você considera a solução a seguir ruim ou melhor? ((MyActivity) getActivity) .myMethod (...)
spacifici
3
onAttach (atividade de atividade) está obsoleta. Em vez disso, use onAttach (contexto de contexto).
precisa saber é o seguinte
23

Abordagem mais fácil, mas não recomendada

Você pode acessar os dados da atividade do fragmento:

Atividade:

public class MyActivity extends Activity {

    private String myString = "hello";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        ...
    }

    public String getMyData() {
        return myString;
    }
}

Fragmento:

public class MyFragment extends Fragment {

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

        MyActivity activity = (MyActivity) getActivity();
        String myDataFromActivity = activity.getMyData();
        return view;
    }
}
Zar E Ahmer
fonte
5
Isso adiciona alto acoplamento ao seu código e é uma prática ruim. Agora você não pode usar o fragmento em nenhuma atividade diferente deMyActivity
AmeyaB 10/11
por que esse caminho é considerado uma má prática e a interface é apenas a solução dessa questão?
androidXP 18/05/19
AmeyaB se toda a atividade se estender de uma BaseActivity ou se a convertermos como BaseActivity activity = (BaseActivity) getActivity (); em seguida, ele irá funcionar em todas as atividades
Zar E Ahmer
21
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    Bundle b = getActivity().getIntent().getExtras();
            wid = b.getString("wid");
            rid = b.getString("rid");
            View view = inflater.inflate(R.layout.categoryfragment, container, false);
    return view;
 }
Jeetu
fonte
14
Isto é como obtê-lo no fragmento, mas como você o define na classe que chama o fragmento, se você tiver seu fragmento no arquivo de layout xml.
Zapnologica
17

Passando dados entre um fragmento e sua atividade de contêiner

Atividade:

        Bundle bundle = new Bundle();
        bundle.putString("message", "Alo Elena!");
        FragmentClass fragInfo = new FragmentClass();
        fragInfo.setArguments(bundle);
        transaction.replace(R.id.fragment_single, fragInfo);
        transaction.commit();

Fragmento:

Lendo o valor no fragmento

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        String myValue = this.getArguments().getString("message");
        ...
        ...
        ...
        }
Jorgesys
fonte
Olá @Elenasys. Como posso enviar dados de pacote para fragmentar a atividade. Eu fui de fragmento para atividade e preciso voltar para o fragmento que já foi criado (chama apenas onStart, onResume, ...) e preciso dos dados que consegui obter na atividade. Existe alguma maneira de fazê-lo ou estou fazendo algo errado?
Michal Moravik
7

Eu não sei se essa é a melhor maneira ou não Bu, eu tenho pesquisado no google por um bom tempo, descobrindo como posso passar um Bundle de um fragmento para sua atividade de contêiner, mas tudo que eu encontrei foi enviar dados da atividade para o fragmento (o que foi um pouco confuso para mim porque sou novato).

depois, tentei algo próprio que funcionou exatamente para mim como eu queria. então eu vou postar aqui caso alguém como eu procure a mesma coisa.

// Passando dados do fragmento.

Bundle gameData = new Bundle();
        gameData.putStringArrayList(Constant.KEY_PLAYERS_ARR,players);
        gameData.putString(Constant.KEY_TEAM_NAME,custom_team_name);
        gameData.putInt(Constant.KEY_REQUESTED_OVER,requestedOver);

        Intent intent = getActivity().getIntent();
        intent.putExtras(gameData);

// Obtendo dados do pacote configurável a partir da atividade do contêiner.

Bundle gameData = getIntent().getExtras();
        if (gameData != null)
        {
            int over = gameData.getInt(Constant.KEY_REQUESTED_OVER);
            ArrayList<String> players = gameData.getStringArrayList(Constant.KEY_PLAYERS_ARR);
            String team = gameData.getString(Constant.KEY_TEAM_NAME);

        }
        else if (gameData == null)
        {
            Toast.makeText(this, "Bundle is null", Toast.LENGTH_SHORT).show();
        }
Diário Frozen
fonte
6

Interface é uma das melhores soluções:

Interface de cola:

public interface DataProviderFromActivity {

    public String getName();
    public String getId);

}  

Minha atividade:

public class MyActivity implements DataProviderFromActivity{

    String name = "Makarov";
    String id = "sys533";

    ... ... ... ... ... .... .... 
    ... ... ... ... ... .... .... 

    public String getName(){
        return name;
    };
    public String getId(){
        return id;
    };
}

MyFragment:

public class MyFragment extends Fragment{

    String fragName = "";
    String fragId = "";

    ... ... ... ... ... .... .... 
    ... ... ... ... ... .... .... 

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

        DataProviderFromActivity myActivity= (DataProviderFromActivity) getActivity();
        fragName = myActivity.getName();
        fragId = myActivity.getId();

        ... ... ... ... ... .... .... 
        ... ... ... ... ... .... .... 

        updateFragmentView();
    }
}
Hassan Tareq
fonte
Oi @HassanMakarov Gosto da sua interface, simples e limpa. Eu tenho um problema, no entanto. Não sei se é específico para o meu viewpager, mas só recebo os dados no máximo 2 fragmentos por vez? As cadeias "Makarov" e "sys533" aparecem nos fragmentos 1 e 2 quando a atividade é criada, mas as cadeias não aparecem no fragmento 3 até que o fragmento 2 ou 3 seja selecionado? Alguma ideia?
JC23
@ JC23 Acho que o problema está relacionado à transação de fragmentos. Qual fragmento é definido quando o MainActivity é iniciado?
Hassan Tareq
@ JC23 O que faço: em vez de Tab / ViewPager, uso a Barra de Ferramentas e o NavigationView. Em onNavigationItemSelected (), executo transações de fragmento para definir fragmentos adequadamente.
Hassan Tareq
2

Eu usei um AppCompatActivity que implementa ouvintes de data. Fragmentos vieram como uma necessidade, pois eu precisava codificar um seletor de período. E também precisava que o contêiner recebesse as datas selecionadas para devolvê-las à atividade pai.

Para a atividade do contêiner, esta é a declaração de classe:

public class AppCompatDateRange extends AppCompatActivity implements
    DateIniRangeFragment.OnDateIniSelectedListener, DateFimRangeFragment.OnDateFimSelectedListener

E as interfaces para os retornos de chamada:

@Override
public void onDateIniSelected(String dataIni) {
    Log.i("data inicial:", dataIni);
}

@Override
public void onDateFimSelected(String dataFim) {
    Log.i("data final:", dataFim);
}

Os retornos de chamada são cadeias porque datas são parâmetros em uma seleção de consulta.

O código para os fragmentos (com base no fragmento de data inicial):

public class DateIniRangeFragment extends Fragment {
OnDateIniSelectedListener callbackIni;

private DatePicker startDatePicker;

public DateIniRangeFragment() {
    ///required empty constructor
}

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

///through this interface the fragment sends data to the container activity
public interface OnDateIniSelectedListener {
    void onDateIniSelected(String dataIni);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ///layout for the fragment
    View v = inflater.inflate(R.layout.date_ini_fragment, container, false);

    ///initial date for the picker, in this case, current date
    startDatePicker = (DatePicker) v.findViewById(R.id.start_date_picker_appcompat);
    Calendar c = Calendar.getInstance();
    int ano = c.get(Calendar.YEAR);
    int mes = c.get(Calendar.MONTH);
    int dia = c.get(Calendar.DAY_OF_MONTH);
    startDatePicker.setSpinnersShown(false);
    startDatePicker.init(ano, mes, dia, dateSetListener);

    return v;
}

///listener that receives the selected date
private DatePicker.OnDateChangedListener dateSetListener = new DatePicker.OnDateChangedListener() {
    public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        if (view.isShown()) { ///if the datepicker is on the screen
            String sDataIni = year + "-" + (monthOfYear + 1) + "-" + dayOfMonth;
            callbackIni.onDateIniSelected(sDataIni); //apply date to callback, string format
        }
    }
};

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

    /*
    * this function guarantees that the container activity implemented the callback interface
    * */
    try {
        callbackIni = (OnDateIniSelectedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " deve implementar OnDateIniSelectedListener");
    }
}

}

Para compor o contêiner + fragmentos, usei um ViewPager (AppCompat) com uma classe personalizada que estende o FragmentPagerAdapter. Sem diálogos.

Pablo
fonte
2

Simplesmente você pode usar o EventBus , é fácil e funciona muito bem

EventBus em 3 etapas

  1. Defina eventos:

    public static class MessageEvent { /* Additional fields if needed */ }

  2. Preparar assinantes: declare e anote seu método de inscrição, opcionalmente, especifique um modo de encadeamento:

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {/* Do something */};

Registre e cancele o registro de seu assinante. Por exemplo, no Android, atividades e fragmentos geralmente devem ser registrados de acordo com seu ciclo de vida:

 @Override
 public void onStart() {
     super.onStart();
     EventBus.getDefault().register(this);
 }

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }
  1. Publicar eventos:

    EventBus.getDefault().post(new MessageEvent());

Sattar
fonte
1

Eu sei que isso pode ser tarde. Mas eu também estava sempre perdida nessa questão. Estou compartilhando este link ... porque esta é possivelmente a melhor explicação que eu já encontrei na web para isso. Isso resolve Fragmento para Atividade e Fragmento para Fragmento !

Resolvido e explicado muito bem

Yo Apps
fonte
0

Isso está funcionando para mim ..

em Atividade, adicione este método

    public void GetData(String data)
     {
        // do something with your data        
     }

e no fragmento adicione esta linha

((YourActivity)getContext).GetData("your data here");
Basant
fonte
0
public class Fragmentdemo extends Fragment {

  public interface onDemoEventListener {
    public void demoEvent(String s);
  }

  onDemoEventListener demoEventListener;

  @Override
  public void onAttach(Activity activity) {
    super.onAttach(activity);
        try {
          demoEventListener = (onDemoEventListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement onDemoEventListener");
        }
  }

  final String LOG_TAG = "TAG";

  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragmentdemo, null);

    Button button = (Button) v.findViewById(R.id.button);
    button.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        demoEventListener.someEvent("Test text to Fragment1");
      }
    });
    enter code here
    return v;
  }
}
Shweta Chandarana
fonte
-12

Outra maneira simples de obter dados, passados ​​de outra atividade, em um fragmento de uma atividade de contêiner: por exemplo:

Activity_A => Activity_B (fragmento)

Em sua Activity_A, você cria uma intenção como se estivesse enviando dados (String aqui) para outra atividade:

Intent intent = new Intent(getBaseContext(),Activity_B.class);
intent.putExtra("NAME", "Value");
startActivity(intent);

no seu fragmento, contido no seu Activity_B:

String data = getActivity().getIntent().getExtras();
SuperZouzou
fonte
getBaseContext()me dá o seguinte erro:The method getBaseContext() is undefined for the type new View.OnClickListener(){}
Si8