当我在我的 android 应用程序中滚动自定义列表时,作为自定义适配器一部分的收藏夹图标会自行更改

when i scroll the custom list in my android app then the favorite icon which is part of the custom adapter changes on its own

我创建了一个自定义list,其中的adapter包含歌曲名称、歌曲编号和收藏夹图标三个组成部分。最喜欢的图标用于标记或取消标记列表中最喜欢的项目。 请观看所附视频以了解问题所在。

Video

当我点击星标时,图标变为 selected/unselected 并触发 setOnFavoriteChangeListener 事件。如果我检查 isFavorite 状态并相应地更新数据库。这是适配器的完整代码:

public class song_index_adapter extends ArrayAdapter<song_index_model>{ //implements View.OnClickListener {
private ArrayList<song_index_model> dataSet;
Context mContext;
private int lastPosition = -1;

public song_index_adapter(ArrayList<song_index_model> data, Context context) {
    super(context, R.layout.song_index_row, data);
    this.dataSet = data;
    this.mContext=context;
}
// View lookup cache
private static class ViewHolder {
    TextView txt_sno;
    TextView txt_title;
    MaterialFavoriteButton favorite;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    // Get the data item for this position
    final song_index_model dataModel = getItem(position);

    // Check if an existing view is being reused, otherwise inflate the view
    final ViewHolder viewHolder; // view lookup cache stored in tag

    final View result;

    if (convertView == null) {
        viewHolder = new ViewHolder();
        LayoutInflater inflater = LayoutInflater.from(getContext());
        convertView = inflater.inflate(R.layout.song_index_row, parent, false);

        viewHolder.txt_sno = (TextView) convertView.findViewById(R.id.sno);
        viewHolder.txt_title = (TextView) convertView.findViewById(R.id.songTitle);
        viewHolder.favorite = (MaterialFavoriteButton) convertView.findViewById(R.id.indexfav);

        result=convertView;

        convertView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) convertView.getTag();
        result=convertView;
    }

    Animation animation = AnimationUtils.loadAnimation(mContext, (position > lastPosition) ? R.anim.up_from_bottom : R.anim.down_from_top);
    result.startAnimation(animation);
    lastPosition = position;

    viewHolder.txt_sno.setText(dataModel.getSno());
    viewHolder.txt_title.setText(dataModel.getTitle());

    //--- following conditional statements take care to
    //--- not to show a star with the index letter
    if(viewHolder.txt_sno.getText().toString().equals(""))
        viewHolder.favorite.setVisibility(View.GONE);
    else
        viewHolder.favorite.setVisibility(View.VISIBLE);

    viewHolder.favorite.setFavorite(dataModel.getFav());

    int fsize = (gvar.fontsize * gvar.fontstep) + gvar.fontmin;
    viewHolder.txt_title.setTextSize(fsize);
    viewHolder.txt_sno.setTextSize(fsize);

    viewHolder.favorite.setOnFavoriteChangeListener(new MaterialFavoriteButton.OnFavoriteChangeListener() {
        @Override
        public void onFavoriteChanged(MaterialFavoriteButton buttonView, boolean isfavorite) {
            DBHelper db = new DBHelper(mContext);
            SQLiteDatabase sdb = db.getWritableDatabase();

            boolean isUpdate = db.updateData(gvar.table,dataModel.getSno(),dataModel.getTitle(),dataModel.getSong(),dataModel.getCategory(),isfavorite);
            if(!isUpdate)
                Toast.makeText(mContext, "Song Selection could not be saved", Toast.LENGTH_SHORT).show();
            else {
                Toast.makeText(mContext, "Updated " + dataModel.getSno(), Toast.LENGTH_SHORT).show();
                Log.e("UPDATED", dataModel.getSno() + " " + isfavorite);
            }
        }
    });
    return convertView;
}

}

此事件位于 listview 上设置的 adapter 文件中,它主要检查最喜欢的明星的状态并更新数据库中歌曲的状态。 您可以看到 Toast 消息提示更新。

我的问题是,即使我只是简单地上下滚动而不按星形图标,setOnFavoriteChangeListener 事件也会继续触发。这可以在 Toast 消息和 Log 记录中看到。我附上日志记录的快照也供您查看。

我个人只换了第9首和42首最喜欢的图标,最后35首。在此期间,我一直上下滚动,您可以看到更新是如何自行发生的。

我的目标是标记最喜欢的项目列表。

为什么 setOnFavoriteChangeListener 在我没碰它的情况下就被解雇了。

有没有其他方法可以从列表中标记最喜欢的项目并将它们保存在数据库中。

提前致谢

我想我明白了你的问题。这与您正在回收 ListView 的项目这一事实有关。在初始加载期间,一切正常,没有任何选择,调用 dataModel.getFav() returns false 所以当你调用 viewHolder.favorite.setFavorite() 时,MaterialFavoriteButton 没有触发它设置了 OnFavoriteChangeListener。这样做的原因是,因为它在内部检查新的收藏夹状态是否与上一个不同,作为一种优化来防止不必要的工作 (I checked the source code)。但是,一旦您做出选择,您的 onFavoriteChanged 将被触发,因为新状态不同,然后您将值存储在数据库中。当您开始滚动时出现问题,因为您的视图被回收,MaterialFavoriteButton 的最喜欢状态将设置但是当您调用 dataModel.getFav() 时它会 return false 并将 MaterialFavoriteButton 最喜欢的状态更改回 false .从而导致您的 MaterialFavoriteButton 的旧 OnFavoriteChangeListener 再次触发(因此按钮未被收藏)。它还使用以前的数据模型更新您的数据库(注意: 这是一个关闭问题),这就是为什么您看到它使用屏幕滚动视图中的文本的原因。旧的 OnFavoriteChangeListener 被调用的原因是视图被回收并且仍然有您在初始加载期间传递给它的实例。因此,一旦所有视图都填满整个屏幕,如果您滚动第一个视图离开顶部屏幕,将传递给 getView(),因为 convertView.You 应该将调用 viewHolder.favorite.setOnFavoriteChangeListener 移到setFavorite() 调用。如果您在代码中放置调试语句并单步执行对 viewHolder.favorite.setFavorite() 的调用,您应该明白我在说什么。我希望这一切对你有所帮助。如果没有,请发表评论,我会尽力为您提供更多帮助。我推荐的解决方案是推迟数据库写入,直到按下保存按钮或 Activity/Fragment 暂停或停止,然后将最喜欢的状态存储在使用 getView(...)位置参数。这样效率更高,因为您不必在滚动 ListView 时不断访问数据库。