从DialogFragment(Xamarin)

时间:2018-03-10 20:40:53

标签: android listview xamarin xamarin.forms xamarin.android

我有一个用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)方法,因为它较慢。

3 个答案:

答案 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();

}

已关联:Data between Activity - Fragment