Феншуй, MatrixCursor+SimpleCursorAdapter+CursorLoader=>

SQLite, Preferences, файлы, SD, Content Provider, XML, JSON
Ответить
ihitmani
Сообщения: 8
Зарегистрирован: 26 ноя 2014, 01:04

Феншуй, MatrixCursor+SimpleCursorAdapter+CursorLoader=>

Сообщение ihitmani » 26 ноя 2014, 01:48

задача:
-нужно сделать бесконечный список подгружаемых картинок.
-использовать CursorLoader чтоб автоматически сетились данные в адаптер, и адаптер автоматически обновлял view(феншуй)

как работает:
-вводишь строку поиска, дергается onQueryTextSubmit, строка сохраняется в SharedPreferences, дергается

Код: Выделить всё

 Loader.ForceLoadContentObserver observer#.onChange
-свой обработчик скрола списка который в нужный момент вызывает

Код: Выделить всё

 Loader.ForceLoadContentObserver observer#.onChange(true)
- loadInBackground заново (пере)использует MatrixCursor
- LoaderCallbacks вызывает adapter.swapCursor(data);

затык:
1.

Код: Выделить всё

LoaderManager.LoaderCallbacks<Cursor>#onLoadFinished(Loader<Cursor> loader, Cursor data) 
вызывается только один раз, даже если второй раз AbsListView.OnScrollListener дергает лоадер, лоадер срабатывает второй раз тоже

Код: Выделить всё

public Cursor loadInBackground()
, но onLoadFinished уже не отрабатывает
2. каждый раз когда вызывается

Код: Выделить всё

 Loader.ForceLoadContentObserver observer#.onChange 
, и записываются строка запроса, происходит обращения к локальной памяти, что не феншуй, как исправить?

Обработчик лоадера:

Код: Выделить всё

package com.example.myapp.views;


import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import com.actionbarsherlock.app.SherlockListFragment;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.widget.SearchView;
import com.example.myapp.Const;
import com.example.myapp.R;
import com.example.myapp.async.ImageCursorLoader;
import com.example.myapp.listeners.InfiniteScrollListener;
import com.example.myapp.model.ImageData;
import com.example.myapp.storage.DBFav;

/**
 * Created by v on 16.11.2014.
 */
public class SearchFragment extends SherlockListFragment implements View.OnClickListener, LoaderManager.LoaderCallbacks<Cursor>, SearchView.OnQueryTextListener {
    DBFav db;
    SimpleCursorAdapter adapter;
    SharedPreferences sp;
    String query;
    Loader.ForceLoadContentObserver observer;
    Uri NOTIFICATION_URI = Uri.parse("content://v.k");
    InfiniteScrollListener listNer = new InfiniteScrollListener() {
        @Override
        public void onLoadMore(int page, int totalItemsCount) {
            observer.onChange(true);
        }
    };

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        ImageCursorLoader loader = null;
        if (id == Const.LOADER_SEARCH_ID) {

            loader = new ImageCursorLoader(getSherlockActivity(), args);
            Log.d(getClass().getName(), "onCreateLoader: " + loader.hashCode());
            observer = loader.new ForceLoadContentObserver();
        }
        return loader;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        Log.d(getClass().getName(), "onLoadFinished for loader " + loader.hashCode()
                + ", result = ");
        if (adapter.getCursor() != null)
            adapter.notifyDataSetChanged();

        else {
            adapter.swapCursor(data);

        }

    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        Log.d(getClass().getName(), "onLoaderReset for loader " + loader.hashCode());
        adapter.swapCursor(null);
        observer = null;
    }


    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        sp = PreferenceManager.getDefaultSharedPreferences(getSherlockActivity());
        if (adapter != null) {
            setListAdapter(adapter);
            getListView().setOnScrollListener(listNer);
            return;
        }
        setHasOptionsMenu(true);

        db = new DBFav(this.getActivity());
        db.open();
        query = sp.getString(Const.KEY_QUERY, null);
    /*    Context context, int layout, Cursor c, String[] from,
        int[] to, int flags) {*/

        adapter = new SimpleCursorAdapter(getSherlockActivity(),
                R.layout.item, null, new String[]{
                DBFav.COLUMN_IS_FAVOURITE,
                DBFav.COLUMN_TITLE,
                DBFav.COLUMN_IMG_ID
        }, new int[]{R.id.cbBox, R.id.descr, R.id.image}, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);

        adapter.setViewBinder(new Main.SeekBarBinder());
        setListAdapter(adapter);
        getListView().setOnScrollListener(listNer);
        if (query != null) onQueryTextSubmit(query);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.list_fragment, container, false);
        return view;
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);

        SearchView searchView =
                (SearchView) menu.findItem(R.id.search).getActionView();

        searchView.setOnQueryTextListener(this);

    }

    @Override
    public void onClick(View view) {
        ImageData dat = (ImageData) view.getTag();
        if (view instanceof CheckBox) {
            CheckBox cb = (CheckBox) view;

            if (cb.isChecked()) {
                db.add(dat);
            } else {
                db.delRec(dat.getImageId());
            }
            dat.setFavourite(cb.isChecked());

        } else {
            ((Main) getActivity()).showImage(dat, R.id.search_fragment);

        }

    }


    @Override
    public boolean onQueryTextSubmit(String query) {

       /* adapter.getDataList().clear();
        adapter.setServerListSize(0);*/
        adapter.notifyDataSetChanged();

        if (observer == null) {
            Bundle req = new Bundle();
            req.putString(Const.KEY_QUERY, query);
            getLoaderManager().initLoader(Const.LOADER_SEARCH_ID, req, this).forceLoad();
        } else {

            SharedPreferences.Editor e = sp.edit();
            e.putString(Const.KEY_QUERY, query);
            e.commit();
            observer.onChange(true);

        }
        return true;
    }


    @Override
    public boolean onQueryTextChange(String newText) {
        return false;
    }

}
и лоадер

