【问题标题】:How can I populate fragments with data that aren't available until after the fragment's creation?如何使用在片段创建之前不可用的数据填充片段?
【发布时间】:2016-07-06 05:38:50
【问题描述】:

我有一个通过WebService 获取数据的活动,它从那里创建元素来显示数据。一些数据是分组的,所以我的解决方案是在主布局下方他们自己的fragments 中显示分组数据,允许用户在组之间滑动,可能在顶部有一个选项卡来显示组名称。

我遇到的问题是活动中的片段是在网络调用发生之前创建的,使它们为空或使用旧数据。然后我创建了一个sharedpreferences 监听器并将fragments 布局创建方法放入其中。主要方法抓取数据,写入 sharedpreferences 片段检测更改并创建它的布局,或者我认为。

项目之间的某些组是相同的,因此从一个组移动到另一个不会触发 onchange 事件,因此不会触发布局创建方法。然后我决定执行以下操作,以便在写入 sharedpreferences 后始终触发 onchange 事件

final Boolean updated = settings.getBoolean("UPDATED_1", false);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("UPDATED_" + pageNum, !updated);

我只是不认为这是最好的解决方案,它也有问题并且不是每次都触发(我还没有排除故障)

对于这一切有什么更好的解决方案?我还有一个尚未诊断出的内存泄漏,这让事情变得更加头疼。

我刚刚想过将我的数据抓取方法移到 ViewPager 初始化之前,但我不确定这是否能解决我的问题。

【问题讨论】:

  • 尝试将片段的容器视图的可见性设置为消失,然后在数据准备好时将其设置回可见
  • 我所做的是在我的 Fragment 模板中有一个公共方法,在我拥有一组数据后我会在我的主要活动中调用它。因为这些(对我来说)在一个 FragmentPagerAdapter 中,它接受片段列表,所以我传递这样的数据:((MyFragmentClass)ListofFragments.get(i))。 -片段的方法-。然后我只需要调用适配器的 notifyDataSetChanged() 即可。这与共享偏好无关,但也许它可以帮助你,因为它在类似情况下对我有用。
  • @VikingPingvin 甚至在片段创建之前就不能执行任务吗?
  • @kimchibooty 会将其设置为可见强制娱乐吗?
  • @VikingPingvin 我可以放弃 sharedpreferences 并使用您的解决方案,但我需要一些更深入的帮助来实现它

标签: android performance android-layout android-fragments


【解决方案1】:

我不建议等到您获得数据后才显示视图,因为这会影响用户体验并且看起来迟钝。

相反,您可以在fragment 中实现AsyncTaskLoader,以便在从服务器获取数据后使用BroadcastReceiver 通知片段视图。同时,只显示一个微调器,直到检索到数据,然后隐藏它并使用adapter.notifyDataSetChanged(); 更新您的列表。

这是AsyncTaskLoader 的示例(在我的例子中,它是一个数据库查询,而不是像你这样的服务器调用):

public class GenericLoader<T extends Comparable<T>> extends AsyncTaskLoader<ArrayList<T>> {
    private Class clazz;

    public GenericLoader(Context context, Class<T> clazz) {
        super(context);
        this.clazz = clazz;
    }

    @Override
    public ArrayList<T> loadInBackground() {
        ArrayList<T> data = new ArrayList<>();
        data.addAll(GenericDAO.getInstance(clazz).queryForAll());
        Collections.sort(data);
        return data;
    }
}

然后在你的片段中:

public class FragmentMobileData extends Fragment implements ListAdapter.OnItemClickListener, LoaderManager.LoaderCallbacks<ArrayList<EntityCategories.EntityCategory>> {

    public static String TAG = "FragmentMobileData";

    private ImageListAdapter adapter;
    private ArrayList<EntityList> mCategories = new ArrayList<>();

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle bundle = intent.getExtras();
            String result = bundle.getString(DatabaseService.RESULT);

            if (DatabaseService.NO_CONNECTION.equals(result)) {
                Utils.showToastMessage(getActivity(), "No internet connexion", true);
            } else if (DatabaseService.RESULT_TIMEOUT.equals(result)) {
                Utils.showToastMessage(getActivity(), "Bad connection. Retry", true);
            }
            getActivity().getSupportLoaderManager().initLoader(1, null, FragmentMobileData.this).forceLoad();
        }
    };

    @Bind(R.id.progressBarEcard)
    ProgressBar spinner;
    @Bind(R.id.list)
    RecyclerView list;
    public FragmentMobileData() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_mobile_plan, container, false);
        ButterKnife.bind(this, view);
        ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle("Mobile");

        list.setLayoutManager(new LinearLayoutManager(context));
        list.addItemDecoration(new DividerItemDecoration(context, R.drawable.divider));
        adapter = new ImageListAdapter(mCategories, this);
        list.setAdapter(adapter);

        Intent intent = new Intent(context, DatabaseService.class);
        intent.setAction(DatabaseService.UPDATE_DATA);
        getActivity().startService(intent);
        return view;
    }

    @Override
    public void onPause() {
        super.onPause();
        getActivity().unregisterReceiver(mReceiver);
    }


    @Override
    public void onResume() {
        super.onResume();
        getActivity().registerReceiver(mReceiver, new IntentFilter(DatabaseService.UPDATE_DATA));
    }

    @Override
    public Loader<ArrayList<EntityCategories.EntityCategory>> onCreateLoader(int id, Bundle args) {
        return new GenericLoader(context, EntityCategories.EntityCategory.class);
    }

    @Override
    public void onLoadFinished(Loader<ArrayList<EntityCategories.EntityCategory>> loader, ArrayList<EntityCategories.EntityCategory> data) {
        if (mCategories.size() != data.size()) {
            mCategories.clear();
            mCategories.addAll(data);
            adapter.notifyDataSetChanged();

            Intent intent = new Intent(context, DownloadFilesService.class);
            context.startService(intent);
        }
        spinner.setVisibility(View.GONE);
    }

    @Override
    public void onLoaderReset(Loader<ArrayList<EntityCategories.EntityCategory>> loader) {
        mCategories.clear();
        adapter.notifyDataSetChanged();
    }
 //...
}

【讨论】:

  • 在托管片段的类中如何使用加载器?这看起来是最好的解决方案,我可能需要一段时间才能理解它。另外,我必须动态创建文本视图来显示数据,而不是填充列表视图
  • 一开始可能看起来很吓人,但是一旦进入它就会明白,最后它是最可靠的方法。在托管 Fragment 的 Activity 中没有什么可做的......此外,动态创建 TextView 可能会使过程复杂化......有必要吗?我还在这里找到了用于服务器请求的 AsyncTaskLoader 的实现:stackoverflow.com/questions/19744022/…
  • 对于这个特定的功能,我会说它是。基本上它是地址、名称等数据。所以我会显示一个标题,然后是值。我需要的只是获取组名的片段,从那里我可以进行另一个网络调用并获取我需要的数据。 mainActivity 进行网络通话,获取组,每个片段获取它的组并获取它需要的数据。无论是那个还是 Fragment 从 webcall 中获取它的组和 json 并解析它。
  • 这个例子看起来不错,它可能会让事情更清楚一些。我想我应该将片段传递给 json 和它的组,因为解析数据比网络调用快得多。
  • 我的情况和你一样,因为我在组内组内有一组元素,每个级别都是一个包含 RecyclerView 的片段。就我而言,我将所有内容都存储在数据库中,每个片段通过数据库调用检索所需的数据。
【解决方案2】:

也许我误解了什么。但在你的情况下,我认为有一个很好的选择来创建,例如,你的片段将显示一些数据组,然后在它的创建阶段在 ui 中显示进度条,同时在后台请求数据。然后处理结果数据并显示,隐藏进度条。

这可以通过实施 MVP 模式来实现,以提供代码的灵活性和易于测试。您还可以使用 rxJava 和 Retrofit 以方便的方式处理请求。有关 MVP 和示例的更多信息,您可以找到 here

如果您出于某种原因不想提供这种方式。例如,您有未确定数量的组,将来会以某种方式收到,并且您想根据收到的数据动态构建片段,那么我建议您可以在活动中组织表示层。在这一层中,您将接收数据,然后将其传递给特殊的处理程序,该处理程序会将其划分为组,并根据它们要求活动创建片段。在构造函数中,您将发送已经接收到的数据(因此需要实现 Parcelable 接口)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多