使用AsyncTaskLoader查询数据库并在RecyclerView

时间:2018-02-07 07:15:57

标签: android sqlite android-recyclerview

我在RecyclerView中有一份食谱列表,当选择一个项目时,活动会显示该食谱的详细信息(即产量,方向,评级,收藏等)。

每个食谱的一般信息在一个表(食谱)上,成分在另一个表(成分)中,一个单独的表(RecipeIngredients)用于将食谱与成分链接起来创建多对多关系。

当我在另一个线程上使用AsyncTaskLoader显示详细信息活动时,我正在尝试检索另一个RecyclerView中的成分列表。但是,我似乎无法弄清楚如何调用加载器并返回特定配方的成分列表。

我的SQL查询确实返回了指定配方项的成分列表(通过“执行SQL”选项卡在数据库浏览器中检查SQLite)。

我有以下内容:

RecipeDisplayActivity.java

public class RecipeDisplayActivity extends AppCompatActivity {

private ArrayList<Ingredient> _ingredientsInRecipe = new ArrayList();
private DBHandler _repository;
private ProgressBar _progressIngredient;
private ProgressBar _progressDirections;
private Recipe _recipe;
private RecipeIngredientAdapter _adapterIngredient;
private RecyclerView _rvIngredient;
private TextView _lblIngredientMessage;
private TextView _lblRecipeCategory;
private TextView _lblRecipeDirections;
TextView _lblRecipeTitle;

private static final int RECIPE_INFORMATION_LOADER_ID = 173281;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_recipe_display);
    initControls(savedInstanceState);
}
private void initControls(Bundle savedInstanceState) {
    this._repository = DBHandler.getInstance(this);
    int recipeId = getIntent().getIntExtra(UIConsts.Bundles.RECIPE_KEY_ID, -1);
    if (recipeId > 0) {
        this._recipe = this._repository.recipes.getById(recipeId);
    } else {
        throw new IllegalStateException("RECIPE_KEY_ID not provided.");
    }

    setTitle(this._recipe.getTitle());
    this._lblRecipeTitle = (TextView) findViewById(R.id.lblRecipeTitle);
    this._lblRecipeTitle.setText(this._recipe.getTitle());
    this._lblRecipeCategory = (TextView) findViewById(R.id.lblRecipeCategory);
    this._lblRecipeCategory.setText(this._recipe.getCategory());
    this._lblRecipeDirections = (TextView) findViewById(R.id.lblRecipeDirections);
    this._lblRecipeDirections.setText(this._recipe.getDirections());
    final RatingBar ratingUser = (RatingBar) findViewById(R.id.ratingUser);
    ratingUser.setRating(this._recipe.displayRating());
    final ImageButton imgbtnFavoriteRecipe = (ImageButton) findViewById(R.id.imgbtnFavoriteRecipe);
    if (this._recipe.isFavorite()) {
        imgbtnFavoriteRecipe.setColorFilter(Utils.CustomColors.FAVORITE);
    } else {
        imgbtnFavoriteRecipe.setColorFilter(Color.rgb(80, 80, 80));
    }
    this._progressIngredient = (ProgressBar) findViewById(R.id.progressIngredient);
    this._lblIngredientMessage = (TextView) findViewById(R.id.lblIngredientMessage);
    this._rvIngredient = (RecyclerView) findViewById(R.id.rvIngredient);
    this._rvIngredient.setHasFixedSize(true);
    this._rvIngredient.setNestedScrollingEnabled(false);
    this._rvIngredient.setLayoutManager(new LinearLayoutManager(this));

    this._adapterIngredient = new RecipeIngredientAdapter(this._ingredientsInRecipe, this);

    this._rvIngredient.setAdapter(this._adapterIngredient);
    this._progressDirections = (ProgressBar) findViewById(R.id.progressDirections);

    if (this._recipe.getId() != null) {
        this._progressIngredient.setVisibility(View.GONE);
        this._lblIngredientMessage.setText(R.string.recipe_no_ingredients);
        this._lblIngredientMessage.setVisibility(View.GONE);
        this._progressDirections.setVisibility(View.GONE);
        loadFullRecipeInformation();
    }
}
public void loadFullRecipeInformation() {

    getSupportLoaderManager().initLoader(RECIPE_INFORMATION_LOADER_ID, null, new LoaderManager.LoaderCallbacks<Recipe>() {

        public Loader<Recipe> onCreateLoader(int id, Bundle args) {
            return new FullRecipeInformationLoader(RecipeDisplayActivity.this.getApplicationContext(), RecipeDisplayActivity.this._recipe.getId());
        }

        public void onLoadFinished(Loader<Recipe> loader, Recipe recipe) {
            if (recipe == null) {
                Exception exception = ((FullRecipeInformationLoader) loader).getException();
                if (exception == null) {
                    return;
                }
                RecipeDisplayActivity.this._progressIngredient.setVisibility(View.GONE);
                RecipeDisplayActivity.this._lblIngredientMessage.setText(R.string.general_msg_something_went_wrong);
                RecipeDisplayActivity.this._lblIngredientMessage.setVisibility(View.VISIBLE);

                Toast.makeText(RecipeDisplayActivity.this, R.string.general_msg_something_went_wrong, Toast.LENGTH_LONG).show();
                return;
            }

            recipe.setId(RecipeDisplayActivity.this._recipe.getId());
            recipe.setRating(RecipeDisplayActivity.this._recipe.getRating());
            recipe.setFavorite(RecipeDisplayActivity.this._recipe.isFavorite());
            recipe.setTimeAdded(RecipeDisplayActivity.this._recipe.getTimeAdded());
            RecipeDisplayActivity.this._recipe = recipe;

            RecipeDisplayActivity.this._adapterIngredient.notifyDataSetChanged();
            RecipeDisplayActivity.this.displayListStateIngredients();
        }

        public void onLoaderReset(Loader<Recipe> loader) {
        }
    }).forceLoad();
}
private void displayListStateIngredients() {
    this._progressIngredient.setVisibility(View.GONE);
    if (this._ingredientsInRecipe.size() < 1) {
        this._rvIngredient.setVisibility(View.GONE);
        this._lblIngredientMessage.setText(R.string.recipe_no_ingredients);
        this._lblIngredientMessage.setVisibility(View.VISIBLE);
        return;
    }
    this._lblIngredientMessage.setVisibility(View.GONE);
    this._rvIngredient.setVisibility(View.VISIBLE);
}
}

