【问题标题】:Refreshing fragments in FragmentActivity after sync service runs同步服务运行后刷新 FragmentActivity 中的 Fragment
【发布时间】:2013-08-20 03:20:29
【问题描述】:

在从SyncAdapter 运行同步Service 后,有没有人有任何优雅的解决方案来刷新Fragments 中的Fragments 中的Views

我尝试在我的适配器上调用notifyDataSetChanged()notifyDataSetInvalidated(),在我的视图上调用refreshDrawableState() (GridViews),但无济于事。也许我一直从错误的地方给他们打电话——我试过在setUserVisibleHint 那里isVisible=true 这样做,希望在看到片段时触发它,但它不起作用。

我也一直在使用对 SQLite 数据库的异步调用来满足我的数据需求,而不是 Content Provider,我认为这会使这更容易一些。我可以想出几种不用Content Provider 的方法,但都不是很好。

有什么想法吗?如果愿意,我可以提供代码。谢谢。

【问题讨论】:

  • 这实际上取决于您首先如何加载数据。您能否解释一下您的数据是如何从数据库加载到适配器的?是光标吗?你有装载机吗?是否使用从光标加载的 ArrayList?
  • 碰巧这是我当时没有解决的问题。适配器是使用游标的子类 CursorAdapter。我不使用 ArrayList。
  • 我想看代码。

标签: android android-fragments android-viewpager android-fragmentactivity android-syncadapter


【解决方案1】:

我假设您使用 AsyncTask 来加载光标只是为了解释,但如果您使用 Loader、ThreadPool 或其他任何东西,它的工作原理是一样的。

从服务中,只要新数据发生更改,我就会发送LocalBroadcast。活动可能存在或不存在,因此广播是让它知道有新数据的好方法。所以从你会做的服务:

    // that's an example, let's say your SyncAdapter updated the album with this ID
    // but you could create a simply "mybroadcast", up to you.
    Intent i = new Intent("albumId_" + albumId);
    LocalBroadcastManager.getInstance(this).sendBroadcast(i);

然后从具有光标的活动/片段中,您将像这样收听此广播:

    public void onResume(){
        // the filter matches the broadcast
        IntentFilter filter = new IntentFilter("albumId_" + albumId); 
        LocalBroadcastManager.getInstance(this).registerReceiver(myReceiver, filter);
    }
    public void onPause(){
        LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver);
    }

    // and of course you have to create a BroadcastReceiver
    private BroadcastReceiver myReceiver = new BroadcastReceiver(){
       @Override
       public void onReceive(Context context, Intent intent){
           // here you know that your data have changed, so it's time to reload it
           reloadData = new ReloadData().execute(); // you should cancel this task onPause()
       }
    };

正如我所说,下一部分取决于您用于加载光标的线程方法,对于这个示例,我将在 AsyncTask 中展示,因为它非常受欢迎(但我真的相信您和world 应该使用 Loaders 模式)。

private class ReloadData extends AsyncTask<Void, Void, Cursor> {
 protected Cursor doInBackground(Void... void) {
     // here you query your data base and return the new cursor
     ... query ...
     return cursor;
 }

 protected void onPostExecute(Cursor result) {
     // you said you're using a subclass of CursorAdater
     // so you have the method changeCursor, that changes the cursor and closes the old one
     myAdapter.changeCursor(result);
 }

}

我之前测试和使用过的上述方法,我知道它有效。有一种方法可以使其与标志 FLAG_REGISTER_CONTENT_OBSERVER 一起使用并覆盖 onContentChanged() 以重新执行查询并交换光标,但我从未测试过它。会是这样的:

使用构造函数CursorAdapter(Context context, Cursor c, int flags) 传递标志FLAG_REGISTER_CONTENT_OBSERVER 并覆盖onContentChanged() 来初始化您的适配器。在 onContentChanged 中,您将像上面一样执行 AsyncTask。这样您就不必使用LocalBroadcastManager,因为数据库会发出警报。该方法不是我的主要答案的原因是因为我从未测试过它。

请注意,autoRequery 已被弃用,不鼓励使用,因为它在 UI 线程中执行数据加载。

编辑:

我刚刚注意到内容观察器是 API 11 的东西。您有两种选择:1 改用支持库:https://developer.android.com/reference/android/support/v4/widget/CursorAdapter.html 或广播选项。

【讨论】:

  • BroadcastReceiver 是一个类,你不能实现它。由于 Java 中不支持多重继承,因此类不能同时从 BroadcastReceiverActivity 扩展。
  • 操作。我的错!当您用心编写代码而不是先放入 IDE 时,事情就会发生。我会解决的。
  • 作者从另一个竞争对手那里复制了好东西后,答案被接受了:)
【解决方案2】:

在您拥有的片段中注册一个BroadcastReceiver,并在其onReceive 调用refresh 中注册一个refresh - 此方法应该根据您内部的内容更新UI。为了使您的代码易于使用,有一个基本的Fragment 类并在那里进行注册/注销以及将由子片段实现的抽象refresh 方法。比如:

public abstract class BaseRefreshableFragment extends Fragment {
    private BroadcastReceiver refreshReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            if ("package_name.REFRESH_ACTION".equals(intent)) {
                refresh();
            }
        }
    };

    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        IntentFilter filter = new IntentFilter("package_name.REFRESH_ACTION");
        LocalBroadcastManager.getInstance(getActivity()).registerReceiver(refreshReceiver, filter);
    }

    @Override
    public void onDestroyView() {
        LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(refreshReceiver);
        super.onDestroyView();
    }

    protected abstract void refresh();
}

在您的Service 中,当您的工作完成后,广播具有上述操作的意图。因此,如果有片段显示更新的数据,它们的接收者将收到通知,并调用refreshON EACH FRAGMENT。来自您的服务:

Intent intent = new Intent("package_name.REFRESH_ACTION");
LocalBroadcastManager.getInstance(MySyncService.this).sendBroadcast(intent);

优点是您不需要关心片段何时显示或不显示,因为接收器在片段视图的整个生命周期内都在那里。

【讨论】:

    【解决方案3】:

    使用新数据从头开始重新创建适配器,并将其重新分配给 ListView(或您拥有的任何视图)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-15
      • 2021-12-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多