从对话片段 (Xamarin) 更新数据库中的记录时更新列表视图和绘图



我有一个用Xamarin/C#编写的Android应用程序。MainActivity 有一个绘图(使用 Oxyplot)和一个列表视图。 启动应用时,将使用 SQLite 数据库中的一些信息填充列表视图。该图是一个条形图,显示列表视图中显示的产品数量。 当我单击按钮时,会弹出一个对话片段,并给我一个窗体来添加新项目或更改现有项目。假设我更改了一个项目(例如数量),这将更新数据库中的记录。

DialogFragment 不会填满整个空间,因此您可以在后台看到部分 MainActivity。 我希望 ListView 和 MainActivity 上的绘图都更新,以便在对话框片段中更新信息时,我可以看到绘图和列表视图在打开的对话片段后面显示更新的信息。一般来说,当我关闭片段时,我已经更新了 MainActivity 中的列表。

我知道我可以从对话框片段中为 MainActivity 开始一个新的意图:

var intent = new Intent(Activity, typeof(MainActivity));
StartActivity(intent);

但我想要实现的是主活动中情节和列表视图的某种背景更新,以便即使 DialogFragment 打开,我也可以看到它们在后台发生变化。

让我只关注列表视图更新。我的主要活动是:

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();
}
}

因此,从数据库读取的信息被放在对象 dbProd 列表中(具有名称/数量/价格等属性)。此列表将传递给我的自定义适配器(见下文),列表视图显示它们。 单击按钮会打开一个对话框片段,该对话片段几乎没有用于输入名称、数量和价格的编辑文本。

我将列表视图和自定义适配器传递给此对话片段,希望我可以在对话片段仍打开时更新它们,以便当它被关闭时,主活动上的列表视图已经更新。对话片段是:

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 显示来自数据库的信息。当对话框片段被打开时,用户可以在数据库中插入新信息,当对话片段关闭时,我希望MainActivity中的ListView已经反映这些更改。 出于这个原因,我想过将ListView及其适配器传递给DialogFragment,以便我可以调用updateAdapter()方法并通知ListView有关基础数据集中的更改。 我不确定上述有什么问题,但它不起作用。 目前,我只能开始一个新的意图,基本上重新创建MainActivity。这将自然刷新列表视图。但我想避免使用StartActivity(意图)方法,因为它更慢。

您应该更改行的优先级

首先,你的代码应该是这样的

MyList.Adapter = MyAdapter;

MyAdapter.updateAdapter();

而不是在更新后分配适配器

将列表视图的实例及其适配器从我对事物的理解中携带到您的对话片段将是一种不好的做法,我通常为此类实现所做的是一些聪明的工作。

在我的活动简历中,如下所示:

protected override void OnResume()
{
base.OnResume();
}

我添加更新我的列表视图适配器,以便它具有来自我的SQLite数据库的最新数据,然后将更新的适配器分配给我的列表视图,瞧它工作。

准确地说是这样的:

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();
}

如果它不起作用,请告诉我。

ListviewMainActivity的 UI 只会在其Listview-Adapter的传递列表值更改时更改。

Adapter.NotifyDataSetChanged()只会通知适配器的更新。除非未更改您传递的同一列表,否则它不会更新 UI。您已经编写了调用更新方法的复杂代码,并且NotifyDataSetChanged()

请注意,在Fragment内,您需要设置列表值的值prods适配器正在为其获取数据MainActivity的值。

db.Insert(new dbProd(name.Text, quantity.Text, price.Text));只是更新数据库,您需要以编程方式指示适配器通过更新传递的值并调用NotifyDataSetChanged来更改它。

无需反复打电话updateAdapter.它是多余的。

prods全球化到MainActivity

prods = new List<dbProd>();声明此外部更新列表

编写一个方法,以便在片段更改数据库值时更新此值

public void UpdateDBValuesForLV(List<dbprod> newValues)
{
prods = newValues;
adapter.NotifyDataSetChanged();
}

从片段更新这个。


注意:不要通过设置全局变量直接更新。

//after update retrive DB values again
prods = new List<dbProd>();
foreach (var prod in table)
{
prods.Add(prod);
}
((MyActivity) getActivity()).UpdateDBValuesForLV(prods);

链接:活动 - 片段之间的数据

最新更新