如何在片段创建之前使用不可用的数据填充片段?

时间:2016-07-05 14:21:22

标签: android performance android-layout android-fragments

我有一个活动通过WebService抓取数据,从那里创建显示数据的元素。一些数据被分组,因此我的解决方案是在主布局下方的fragments中显示分组数据,允许用户在组中滑动,可能在顶部有一个选项卡以显示组名。

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

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

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

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

对于所有这些,更好的解决方案是什么?我也有一个记忆泄漏我还没有被诊断出来让事情变得更加令人头疼。

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

2 个答案:

答案 0 :(得分:1)

我不建议您等到显示视图的数据,因为它会影响用户体验并且看起来很迟钝。

相反,您可以在AsyncTaskLoader中实施fragment,这样一旦从服务器获取数据,就可以通过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();
    }
 //...
}

答案 1 :(得分:0)

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

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

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