Android ViewPager Nested Fragment Lifecycle - android

I am using nested fragments and viewPager together. Following is my code for HomeFragment which is one of the fragments in viewPager. HomeFragment holds another fragment called FeedFragment which is just a RecyclerView. Inside FeedFragment I am using a callback interface which notifies HomeFragment that FeedFragment was successfully created. The problem I am having is, first time HomeFragment is created, mListener value is set to an instance of HomeFragment inside onCreateView and everything is loaded properly. When I move to a different fragment in viewPager and go back to HomeFragment, HomeFragment onCreateView method is called and mListener is again set to an instance of HomeFragment and after that onCreateView method is called from FeedFragment but mListener is null this time.
Following is the code for FeedFragment:
public class FeedFragment extends Fragment {
RecyclerView feedRecyclerView;
FeedRecyclerViewAdapter feedRecyclerViewAdapter;
private OnCompleteListener mListener;
public FeedFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_feed, container, false);
feedRecyclerView = (RecyclerView) view.findViewById(R.id.postFeedItemContainer);
feedRecyclerViewAdapter = new FeedRecyclerViewAdapter(getActivity());
feedRecyclerView.setAdapter(feedRecyclerViewAdapter);
feedRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mListener.onComplete(feedRecyclerViewAdapter, this);
return view;
}
public interface OnCompleteListener {
void onComplete(
FeedRecyclerViewAdapter feedRecyclerViewAdapter, FeedFragment feedFragment);
}
public void setmListener(OnCompleteListener mListener) {
this.mListener = mListener;
}
}
Following is the code for HomeFragment:
public class HomeFragment extends Fragment implements FeedFragment.OnCompleteListener {
// presenter instance
private HomeFragmentPresenter homeFragmentPresenter = HomeFragmentPresenter.getInstance();
public HomeFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
FeedFragment feedFragment = new FeedFragment();
feedFragment.setmListener(this);
fragmentTransaction.add(R.id.fragmentHomeFeedContainer, feedFragment);
fragmentTransaction.commit();
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false);
}
#Override
public void onComplete(FeedRecyclerViewAdapter feedRecyclerViewAdapter, FeedFragment feedFragment) {
homeFragmentPresenter.fetchDataForAdapter(feedRecyclerViewAdapter, feedFragment);
}
}

Related

Textview not updating on first Tab unless scrolled to 3rd Tab

I have a tablayout (with 3 tabs) with viewpager and fragments.
I m trying to send the parsed Json data from MainActivity( When searchview data submitted ) to show in the textview of tabs fragments
See this Image link
The data is succesfully parsing but textview with data(in first tab) is not showing unless scrolled to 3rd tab
//Passing data from MainActivity
public String getMyData() {
return meaning;
}
//Setting value to textview from Fragment
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v=inflater.inflate(R.layout.fragment_meaning, container, false);
MainActivity mainActivity= (MainActivity) getActivity();
assert mainActivity != null;
String data= mainActivity.getMyData();
TextView textView=v.findViewById(R.id.textVIew);
textView.setText(data);
return v;
}
Want to able to show data changes instantly as it is parsed, instead of scrolling to 3rd tab to see changes
Here are some steps that might help you.
On the ViewPager adaptor you have created, make the fragment objects. like below
FragmentOne fragOne; // this should be global
On the viewPager adaptor, do some thing like this,
fragOne = new FragmentOne() // whatever your implementation is.
Then after fetching the data from the server,
if ( fragOne != null ) {
fragOne.setValueOnView( " your data to be passed" );
}
and on the FragmentOne, create a function called setValueOnView
void setValueOnView(String yourString) {
v.findViewById(R.id.textVIew).setText(yourString);
}
And one more thing, while initializing the fragment onCreateView, create an object of View
View v; // global variable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v=inflater.inflate(R.layout.fragment_meaning, container, false);
Use this approach for other fragments as well
Inside getItem() method in ViewPager class use Fragment constructors with String parameter
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
FragmentOne tab1 = new FragmentOne("string parameter");
return tab1;
case 1:
FragmentTwo tab2 = new FragmentTwo("string parameter");
return tab2;
case 2:
FragmentThree tab3 = new FragmentThree("string parameter");
return tab3;
default:
return null;
}
}
Inside your Fragment:
public FragmentOne(String stringParameter) {
yourLocalVariable = stringParameter; // yourLocalVariable is declared inside Fragment class;
//now you can setText() for your TextView inside onViewCreated()
}
Of course you pass your String from MainActivity to ViewPager like you did earlier.
Use Observer
public class FragmentObserver extends Observable {
#Override
public void notifyObservers() {
setChanged(); // Set the changed flag to true, otherwise observers won't be notified.
super.notifyObservers();
}
}
Activity:
public class MyActivity extends Activity {
private MyAdapter mPagerAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.my_activity);
ViewPager pager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new MyAdapter();
pager.setAdapter(mPagerAdapter);
}
private void updateFragments() {
mPagerAdapter.updateFragments();
}
}
Viewpager adapter
public class MyAdapter extends FragmentPagerAdapter {
private Observable mObservers = new FragmentObserver();
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
mObservers.deleteObservers(); // Clear existing observers.
Fragment fragment = new MyFragment();
if(fragment instanceof Observer)
mObservers.addObserver((Observer) fragment);
return fragment;
}
public void updateFragments() {
mObservers.notifyObservers();
}
}
Your Fragment
public class MyFragment extends Fragment implements Observer {
/* Fragment related stuff... */
#Override
public void update(Observable observable, Object data) {
View root = getView();
// Update your views here.
}
}
You will get data to update method even your fragment already loaded