RecipeIngredientAdapter.java

public class RecipeIngredientAdapter extends RecyclerView.Adapter<RecipeIngredientAdapter.ViewHolder> {
private Activity _activity;
private ArrayList<Ingredient> _ingredient;

// Provide a direct reference to each of the views within a data item
// Used to cache the views within the item layout for fast access
public class ViewHolder extends RecyclerView.ViewHolder {

    // Your holder should contain a member variable
    // for any view that will be set as you render a row
    private CardView cardIngredient;
    private TextView lblIngredientName;
    private Ingredient ingredient;

    // We also create a constructor that accepts the entire item row
    // and does the view lookups to find each subview
    public ViewHolder(View itemView) {
        super(itemView);

        this.cardIngredient = (CardView) itemView.findViewById(R.id.cardIngredient);
        this.lblIngredientName = (TextView) itemView.findViewById(R.id.lblIngredientName);
    }

    void bindViewHolder(Ingredient ingredient) {
        this.ingredient = ingredient;

        this.lblIngredientName.setText(ingredient.getName());
    }
}

public RecipeIngredientAdapter(ArrayList<Ingredient> ingredientInRecipe, Activity activity) {
    this._ingredient = ingredientInRecipe;
    this._activity = activity;
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_row_ingredient, parent, false));
}

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
    viewHolder.bindViewHolder((Ingredient) this._ingredient.get(position));
}

@Override
public int getItemCount() {
    return this._ingredient.size();
}
 public Context getContext() {
    return this._activity.getApplicationContext();
}
}

FullRecipeInformationLoader.java

