简体   繁体   中英

Add fragment to programmatically generated FrameLayout

I have inherited some code that requires a change in how it works. The original way didn't have the flexibility now required.

The application is a form generator, and hence has to create the UI on demand. This is Xamarin native, not Xamarin forms.

A FrameLayout for each form question is being created programmatically, added to the view, then a fragment is being added to this FrameLayout. All this is happening AFTER OnCreateView once the UI has been loaded to show a progress circle.

After working through a bunch of exceptions, I have become stuck with the exception

Java.Lang.IllegalArgumentException: No view found for id 0x50 (unknown) for fragment UploadFragment{a31e878 #7 id=0x50 upload_80}

My guess is that the FrameLayout doesn't exist when the fragment is trying to be displayed.

The exception occurs after the OnCreate() method runs after OnCreateView() completes.

I have not been able to find any code precedent for adding FrameLayouts programmatically with Fragments.

CODE Snippet

frame = new FrameLayout(this.Context);
frame.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
upload = new Widgets.UploadFragment(control, binding, Inflater, a, xFormInstance);
MainFormLayout.AddView(frame);
frame.Id = control.id;
fragmentTx.Add(frame.Id, upload, $"upload_{control.id}");    
fragmentTx.Commit();

Any advice would be greatly appreciated. Thanks.

Extended Explanation

It may be a bit much to put in everything it does, but will try and put in as much as I can. The Hierarchy of the page is

Activity -> FormFragment -> UploadFragment

So the parent of the UploadFragment is also a fragment, not the Activity.

Upload Fragment

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
    <RelativeLayout>
        <LinearLayout>
            <TextView/>
            <ImageButton/>
        </LinearLayout>
        <ImageView/>
    </RelativeLayout>
</LinearLayout>

CODE

 public override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
        }

 public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            // Use this to return your custom view for this Fragment
            _inflater = inflater;
            v = _inflater.Inflate(Resource.Layout.BindImageInput, container, false);            
            SetUpload();
            return v;
            //return base.OnCreateView(inflater, container, savedInstanceState);
        }

SetUpload() Sets the values of the label, the events for the buttons, and the image (if exists) to the imageview. It also deals with a few extra events to do with form event handling. Stopping SetUpload() from running still has the exception occur.

FormFragment

<RelativeLayout>
    <TextView />
    <View />
    <ScrollView>
        <LinearLayout />
    </ScrollView>
</RelativeLayout>

CODE

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            ShowLoading();
            View v = inflater.Inflate(Resource.Layout.Form2, container, false);    
            MainFormLayout = v.FindViewById<LinearLayout>(Resource.Id.mainFormView);
            MainScrollView = v.FindViewById<ScrollView>(Resource.Id.mainScrollView);

            formBuilderWorker = new BackgroundWorker();
            return v;
        }

OnResume() Calls the method where formBuilderWorker.DoWork() exists

formBuilderWorker.DoWork += delegate
{
    Form.LoadForm(null, this, FormInstance);
}

LoadForm() uses a Interface to tell the FormFragment to display a control. One of which is the UploadFragment.

public void AddControl(Controls control, int? sectionID)
        {
///CODE REMOVED FOR OTHER CONTROL TYPES (they still use old codebase)
            Bindings binding = XForm.GetBindingForControl(control, FormInstance);
            try
            {
                // Create a new fragment and a transaction.
                FragmentTransaction fragmentTx = this.FragmentManager.BeginTransaction();    

                FrameLayout frame = null;    
                Widgets.UploadFragment upload = null;
                frame = new FrameLayout(this.Context);
                frame.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
                frame.Id = control.id;
                upload = new Widgets.UploadFragment(control, binding, Inflater, a, xFormInstance);                    
                MainFormLayout.AddView(frame);
                ControlViews.Add(frame);             
                fragmentTx.Replace(frame.Id, upload, $"upload_{control.id}");
                //fragmentTx.Show(upload);
                fragmentTx.Commit();


            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

This is cleaned code to remove as much irrelevant code as possible. The code shown is the path the code in question moves through.

I found the issue. Part of what I took out of the code above, was the Activity.RunOnUiThread() calls that add the frame to the main view. The issue was caused by Thread Timing. The UI thread was taking so long to add the frame to the view, that when the FragmentTransaction was trying to commit the changes, the frame still did not exist.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM