【问题标题】:Use AsyncTaskLoader to query DB and display in RecyclerView使用AsyncTaskLoader查询DB并显示在RecyclerView
【发布时间】:2018-02-07 07:15:56
【问题描述】:

我在RecyclerView和选择项目时,活动将显示该配方的详细信息(即产量,方向,评级,收藏夹等)。

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

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

我的 SQL 查询确实返回了指定配方项目的成分列表(在 DB 浏览器中通过“执行 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;
    }
}

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

【问题讨论】:

  • @cricket_007 skyfishjy 提供的适配器真的很差:那个更好:gist.github.com/Shywim/127f207e7248fe48400b 因为它有FilterQueryProvider
  • 如果我错过了它,我很抱歉,但我看不出任何一个链接是如何建议的,并且使用修改后的光标适配器与所提出的问题有关。问题是关于在数据库中查询特定 ID 并返回结果列表并将它们显示在 RecyclerView 中。原谅我的无知...

标签: android sqlite android-recyclerview


【解决方案1】:

将 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

【讨论】:

  • 我从我的项目中获取了这个示例,因此请改用 CursorLoader。 1 秒,已编辑。
  • 我仍然不清楚如何实现我的目标。谁有任何源代码、示例或教程可以推荐?
  • 我认为,也许,我的问题被误解了。 I can and have my initial list (RecyclerView) populated with the list of recipes and, when an item is selected, a "details" window shows the basic info for the recipe.如何使用此配方 ID 查询数据库以获取填充另一个 RecyclerView 的成分列表?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-05
  • 1970-01-01
  • 2019-03-10
  • 1970-01-01
  • 2012-04-12
  • 2014-02-25
  • 2019-10-15
相关资源
最近更新 更多