Create an interface to communicate with another fragment - android

I created an interface so I can set text on a FragmentB when I press a TextView on FragmentA. Something is not working and I can't figure this out.
I've created an interface called Communicator:
public interface Communicator {
void respond(String data);
}
On FragmentA I've set a reference on the interface called Communcator and an OnClickListener on the TextView:
Communicator comm;
homeTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
comm.respond("Trying to set text on FragmentB from here");
}
});
FragmentB, set my method to change text:
public void setText(final String data) {
startTripTxt.setText(data);
}
Finally in MainActivity I've implemented the interface .. I think here is where I'm doing something wrong:
#Override
public void respond(String data) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container_main, new FragmentB(), "fragment2").addToBackStack(null).commit();
FragmentB fragmentB= (FragmentB) getSupportFragmentManager().findFragmentByTag("fragment2");
if (fragmentB != null) {
fragmentB.setText(data);
}
}
Fragment 2 loads, but the text is empty.

Fragment 2 loads, but the text is empty.
You implement Communicator is ok but the way you call FragmentB and passing data is not ok. That 's is the reason why you cannot get text from FragmentB. the right way to send data to FragmentB should be like this:
public static FragmentB createInstance(String data) {
FragmentB fragment = new FragmentB();
Bundle bundle = new Bundle();
bundle.putString("data", data);
fragment.setArguments(bundle);
return fragment;
}
And you can get data from FragmentB by:
Bundle bundle = getArguments();
if (bundle != null) {
String data = bundle.getString("data");
}

It looks like after you declare fragmentB, you're meaning to set the text on that fragment. You are Instead calling trainFinderFragment.setText(). Is that your issue?
FragmentB fragmentB= (FragmentB) getSupportFragmentManager().findFragmentByTag("fragment2");
if (fragmentB != null) {
fragmentB.setText(data);
}

Related

Android - Fragments and Tabs communication

