尽管我使用了 Adapter.notifyDataSetChanged(),但数据集更改后 Android ListView 不刷新

Posted

技术标签:

【中文标题】尽管我使用了 Adapter.notifyDataSetChanged(),但数据集更改后 Android ListView 不刷新【英文标题】:Android ListView not refreshing after dataset changed, though I have used the Adapter.notifyDataSetChanged() 【发布时间】:2016-04-10 00:58:54 【问题描述】:

我知道类似的问题已经在这里问过几次了,但没有一个可以帮助我解决我的问题,所以我只好再问一次了。

我有一个应用程序,它有一个在主活动中保存 ListView 的片段,我使用了 PullableListView,这样当我向上拖动 ListView 时,它会触发 onLoadMore () 回调方法从服务器加载更多数据。加载数据后,数据将保存到 SQLiteDB,然后由 ListView 使用以显示更新的数据。

这是我的 PullableListViewFragment.java:

public class PullableListViewFragment extends Fragment implements
    OnItemClickListener 

private ListView listView;
private PullToRefreshLayout ptrl;

private MissionDB mMissionDB;
private List<MissionEntity> mData;

private MissionListAdapter mAdapter;

/**
 * Specify the exact mission that will be displayed in MissionDetailActivity
 */
private int mIndexOfMission;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) 
    View pullableLayout = inflater.inflate(R.layout.pullable_layout, container, false);
    listView = (ListView) pullableLayout.findViewById(R.id.content_view);
    listView.setDivider(null);
    ptrl = (PullToRefreshLayout) pullableLayout.findViewById(R.id.refresh_view);
    ptrl.setOnRefreshListener(new RefreshListener());
    loadData();

    Log.d(Constants.LOG_TAG, "onCreateView Called from PullableListViewFragment");

    return pullableLayout;


/**
 * Initialise ListView
 */
@Override
public void onActivityCreated(Bundle savedInstanceState) 
    super.onActivityCreated(savedInstanceState);
    mAdapter = new MissionListAdapter(getActivity(), mData);
    listView.setAdapter(mAdapter);
    listView.setOnItemClickListener(this);
    mAdapter.notifyDataSetChanged();
    Log.d(Constants.LOG_TAG, "onActivityCreated Called from PullableListViewFragment");


/**
 * Load data from db
 */
private void loadData() 
    mData = new ArrayList<>();
    mMissionDB = MissionDB.getInstance(MyApplication.getContext());
    mData = mMissionDB.loadMission();


/**
 * OnItemClick event
 */
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
        long id) 
    Intent intent = new Intent(getActivity(), MissionDetailActivity.class);
    mIndexOfMission = mData.get((int) id).getId();
    intent.putExtra("Position", mIndexOfMission);
    startActivity(intent);



这是 RefreshListener.java:

public class RefreshListener implements PullToRefreshLayout.OnRefreshListener 

private MissionDB mMissionDB = MissionDB.getInstance(MyApplication.getContext());

