Android Recycler+CardView with Icon and Spinner - 排序问题
Android Recycler+CardView with Icon and Spinner - Sorting Problem
所以我现在的申请遇到了更大的问题。
首先让我总结一下整体设置:
我正在为我的数据库使用 Room,我的实体称为 Sub - 一列大约 "numUsage"(使用次数)。 Activity 的 UI 是带有 CardView 的 RecyclerView - 更好的说法是每行两张卡片加一个心形图标。
- 第一张卡片:显示子名
- 第二张卡片:显示 Sub
的使用次数
- 心形图标:如果点击这个图标,它应该会变成一个充满心形的心形(像 Instagram),并计算它的 Sub 的使用点数(这个想法也是让它每天只能点击一次,这是一个未来的问题)
在顶部我有一个微调器,让用户可以 "sort" 他得到的列表。他可以在 "entry"(按照 Subs 如何进入数据库的顺序)、"top" (numUsage DESC) 和 "flop" (numUsage ASC)
之后对其进行排序
排序本身是通过数据库中的 DAO 发生的,但是微调器的选择是,Activity 调用 "fitting" DAO(在下面的代码中显示)。
排序有效。此外,每当我点击 Sub 的心形图标时,numUsage 就会加一,实体也会更新。
现在我们来解决问题:
当我点击心形图标时,它会发生变化(如我所愿)——但是当我现在将微调器设置在另一个 "sorting way" 上时(f.e。从 "entry" 到 "top"),这两个CardViews update/sort 但心形图标保持在相同位置(但它也应该移动,因为我想看看哪个 Sub 已经计数了)
我知道这是因为我实际上并没有对卡片进行排序,而只是将数据从数据库中取出并写入新卡片的方式(因此心形图标保持在其原始位置)。但我只是错过了如何解决这个问题的正确想法。
如果你能帮助我,那就太棒了!
这里有一张小图来说明我的问题:
SubDAO:
//Get all subs
@Query("SELECT * FROM subscriptions")
LiveData<List<Sub>> getAllSubs();
//Get sub with less used points (=worst/flop Sub)
@Query("SELECT * FROM subscriptions ORDER BY numUsage ASC")
LiveData<List<Sub>> getAllFlopSubs();
//Get sub with most used points (=top Sub)
@Query("SELECT * FROM subscriptions ORDER BY numUsage DESC")
LiveData<List<Sub>> getAllTopSubs();
用法Activity - 微调器会发生什么:
@Override
public void onItemSelected(AdapterView<?> parent, View v, int position, long id){
switch (position){
case 0:
((TextView) parent.getChildAt(0)).setTextColor(getResources().getColor(R.color.midBlue));
setupListViewEntry();
break;
case 1:
((TextView) parent.getChildAt(0)).setTextColor(getResources().getColor(R.color.midBlue));
setupListViewTop();
break;
case 2:
((TextView) parent.getChildAt(0)).setTextColor(getResources().getColor(R.color.midBlue));
setupListViewFlop();
break;
}
}
@Override
public void onNothingSelected(AdapterView<?> arg0){
setupListViewEntry();
}
private void setupListViewEntry() {
mSubViewModel.getAllSubs().observe(this, new Observer<List<Sub>>() {
@Override
public void onChanged(@Nullable List<Sub> subs) {
mAdapter.setSubs(subs);
}
});
}
private void setupListViewTop() {
mSubViewModel.getAllTopSubs().observe(this, new Observer<List<Sub>>() {
@Override
public void onChanged(@Nullable List<Sub> subs) {
mAdapter.setSubs(subs);
}
});
}
private void setupListViewFlop() {
mSubViewModel.getAllFlopSubs().observe(this, new Observer<List<Sub>>() {
@Override
public void onChanged(@Nullable List<Sub> subs) {
mAdapter.setSubs(subs);
}
});
}
使用适配器:
public class UsageSubAdapter 扩展 RecyclerView.Adapter{
List<Sub> subList;
Context context;
private final LayoutInflater mInflater;
UsageSubAdapter (Context context) {
mInflater = LayoutInflater.from(context);
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView subUsage_name_cv_dummy;
public TextView subUsage_times_cv_dummy;
public View clickable;
public ImageView plus_usage;
public ViewHolder(View itemView) {
super(itemView);
subUsage_name_cv_dummy = itemView.findViewById(R.id.usage_name_dummy);
subUsage_times_cv_dummy = itemView.findViewById(R.id.usage_times_dummy);
clickable = itemView.findViewById(R.id.usage_cv_left);
plus_usage = itemView.findViewById(R.id.usage_like_btn);
}
}
@Override
public UsageSubAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View subview = mInflater.inflate(R.layout.cv_usage, parent, false);
return new UsageSubAdapter.ViewHolder(subview);
}
@Override
public void onBindViewHolder (final UsageSubAdapter.ViewHolder holder, int position) {
//Set Texts for CardViews
if (subList!= null){
final Sub current = subList.get(position);
holder.subUsage_name_cv_dummy.setText(current.getSubName());
holder.subUsage_times_cv_dummy.setText(Integer.toString(current.getNumUsage()));
holder.clickable.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Click on left CV to open up InfoFragment
ShowInfoFragment currentFrag = ShowInfoFragment.newInstance(current);
UsageActivity.fragmentManager.beginTransaction().replace(R.id.compLayout, currentFrag).addToBackStack(null).commit();
Log.d("Clicking: ", current.getSubName() + " was clicked - Listener worked");
}
});
holder.plus_usage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Count up usage point
int currentUsage = current.getNumUsage();
int newUsage = currentUsage + 1;
current.setNumUsage(newUsage);
updateNumUsage(current);
//change icon so the user knows it was clicked (for today)
holder.plus_usage.setImageResource(R.drawable.ic_usage_point_given);
//make it only clickable once a day
}
});
}
else {
holder.subUsage_name_cv_dummy.setText("Create a new Sub!");
holder.subUsage_times_cv_dummy.setText("00");
}
}
//Updating the number of Usages
private void updateNumUsage (final Sub sub){
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
SubDatabase.getInstance(context)
.getsubDAO()
.updateSub(sub);
}
}).start();
}
public void setSubs(List<Sub> subs){
this.subList = subs;
notifyDataSetChanged();
}
@Override
public int getItemCount(){
if(subList!=null){
return subList.size();
} else
{
return 0;
}
}
}
问题是您目前没有存储心形图标的值。如果没有此信息,您将无法告诉 RecyclerView
如何呈现它。
查看您的 onBindViewHolder
,您正在更新名称和计数的值,但不是心脏。您仅在单击时将心设置为活动状态。
final Sub current = subList.get(position);
holder.subUsage_name_cv_dummy.setText(current.getSubName());
holder.subUsage_times_cv_dummy.setText(Integer.toString(current.getNumUsage()));
// TODO - Set the current state of the heart icon.
holder.clickable.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// ...
}
});
holder.plus_usage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
holder.plus_usage.setImageResource(R.drawable.ic_usage_point_given);
}
});
您需要做的是保存一个值,并在渲染时使用它。对于此示例,假设您已将 boolean isActive = false;
添加到 Sub
class.
首先,由于我们需要在两个地方(渲染和 onClick)更新图标,我会创建一个像这样的小函数。
void updateUsageIcon(UsageSubAdapter.ViewHolder holder, Sub sub) {
if( sub.isActive ) {
holder.plus_usage.setImageResource(R.drawable.ic_usage_point_given);
} else {
// TODO show inactive state
}
}
然后,在你的 ViewHolder
中,你可以做这样的事情。
final Sub current = subList.get(position);
holder.subUsage_name_cv_dummy.setText(current.getSubName());
holder.subUsage_times_cv_dummy.setText(Integer.toString(current.getNumUsage()));
// Set the current value.
updateUsageIcon(holder, sub);
holder.plus_usage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Add logic to check if the user is allowed to set this to true today.
sub.isActive = true;
// Update the current value.
updateUsageIcon(holder, sub);
}
});
所以我现在的申请遇到了更大的问题。
首先让我总结一下整体设置:
我正在为我的数据库使用 Room,我的实体称为 Sub - 一列大约 "numUsage"(使用次数)。 Activity 的 UI 是带有 CardView 的 RecyclerView - 更好的说法是每行两张卡片加一个心形图标。
- 第一张卡片:显示子名
- 第二张卡片:显示 Sub 的使用次数
- 心形图标:如果点击这个图标,它应该会变成一个充满心形的心形(像 Instagram),并计算它的 Sub 的使用点数(这个想法也是让它每天只能点击一次,这是一个未来的问题)
在顶部我有一个微调器,让用户可以 "sort" 他得到的列表。他可以在 "entry"(按照 Subs 如何进入数据库的顺序)、"top" (numUsage DESC) 和 "flop" (numUsage ASC)
之后对其进行排序排序本身是通过数据库中的 DAO 发生的,但是微调器的选择是,Activity 调用 "fitting" DAO(在下面的代码中显示)。
排序有效。此外,每当我点击 Sub 的心形图标时,numUsage 就会加一,实体也会更新。
现在我们来解决问题: 当我点击心形图标时,它会发生变化(如我所愿)——但是当我现在将微调器设置在另一个 "sorting way" 上时(f.e。从 "entry" 到 "top"),这两个CardViews update/sort 但心形图标保持在相同位置(但它也应该移动,因为我想看看哪个 Sub 已经计数了)
我知道这是因为我实际上并没有对卡片进行排序,而只是将数据从数据库中取出并写入新卡片的方式(因此心形图标保持在其原始位置)。但我只是错过了如何解决这个问题的正确想法。
如果你能帮助我,那就太棒了!
这里有一张小图来说明我的问题:
SubDAO:
//Get all subs
@Query("SELECT * FROM subscriptions")
LiveData<List<Sub>> getAllSubs();
//Get sub with less used points (=worst/flop Sub)
@Query("SELECT * FROM subscriptions ORDER BY numUsage ASC")
LiveData<List<Sub>> getAllFlopSubs();
//Get sub with most used points (=top Sub)
@Query("SELECT * FROM subscriptions ORDER BY numUsage DESC")
LiveData<List<Sub>> getAllTopSubs();
用法Activity - 微调器会发生什么:
@Override
public void onItemSelected(AdapterView<?> parent, View v, int position, long id){
switch (position){
case 0:
((TextView) parent.getChildAt(0)).setTextColor(getResources().getColor(R.color.midBlue));
setupListViewEntry();
break;
case 1:
((TextView) parent.getChildAt(0)).setTextColor(getResources().getColor(R.color.midBlue));
setupListViewTop();
break;
case 2:
((TextView) parent.getChildAt(0)).setTextColor(getResources().getColor(R.color.midBlue));
setupListViewFlop();
break;
}
}
@Override
public void onNothingSelected(AdapterView<?> arg0){
setupListViewEntry();
}
private void setupListViewEntry() {
mSubViewModel.getAllSubs().observe(this, new Observer<List<Sub>>() {
@Override
public void onChanged(@Nullable List<Sub> subs) {
mAdapter.setSubs(subs);
}
});
}
private void setupListViewTop() {
mSubViewModel.getAllTopSubs().observe(this, new Observer<List<Sub>>() {
@Override
public void onChanged(@Nullable List<Sub> subs) {
mAdapter.setSubs(subs);
}
});
}
private void setupListViewFlop() {
mSubViewModel.getAllFlopSubs().observe(this, new Observer<List<Sub>>() {
@Override
public void onChanged(@Nullable List<Sub> subs) {
mAdapter.setSubs(subs);
}
});
}
使用适配器:
public class UsageSubAdapter 扩展 RecyclerView.Adapter{
List<Sub> subList;
Context context;
private final LayoutInflater mInflater;
UsageSubAdapter (Context context) {
mInflater = LayoutInflater.from(context);
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView subUsage_name_cv_dummy;
public TextView subUsage_times_cv_dummy;
public View clickable;
public ImageView plus_usage;
public ViewHolder(View itemView) {
super(itemView);
subUsage_name_cv_dummy = itemView.findViewById(R.id.usage_name_dummy);
subUsage_times_cv_dummy = itemView.findViewById(R.id.usage_times_dummy);
clickable = itemView.findViewById(R.id.usage_cv_left);
plus_usage = itemView.findViewById(R.id.usage_like_btn);
}
}
@Override
public UsageSubAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View subview = mInflater.inflate(R.layout.cv_usage, parent, false);
return new UsageSubAdapter.ViewHolder(subview);
}
@Override
public void onBindViewHolder (final UsageSubAdapter.ViewHolder holder, int position) {
//Set Texts for CardViews
if (subList!= null){
final Sub current = subList.get(position);
holder.subUsage_name_cv_dummy.setText(current.getSubName());
holder.subUsage_times_cv_dummy.setText(Integer.toString(current.getNumUsage()));
holder.clickable.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Click on left CV to open up InfoFragment
ShowInfoFragment currentFrag = ShowInfoFragment.newInstance(current);
UsageActivity.fragmentManager.beginTransaction().replace(R.id.compLayout, currentFrag).addToBackStack(null).commit();
Log.d("Clicking: ", current.getSubName() + " was clicked - Listener worked");
}
});
holder.plus_usage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Count up usage point
int currentUsage = current.getNumUsage();
int newUsage = currentUsage + 1;
current.setNumUsage(newUsage);
updateNumUsage(current);
//change icon so the user knows it was clicked (for today)
holder.plus_usage.setImageResource(R.drawable.ic_usage_point_given);
//make it only clickable once a day
}
});
}
else {
holder.subUsage_name_cv_dummy.setText("Create a new Sub!");
holder.subUsage_times_cv_dummy.setText("00");
}
}
//Updating the number of Usages
private void updateNumUsage (final Sub sub){
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
SubDatabase.getInstance(context)
.getsubDAO()
.updateSub(sub);
}
}).start();
}
public void setSubs(List<Sub> subs){
this.subList = subs;
notifyDataSetChanged();
}
@Override
public int getItemCount(){
if(subList!=null){
return subList.size();
} else
{
return 0;
}
}
}
问题是您目前没有存储心形图标的值。如果没有此信息,您将无法告诉 RecyclerView
如何呈现它。
查看您的 onBindViewHolder
,您正在更新名称和计数的值,但不是心脏。您仅在单击时将心设置为活动状态。
final Sub current = subList.get(position);
holder.subUsage_name_cv_dummy.setText(current.getSubName());
holder.subUsage_times_cv_dummy.setText(Integer.toString(current.getNumUsage()));
// TODO - Set the current state of the heart icon.
holder.clickable.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// ...
}
});
holder.plus_usage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
holder.plus_usage.setImageResource(R.drawable.ic_usage_point_given);
}
});
您需要做的是保存一个值,并在渲染时使用它。对于此示例,假设您已将 boolean isActive = false;
添加到 Sub
class.
首先,由于我们需要在两个地方(渲染和 onClick)更新图标,我会创建一个像这样的小函数。
void updateUsageIcon(UsageSubAdapter.ViewHolder holder, Sub sub) {
if( sub.isActive ) {
holder.plus_usage.setImageResource(R.drawable.ic_usage_point_given);
} else {
// TODO show inactive state
}
}
然后,在你的 ViewHolder
中,你可以做这样的事情。
final Sub current = subList.get(position);
holder.subUsage_name_cv_dummy.setText(current.getSubName());
holder.subUsage_times_cv_dummy.setText(Integer.toString(current.getNumUsage()));
// Set the current value.
updateUsageIcon(holder, sub);
holder.plus_usage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Add logic to check if the user is allowed to set this to true today.
sub.isActive = true;
// Update the current value.
updateUsageIcon(holder, sub);
}
});