I'm pretty new to Android.
This is my scenario: I have a simple app with 3 tabs. In each tab i want to use one or more fragments. This is the situation:
Tab 1:
Fragment A
Tab 2:
Fragment B
Fragment C
Fragment D
Tab 3:
Fragment E
Fragment F
In "Tab 1" I have no issue. All works pretty good. Issues arise when I need to move in "Tab 2" and "Tab 3".
In Tab 2 I have to propagate some parameters from "Fragment B" to "Fragment C" and from "Fragment C" to "Fragment D".
Then it can happen that when user clicks on some button in "Fragment D" I have to pass to "Tab 3" and I have to propagate some parameters from "Fragment D" to "Fragment E".
In my main Activity for Tab handling I'm using these components:
android.support.design.widget.TabLayout
android.support.v4.view.ViewPager
android.support.v4.app.FragmentStatePagerAdapter (I created a custom
class)
android.support.design.widget.TabLayout.OnTabSelectedListener (I created a custom class)
My very simple FragmentStatePagerAdapter extension is:
public class MyOwnPageAdapter extends FragmentStatePagerAdapter {
private int numeroTab;
public MyOwnPageAdapter(FragmentManager fm, int numeroTab) {
super(fm);
this.numeroTab = numeroTab;
}
#Override
public Fragment getItem(int position) {
switch (position){
case 0:
return new FragmentA() ;
case 1:
return new FragmentB() ;
case 2:
return new FragmentC() ;
default:
return null;
}
}
#Override
public int getCount() {
return numeroTab;
}
}
My very simple TabLayout.OnTabSelectedListener extension is:
public class TabSelectedListener implements TabLayout.OnTabSelectedListener {
private ViewPager viewPager;
public TabSelectedListener(ViewPager viewPager){
this.viewPager = viewPager;
}
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
}
I'm able in switching fragments inside tabs that is in Tab 2 i can switch from Fragment B to Fragment C and so on. I'm having issues in passing parameters between fragments and above all from Fragment D in Tab 2 to Fragment E in Tab 3
In my Fragments implementation byt using the android.support.v4.app.FragmentManager I can add and remove views (e.g. fragments) by doing something like this:
mFragmentManager.beginTransaction().add(rootView.getId(),mListaEdificiFragment, "BUILDS").addToBackStack(null).commit();
The problem is the param propagation that since the FragmentStatePagerAdapter seems to cache views it happens that the fragment constructor is called but the onCreate and onCreateView are no more called so I can't handle the propagated parameters.
Is there any solution to this? Or am I simply wrong in my navigation pattern? I would like to avoid to collapse Fragment B,Fragment C and Fragment D in one "big view" where to hide some section (the same for Fragment E e Fragment F)
Any suggestion is more then welcome
Angelo
One simple solution to transfer a variable value from one fragment to another is shared preferences (can also be used to transfer values from one activity to another too). Shared preference will save data against variables that will persist across all the activities and fragments in an android app.
Now in your case, lets assume you want to transfer a value name = angelo from your fragment A to fragment B. In your fragment A, write this code:
Button updateName = findViewById(R.id.btnupdateTeamName);
updateTeamName .setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString("name", "angelo");
editor.commit();
}
});
When executed, the above code will update a value name with angelo in shared preferences. This will be available throughout your app.
For more info about shared preference, check out this official document.
I write my Fragment like this for passing data to it.
public class MyFragment extends Fragment {
private static String ARG_PARAM1 = "data";
private String data;
public MyFragment() {
// Required empty public constructor
}
public static MyFragment newInstance(String data) {
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, data);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
data = getArguments().getString(ARG_PARAM1);
}
}
}
Now data can be passed to the Fragment by calling MyFragment.newInstance("Hello"). I hope this helps.
I have faced a similar issue in my project.
In my case, I have viewpager and each tab has multiple fragment.
So one of the simple solutions is to use LiveData and ViewModel.
In your Tab2:
Fragment B
Fragment C
Fragment D
TabTwoViewModel (with live data)
In mutable Live data observer this live data to Fragment B, C, and D.
When you update live data object, Live data notify automatically all fragment.
Finally I got a solution.
Since the main problem is the fragments communication, I followed the official documentation
Let's suppose I have Fragment A with list of articles and Fragment B where to see the selected article detail, in my Fragment A i wrote:
public class FragmentA extends Fragment {
private OnArticleSelectionListener mCallback;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
}
}
#Override
public void onStart() {
super.onStart();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activityedifici, container, false);
return rootView;
}
public interface OnArticleSelectionListener {
void onArticleSelection(String articleId);
}
#Override
public void onDetach() {
super.onDetach();
mCallback = null;
}
public void setOnArticleSelectionListener(OnArticleSelectionListener mCallback) {
this.mCallback = mCallback;
}
}
As you can see I declared the following interface
public interface OnArticleSelectionListener {
void onArticleSelection(String articleId);
}
This is the article selection listener.
In my Main Activity I wrote the following:
public class MainActivity implements FragmentA.OnArticleSelectionListener{
//All my own stuffs
#Override
public void onAttachFragment(Fragment fragment) {
if (fragment instanceof FragmentA){
FragmentA ef = (FragmentA)fragment;
ef.setOnArticleSelectionListener(this);
}
}
#Override
public void onArticleSelection(String articleId) {
if( getSupportFragmentManager().findFragmentByTag(TAG_ARTICLE_DETAIL) != null ){
//FragmentB is the article detail and it has already been created and cached
FragmentB dcf = (FragmentB)getSupportFragmentManager().findFragmentByTag(TAG_ARTICLE_DETAIL);
dcf.updateArticleDetail( articleId );
}else{
//FragmentB is the article detail and it has never been created I create and replace the container with this new fragment
FragmentB dcf = new FragmentB();
//Parameter propagation
Bundle args = new Bundle();
args.putString(FragmentB.ARG_ARTICLE_ID, articleId);
dcf.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.container_articles, dcf, TAG_ARTICLE_DETAIL);
transaction.addToBackStack(null);
transaction.commit();
}
}
}
In this way I'm able in intercepting events in FragmentA and propagate them to the FragmentB; when I need to open a Tab all remains the same and finally (after transaction.commit() or the dcf.updateArticleDetail( articleId )) I do the following tabLayout.getTabAt(2).select(); and the third tab (tab index starts from 0) is open and the Detail is showed.
I hope this can be useful
Angelo

