我有一个用Xamarin / C#编写的Android应用程序。 MainActivity有一个图(使用Oxyplot)和ListView。 启动应用程序时,ListView将填充SQLite数据库中的一些信息。该图是一个条形图,显示了ListView中显示的产品数量。 当我单击一个按钮时,会弹出一个DialogFragment,并为我提供一个表单来添加新项目或更改现有项目。假设我更改了一个项目(例如数量),这将更新数据库中的记录。
DialogFragment没有填满整个空间,因此您可以在后台看到部分MainActivity。 我想要更新ListView和MainActivity上的绘图,以便在对话框片段中更新信息时,我可以看到绘图和ListView显示打开的DialogFragment背后的更新信息。一般来说,当我关闭片段时,我已经更新了MainActivity中的列表。
我知道我可以从对话框片段中启动MainActivity的新意图:
var intent = new Intent(Activity, typeof(MainActivity));
StartActivity(intent);
但我想要实现的是MainActivity中的情节和ListView的某种背景更新,这样即使DialogFragment打开,我也能看到它们在后台发生变化。
让我只关注ListView更新。我的主要活动是:
public class MainActivity : Activity
{
Button btn;
ListView myList;
private MyListViewAdapter adapter;
private SQLiteConnection db;
string dbPath = System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "dbProd.db3");
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView (Resource.Layout.Main);
myList = FindViewById<ListView>(Resource.Id.mListView);
db = new SQLiteConnection(dbPath);
btn.Click += Btn_Click;
}
protected override void OnResume()
{
base.OnResume();
updateList();
}
private void Btn_Click(object sender, EventArgs e)
{
FragmentTransaction transaction = FragmentManager.BeginTransaction();
dialog_form createProd = new dialog_form(db, myList, adapter);
createProd.Show(transaction, "dialog fragment");
}
private void updateList()
{
// read info from db
var table = from d in db.Table<dbProd>()
select d;
prods = new List<dbProd>();
foreach (var prod in table)
{
prods.Add(prod);
}
adapter = new MyListViewAdapter(this, prods, Resource.Layout.listview_row, db);
myList.Adapter = adapter;
adapter.NotifyDataSetChanged();
}
}
因此,从db读取的信息将放入对象dbProd列表中(其名称/数量/价格等属性)。此列表将传递给我的自定义适配器(请参阅下文),ListView将显示它们。 按钮单击打开一个DialogFragment,其中包含很少的EditText来输入名称,数量和价格。
我将ListView和自定义适配器传递给此DialogFragment,当DialogFragment仍然打开时,hopeI可以更新它们,这样当它被解除时,MainActivity上的ListView已经更新。 DialogFragment是:
class dialog_form : DialogFragment
{
private MyListViewAdapter Adapter;
private ListView MyList;
private SQLiteConnection db;
private EditText name;
private EditText quantity;
private EditText price;
private Button saveButton;
public dialog_form(SQLiteConnection Db, ListView myList, MyListViewAdapter adapter)
{
db = Db;
MyList = myList;
Adapter = adapter;
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
var view = inflater.Inflate(Resource.Layout.dialog_form, container, false);
Dialog.Window.RequestFeature(Android.Views.WindowFeatures.NoTitle);
name = view.FindViewById<EditText>(Resource.Id.name);
quantity = view.FindViewById<EditText>(Resource.Id.quantity);
price = view.FindViewById<EditText>(Resource.Id.price);
saveButton = view.FindViewById<Button>(Resource.Id.buttonUpdate);
saveButton.Click += SaveButton_Click;
return view;
}
private void SaveButton_Click(object sender, EventArgs e)
{
db.Insert(new dbProd(name.Text, quantity.Text, price.Text));
// with this I wanted to refresh the ListView in MainActivity, but it doesn't work
MyList.Adapter = Adapter;
Adapter.NotifyDataSetChanged();
Adapter.updateAdapter();
//var intent = new Intent(Activity, typeof(MainActivity));
//StartActivity(intent);
this.Dismiss();
}
}
自定义适配器及其updateAdapter()方法是:
class MyListViewAdapter : BaseAdapter<dbProd>
{
public List<dbProd> mItems;
private Context mContext;
private int mRowLayout;
private List<dbProd> prods;
private SQLiteConnection db;
// Default constructor
public MyListViewAdapter(Context context, List<dbProd> items, int rowLayout, SQLiteConnection Db)
{
mItems = items;
mContext = context;
mRowLayout = rowLayout;
db = Db;
}
public void updateAdapter()
{
mItems.Clear();
var table = from d in db.Table<dbProd>()
select d;
prods = new List<dbProd>();
foreach (var prod in table)
{
prods.Add(prod);
}
mItems.AddRange(prods));
NotifyDataSetChanged();
}
// Tells how many rows are in the dataset
public override int Count
{
get { return mItems.Count; }
}
// Return a row identifier
public override long GetItemId(int position)
{
return position;
}
// Return the data associated with a particular row
public override dbProd this[int position]
{
get { return mItems[position]; }
}
// Return a view for each row
public override View GetView(int position, View convertView, ViewGroup parent)
{
View row = convertView;
if (row == null)
{
row = LayoutInflater.From(mContext).Inflate(Resource.Layout.listView_rowProd, null, false);
}
TextView itemName = row.FindViewById<TextView>(Resource.Id.itemName);
itemName.Text = mItems[position].Code;
TextView quantity = row.FindViewById<TextView>(Resource.Id.quantity);
quantity.Text = mItems[position].Quantity;
TextView price = row.FindViewById<TextView>(Resource.Id.price);
price.Text = mItems[position].Price;
return row;
}
}
所以基本上这个过程非常简单。 MainAvtivity中的ListView显示来自DB的信息。打开DialogFragment时,用户可以在数据库中插入新信息,当DialogFragment关闭时,我希望MainActivity中的ListView已经反映了这些更改。 出于这个原因,我考虑将ListView及其适配器传递给DialogFragment,以便我可以调用updateAdapter()方法并通知ListView有关基础数据集中的更改。 我不确定上面有什么问题,但它不起作用。 目前我只能开始一个新的意图,并基本上重新创建MainActivity。这将自然刷新ListView。但我想避免使用StartActivity(intent)方法,因为它较慢。
答案 0 :(得分:0)
您应该更改线路的优先级
首先你的代码应该是这样的
MyList.Adapter = MyAdapter;
MyAdapter.updateAdapter();
而不是在更新后分配适配器
答案 1 :(得分:0)
从我对事物的理解中将listview及其适配器的实例传递到对话框片段将是一种不好的做法,我通常为这些实现做的是一些聪明的工作。
在我的活动的onResume中,如下所示:
protected override void OnResume()
{
base.OnResume();
}
我添加更新我的listview适配器,以便它具有来自我的SQLite数据库的最新数据,然后我将更新的适配器分配给我的listview,并且它可以工作。
准确地说是这样的:
protected override void OnResume()
{
base.OnResume();
_myListView.adapter=new myAdapterClass(); // in your case your adapter class with parameters.
//Also your listview object must be a global object for this to work as expected.
}
你也可以使用adapter.NotifyDataSetChanged();
这也可以做同样的事情。
这基本上做的是再次调用我的适配器类并更新我的数据。
您可以做的另一件事是将您的活动实例带到对话片段类
Public dialog_form (Activity activity)
{
}
然后在一个像这样的
的局部变量中得到它Public Activity _activity;
Public dialog_form (Activity activity)
{
_activity=activity;
}
然后使用此类实例调用更新活动中的函数:
Public void svbtn(Sender s, EventArgs e)
{
//Lines of code .....
_activity.yourUpdateFunction();
this.Dismiss();
}
如果它没有工作让我知道。
答案 2 :(得分:0)
MainActivity
的{{1}}用户界面只有在Listview
的传递列表值发生变化时才会更改。
Listview-Adapter
只会通知Adapter的更新。除非您传递的相同列表未更改,否则不会更新UI。您编写了复杂的代码,在任何地方调用更新方法,Adapter.NotifyDataSetChanged()
也是如此。
NotifyDataSetChanged()
中,您需要设置适配器正在获取数据的Fragment
的列表值prods
的值。 MainActivity
仅更新数据库,您需要以编程方式指示适配器通过更新传递的值并调用db.Insert(new dbProd(name.Text, quantity.Text, price.Text));
来更改它。
无需反复拨打NotifyDataSetChanged
。它是多余的。
将updateAdapter
全局设为prods
MainActivity
在updateList
当Fragment更改DB值
时,编写一个更新此方法prods = new List<dbProd>();
来自Fragment的注意: 请勿通过设置全局变量直接更新。
public void UpdateDBValuesForLV(List<dbprod> newValues)
{
prods = newValues;
adapter.NotifyDataSetChanged();
}