Android - 返回时的空列表视图

时间:2012-11-08 09:46:48

标签: java android android-listview simplecursoradapter textwatcher

我通过ContentProvider和SimpleCursorAdapter加载ListView。我添加了一个TextWatcher来过滤结果。这工作正常,但是当我点击ListItem加载另一个片段后,当我回击时,我得到一个空的ListView。任何想法为什么会发生这种情况?

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.text.Editable;
import android.text.Html;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FilterQueryProvider;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Toast;

import com.actionbarsherlock.app.SherlockListFragment;
import com.brndwgn.RequestHelper.GetResponse;
import com.brndwgn.database.BlogContentProvider;
import com.brndwgn.database.BlogTable;
import com.handmark.pulltorefresh.library.PullToRefreshBase;
import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener;
import com.handmark.pulltorefresh.library.PullToRefreshListView;

public class NewsFragment extends SherlockListFragment implements LoaderCallbacks<Cursor> {

    public boolean isNetworkAvailable() {

        ConnectivityManager connectivityManager = (ConnectivityManager) parent.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();

        return activeNetworkInfo != null;

    }   

    private SimpleCursorAdapter adapter;

    private boolean dataRetrieved;

    private SlidingArea parent;

    PullToRefreshListView pullToRefreshView;

    EditText searchBox;

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        parent = (SlidingArea) getActivity();

        setHasOptionsMenu(true);    

        parent.getSupportActionBar().setCustomView(R.layout.actionbar_news_list);
        parent.getSupportActionBar().setDisplayShowCustomEnabled(true);
        parent.getSupportActionBar().setDisplayShowTitleEnabled(false);
        parent.getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        final ImageView searchButton = (ImageView) parent.findViewById(R.id.image_search_list);

        searchButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                searchBox.setVisibility(View.VISIBLE);            }

        });

        final ImageView refreshButton = (ImageView) parent.findViewById(R.id.image_refresh_list);

        refreshButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                getData(getString(R.string.json_get_posts_url), true);
                refreshButton.setImageResource(R.drawable.menu_refresh_dark);
                fillData();
            }

        });
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View V = inflater.inflate(R.layout.fragment_news, null);
        pullToRefreshView = (PullToRefreshListView) V.findViewById(R.id.pull_to_refresh_listview);
        searchBox = (EditText)V.findViewById(R.id.edittext_search);

        return V;
    }



    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onActivityCreated(savedInstanceState);

        pullToRefreshView.setOnRefreshListener(new OnRefreshListener<ListView>() {

            @Override
            public void onRefresh(PullToRefreshBase<ListView> refreshView) {
                // Do work to refresh the list here.
                getData(getString(R.string.json_get_posts_url), false);
            }

        });

    }

    @Override
    public void onResume() {
        super.onResume();

        fillData();

        parent.getSupportActionBar().setCustomView(R.layout.actionbar_news_list);
        parent.getSupportActionBar().setDisplayShowCustomEnabled(true);
        parent.getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        final ImageView searchButton = (ImageView) parent.findViewById(R.id.image_search_list);

        searchButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                searchBox.setVisibility(View.VISIBLE);            
            }

        });


        final ImageView refreshButton = (ImageView) parent.findViewById(R.id.image_refresh_list);

        refreshButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                getData(getString(R.string.json_get_posts_url), true);
                refreshButton.setImageResource(R.drawable.menu_refresh_dark);
                fillData();
            }

        }); 

        adapter.setFilterQueryProvider(new FilterQueryProvider() {

            public Cursor runQuery(CharSequence constraint) {

                String[] projection = new String[] { BlogTable.TITLE_PLAIN, BlogTable.DATE_MODIFIED, BlogTable.EXCERPT, BlogTable.ID };  
                return parent.getContentResolver().query(BlogContentProvider.CONTENT_URI, projection, BlogTable.TITLE_PLAIN  + " LIKE '%"+constraint+"%'", null, BlogTable.DATE_MODIFIED + " DESC");

          }

        }); 

        searchBox.addTextChangedListener(new TextWatcher() {

            @Override
            public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
                // When user changed the Text

                adapter.getFilter().filter(cs);
            }

            @Override
            public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
                    int arg3) {
                // TODO Auto-generated method stub

            }

            @Override
            public void afterTextChanged(Editable s) {
                // TODO Auto-generated method stub

            }
        });
    }

    //Constant used as key for ID being passed in the bundle between fragments 
    public static final String NEWS_ID = "newsID";

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {


        Bundle detailBundle = new Bundle();
        detailBundle.putLong(NEWS_ID, id);

        FragmentTransaction ft = parent.getSupportFragmentManager().beginTransaction();

        //Instance must be created here to setArguments rather then in the replace() method
        NewsDetailFragment newsDetailFragment = new NewsDetailFragment();
        newsDetailFragment.setArguments(detailBundle);

        ft.replace(R.id.content_frame, newsDetailFragment);
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        ft.addToBackStack(null);
        ft.commit();

    }    

    private void getData(String url, boolean showProgressDialog) {
        new Request(showProgressDialog).execute(new String[] {url});    
    }

    public class Request extends AsyncTask<String, Void, String> {

        ProgressDialog dialog;

        /* This is the only file that needs to be edited */

        private GetResponse response = null;

        private boolean showProgressDialog = true;

        public Request(boolean showProgressDialog)
        {
            super();
            this.showProgressDialog = showProgressDialog;
            response = new GetResponse();
        }   

        @Override
        protected void onPreExecute() {

            if (showProgressDialog) {
                dialog = new ProgressDialog(parent);
                dialog.setMessage("Loading stuff to read...");
                dialog.setIndeterminate(true);
                dialog.setCancelable(false);
                dialog.show();
            }
        }

        //This method must return the type specified in the constructor
        @Override
        protected String doInBackground(String... url) {
            response.setUrl(url[0]);

            String res = response.execute();

            // When it returns the "res" it will call onPostExecute
            return res;
        }

        @Override
        protected void onPostExecute(String result) {

            // Here we have response from server
            if ( isNetworkAvailable() ){

                try {

                    JSONObject json = new JSONObject(result);
                    JSONArray arr = json.getJSONArray("posts");

                    for (int i = arr.length() - 1; i >= 0; --i) {
                        JSONObject row = arr.getJSONObject(i);

                        if (row.getString("type").equals("post")) {

                            if (row.getString("status").equals("publish")) {

                                ContentValues values = new ContentValues();
                                values.put(BlogTable.POST_ID, row.getString("id"));
                                values.put(BlogTable.URL, row.getString(BlogTable.URL));
                                values.put(BlogTable.TITLE, row.getString(BlogTable.TITLE));
                                values.put(BlogTable.TITLE_PLAIN, Html.fromHtml(row.getString(BlogTable.TITLE_PLAIN)).toString());
                                values.put(BlogTable.CONTENT, row.getString(BlogTable.CONTENT));
                                values.put(BlogTable.EXCERPT, Html.fromHtml(row.getString(BlogTable.EXCERPT)).toString());
                                values.put(BlogTable.DATE, row.getString(BlogTable.DATE));
                                values.put(BlogTable.DATE_MODIFIED, row.getString(BlogTable.DATE_MODIFIED));

                                //getAuthorID
                                JSONObject objAuthor = row.getJSONObject("author");
                                values.put(BlogTable.AUTHOR_ID, objAuthor.getInt("id"));

                                //Check to insert new author

                                //getThumbnail
                                byte[] image = AppHelper.getBlobFromURL(row.getString(BlogTable.THUMBNAIL));
                                if (image != null) {

                                    values.put(BlogTable.DATE_MODIFIED, image);

                                }

                                //isFavourite
                                values.put(BlogTable.IS_FAVOURITE, 0);

                                // when this is called here it is referring to the activity
                                parent.getContentResolver().insert(BlogContentProvider.CONTENT_URI, values);    

                                dataRetrieved = true;

                            }
                            else {

                                String currentID = row.getString("id");

                                // Delete unpublished fields
                                parent.getContentResolver().delete(BlogContentProvider.CONTENT_URI, "_id = " + currentID, null);

                            }
                        }
                    }
                } catch (JSONException e) {

                    e.printStackTrace();

                }
            }
            else {

                Toast toast = Toast.makeText( parent , "Oops! There is no connection to the Internet", Toast.LENGTH_SHORT);
                toast.show();

            }           

            // Call onRefreshComplete when the list has been refreshed.
            pullToRefreshView.onRefreshComplete();
            super.onPostExecute(result);

            ImageView refreshButton = (ImageView) parent.findViewById(R.id.image_refresh_list);
            refreshButton.setImageResource(R.drawable.menu_refresh);

            if (showProgressDialog) {
                dialog.dismiss();   
            }
        }

    }

    @Override
    public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {

        String[] projection = new String[] { BlogTable.TITLE_PLAIN, BlogTable.DATE_MODIFIED, BlogTable.EXCERPT, BlogTable.ID };

        CursorLoader loader = new CursorLoader(parent, BlogContentProvider.CONTENT_URI, projection, null, null, BlogTable.DATE_MODIFIED + " DESC");

        return loader;
    }

    private void fillData(){

        //_id is expected from this method that is why we used it earlier
        String[] from = new String[] { BlogTable.TITLE_PLAIN, BlogTable.DATE_MODIFIED, BlogTable.EXCERPT};
        int[] to = new int[] { R.id.text_news_title, R.id.text_news_date, R.id.text_news_excerpt};

        //initialize loader to call this class with a callback
        getLoaderManager().initLoader(0, null, this);

        //We create adapter to fill list with data, but we don't provide any data as it will be done by loader
        adapter = new SimpleCursorAdapter(parent, R.layout.news_list_view, null, from, to, 0);

        setListAdapter(adapter);
    }

    public void onLoadFinished(Loader<Cursor> arg0, Cursor data) {
        adapter.swapCursor(data);

    }

    public void onLoaderReset(Loader<Cursor> arg0) {
        adapter.swapCursor(null);       
    }
}

1 个答案:

答案 0 :(得分:1)

适配器看到的内容:

您观察空ListView的原因是因为返回的Cursor的位置是 STILL 指向最后一个元素。当您将Cursor swap()转换为适配器时,适配器会尝试使用while(Cursor.moveToNext())循环进行迭代。因为该循环总是计算FALSE,所以ListView会给你一个空Cursor的错觉。

Cursor.getCount()中打印Cursor.getPosition()onLoadFinished()的值。如果我是正确的,这两个值应该相等。这种索引冲突造成了上述错觉。

为什么适配器会看到:

加载器将尽可能重用光标。如果您为一组未更改的数据请求Loader,则加载程序是智能的并通过onLoadFinished返回Cursor而不执行任何附加工作,甚至不将Cursor的位置设置为-1。

ANS 手动调用Cursor.moveToPosition(-1)中的onLoadFinished()以解决此问题。