Код: Выделить всё

package com.example.myapp.async;

import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.content.CursorLoader;
import android.util.Log;
import com.example.myapp.Const;
import com.example.myapp.model.ImageData;
import com.example.myapp.parse.ResponseDataWrapper;
import com.example.myapp.storage.DBFav;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Created by v on 25.11.2014.
 */
public class ImageCursorLoader extends CursorLoader {
    Navigator navigator;
    ImageCacheService cacheService;
    volatile String query;
    MatrixCursor result;
    ResponseDataWrapper data;
    LinkedList<ImageData> tmpDataList = new LinkedList<>();
    AtomicLong idCounter = new AtomicLong();
    ImageData tmpImage;
    List tmpData;

    public ImageCursorLoader(Context context, Bundle args) {
        super(context);

        navigator = new Navigator();
        resetQuery(args.getString(Const.KEY_QUERY));
        cacheService = ImageCacheService.getInstance();
    }

    private MatrixCursor makeCursor() {
        MatrixCursor cursor;
        cursor = new MatrixCursor(DBFav.PROJECTION_ARR);
        return cursor;
    }


    @Override
    public Cursor loadInBackground() {
        int counter = Const.CACHE_SIZE;

        if (!resetIfNeed())
            return result;

        while (counter > 0) {

            while (counter > 0 && tmpDataList.size() > 0) {
                tmpImage = tmpDataList.removeFirst();
                try {
                    tmpData = tmpImage.toList(idCounter.incrementAndGet());
                } catch (Exception ex) {
                    ex.printStackTrace();
                }

                result.addRow(tmpData);

                counter--;
            }
            if (counter > 0) {
                data = navigator.next();

                try {
                    tmpDataList.addAll(cacheService.processMini(data.getResponseData().getResponseData()));
                } catch (Exception ex) {
                    return result;
                }
            }
        }

        return result;
    }

    private boolean resetIfNeed() {
        if (query == null)
            return false;

        boolean res = true;
        if (!query.equals(navigator.getQueryString())) {
            navigator.setQueryString(query);
            tmpDataList.clear();
        }
        if (result == null)
            result = makeCursor();
        return res;
    }

    private boolean checkAndReset() {
        boolean res = true;
        res &= isStarted() & !isAbandoned() & !isReset();
        return res;
    }

    @Override
    protected void onForceLoad() {
        super.onForceLoad();

        Log.d(getClass().getName(), hashCode() + " onForceLoad called");
    }


    @Override
    public void onCanceled(android.database.Cursor cursor) {
        super.onCanceled(cursor);
        Log.d(getClass().getName(), hashCode() + " onCanceled called");
    }

    @Override
    public void forceLoad() {
        super.forceLoad();
        Log.d(getClass().getName(), hashCode() + " forceLoad called");
    }

    @Override
    protected void onStartLoading() {
        super.onStartLoading();
        Log.d(getClass().getName(), hashCode() + " onStartLoading called");

    }

    @Override
    protected void onStopLoading() {
        super.onStopLoading();
        Log.d(getClass().getName(), hashCode() + " onStopLoading called");

    }

    @Override
    protected void onAbandon() {
        super.onAbandon();
        Log.d(getClass().getName(), hashCode() + " onAbandon called");
    }

    @Override
    protected void onReset() {
        super.onReset();
        Log.d(getClass().getName(), hashCode() + " onReset called");
    }

    private void resetQuery(String newQueryOrNot) {
        if (query != null && query.equals(newQueryOrNot)) {
            return;

        } else {
            query = newQueryOrNot;
        }
    }

    @Override
    public void onContentChanged() {
        super.onContentChanged();
        Log.d(getClass().getName(), hashCode() + " onContentChanged called");
        Log.d(getClass().getName(), hashCode() + " setting new query");
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
        resetQuery(sp.getString(Const.KEY_QUERY, "google search"));
    }


    @Override
    public boolean cancelLoad() {
        Log.d(getClass().getName(), hashCode() + " cancelLoad");
        return super.cancelLoad();

    }

    @Override
    public void stopLoading() {
        Log.d(getClass().getName(), hashCode() + " stopLoading");
        super.stopLoading();
    }
}

Пробовал MatrixCursor и каждый раз новый, и передавать в аргументе адаптера, и ключи(CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER) адаптера тоже пробовал, LoaderCallbacks вызывается только один раз при вводе новой строки.

Ответить