ViewPager IllegalStateException with Fragments - android

In Xamarin, I have a ViewPager that is performing the following error:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
This error occurs when I have three Android.Support.V4.App.Fragments and I swipe between them. This error does not happen with two Android.Support.V4.App.Fragments.
Here is my Android.Support.V4.App.Fragment code:
public class FragmentItem : Android.Support.V4.App.Fragment
{
View rootView;
TextView textView;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (rootView == null)
{
rootView = inflater.Inflate(Resource.Layout.FragmentItem, container,false);
textView = rootView.FindViewById<TextView>(Resource.Id.textViewDisplay);
}
return rootView;
}
}
Here is my ViewPagerAdapter code:
public class ViewPagerAdapter : FragmentPagerAdapter {
private List<Android.Support.V4.App.Fragment> fragments;
int fragmentCount;
public ViewPagerAdapter(Android.Support.V4.App.FragmentManager fm, List<Android.Support.V4.App.Fragment> fragments) : base(fm)
{
this.fragments = fragments;
fragmentCount = fragments.Count;
}
public override int Count
{
get
{
return fragmentCount;
}
}
public override Android.Support.V4.App.Fragment GetItem (int position)
{
return fragments[position];
}
}
Why is this error happening and how can I prevent this error from happening?
Thanks in advance
EDIT
Here is the code that I have tried:
if (rootView != null)
{
ViewGroup parent = (ViewGroup)View.Parent;
parent.RemoveView(rootView);
} else {
rootView = inflater.Inflate(Resource.Layout.FragmentItem, container, false);
textView = rootView.FindViewById<TextView>(Resource.Id.textViewDisplay);
}
return rootView;
Here is the error that I am getting:
System.NullReferenceException: Object reference not set to an instance
of an object
At this line of code:
ViewGroup parent = (ViewGroup)View.Parent;

Use this in your onCreateView() method,
if (rootView != null) {
ViewGroup parent = (ViewGroup) rootView.getParent();//Updated
parent.removeView(rootView);
} else {
rootView = inflater.inflate(Resource.Layout.FragmentItem, container,
false);
}
this will remove the view from its parent view if this already has parent view.

Related

Moving Data from Activity to Another Activity's TabFragment's Fragment

I want to move some data from my Mainactivity to a Fragment...which is inside a tab fragment (which handles a SectionPagerAdapter)....which is inside a fragment in Mainactivity2.
I feel its too complicated and cant get it done.
GameEndedActivity (See wrong and correct variable...these are to be transferred)
public class GameEndedActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_ended);
int correct = getIntent().getIntExtra("correct", 0);
int wrong = getIntent().getIntExtra("wrong", 0);
}
}
TabFragment
public class TabFragment extends Fragment {
private int tabsCount;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_tab, container, false);
if(this.getContext() instanceof ChallengeOverviewActivity){
tabsCount = 2;
} else {
tabsCount = 3;
}
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this.getContext(), getFragmentManager(), tabsCount);
ViewPager viewPager = view.findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
TabLayout tabs = view.findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
return view;
}
}
ResultFragment (Transfer data here - wrong and correct variables)
public class ResultDetailsFragment extends Fragment {
private TextView tvWrong, tvCorrect;
private AnswerListener answerListener;
public interface AnswerListener {
public void correct(int i);
public void wrong(int i);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_result_details, container, false);
tvCorrect = view.findViewById(R.id.tvCorrect);
tvWrong = view.findViewById(R.id.tvWrong);
return view;
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
if (context != null) {
answerListener = (AnswerListener) context;
}
}
}
One way to do this is the add arguments to the fragment when you create an instance of it. In your activity when you create the fragment you want to attach you add some arguments to it. These arguments can then be accessed from the fragments onCreate method.
Just do this same thing again when you are in your fragment and create your second one.
Check this answer out which explains more in detail how to do it: https://stackoverflow.com/a/17436739/964887

get the fragment that includes a particular view

How do I find the fragment that includes a particular view?
It is easy to find a view knowing the fragment and the view id:
fragment.getView().findViewById(R.id.foo)
But what I want to do is the reversal: knowing the view, find the fragment.
So far the best idea is:
I. Set the root view's tag to the fragment that inflated it:
// in a class that extends Fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_id, container, false);
rootView.setTag(this); // <======= !!!
return rootView;
}
II. Then, it's relatively easy to find that root view among view's parents:
// in some utility class:
public static Fragment getTagFragment(View view) {
for (View v = view; v != null; v = (v.getParent() instanceof View) ? (View)v.getParent() : null) {
Object tag = v.getTag();
if (tag != null && tag instanceof Fragment) {
return (Fragment)tag;
}
}
return null;
}
III. Usage:
Fragment f = getTagFragment(view);
PS I can even place an onClick handler into a Fragment, and it works!