public class FullRecipeInformationLoader extends AsyncTaskLoader<Recipe> {
private Context _context;
private Exception _exception;
private int _recipeId;
private DBHandler _repository;

public FullRecipeInformationLoader(@NonNull Context context, int _recipeId) {
    super(context);
    this._context = context;
    this._recipeId = _recipeId;
}

public Recipe loadInBackground() {

    try {
        // Return list of Ingredients
return this._repository.recipeIngredients.getIngredientForRecipeById(this._recipeId);

    } catch (Exception e) {
        this._exception = e;
        return null;
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    }
    return null;
}

public Exception getException() {
    return this._exception;
}
}

RecipeIngredients类

public class RecipeIngredients {
public ArrayList<Ingredient> getIngredientForRecipeById(int recipeId) {
        if (recipeId < 1) {
            throw new IllegalArgumentException("recipeId");
        }
        Cursor cursor = DBHandler.this._helper.getReadableDatabase().rawQuery("SELECT Ingredients.* FROM Recipes, Ingredients, RecipeIngredient WHERE RecipeIngredient._ingredient_id = Ingredients._ingredient_id AND Recipes._id = " + recipeId, null);
        ArrayList<Ingredient> ingredients = new ArrayList();
        while (cursor.moveToNext()) {
            ingredients.add(createIngredientFromCursor(cursor));
        }
        cursor.close();
        return ingredients;
    }

private Ingredient createIngredientFromCursor(Cursor cursor) {

        boolean z2 = true;
        Ingredient ingredient = new Ingredient();
        int id = cursor.getInt(cursor.getColumnIndex(DBConsts.Table_Ingredient.COLUMN_IngredientId));
        ingredient.setId(Integer.valueOf(id));
        ingredient.setName(cursor.getString(cursor.getColumnIndex(DBConsts.Table_Ingredient.COLUMN_IngredientName)));

        return ingredient;
    }
}

如果您需要我遗漏的任何信息,请告诉我。 Thanx提前...

1 个答案:

答案 0 :(得分:0)

将cursorLoaders与recyclerView.Adapter一起使用

示例:

public class RecipeDisplayActivity extends AppCompatActivity {
    Cursror bdCursor;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        RecyclerView newList = findViewById(android.R.id.list);
        BottleDataAdapter adapter = new BottleDataAdapter(bdCursor);
        LinearLayoutManager llm = new LinearLayoutManager(this);
        newList.setLayoutManager(llm);
        newList.setAdapter(adapter);
        //next line call cursorLoader 
        //(CursorLoader is subclass of AsyncTaskLoader, so you can call ATL with next line)
        //and next line you may use whenever you want e.g in onClick etc.
        getSupportLoaderManager().restartLoader(id, null, this);
    }
}

下一步是覆盖回调(在活动中)

@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
    return new SelectItems(this, i);
}

@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
    adapter.changeCursor(cursor);//!!!!! This method was written manualy for changing cursor. You can see it in bottom
}

@Override
public void onLoaderReset(Loader<Cursor> cursorLoader) { }

SelectItems的位置是:

public class SelectItems extends CursorLoader {

    //you can give your params from activity to query here
    public SelectItems(Context context, int param) {
        super(context);
    }

    @Override
    public Cursor loadInBackground() {
        //ProxyManager is just class for db queries, so next line you must do db query
        Cursor cursor = getProxyManager().getSectionsItems(mTypeSection);
        return cursor;
    }
}

和完成步骤是写我们的适配器

public class BottleDataAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<Bottle> bottles = new ArrayList<>();
    private Cursor cursor;

    //in constructor we set our cursor and init our data from this cursor
    public BottleDataAdapter(Cursor cursor) {
        this.cursor = cursor;
        initData();
    }

    private void initData() {
        if (cursor != null) {
            for (int pos = 0; pos < cursor.getCount(); pos++) {
                cursor.moveToPosition(pos);
                bottles.add(new Bottle(cursor.getString(0)));
            }
        }
    }

    public void changeCursor(Cursor cursor) {
        this.cursor = cursor;
        initData();
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {...}

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {...}

    @Override
    public int getItemCount(){...}
}

此外,您可以在RecyclerViewAdapter中使用CursorAdapter而不是Cursor