I'm new to Xamarin and new to Android development (this is my first week coming from a windows background). I have an adapter that I'm attempting to add to a listview but I'm not sure where to place my code. I have multiple fragments being inflated from a flyout menu. I attempted placing the code in the fragment itself but this caused compilation errors, I also attempted placing the code in the main activity. Neither seemed to work correctly.
This is the code segment
var items = new String[] { "test", "test2" };
ArrayAdapter<string> adapter = new ArrayAdapter<string> (this, Android.Resource.Layout.SimpleListItem1, items);
var listViewMeds = FindViewById<ListView> (Resource.Id.medicationListView);
listViewMeds.Adapter = adapter;
You will do this in the OnCreateView inside the fragment.
eg
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = inflater.Inflate(Resource.Layout.MyLayoutId, container, false);
ListAdapter = new MyAdapter(Activity, Items);
return view;
}
Note that your fragment will need to be a ListFragment to be able to access the ListAdapter property.
Alternatively, if you don't have a ListFragment you can find your list view in the OnCreateView, eg.
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = inflater.Inflate(Resource.Layout.MyLayoutId, container, false);
var myListView = view.FindViewById<ListView>(Resource.Id.MyListViewId);
myListView.ListAdapter = new MyAdapter(Activity, Items);
}
Related
I have a an Android fragment which essentially displays a custom ListView. My Fragment looks like this:
public class ResultSummaryFragment : Android.Support.V4.App.Fragment
{
private List<ResultSummary> data;
public override async void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
ISharedPreferences preferences = Application.Context.GetSharedPreferences("UserInfo", FileCreationMode.Private);
string id = preferences.GetString("ID", string.Empty);
if (id != null)
{
RunnerData RunnerData = new RunnerData(id);
// This is not happening before OnCreateView?
data = await RunnerData.GetAllResults();
}
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
Context context = Application.Context;
View view = inflater.Inflate(Resource.Layout.results_summary_view, container, false);
ListView listview = view.FindViewById<ListView>(Resource.Id.AllResultsListView);
//data is being passed in as null...
ResultsSummaryListAdapter adapter = new ResultsSummaryListAdapter(context, data);
listview.Adapter = adapter;
return view;
}
}
The problem is that data being passed into ResultsSummaryListAdapter is null because the OnCreateView() code seems to get executed before data = await RunnerData.GetAllResults(); returns. I'm absolutely sure that this does return data (it can take time due to download time).
What am I doing wrong here?
I feel like a better way to handle this would be to instantiate the fragment, start getting the data and then trigger the below code:
ResultsSummaryListAdapter adapter = new ResultsSummaryListAdapter(context, data);
listview.Adapter = adapter;
I'm not sure how to go about that in Android.
Please refer to the below links on how to use Fragments to achieve your requirements.
https://learn.microsoft.com/en-us/xamarin/android/platform/fragments/implementing-with-fragments/
The below link gives you an overview of fragments and is well explained.
https://learn.microsoft.com/en-us/xamarin/android/platform/fragments/
Also, the attached image will explain to you the life cycle of fragments.
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.
How can I assign a drawable png to ImageView in Fragment?
I declare an ImageView in tab's layout:
ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content "
android:layout_rowSpan="2"
android:id ="#+id/iv_icon"
The tab class is here:
class tabFragment1 : V4Fragment
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved)
{
var v = inflater.Inflate(Resource.Layout.tabLayout1, container, false);
return v;
}
}
I want to assign an .png from drawable folder , something like this:
ImageView iv_icon = FindViewById(Resource.Id.iv_icon);
iv_icon.SetImageResource(Resource.Drawable.ic_weather_cloudy_white_48dp);
If I put it in the MainActivity OnCreate() method (below the ViewPager initialisation, of course), I get NullRefenceException Error (on the second row).
The question is: in which class should I put these statements?
Thanks!
In your Fragment OnCreateView once you had inflated the view/layout you can access its ui elements, in your case your ImageView.
Modify this method:
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved)
{
var v = inflater.Inflate(Resource.Layout.tabLayout1, container, false);
ImageView iv_icon = v.FindViewById(Resource.Id.iv_icon);
iv_icon.SetImageResource(Resource.Drawable.ic_weather_cloudy_white_48dp);
return v;
}
Hope this helps.-
The problem lies in the instantiation of your ImageView. You can't just call FindViewById, you have to call FindViewById from your fragment reference. Such as:
ImageView iv_icon = fragmentName.FindViewById(Resource.Id.iv_icon);
//fragmentName is the reference to your fragment *after its been inflated*
The exception is being thrown because your trying to call SetImageResource from an ImageView that was instantiated improperly. The ImageView is null and hence a NullReferenceException is being thrown.
On a side note, inflation of a view from an Activity will work as follows:
View v = FindViewById(Resource.Id.X);
FindViewById is a convenience method so that you don't have to keep calling activityName.FindViewById(). This convenience does not exist for fragments.
In summary, inflating a view from an Activity:
View v = FindViewById(Resource.Id.X);
Inflating a view from a fragment:
View v = fragmentName.FindViewById(Resource.Id.X);
I am using a customized list view in my application. I have list of items to be displayed. For each cell I need to show the "Title of the Item", "Description of the Item", and "Count".
The data is displayed correctly when list is launched for the first time. When I scroll the list the count of each cell item shuffle with each other, whereas the title and description is displayed correctly only count gets shuffle when scrolled.
adapter class code for GetView()
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = tableItems[position];
ViewHolder holder;
View view = convertView;
LayoutInflater layoutInflator = LayoutInflater.From(mContext);
if(view==null)
{
view = layoutInflator.Inflate(Resource.Layout.list_cell, null);
holder=new ViewHolder();
holder.mTvTitleName = view.FindViewById<TextView>(Resource.Id.tv_title);
holder.mTvDescriptionName = view.FindViewById<TextView>(Resource.Id.tv_description);
holder.mTvCount = view.FindViewById<LinearLayout>(Resource.Id.tv_Count);
view.Tag=holder
}
else
{
holder = (ViewHolder)view.Tag;
}
return view;
}
I gone through the similar links :
Text values are changed in customized listview when scroll the listview in android?
It is suggested to use final keyword. But in C# there is no final keyword instead there is const but const variable require to be initialised with certain value.
Can anyone suggest the correct approach to do this in xamarin.android.
I'm using fragment based navigation, each fragment has it's own toolbar.
When navigating to a fragment I want the back button to display in the toolbar.
I have overridden the OnCreateView method as follows:
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var ignored = base.OnCreateView(inflater, container, savedInstanceState);
var view = this.BindingInflate(_fragmentId, null);
_toolbar = view.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
if (_toolbar != null)
{
ParentActivity.SetSupportActionBar(_toolbar);
ParentActivity.SupportActionBar.Title = _title;
ParentActivity.SupportActionBar.SetDisplayHomeAsUpEnabled(true);
_drawerToggle = new MvxActionBarDrawerToggle(
Activity,
(ParentActivity as MainView).DrawerLayout,
_toolbar,
Resource.String.drawer_open,
Resource.String.drawer_close);
(ParentActivity as MainView).DrawerLayout.AddDrawerListener(_drawerToggle);
}
return view;
}
SetDisplayHomeAsUpEnabled(true) should be changing the button to the back button, according to numerous other stack overflow answers, However this is not working as can be seen in the following screenshot:
I have checked that the SetDisplayHomeAsUpEnabled(true) line is hit when I navigate to the fragment.
For reference I am using Xamarin with MvvmCross.
How do I make change the toolbar to the up/back button when using fragment based navigation?
Solved it:
The DrawerToggle was somehow overriding the toolbar settings. Adding the following line fixes the problem.
_drawerToggle.DrawerIndicatorEnabled = false;
I'm not very familiar with Xamarin. Try setting the indicator with ParentActivity.SupportActionBar.SetHomeAsUpIndicator(Resource.Drawable.ic_menu_white_24dp);. This might help.