replace fragment with another fragment along with remove view

I have one activity and two Fragments. FragmentA and FragmentB.
I need to implement like, Initially FragmentA should be there.
and After Button click FragmentB should be there.
in my condition when activity is being displayed,fragmentA is showing and when click to button,fragmentB placed at the bottom of fragmentA and do not replace FragmentA
My activity Code is:
public class FragmentActivity extends AppCompatActivity {
FrameLayout activityFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
activityFragment = (FrameLayout) findViewById(R.id.activityFragment);
Fragment fragment = new FragmentA();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.activityFragment, fragment);
transaction.commit();
}
public void goToFrag(View view) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
Fragment fragment = new FragmentB();
transaction.add(R.id.fragmentRoot, fragment);
transaction.commit();
}
}
my first fragment code:
public class FragmentA extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view=inflater.inflate(R.layout.fragment_a,container,false);
return view;
}
}
my second fragment code:
public class FragmentB extends Fragment {
public FragmentB() {
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view=inflater.inflate(R.layout.fragment_b,container,false);
return view;
}
}
First of all, you should always have a FrameLayout in xml to replace your fragments there, then you use different containers to add fragments activityFragment and fragmentRoot .You should replace fragments in one container. You can use either add or replace for FragmentManager , so it activityFragment is your container (which should be a framelayout as a wrapper as the docs suggest) you use
public void goToFrag(View view) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
Fragment fragment = new FragmentB();
transaction.replace(R.id.activityFragment, fragment); // use the same container where you switch A and B
transaction.commit();
}

How can I replace fragment inside a fragment?

I have an Activity with a ViewPager, each page of the ViewPager has a Fragment.
Inside my Screen3Fragment I have a LinearLayout (lly_fragments) where I am showing some other fragments. I start by showing the fragment Screen3_1
public class Screen3Fragment extends Fragment {
private FragmentManager manager;
private FragmentTransaction transaction;
public static Screen3Fragment newInstance() {
final Screen3Fragment mf = new Screen3Fragment();
return mf;
}
public Screen3Fragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_screen3, container, false);
Screen3_1Fragment frag31 = new Screen3_1Fragment();
manager = getChildFragmentManager();
transaction = manager.beginTransaction();
transaction.add(R.id.lly_fragments,frag31,"frag31");
transaction.addToBackStack("frag31");
transaction.commit();
return v;
}
}
This works fine without problems. Problem comes when, from within frag31 (which is inside Screen3Fragment), I want to call fragt32, for that I do the following.
public class Screen3_1Fragment extends Fragment {
private ImageButton imgbt_timer;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_screen3_1,container,false);
imgbt_timer = (ImageButton) v.findViewById(R.id.bT_scr31_timer);
imgbt_timer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getChildFragmentManager().beginTransaction()
.replace(R.id.lly_fragments, new Screen3_2Fragment(), "frag32")
.commit();
}
});
return v;
}
}
As I read in other answers, the line transaction.replaceshould do the trick and replace the existing frag31 by the given frag32 inside the same given container lly_fragments.
However, I get java.lang.IllegalArgumentException: No view found for id..... I am not sure why.
getFragmentManager() will return always attributes from parent, in most cases the activities. getChildFragmentManager() wil return parent attributes (in your case, Screen3Fragment attributes). It should be used when you add a fragment inside a fragment.
In your case, Screen3Fragment should be added using getFragmentManager() and Screen3_1Fragment should be added using getChildFragmentManager() because Screen3_1Fragment is Screen3Fragment child. Screen3Fragment is the parent.
I recomand you to use always getFragmentManager() with add method, not replace because your parent will be the same.
getChildFragmentManager() can be used when you add a ViewPager inside a fragment.
You can use the callback, as following :
that worked for me, I hope my answers helps you
1) create a interface
public interface ChangeFragmentListener {
void onChangeFragmentLicked(int fragment);
}
2) implement the interface and transaction methods in your activity:
public class MainActivity extends AppCompatActivity implements ChangeFragmentListener {
FragmentTransaction fragmentTransaction;
Fragment1 fragment1;
Fragment2 fragment2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
fragment1 = new Fragment1();
fragment1.setChangeFragmentListener(this);
fragment2 = new Fragment2();
fragment2.setChangeFragmentListener(this);
initListeners();
}
void changeToFrag1() {
fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.activity_main_fragment_container,fragment1, "");
fragmentTransaction.commit();
}
void changeToFrag2() {
fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.activity_main_fragment_container, fragment2, "");
fragmentTransaction.commit();
}
#Override
public void onChangeFragmentLicked(int fragment) {
switch (fragment){
case 1:
changeToFrag1();
break;
case 2:
changeToFrag2();
break;
}
}
3) Create object from the interface to handle the callback:
public class Fragment1 extends Fragment {
private ChangeFragmentListener changeFragmentListener;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_fragment1, container, false);
view.findViewById(R.id.fragment1_textView).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
changeFragmentListener.onChangeFragmentLicked(2);
}
});
return view;
}
public Fragment1 setChangeFragmentListener(ChangeFragmentListener changeFragmentListener) {
this.changeFragmentListener = changeFragmentListener;
return this;
}
}