Set Fragment EditText from the parent Activty

I have a ProfileFragment class which contains two setters:
public void setPseudo(String pseudo){
textPseudo.setText(pseudo);
}
public void setEmail(String email){
textEmail.setText(email);
}
And in my Activity I would like to call these functions:
user = new ProfileFragment();
if (intent != null) {
user.setPseudo(intent.getStringExtra(USER_PSEUDO));
user.setEmail(intent.getStringExtra(USER_EMAIL));
}
It says "can't resolve method...".
Does it mean I can't do this?
Are you sure you don't have a Profile class with setters? Not a Fragment?
Fragments generally don't use setters, they use arguments.
Reason being: If you call setEmail, and then you called to some view setText within the new Fragment, you get a NullPointerException because that TextView was never initialized
Fragment profileFragment = new ProfileFragment();
Bundle args = new Bundle();
if (intent != null) {
args.putAll(intent.getExtras());
}
profileFragment.setArguments(args);
// Show new Fragment
getSupportFragmentManager()
.replace(R.id.content, profileFragment)
.commit();
And inside your Fragment's onCreateView, you can now use this, for example
final Bundle args = getArguments();
String pseudo = "";
if (args != null) {
pseudo = args.getString(YourActivity.USER_PSEUDO);
}
textPseudo.setText(pseudo);

How to get data from two different fragments in tabs to main activity

I want to know how to get data from 2 different fragments inside tab layout.
For example: In this image there two tabs tab1 and tab2 both have different values stored in edit text .
tab1 helloooooo,
tab2 hiiiiiii
and i have a button available in main activity which is used for getting the data from both tabs in same time.
Now my Problem is How i can get the data from both tabs at same time when user click on the
get data button
1.) Keep a reference of your fragment in your activity.
public class Activity extends AppCompatActivity {
private yourFargment frag1,frag2;
}
2.) Get the object reference into your reference variables in Adapter's getItem().
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
frag1 = yourFargment.newInstance(index);
//frag1 =new yourFargment (); can also be used
return frag1;
case 1:
frag2 = yourFargment.newInstance(index);
return frag2;
}
return null;
}
3.) Inside your onClick you can simply get those values from edittext.
onClick(View..){
String val1 = frag1.edittext1.getText().toString(); // Note: make your `edittext` public
String val2 = frag2.edittext2.getText().toString();
or
String val1 = frag1.getData();
String val2 = frag2.getData();
}
where getData is a public method inside your fragment
public class yourFragment..{
public String getData(){
return edittext.getText().toString();
// Note: with this no need to make your `edittext` public
}
}
Take member object of both Fragment in your activity..
FirstFragment mFirstFragment;
SecondFragment mSecondFragment;
Create instance of both in getItem(int position) method of ViewPagerAdapter.
#Override
public Fragment getItem(int position) {
// instantiate the fragment for the given page.
switch (position) {
case 0:
if (null == mFirstFragment) {
mFirstFragment = FirstFragment.newInstance();
}
return mFirstFragment;
case 1:
if( null == mSecondFragment ) {
mSecondFragment = SecondFragment.newInstance();
}
return mSecondFragment;
}
return null;
}
Call getData method of both Fragment when button clicked from activity.
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
// Get data and use it as you want.
mFirstFragment.getData();
mSecondFragment.getData();
break;
}
}
At the time when you are settings fragment in tabs you have 2 objects of those fragments. By using those objects, you can retrieve the value from your fragments. Write following code into your MainActivity e.g.
if(tab1fragment != null) {
String text = tab1fragment.getData();
}
and in Fragment, create a method like
public String getData(){
return edittext.getString().toString();
}
This way you can retrieve the data from fragments.
Tablayout only contains TabItems not Fragment. So, I believe that you use TabLayout combines with ViewPager, which actually contains 2 different Fragments. I suppose maybe you want to getData() at the Activity from 2 Fragments. At this point, you can easily Fragment callback the Activity and provide data just like getData(). The code will look like
In Fragment
public class BaseFragment extends Fragment {
protected DataCallback mCallback;
#Override public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof DataCallback) {
mCallback = (DataCallback) context;
}
}
//some place fragment affect to data
private void someFunction() {
mCallback.updateDataToActivity(data);
}
}
In Activity
public class MyActivity extends BaseActivity implements DataCallback {
T data;
public void updateDataToActivity(T data) {
this.data = data
}
// This is your getData function
private T getData() {
return data;
}
}
And DataCallback
public interface DataCallback {
void updateDataToActivity(T data);
}
You can use like this way:
Bundle bundle = new Bundle();
bundle = getActivity().getIntent().getExtras();
YourFragment fragment = new YourFragment();
bundle.putString("yourKey", yourValue);
fragment.setArguments(bundle);
Then you can get them in your second fragment
in onCreateView
if (getArguments() != null)
{
mParam1 = getArguments().getString("yourKey");
}
You can use data bus library like Otto : http://square.github.io/otto/
Or Rxjava : https://github.com/ReactiveX/RxJava
Both of these libraries are made for propagate data between classes.
Each tab has an EditText with an id
in tab1.xml it's edit1
<EditText
android:id="#+id/edit1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
and it's edit2 in tab2.xml
<EditText
android:id="#+id/edit2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
tab1.xml and tab2.xml also has a button which can be clicked. It has the same id if you want on tab1 and tab2.
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/knop"
android:onClick="klik"/>
The item that matters is the OnClick. It point to a piece of code which must be implemented in the MainActivity.java code
public void klik(View v){
alert("Just an alert.");
String val1 = ((EditText) findViewById(R.id.edit1)).getText().toString();
String val2 = ((EditText) findViewById(R.id.edit2)).getText().toString();
alert(val1);
alert(val2);
alert(val1 + val2);
}
At last the alert routine
private void alert(String message){ //mies
AlertDialog alertDialog = new AlertDialog.Builder(this).create();
alertDialog.setTitle("Alert");
alertDialog.setMessage(message);
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alertDialog.show();
}
In fact the fragments can be treated as one page. You can retrieve the information from each field by id from the calling main page, in this case MainActivity.java.