@Override
public void onLoadMore(final PullToRefreshLayout pullToRefreshLayout) 
    // LoadMore
    new Handler() 

        @Override
        public void handleMessage(Message msg) 

            /** When drag up, load more mission that is older and not out of date */
            mMissionDB.open();
            int id = mMissionDB.getMaxOrMinId("MIN");
            final JSONObject oldMission = new JSONObject();
            try 
                oldMission.put("platform", "1");
                oldMission.put("more", 0);
                oldMission.put("id", id);
                oldMission.put("size", 1);
             catch (JSONException e) 
                e.printStackTrace();
            

            Thread t = new Thread(new Runnable() 

                @Override
                public void run() 
                    HttpRequest.sendHttpRequest(Constants.PULLORDRAG_TO_LOAD_MISSION_URL, oldMission, new HttpCallbackListener() 

                        @Override
                        public void onFinish(String response) 
                            MissionEntity mMission = new MissionEntity();

                            /** Save new mission to mission database */
                            try 
                                JSONObject jsonObject = new JSONObject(response);
                                JSONArray missionList = jsonObject.getJSONArray("taskList");
                                for (int i = 0; i < missionList.length(); i++) 
                                    JSONObject mission = missionList.getJSONObject(i);
                                    mMission.setId(mission.getInt("id"));
                                    mMission.setDownloadUrl(mission.getString("appPath"));
                                    mMission.setCreateTime(mission.getString("taskId"));
                                    mMission.setImageUrl(mission.getString("appImg"));
                                    mMission.setTitleName(mission.getString("appName"));
                                    mMission.setRemainTime("Remain: 1d 11h 23m 36s");
                                    mMission.setParticipant("135");
                                    mMission.setCreator("Google");
                                    mMission.setRequirement(mission.getString("description"));
                                    mMission.setRewards("TODO");
                                    mMission.setAttention("TODO");
                                    mMission.setValidDate(mission.getString("deadline"));
                                    mMission.setAccepted("0");
                                    mMission.setCollected("0");
                                    mMission.setAccomplished("0");
                                    mMissionDB.saveMission(mMission);
                                
                             catch (JSONException e) 
                                e.printStackTrace();
                            
                        

                        @Override
                        public void onError(Exception e) 
                            e.printStackTrace();
                            Log.e(Constants.LOG_TAG, "Error while loading more");
                        
                    );
                
            );

            try 
                t.start();
                t.join();
             catch (InterruptedException e) 
                e.printStackTrace();
            

            pullToRefreshLayout.loadmoreFinish(PullToRefreshLayout.SUCCEED);

            //==================Update 1==================
            ListView list = (ListView) pullToRefreshLayout.findViewById(R.id.content_view);
            List<MissionEntity> missionList = mMissionDB.loadMission();
            Log.d(Constants.LOG_TAG, "mission size" + missionList.size());
            MissionListAdapter adapter = (MissionListAdapter) list.getAdapter();
            adapter.setData(missionList);
            adapter.notifyDataSetChanged();
            list.setAdapter(adapter);
            //==================Update 1==================
        
    .sendEmptyMessageDelayed(0, 1000);



PullToRefreshLayout 是一个自定义的 RelativeLayout,它定义了一个内部接口 OnRefreshListener,一旦调用 onLoadMore() 回调方法就会调用该内部接口。

我在Fragment中使用的ListView是一个PullableListView,实现了一个可以向上拖动的Pullable界面。

我的 Pullable.java:

public interface Pullable

    /**
     * If pull up is not needed, set canPullUp to false
     * 
     * @return true if the View can pull up
     */
    boolean canPullUp();

这是片段的layout.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_
          android:layout_
          android:background="#f2f2f2"
          android:orientation="vertical">

<View
    android:layout_
    android:layout_ />

<pulltorefresh.PullToRefreshLayout
    android:id="@+id/refresh_view"
    android:layout_marginLeft="8dp"
    android:layout_marginRight="8dp"
    android:layout_
    android:layout_>

    <include layout="@layout/refresh_head"/>

    <!-- Supports all view that implemented the Pullable interface -->
    <pulltorefresh.PullableListView
        android:id="@+id/content_view"
        android:layout_
        android:layout_/>

    <include layout="@layout/load_more"/>

</pulltorefresh.PullToRefreshLayout>

</LinearLayout>

问题是当 SQLiteDB 中的数据在 onLoadMore() 方法之后发生更改时,片段中的 ListView 不会自行刷新,除非我导航到同一主活动中的另一个片段,然后再导航回来。

我已经厌倦了在这里可以找到的所有方法,但它们都无济于事。

谁能告诉我当 SQLiteDB 中的数据发生变化时如何让 ListView 自行刷新。

[更新 1] 我在 onLoadMore() 回调中添加了一些代码,从服务器获取一些数据后数据的大小保持不变,我认为这是问题所在为什么 ListVie 不刷新,有趣的是,如果我在 mMission.loadMission() 之前放一个 Thread.sleep(500),数据的大小是正确的,一切都很好。知道为什么吗?