Save RecyclerView State inside Fragment in ViewPager created inside another Fragment

I have a ViewPager inside a Fragment
public class FragmentPager extends BaseFragment {
#Bind(R.id.viewpager)
ViewPager viewPager;
#Bind(R.id.detail_tabs)
TabLayout detailTabs;
private Activity mActivity;
private PagerAdapter mPagerAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mPagerAdapter = new PagerAdapter(getChildFragmentManager());
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_pager, container, false);
ButterKnife.bind(this, v);
viewPager.setAdapter(mPagerAdapter);
final int pageMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources()
.getDisplayMetrics());
viewPager.setPageMargin(pageMargin);
detailTabs.setupWithViewPager(viewPager);
return v;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
#Override
public void onDestroyView() {
super.onDestroyView();
ButterKnife.unbind(this);
}
}
I am already using FragmentStatePagerAdapter to manage my fragments in ViewPager. Each Fragment has a RecyclerView. On clicking each item of the RecyclerView I am replacing the FragmentPager with FragmentDetail. When I navigate back to the FragmentPager, the RecyclerView starts from the beginning instead of position scrolled.
I think you change your fragment with fragmentTransaction replace method. You need to do it with fragmentTransaction add method. See the below
Basic difference between add() and replace() method of Fragment

android fragment replace to second fragment not working

My situation is I have a Activity(only FrameLayout),one fragment has a butoon that it's function is changing to second fragmnet,but when i set replace and commit.It's fail.
I tried this function success brfore,but it's not working now.
Is anyone can tell me the problem ? It will be grateful.
The first fragment code:
public class StartPage extends Fragment implements View.OnClickListener {
}
public static StartPage newInstance() {
return new StartPage();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.start_page_fragment, container, false);
//findViews();
imageRegistered = (ImageView) view.findViewById(R.id.imageRegistered);
imageRegistered.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FragmentManager fragment=getFragmentManager();
FragmentTransaction transaction=fragment.beginTransaction();
transaction.replace(R.id.frameStartPage,Register.newInstance(),null);
transaction.commit();
Toast.makeText(getActivity(),"Test",Toast.LENGTH_SHORT).show();
}
});
return view;
}
and it's my frameLayout on the Activity:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/frameStartPage"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.huaweb_system.taiwanuniversityhome.MainActivity">
</FrameLayout>
MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
if (savedInstanceState == null) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.frameStartPage, StartPage.newInstance());
transaction.commit();
}
}
}
second fragment:
public class Register extends Fragment implements View.OnClickListener {
public static StartPage newInstance() {
return new StartPage();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.register_activity, container, false);
findViews();
return view;
}
}
Actually getting instance using new operator and using newInstance() static method is same.
Problem is in your code :
public static StartPage newInstance() {
return new StartPage();
}
you are using the same method in both the fragments StartPage and Register.
Therefore You should change newInstance method of your Register fragment like this
public static Register newInstance() {
return new Register();
}

Categories

Resources