How to Get Elements of Fragments Created by Viewpager in Mainactivity

How to get elements of fragments created by ViewPager in MainActivity?

Unfortunately the only "safe" way to do it is to check the source code of FragmentPagerAdapter, and see what tag is the fragment is added with - because findFragmentByTag is the only safe way to get a Fragment from a FragmentManager.

Of course, we need to hope that this implementation detail doesn't change between support library versions, but it actually hasn't changed in years :)


Anyways, so FragmentPagerAdapter says:

    // Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);

So what is name?

private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}

What is viewId and what is id?

ViewId is container.getId(), where container is the ViewPager itself.

id is: final long itemId = getItemId(position); which by default is return position;.


So you can find the Fragment as follows:

Fragment fragment = fragmentManager.findFragmentByTag("android:switcher:" + viewPager.getId() + ":" + fragmentPosition);
// if fragment is null, it was not created yet by ViewPager

NOTE:

It is worth noting that you should NOT rely on getItem(int position) { to return the same fragment instance as you passed in, because that will lead you to this problem. (This question does it correctly, the getItem() method must always return a new Fragment instance.)

Getting the current Fragment instance in the viewpager

by selecting an option, I need to update the fragment that is
currently visible.

A simple way of doing this is using a trick related to the FragmentPagerAdapter implementation:

case R.id.addText:
Fragment page = getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + ViewPager.getCurrentItem());
// based on the current position you can then cast the page to the correct
// class and call the method:
if (ViewPager.getCurrentItem() == 0 && page != null) {
((FragmentClass1)page).updateList("new item");
}
return true;

Please rethink your variable naming convention, using as the variable name the name of the class is very confusing(so no ViewPager ViewPager, use ViewPager mPager for example).

How to get the current fragment displayed in a specific tab of a viewpager?

First define a SparseArray in your ViewPagers' adapters like below. In this array we'll hold the instance of fragments.

SparseArray<Fragment> registeredFragments = new SparseArray<>();

And Override your Adapters' instantiateItem method.

@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}

Also Override destroyItem method of your ViewPagers

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}

And define a new method to get your ViewPager Fragments instance.

public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}

And finally set add a PageChangeListener to your ViewPagers:

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

@Override
public void onPageSelected(int position) {
// Here's your instance
YourFragment fragment =(YourFragment)yourPagerAdapter.getRegisteredFragment(position);

}

@Override
public void onPageScrollStateChanged(int state) {

}
});

I hope this'll help you. Good luck.

Edit: I'm sorry i cannot understand exactly what you're planning to do but if you need to keep sub fragment (b_1, b_2) instance you can define a method to your activity such as

public void setCurrentFragment(Fragment fragment){
this.currentFragment = fragment;
}

and in your sub view pager's adapter you can call this method like below:

subViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

@Override
public void onPageSelected(int position) {
// Here's your instance
YourFragment fragment =(YourFragment)yourSubPagerAdapter.getRegisteredFragment(position);
((MyActivity)getActivity).setCurrentFragment(fragment);
}

@Override
public void onPageScrollStateChanged(int state) {

}
});

With this way you can keep one instance and your top fragment.

Unable to get a Fragment Object into MainActivity : android

You can see this doc https://developer.android.com/training/basics/fragments/communicating
If you want to make you own bicycle for some reasons, you could try something like this (observer pattern)

MyModel

import java.util.ArrayList;

public class MyModel {
private int color = R.color.colorPrimary;

private ArrayList<MyObserver> observers = new ArrayList<>();

interface MyObserver {
void setColor(int color);
}

public void setColor(int color) {
this.color = color;
for (int i = 0; i < observers.size(); i++) {
observers.get(i).setColor(color);
}
}

public void addObserver(MyObserver newObserver) {
if (observers.indexOf(newObserver) < 0) {
observers.add(newObserver);
}
}

public void deleteObserver(MyObserver oldObserver) {
if (observers.indexOf(oldObserver) > -1) {
observers.remove(oldObserver);
}
}
}

FirstFragment

public class FirstFragment extends Fragment {

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

Button button = view.findViewById(R.id.button);

button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MainActivity mainActivity = (MainActivity) getActivity();
mainActivity.myModel.setColor(R.color.colorAccent);
}
});

return view;
}
}

SecondFragment

public class SecondFragment extends Fragment implements MyModel.MyObserver {

RelativeLayout relativeLayout;

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

relativeLayout = view.findViewById(R.id.layout);
MainActivity mainActivity = (MainActivity) getActivity();
mainActivity.myModel.addObserver(this);
return view;
}

@Override
public void onDestroy() {
MainActivity mainActivity = (MainActivity) getActivity();
mainActivity.myModel.deleteObserver(this);
super.onDestroy();
}

@Override
public void setColor(int color) {
relativeLayout.setBackgroundColor(getResources().getColor(color, null));
}
}

MainActivity

public class MainActivity extends AppCompatActivity {

ViewPager viewPager;
MyPagerAdapter adapter;

public MyModel myModel = new MyModel();

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

viewPager = findViewById(R.id.view_pager);
adapter = new MyPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);

}
}

Get ViewPager views from MainActivity

The textview is encapsulated in the container of the fragment. If you get a reference to the correct fragment and get a reference to the container you can call findViewById(R.id.someid).


Sample code:

// TODO cast checks
int item = mViewPager.getCurrentItem();
Fragment frag = (Fragment) mPagerAdapter.getItem(item);
View container = frag.getView();
TextView searchTextView = (TextView) container.findViewById(R.id.someid);

Or is your case:

// TODO add null + cast checks
TextView textView = (TextView) ((Fragment)adapter.getItem(0)).getView().findViewById(R.id.someText);

Listening to Fragment onClicks in Main Activity

Instead of creating the listener in the Activity, I would implement the listener method in the Activity and use the methods onAttach/onDetach of the Fragment to register/unregister the Activity as a callback.

(This approach has the advantage, that the Activity is re-registered as a listener after a configuration change.)

Activity:

public MainActivty extends ... implements OnSongClickListener {

...

public void onSongClicked(Audio audio) {
...
}

}

Fragment:

public SongListFragment extends ... {

private OnSongClickListener mListener;

...

@Override
public void onAttach(Context context) {
super.onAttach(context);

// Set parent activity as callback
try {
mListener = (OnSongClickListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement " + OnSongClickListener.class.getName());
}
}

@Override
public void onDetach() {
super.onDetach();

// Reset callback
mListener = null;
}

...

@Override
public void onBindViewHolder(SongListItemViewHolder holder, int position) {
Audio audio = audioList.get(position);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null) {
mListener.onSongClick(audio);
}
}
});
}

...

}


Related Topics



Leave a reply



Submit