【发布时间】:2017-03-16 01:15:13
【问题描述】:
它是通过 C# 实现的 Xamarin.Android。
如果从 Fragment 代码中使用适配器,则问题是自定义适配器在 ListView 中显示的项目过多。如果我从 MainActivity 中使用它,它会正常工作。
我需要在位于 Tab (Fragment) 中的 ListView 元素中显示动态自定义信息。 ListView 的每一行都应该包含动态数量的文本框,一排一排。为此,自定义 adapter 生成带有 TextView 元素的 GridLayout。行数也是动态的,这就是使用 List 的原因。我试图尽可能地简化我的代码。现实生活中Data类比较复杂,使用了很多Tab等。
输入: 1. 一些数据的简单类
public class MyData
{
internal int ID;
internal string Nick;
public MyData(int _id, string _nick)
{
ID = _id;
Nick = _nick;
}
}
2。 Tab1.axml 是我们需要显示的 Tab 的布局。它只包含 Adapter 应该放置数据的 ListView 元素
<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 是一个布局,它将代表 ListView 的每一行。它将在 GridView 内包含动态数量的 TextView 元素
<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 是包含 Tabs 的主布局(本例中只有 1 个)。 fragmentContainer 是我们的 Tab 容器。 listViewMain 是检查Adapter是否正常工作的listview。
<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 包含所有代码
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);*/
}
创建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);
}
我们将自定义适配器绑定到列表视图的 Tab 片段:
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;
}
}
最后,我们自定义数据的适配器:
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]; }
}
}
所有这些都应该向最终用户显示两行 ListView。第一行包含 GridView,其中一行中有两个 TextView 元素“1”和“Nick#1”。第二行应包含 GridView,其中一行包含“2”和“Nick#2”,如下所示: calling from MainActivity
...但是 2 个 ListView 行中的每一行都包含大量带有数据的 GridView 行: calling from Fragment
如果您将通过评论禁用 Tab
//ActionBar.NavigationMode = ActionBarNavigationMode.Tabs;
//AddTab("Tab 1", new SampleTabFragment1());
并取消注释 MainActivity.cs 中的代码区域
//to test if Adapter works properly without Fragment
它将使用与片段 SampleTabFragment1() 调用中使用的完全相同的自定义适配器和布局结构显示正确的结果。 看起来有一些关于使用带有自定义适配器和/或布局的片段的技巧,你怎么看?
【问题讨论】:
-
我认为您可能想再看一下片段的设计理念 - developer.android.com/guide/components/fragments.html 看起来您可能正在“嵌套”布局,其中您的预期列表被多次添加。简化您的布局,以确保您有一个简单的
FrameLayout,您可以将片段插入其中。然后,您应该在list.Count上设置断点并确保它只有 2 个项目。然后检查是否也填充了其他控件。 -
但是已经尽可能的简化了:Main Layout 包含 FragmentContainer for Fragment。 Fragment 的布局非常简单 - 只是 ListView。而已。网格布局仅用于自定义列表适配器,效果很好。问题是 list.Count 属性没问题,==2。但由于某种原因,它从 Fragment 调用了几次,这是不正确的。如果使用 MainActivity 而不是 Fragment,则调用正确。
-
我更喜欢使用
FragmentTransaction.Replace(),因为添加和删除片段有时很容易出错,除非您了解生命周期。尝试将该代码重构为Replace()! -
看起来问题是
ListView回收机制是如何工作的。这里some info 但在我的情况下,我什至不能将match_parent用于layout_height的ListView。相反应该使用固定值。无论如何,使用固定大小不是解决方案,因为我所有的数据都是动态的,而且使用固定大小根本不是一个好主意。看起来需要看看 ArrayAdapter Android 实现,并尝试理解“神奇”为什么它适用于 Fragment(如果大小不固定)以及为什么所有自定义适配器都不能...
标签: c# android android-fragments gridview xamarin