【问题讨论】:

hmm,也许不是最聪明的解决方案,但我以前做的是刷新我的 ArrayList,用新的 ArrayList 重新加载 ArrayAdapter,然后将 ArrayAdapter 重新分配给 ListView 变量——就是这样. @Razgriz 我已经尝试过您使用的方式,但效果不佳。我所做的是在 onLoadMore() 回调方法中,在 pullToRefreshLayout.loadmoreFinish(PullToRefreshLayout.SUCCEED); 之后,我确实从数据库中手动创建了一个新的 ArrayList 并通过 findViewById 获取 ListView 的实例,并将 ArrayList 分配给适配器,然后将其设置为 ListView,但它不起作用。这件事真的让我发疯了。 嗯,这很奇怪。在创建“重新加载”ArrayAdapter 之前尝试记录 ArrayList 的大小。也许,只是也许,您无法完全加载记录...? @Razgriz ListView list = (ListView) pullToRefreshLayout.findViewById(R.id.content_view); List&lt;MissionEntity&gt; missionList = mMissionDB.loadMission(); Log.d(Constants.LOG_TAG, "mission size" + missionList.size()); MissionListAdapter adapter = (MissionListAdapter) list.getAdapter(); adapter.setData(missionList); list.setAdapter(adapter); 这是我在 loadMoreFinish() 回调后添加的,重新加载后大小仍然相同,知道为什么吗?对不起格式 【参考方案1】:

在 onLoadMore 方法中,您只需加载数据并保存到数据库中。加载数据后,您应该更新适配器的数据源并调用 adapter.notifyDataSetChanged() 以刷新列表视图。或者您可以通知片段从数据库重新加载数据。

【讨论】:

请看我的问题的评论,我在onLoadMore()回调后添加了一些代码,数据的大小还是和以前一样,你知道为什么? 尝试在 onFinish(String response) 中添加一个 List 对象,并在 for 语句中将 mMission 添加到列表中。在 for 语句之后,使用 runOnUiThread() 将所有列表附加到适配器的日期源。【参考方案2】:

确保它在首先加载更多时获取数据。而且我认为您的 mMissionDB 确实发生了变化,但是 mData 呢?您的适配器实际上使用 mData 作为数据源,因此您应该更新 mData,并调用 adapter.notifyDataSetChanged() 来刷新列表视图。希望对你有帮助。

【讨论】:

谢谢,请参考我的更新,这样更新数据集合适吗?如果正确,为什么数据集的大小保持不变? 在我看来,插入数据库可能会花费相同的时间,并且当您从数据库查询时,插入还没有完成。所以,你得到了相同的日期。 我想一件事。它从新线程获取数据对吗?你在主线程绑定数据。当您在 mainThread 更新数据时,是什么让您确保它已经在 newThread 获取数据。如果您按照我上面所说的那样做,我无法确定这是否是您处理数据的方式。这可能是问题所在。 @L.Meng 据我所知,我在主线程中获取数据,但在另一个线程中更新它。使用 Thread.join 后,我假设用于获取已更新数据的代码将在用于从网络收集数据的工作线程之后执行。不是这样吗? @Lauren.Liuling 是的,这就是问题所在,那么除了让线程休眠一段时间之外,您知道如何解决这个问题吗?

以上是关于尽管我使用了 Adapter.notifyDataSetChanged(),但数据集更改后 Android ListView 不刷新的主要内容,如果未能解决你的问题,请参考以下文章

Flutter App 从 Play 商店下载后显示空白屏幕,尽管我使用了 SHA-1

尽管我使用了 Adapter.notifyDataSetChanged(),但数据集更改后 Android ListView 不刷新

尽管我使用了有效的令牌,但 Laravel Passport 得到未经身份验证的响应

尽管我启用了 CORS,restdb.io 不会让我使用 GET 动词访问

尽管使用了 if 语句,但 RelatedObjectDoesNotExist 错误

为啥引导程序不起作用,尽管我已经加载了它等(Django 项目)?