update a listview in a fragment in a viewpager from activity

I understand that this is quite a common issue, and I have referred to many different other questions but I still can't get this to work.
My activity implements a view pager with two tabs and in each tab is a listview. I have a adapter for my view pager which links the two fragments and in each fragment, a adapter to link the data to the listview.
In my activity menu, I have a menu which creates an edittext in an alertdialog for me to input new fields into one of the listview in one of the fragment.
My activity (contains a viewpager)
protected void onCreate(Bundle savedInstanceState)
{
...
subAdapter = new SubAdapter(getSupportFragmentManager(), data);
((ViewPager) findViewById(R.id.viewPager)).setAdapter(subGroupAdapter);
}
My viewpager adapter
public class SubAdapter extends FragmentPagerAdapter
{
public SubGroupAdapter(FragmentManager fm, data data)
{
super(fm);
}
#Override
public Fragment getItem(int position)
{
Bundle bundle = new Bundle();
bundle.putString("data", data);
switch (position)
{
case 0:
Fragment1 frag1 = new Fragment1();
frag1.setArguments(bundle);
return frag1;
case 1:
Fragment2 frag2 = new Fragment2();
frag2.setArguments(bundle);
return frag2;
}
return null;
}//some other methods below
Fragment1 / Fragment2 (both fragments have a listview)
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
frag1Adapter = new frag1Adapter(this, data);
((ListView) getView().findViewById(R.id.listView)).setAdapter(frag1Adapter);
}
Custom listview adapter for both listviews in both fragments
public class ExpenseAdapter extends BaseAdapter implements OnClickListener
{ ... }
As I mentioned earlier, I can input a new entry into either listview from the activity action bar button. However, the listview does not get updated and I can't reference the listview adapter to call notifydatasetchanged() from the activity.
What is the best way I can proceed from here onwards? thank you so much!
I have tried exploring using interfaces, tags but can't get it to work currently.
What you should do is create a public method for your Fragment1 and Fragment2 like so:
define in your Activity:
Fragment1 frag1;
Fragment2 frag2;
then your ViewPager:
public class SubAdapter extends FragmentPagerAdapter
{
public SubGroupAdapter(FragmentManager fm, data data)
{
super(fm);
}
#Override
public Fragment getItem(int position)
{
Bundle bundle = new Bundle();
bundle.putString("data", data);
switch (position)
{
case 0:
frag1 = new Fragment1();
frag1.setArguments(bundle);
return frag1;
case 1:
frag2 = new Fragment2();
frag2.setArguments(bundle);
return frag2;
}
return null;
}
}
Put this method in Fragment1.java :
public void updateFragment1ListView(){
if(adapter != null){
adapter.notifyDataSetChanged();
}
}
and from your activity call:
if(frag1 != null){
frag1.updateFragment1ListView();
}
obviously change the names for your adapter if its not called adapter...
Just do the same for Fragment2.java as well
I would recommend creating an interface. Here's an Example:
public interface UpdateListFragmentInterface {
void updateList();
}
Then in the Activity, add the delegate as a property:
UpdateListFragmentInterface yourDelegate;
Wherever you create the Fragment within the Activity, assign the Fragment as the delegate:
// Set up and create your fragment.
(if yourFragment instanceof UpdateListFragmentInterface) {
yourDelegate = yourFragment;
}
Then in the Fragment, implement the interface:
public class YourListFragment extends android.support.v4.app.ListFragment implements UpdateListFragmentInterface {
// Constructors and other methods for the ListFragment up here.
// Override the interface method
public void updateList() {
//Update the list here.
}
}
Then finally, from within your Activity, when you need to update the list, simply call the delegate:
yourDelegate.updateList();
EDIT:
Sorry, I think you actually need to create a public method in your activity that is something like:
public void setUpdateListFragmentDelegate(UpdateListFragmentDelegate delegate) {
this.delegate = delegate
}
Then in the on attach of the List Fragment you will assign it there:
if (context instance of YourActivity) {
((YourActivity)context.setUpdateListFragmentDelegate(this);
}

Android: Passing data from child fragment to parent fragment

I need to pass some data from the child fragment to the parent fragment that I will be able to read when I go back to the parent fragment. In detail:
I have a FragmentActivity that calls FragmentParent. From FragmentParent I call FragmentChild like this:
FragmentChild fragmentChild = new FragmentChild();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.frl_view_container, fragmentChild);
transaction.addToBackStack(null);
ctransaction.commit();
In FragmentChild I set a string value which I need to pass back to FragmentParent and then I return back to FragmentParent.
String result = "OK";
getFragmentManager().popBackStack();
What is the best/proper way to read the result string in FragmentParent?
Android architecture components solution:
In case you are using Android architecture components, it possible to share data between all Fragments of an Activity with a ViewModel. Ensure ViewModelProviders makes use of Activity context to create ViewModels.
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
Non Android architecture components solution:
You can use setTargetFragment and onActivityResult to achieve this.
Set FragmentParent instance as target fragment on FragmentChild instance i.e.
FragmentChild fragmentChild = new FragmentChild();
fragmentChild.setTargetFragment(this, FRAGMENT_CODE);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.frl_view_container, fragmentChild);
transaction.addToBackStack(null);
transaction.commit();
In FragmentChild, wherever you are invoking the popBackStack, call onActivityResult on the set target Fragment. Use Bundle to pass on additional data.
Intent intent = new Intent();
intent.putExtra(FRAGMENT_KEY, "Ok");
getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, intent);
getFragmentManager().popBackStack();
Back in FragmentParent, override the default onActivityResult method.
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == FRAGMENT_CODE && resultCode == Activity.RESULT_OK) {
if(data != null) {
String value = data.getStringExtra(FRAGMENT_KEY);
if(value != null) {
Log.v(TAG, "Data passed from Child fragment = " + value);
}
}
}
}

Categories

Resources