Notify from child fragment inside ViewPager to his parent an event

i have a fragment inside a activity, this fragment contains a viewpager, this is the code:
public class RepliesContainerFragment extends Fragment {
#InjectView(R.id.viewPager)
ViewPager pager;
private PagerAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ReplyActivity replyActivity = (ReplyActivity) getActivity();
List<Replie> replies = getArguments().getParcelableArrayList("replies");
adapter = new PagerAdapter(replyActivity.getSupportFragmentManager(), replies);
setActionBar();
}
private void setActionBar() {
getActivity().getActionBar().setDisplayHomeAsUpEnabled(false);
getActivity().getActionBar().setIcon(getResources().getDrawable(R.drawable.ic_back));
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
getActivity().getActionBar().setTitle("Respuestas");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.work_containter_replies, container, false);
ButterKnife.inject(this, view);
pager.setAdapter(adapter);
return view;
}
}
Inside the adapter i had instantiate some fragments like this:
public class ReplyFragment extends Fragment {
#InjectView(R.id.tvQuestion)
TextView tvQuestion;
#InjectView(R.id.frameContainter)
FrameLayout replyContainer;
private ReplyView replyView;
private String question;
private String currentReplie;
private int replieType;
private int questionId;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
replieType = getArguments().getInt("replyType");
question = getArguments().getString("question");
questionId = getArguments().getInt("questionId");
currentReplie = getArguments().getString("currentReplie");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.work_reply_fragment, container, false);
ButterKnife.inject(this, view);
initQuestionText();
return view;
}
private void persistReply() {
ReplyActivity replyActivity = (ReplyActivity) getActivity();
if (replyActivity != null) {
//DO SOMETHING
//Notify to adapter from here
}
}
}
}
As you can see, i want to notify to my RepliesContainerFragment and his inside adapter that some event happens and do an action..
How can i do it this?
With broadcastreceivers, with callbacks(Interfaces)..?I am not sure.
The very simple solution for this is to pass a listener instance from your parent fragment to a child fragment. You have to create a listener interface, create an instance of it in your parent fragment, and pass that instance to all your children fragments.
By the way there is a topic regarding this problem in the official tutorial.
Hope this helps.
I think it is easier:
private void persistReply() {
ReplyActivity replyActivity = (ReplyActivity) getActivity();
if (replyActivity != null) {
//DO SOMETHING
RepliesContainerFragment frag = ((RepliesContainerFragment)this.getParentFragment());
frag.getAdapter().notifyDataSetChanged();
}
}
}
and make getter setter for adapter

onCreateView fragment on tabhost called every time I change the tab

