It's Xamarin.Android via C#.
The problem is custom adapter shows too many items in ListView if adapter was used from Fragment code. If I'll use it from MainActivity it will work fine.
I Need to show dynamic custom info inside ListView element which is located in Tab (Fragment). Each row of ListView should contain dynamic quantity of text boxes one by one in one row. For this purpose custom adapter generates GridLayout with TextView elements. Quantity of rows also is dynamic, that's why List<> was used. I tried to simplify my code as much as possible. In real life Data class is more complicated, a lot of Tabs used, etc.
Input: 1. Simple Class for some data
public class MyData
{
internal int ID;
internal string Nick;
public MyData(int _id, string _nick)
{
ID = _id;
Nick = _nick;
}
}
2. Tab1.axml is layout for our Tab we need to show. It contains only ListView element where Adapter should place data
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listView1" />
3. Grid.axml is layout which will represent every single line for ListView. It will contain dynamic quantity of TextView elements inside of GridView
<GridLayout xmlns:p1="http://schemas.android.com/apk/res/android"
p1:minWidth="25px"
p1:minHeight="25px"
p1:layout_width="match_parent"
p1:layout_height="match_parent"
p1:id="@+id/gridLayout1"
p1:columnCount="2" />
4. Main.axml is main layout which contains Tabs (only 1 for this example). fragmentContainer is our container for Tab. listViewMain is listview to check if Adapter works properly.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/linearLayout1">
<FrameLayout
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/fragmentContainer" />
<ListView
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listViewMain" />
5. MainActivity.cs contains all the code
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
//creating one main tab
ActionBar.NavigationMode = ActionBarNavigationMode.Tabs;
AddTab("Tab 1", new SampleTabFragment1());
/* //to test if Adapter works properly From MainActivtity instead of Fragment
List<MyData> listOfData = new List<MyData>();
MyData item = new MyData(1, "Nick#1");
listOfData.Add(item);
item = new MyData(2, "Nick#2");
listOfData.Add(item);
ListView table = this.FindViewById<ListView>(Resource.Id.listViewMain);
table.Adapter = new MyDataAdapter(this, listOfData);*/
}
Standard function for creating Tab:
void AddTab(string tabText, Fragment view)
{
var tab = this.ActionBar.NewTab();
tab.SetText(tabText);
// must set event handler before adding Tab
tab.TabSelected += delegate (object sender, ActionBar.TabEventArgs e)
{
var fragment = this.FragmentManager.FindFragmentById(Resource.Id.fragmentContainer);
if (fragment != null)
e.FragmentTransaction.Remove(fragment);
e.FragmentTransaction.Add(Resource.Id.fragmentContainer, view);
};
tab.TabUnselected += delegate (object sender, ActionBar.TabEventArgs e)
{
e.FragmentTransaction.Remove(view);
};
this.ActionBar.AddTab(tab);
}
Fragment for Tab where we bind custom adapter to listview:
class SampleTabFragment1 : Fragment
{
public override void OnDestroyView()
{
base.OnDestroyView();
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
var view = inflater.Inflate(Resource.Layout.Tab1, container, false);
//Creating our data
List <MyData> listOfData = new List<MyData>();
MyData item = new MyData(1, "Nick#1");
listOfData.Add(item);
item = new MyData(2, "Nick#2");
listOfData.Add(item);
//Find listview from Tab1.axml which is actually our Tab Fragment
ListView table = view.FindViewById<ListView>(Resource.Id.listView1);
//Binding listview with custom adapter
table.Adapter = new MyDataAdapter(this.Activity, listOfData);
return view;
}
}
Finally, adapter for our custom data:
public class MyDataAdapter : BaseAdapter<MyData>
{
Activity context;
List<MyData> list;
public MyDataAdapter(Activity _context, List<MyData> _list)
{
context = _context;
list = _list;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
// re-use an existing view, if one is available
// otherwise create a new one
if (view == null)
view = context.LayoutInflater.Inflate(Resource.Layout.Grid, parent, false);
//find GridView from Grid.axml and set columns count to 2 (only ID and Nick)
GridLayout tableView = view.FindViewById<GridLayout>(Resource.Id.gridLayout1);
tableView.ColumnCount = 2;
//Creating TextView to show ID, place position is 0 - first
TextView textCaption = new TextView(view.Context);
textCaption.SetText(list[position].ID.ToString(), TextView.BufferType.Normal);
tableView.AddView(textCaption, 0);
//Creating TextView to show ID, place position is 1 - second in a row of Grid
textCaption = new TextView(view.Context);
textCaption.SetText(list[position].Nick, TextView.BufferType.Normal);
tableView.AddView(textCaption, 1);
return view;
}
public override int Count
{
get { return list.Count; }
}
public override long GetItemId(int position)
{
return position;
}
public override MyData this[int index]
{
get { return list[index]; }
}
}
All of this should show to end-user two rows of ListView. First row contains GridView with two TextView elements "1" and "Nick#1" in one row. Second row should contains GridView with "2" and "Nick#2" in one row like this: calling from MainActivity
...but instead each of 2 ListView rows contains a lot of GridView rows with data: calling from Fragment
If you will disable Tab by commenting
//ActionBar.NavigationMode = ActionBarNavigationMode.Tabs;
//AddTab("Tab 1", new SampleTabFragment1());
and uncommenting area of code from MainActivity.cs
//to test if Adapter works properly without Fragment
it will show correct result using exactly the same Custom Adapter and Layouts structure as it was used from Fragment SampleTabFragment1() call. Look like there are some tricks about using Fragments with custom Adapter and\or layouts, what do you think?