ListView Adapter Async Manipulation

SamzSakerz :

I have a Custom ListView this is the ListView I am using that shows some data on the screen, Pretty simple. Now I need to theme the shown data view. the way I do this is by saving a key, value to SQLite adapter, I don't want to use SharedPrefs, This takes a long process to read over 120+ keys! and lags the UI a lot so I thought lets make an async setBackground so here is what I put together.

public static HashMap<String, String> lruCache = new HashMap<>();

I cache all the keys in a hashset

and then I made a method that checks if the key exists if not it gets the key using AsyncTask

public static void setBackgroundColor(View view, String key, String defaultValue) {
    String val = lruCache.get(key);
    if (val != null) {
        view.setBackgroundColor(ThemeUtils.parseColor(val));
        return;
    }
    new AsyncBackgroundColor(view).execute(key, defaultValue);
}

public static class AsyncBackgroundColor extends AsyncTask<String, String, Integer> {
    WeakReference<View> view;

    AsyncBackgroundColor(View view) {
        this.view = new WeakReference<>(view);
    }

    @Override
    protected Integer doInBackground(String... strings) {
        return ThemeUtils.getColor(strings[0], strings[1]);
    }

    @Override
    protected void onPostExecute(Integer color) {
        view.get().setBackgroundColor(color);
    }
}

and this is how my getColor method works.

public static int getColor(String str, String defaultValue) {
    ThemeDatabaseManager lynxDatabaseHelper = new ThemeDatabaseManager(LynxBase.getApplicationContext()).open();
    return ThemeUtils.parseColor(lynxDatabaseHelper.getString(str, defaultValue));
}

It gets the string from my SQlite database and parses it to an int. This is my getString method

public String getString(String key, String defaultValue) {
    String cachedValue = ThemeDatabaseCache.lruCache.get(key);
    if (cachedValue != null) {
        return cachedValue;
    }
    if (!database.isOpen()) open();
    String[] columns = new String[]{ThemeDatabaseHelper.COLUMN_NAME_TITLE, ThemeDatabaseHelper.COLUMN_NAME_SUBTITLE};
    Cursor cursor = database.query(TABLE_NAME, columns, null, null, null, null, null);
    if(cursor != null) {
        cursor.moveToFirst();
        if(cursor.getCount() != 0) {
            do {
                if (!(cursor.getColumnCount() <= 1)) {
                    String k = cursor.getString(cursor.getColumnIndex(ThemeDatabaseHelper.COLUMN_NAME_TITLE));
                    String value = cursor.getString(cursor.getColumnIndex(ThemeDatabaseHelper.COLUMN_NAME_SUBTITLE));
                    if (k.equals(key)) {
                        cursor.close();
                        if (database.isOpen()) database.close();
                        ThemeDatabaseCache.lruCache.put(key, defaultValue);
                        return value;
                    }
                }
            } while (cursor.moveToNext());
        }
        cursor.close();
    }
    insertOrUpdate(key, defaultValue);
    if (database.isOpen()) database.close();
    return defaultValue;
}

I fetch all the SQLite colums and loop until I find the correct key and then return that how ever if the value doesn't exist I just insert the default value into the SQLite database, that way I always end up with a key the other time.

The problem happens here. It doesn't theme all the tabs in the adapter.

enter image description here

as you can see It only themed the 3rd adapter item, But when I scroll up and down, the position changes. So It wont change 3rd its gonna be 5th and you get the point, Does anyone know how I can resolve this? I am been debugging this for about 5 days now, Tried all sorts of stuff can't seem to fix it.

The black is what all of the item is suppose to look like once the setBackgroundColor is done. The white is the default color applied using XML Layout.

This is how I call it on my adapter.

public final View getView(int i, View view, ViewGroup viewGroup){\
    ...
    view = inflate(R.layout.my_view, viewGroup, false);
    setBackground(view);
    ...
}

and my class is extending a custom class that I made that extends BaseAdapter if that helps!

This is what I have tried according to the answer.

public static void setBackgroundColor(BaseAdapter baseAdapter, View view, String key, String defaultValue) {
    String val = lruCache.get(key);
    if (val != null) {
        Log.wtf("Lynx", "background set using cached Color.");
        view.setBackgroundColor(ThemeUtils.parseColor(val));
        baseAdapter.notifyDataSetChanged();
        return;
    }
    new AsyncBackgroundColor(baseAdapter, view).execute(key, defaultValue);
}

..

public static class AsyncBackgroundColor extends AsyncTask<String, String, Integer> {
    WeakReference<View> view;
    BaseAdapter baseAdapter;

    AsyncBackgroundColor(BaseAdapter baseAdapter, View view) {
        this.view = new WeakReference<>(view);
        this.baseAdapter = baseAdapter;
    }

    @Override
    protected Integer doInBackground(String... strings) {

        return ThemeUtils.getColor(strings[0], strings[1]);
    }

    @Override
    protected void onPostExecute(Integer color) {
        Log.wtf("Lynx", "background set using async task.");
        view.get().setBackgroundColor(color);
        if(baseAdapter != null)
        baseAdapter.notifyDataSetChanged();
    }
}

But It's still the same as before.

Here is the Catlog dump:

enter image description here

113408 :

This works if I just set the color from the database and skip the async bit. But when I use async it doesn't work.

This happens because your AsyncTask doesn't work on the UIThread and therefore when the result is back it doesn't redraw your items. To do so you need to notify the adapter that new elements are present using myAdapter.notifyDataSetChanged();

From the code you shared, I guess you can just call it from your postExecute:

public static class AsyncBackgroundColor extends AsyncTask<String, String, Integer> {
    WeakReference<View> view;
    WeakReference<Adapter> adapter;

    AsyncBackgroundColor(Adapter ad, View view) {
        this.view = new WeakReference<>(view);
        this.adapter = new WeakReference<>(ad);
    }

    @Override
    protected Integer doInBackground(String... strings) {
        return 1;
    }

    @Override
    protected void onPostExecute(Integer color) {
        view.get().setBackgroundColor(color);
        adapter.get().notifyDataSetChanged();
    }
}

Also on your getString() change this part:

if (k.equals(key)) {
                    cursor.close();
                    if (database.isOpen()) database.close();
                    ThemeDatabaseCache.lruCache.put(key, value);
                    return value;
                }

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=152412&siteId=1