I have an Android application written in Xamarin/C#. The MainActivity has a plot (using Oxyplot) and a ListView. When the app is launched, the ListView is populated with some information from a SQLite database. The plot is a barchart showing the quantities of the products shown in the ListView. When I click a button, a DialogFragment pops up and gives me a form to add a new item or to change an existing one. Say I change one item (for example the quantity) and this will update the record in the database.
The DialogFragment does not fill the whole space so you can see part of the MainActivity in the background. I want both the ListView and the plot on the MainActivity to update so that when the information is updated in the dialog fragment, I can see the plot and the ListView showing the updated information behind the open DialogFragment. And in general, when I dismiss the fragment I have the list in MainActivity already updated.
I know I can start a new intent for the MainActivity from the dialog fragment:
var intent = new Intent(Activity, typeof(MainActivity));
StartActivity(intent);
but what I want to achieve is some kind of background update of the plot and ListView in the MainActivity so that I can see them changing in background even when the DialogFragment is open.
Let me focus on the ListView update only. My MainActivity is:
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();
}
}
So the information read from the db is put in a list of object dbProd (which have properties like name/quantity/price). This list is passed to my custom adapter (see below) and the ListView shows them. The button click opens a DialogFragment that has few EditText to input name, quantity and price.
I pass the ListView and the custom adapter to this DialogFragment with the hopeI can update them when the DialogFragment is still on, so that when it gets dismissed the ListView on MainActivity is already updated. The DialogFragment is:
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();
}
}
The custom adapter and its updateAdapter() method are:
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;
}
}
So basically the process is pretty simple. A ListView in MainAvtivity is showing information from a DB. When a DialogFragment is opened, the user can insert new information in the DB and when the DialogFragment is closed I expect the ListView in MainActivity to reflect already the changes. For this reason I thought about passing the ListView and its adapter to the DialogFragment so that I can call the updateAdapter() method and notify the ListView about the change in the underlying dataset. I am not sure what is wrong with the above, but it doesn't work. At the moment I can only start a new intent and basically recreate the MainActivity. This will naturally refresh the ListView. But I would like to avoid using the StartActivity(intent) approach, given it is slower.