I am using tab host with fragment, the following is the code of the main activity
public class HomeActivity extends Activity{
private FragmentTabHost mTabHost;
private ArrayList<CustomTabIndicator> mCustomTabIndicator;
private ArrayList<BaseFragment> mTabFragments;
private class CustomTabIndicator {
private int mIdResId;
private int mTitleResId;
private int mIconResId;
public CustomTabIndicator(int idResId, int titleResId, int iconResId) {
this.mIdResId = idResId;
this.mTitleResId = titleResId;
this.mIconResId = iconResId;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home_screen);
mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
initializeTabIndicatorsAndFragments();
addTabIndicatorsToTabHost();
}
private void initializeTabIndicatorsAndFragments() {
mCustomTabIndicator = new ArrayList<HomeActivity.CustomTabIndicator>();
mCustomTabIndicator.add(new CustomTabIndicator(R.string.tab_dashboard,
R.string.tab_dashboard, R.drawable.tab_dashboard));
mCustomTabIndicator.add(new CustomTabIndicator(R.string.tab_feed,
R.string.tab_feed, R.drawable.tab_feed));
mCustomTabIndicator.add(new CustomTabIndicator(R.string.tab_lists,
R.string.tab_lists, R.drawable.tab_lists));
mCustomTabIndicator.add(new CustomTabIndicator(R.string.tab_me,
R.string.tab_me, R.drawable.tab_me));
mTabFragments = new ArrayList<BaseFragment>();
mTabFragments.add(new DashboardFragment());
mTabFragments.add(new FeedFragment());
mTabFragments.add(new ListsFragment());
mTabFragments.add(new MeFragment());
}
private void addTabIndicatorsToTabHost() {
for (int i = 0; i < mCustomTabIndicator.size(); i++) {
mTabHost.addTab(
mTabHost.newTabSpec(
getString(mCustomTabIndicator.get(i).mIdResId))
.setIndicator(
createTabView(
this,
mCustomTabIndicator.get(i).mTitleResId,
mCustomTabIndicator.get(i).mIconResId)),
mTabFragments.get(i).getClass(), null);
}
}
#SuppressLint("InflateParams")
private View createTabView(final Context context, final int textStringId,
final int imageResId) {
View view = LayoutInflater.from(context).inflate(
R.layout.fragment_tab_header_image_text_layout, null);
ImageView tabIV = (ImageView) view.findViewById(R.id.tab_icon);
tabIV.setImageResource(imageResId);
//TextView titleTV = (TextView) view.findViewById(R.id.tab_title);
//titleTV.setText(textStringId);
return view;
}
}
This is the code of one of the fragements
public class MeFragment extends BaseFragment {
private View mFragementView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.i("AMIRA", "MeFragment - onCreateView");
mFragementView = inflater.inflate(R.layout.fragment_me_screen,container, false);
initializeUIComponents();
initializeUIComponentsData();
initializeUIComponentsTheme();
initializeUIComponentsAction();
return mFragementView;
}
}
The problem now that onCreateView called every time I change the tab, and take long time to render and draw the content of fragment.
So I have tried the following code
public class MeFragment extends BaseFragment {
private View mFragementView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (mFragementView != null) {
return mFragementView;
} else {
Log.i("AMIRA", "MeFragment - onCreateView");
mFragementView = inflater.inflate(R.layout.fragment_me_screen,container, false);
initializeUIComponents();
initializeUIComponentsData();
initializeUIComponentsTheme();
initializeUIComponentsAction();
return mFragementView;
}
}
}
and i got the following exception
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
so can anyone help here ?
First, about the java.lang.IllegalStateException,
You haven't initialized mFragmentView.
Say something like:
mFragmentView = (View)getActivity().findViewById(R.id.mFragmentView);
or:
mFragmentView = (View)getActivity().findFragmentById(R.id.mFragmentView);
Second about the Fragment changing every time you change the tab.
Try this:
add setRetainInstance(true); to the Fragments onAttach() or onCreateView().
Open for correction, as always!
Regards,
Edward Quixote.

Access to view elements in fragments

I have an activity which receive data from other activity. This data has to be showed in a TextView in a fragment inflated in the activity. So, the code is:
public class DetailActivity extends Activity {
private int idEstablecimiento;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit(); //Here the fragment is added to the activity
}
Bundle extras = getIntent().getExtras();
if (extras != null) {
idEstablecimiento = extras.getInt("idEstablecimiento");
}
TextView textView = (TextView) this.findViewById(R.id.nombreView); //Declared in fragmentDetail, which has been already inflated at this point, but still null
textView.setText(this.idEstablecimiento);
}
//...
//Non related stuff...
//...
public static class PlaceholderFragment extends Fragment {
private int idEstablecimiento;
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_detail,
container, false);
return rootView;
}
}
The problem is that the textView in the onCreate() method is null, why is this happening if the fragment has been already inflated? Shouldn't I be able to access to the IU elements once the fragment has been inflated in the onCreate() method? Or if not, what is the rigth way to do access to it?
You have to find the TextView in the Fragment.
public static class PlaceholderFragment extends Fragment {
private int idEstablecimiento;
public PlaceholderFragment() {
}
public PlaceholderFragment(int idEstablecimiento) {
this.idEstablecimiento = idEstablecimiento;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_detail,
container, false);
TextView text = rootView.findViewById(R.id.nombreView);
text.setText(idEstablecimiento);
return rootView;
}
And then you can get the text and commit it.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
Bundle extras = getIntent().getExtras();
if (extras != null) {
idEstablecimiento = extras.getInt("idEstablecimiento");
}
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment(idEstablecimiento)).commit();
}
}
Regards
Nils
If you want to get the View from a fragment layout you should use the view inside the oncreateView of the fragment and then get the textView inside the oncreate view..
example:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_detail,
container, false);
TextView textView = (TextView) rootView.this.findViewById(R.id.nombreView); //Declared in fragmentDetail, which has been already inflated at this point, but still null
textView.setText("I am here");
return rootView;
}

Categories

Resources