2

我对适配器的 getView 方法中的位置变量有这个奇怪的问题。这 6 种不同视图类型中的 4 种,在其中有一个按钮。该按钮向服务发送消息(在服务上引发一些异步内容),并且在无限时间之后,此适配器会收到由服务引发的 notifyDataSetChanged()。

当我向发送消息的按钮发送垃圾邮件时,问题就出现了。如果我向它发送垃圾邮件的速度足够快,则会将错误的位置发送到服务。我认为问题在于,在垃圾邮件期间,我将在 notifyDataSetChanged() 期间点击按钮,因为如果我对服务正在使用的方法的调用进行评论,则不会发生这种不一致。

这是我第一次使用 BaseAdapter,我遵循了这个不错的教程: Base Adapter Tutorial

以下是我认为可能与查明问题相关的代码部分。

此适配器管理 6 种不同的视图类型:

private static final int MAX_COUNT = 6;

以下是我重写的方法:

@Override
    public int getViewTypeCount() {
        return MAX_COUNT;
    }

    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public ListViewDataItem getItem(int position) {
        return data.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

     @Override
     public int getItemViewType(int position) {
        return getItem(position).getType();
    }

继承人 getView 方法:

@Override
    public View getView(final int position, View convertView, ViewGroup parent) {
//      Thread.currentThread().setContextClassLoader(MyParcelableFile.class.getClassLoader());
        View row = convertView;
        StandardFolderViewHolder standardFolderViewHolder = null;
        StandardFileViewHolder standardFileViewHolder = null;
        MusicFileStoppedViewHolder musicFileStoppedHolder = null;
        MusicFilePlayingViewHolder musicFilePlayingHolder = null;
        MusicFolderStoppedViewHolder musicFolderStoppedHolder = null;
        MusicFolderPlayingViewHolder musicFolderPlayingHolder = null;

        switch(getItemViewType(position)) {
        case Constants.MEDIA_FILE.TYPE.STANDARD_DIRECTORY:
            if(row == null) {
                standardFolderViewHolder = new StandardFolderViewHolder();
                row = inflater.inflate(R.layout.listview_mixed_folder_row, parent, false);
                standardFolderViewHolder.icon = (ImageView)row.findViewById(R.id.filetype_icon);
                standardFolderViewHolder.tempTV = (TextView)row.findViewById(R.id.listview_mixed_folder_row_test_tv);
                row.setTag(standardFolderViewHolder);
            }
            else {
                standardFolderViewHolder = (StandardFolderViewHolder)row.getTag();
            }
            standardFolderViewHolder.icon.setImageDrawable(getItem(position).getDrawable());
            standardFolderViewHolder.tempTV.setText(getItem(position).getName());
            standardFolderViewHolder.tempTV.setSelected(true);
            break;
        case Constants.MEDIA_FILE.TYPE.MUSIC_DIRECTORY:
            if(getItemViewState(position) == Constants.MEDIA_FILE.TYPE.STATE.PLAYING) {
                if(row == null || (row !=null && row.getTag() instanceof MusicFolderStoppedViewHolder)) {
                    musicFolderPlayingHolder = new MusicFolderPlayingViewHolder();
                    row = inflater.inflate(R.layout.listview_music_folder_playing_row, parent, false);
                    musicFolderPlayingHolder.icon = (ImageView)row.findViewById(R.id.filetype_icon);
                    musicFolderPlayingHolder.songName = (TextView)row.findViewById(R.id.row_title_tv);
                    musicFolderPlayingHolder.playButton = (Button)row.findViewById(R.id.row_play_button);
                    musicFolderPlayingHolder.durationTV = (TextView)row.findViewById(R.id.row_duration_tv);
                    musicFolderPlayingHolder.progressBar = (ProgressBar)row.findViewById(R.id.folder_progress_bar);
                    row.setTag(musicFolderPlayingHolder);
                }
                else {
                    musicFolderPlayingHolder = (MusicFolderPlayingViewHolder)row.getTag();
                }
                musicFolderPlayingHolder.icon.setImageDrawable(getItem(position).getDrawable());
                musicFolderPlayingHolder.songName.setText(getItem(position).getName());
                musicFolderPlayingHolder.songName.setSelected(true);
                musicFolderPlayingHolder.durationTV.setText(mActivity.formattedMillis(getItem(position).getDuration()));
                musicFolderPlayingHolder.progressBar.setMax(getItem(position).getDuration());
                musicFolderPlayingHolder.progressBar.setProgress(getItem(position).getProgress()); 
                musicFolderPlayingHolder.playButton.setOnClickListener(new OnClickListener() {

                    @Override
                    public void onClick(View arg0) {
                        Log.e("clicked", getItem(position).getName());
                        Bundle bun = new Bundle();
                        bun.putString(Constants.BUNDLE_KEYS.PLAY_FILES, getItem(position).getPath());
                        Message message = Message.obtain(null, Constants.OP_CODE.PLAY_FILES);
                        message.setData(bun);
                        try {
                            mActivity.mService.send(message);
                        }
                        catch (RemoteException re) {
                            re.printStackTrace();
                        }
                    }

                });
            }
            else if(getItemViewState(position) == Constants.MEDIA_FILE.TYPE.STATE.STOPPED) {
                if(row == null || (row !=null && row.getTag() instanceof MusicFolderPlayingViewHolder)) {
                    musicFolderStoppedHolder = new MusicFolderStoppedViewHolder();
                    row = inflater.inflate(R.layout.listview_music_folder_stopped_row, parent, false);
                    musicFolderStoppedHolder.icon = (ImageView)row.findViewById(R.id.filetype_icon);
                    musicFolderStoppedHolder.songName = (TextView)row.findViewById(R.id.row_title_tv);
                    musicFolderStoppedHolder.playButton = (Button)row.findViewById(R.id.row_play_button);
                    musicFolderStoppedHolder.durationTV = (TextView)row.findViewById(R.id.row_duration_tv);
                    row.setTag(musicFolderStoppedHolder);
                }
                else {
                    musicFolderStoppedHolder = (MusicFolderStoppedViewHolder)row.getTag();
                }
                musicFolderStoppedHolder.icon.setImageDrawable(getItem(position).getDrawable());
                musicFolderStoppedHolder.songName.setText(getItem(position).getName());
                musicFolderStoppedHolder.songName.setSelected(true);
                musicFolderStoppedHolder.durationTV.setText(mActivity.formattedMillis(getItem(position).getDuration()));
                musicFolderStoppedHolder.playButton.setOnClickListener(new OnClickListener() {

                    @Override
                    public void onClick(View arg0) {
                        Log.e("clicked", getItem(position).getName());
                        Bundle bun = new Bundle();
                        bun.putString(Constants.BUNDLE_KEYS.PLAY_FILES, getItem(position).getPath());
                        Message message = Message.obtain(null, Constants.OP_CODE.PLAY_FILES);
                        message.setData(bun);
                        try {
                            mActivity.mService.send(message);
                        }
                        catch (RemoteException re) {
                            re.printStackTrace();
                        }
                    }

                });
            }
            break;
        case Constants.MEDIA_FILE.TYPE.STANDARD_FILE:
            if(row == null) {
                standardFileViewHolder = new StandardFileViewHolder();
                row = inflater.inflate(R.layout.listview_mixed_folder_row, parent, false);
                standardFileViewHolder.icon = (ImageView)row.findViewById(R.id.filetype_icon);
                standardFileViewHolder.tempTV = (TextView)row.findViewById(R.id.listview_mixed_folder_row_test_tv);
                row.setTag(standardFileViewHolder);
            }
            else {
                standardFileViewHolder = (StandardFileViewHolder)row.getTag();
            }
            standardFileViewHolder.icon.setImageDrawable(getItem(position).getDrawable());
            standardFileViewHolder.tempTV.setText(getItem(position).getName());
            standardFileViewHolder.tempTV.setSelected(true);
            break;
        case Constants.MEDIA_FILE.TYPE.MUSIC_FILE:
            if(getItemViewState(position) == Constants.MEDIA_FILE.TYPE.STATE.PLAYING) {
                if(row == null || (row !=null && row.getTag() instanceof MusicFileStoppedViewHolder)) {
                    musicFilePlayingHolder = new MusicFilePlayingViewHolder();
                    row = inflater.inflate(R.layout.listview_music_file_playing_row, parent, false);
                    musicFilePlayingHolder.icon = (ImageView)row.findViewById(R.id.filetype_icon);
                    musicFilePlayingHolder.songName = (TextView)row.findViewById(R.id.row_title_tv);
                    musicFilePlayingHolder.playButton = (Button)row.findViewById(R.id.row_play_button);
                    musicFilePlayingHolder.durationTV = (TextView)row.findViewById(R.id.row_duration_tv);
                    musicFilePlayingHolder.progressBar = (ProgressBar)row.findViewById(R.id.folder_progress_bar);
                    row.setTag(musicFilePlayingHolder);
                }
                else {
                    musicFilePlayingHolder = (MusicFilePlayingViewHolder)row.getTag();
                }
                musicFilePlayingHolder.icon.setImageDrawable(getItem(position).getDrawable());
                musicFilePlayingHolder.songName.setText(getItem(position).getName());
                musicFilePlayingHolder.songName.setSelected(true);
                musicFilePlayingHolder.durationTV.setText(mActivity.formattedMillis(getItem(position).getDuration()));
                musicFilePlayingHolder.progressBar.setMax(getItem(position).getDuration());
                musicFilePlayingHolder.progressBar.setProgress(getItem(position).getProgress());
                musicFilePlayingHolder.playButton.setOnClickListener(new OnClickListener() {

                    @Override
                    public void onClick(View arg0) {
                        Log.e("clicked", getItem(position).getName());
                        Bundle bun = new Bundle();
                        bun.putString(Constants.BUNDLE_KEYS.PLAY_FILES, getItem(position).getPath());
                        Message message = Message.obtain(null, Constants.OP_CODE.PLAY_FILES);
                        message.setData(bun);
                        try {
                            mActivity.mService.send(message);
                        }
                        catch (RemoteException re) {
                            re.printStackTrace();
                        }
                    }

                });
            }
            else if(getItemViewState(position) == Constants.MEDIA_FILE.TYPE.STATE.STOPPED) {
                if(row == null || (row !=null && row.getTag() instanceof MusicFilePlayingViewHolder)) {
                    musicFileStoppedHolder = new MusicFileStoppedViewHolder();
                    row = inflater.inflate(R.layout.listview_music_file_stopped_row, parent, false);
                    musicFileStoppedHolder.icon = (ImageView)row.findViewById(R.id.filetype_icon);
                    musicFileStoppedHolder.songName = (TextView)row.findViewById(R.id.row_title_tv);
                    musicFileStoppedHolder.playButton = (Button)row.findViewById(R.id.row_play_button);
                    musicFileStoppedHolder.durationTV = (TextView)row.findViewById(R.id.row_duration_tv);

                    row.setTag(musicFileStoppedHolder);
                }
                else {
                    musicFileStoppedHolder = (MusicFileStoppedViewHolder)row.getTag();
                }
                musicFileStoppedHolder.icon.setImageDrawable(getItem(position).getDrawable());
                musicFileStoppedHolder.songName.setText(getItem(position).getName());
                musicFileStoppedHolder.songName.setSelected(true);
                musicFileStoppedHolder.durationTV.setText(mActivity.formattedMillis(getItem(position).getDuration()));
                musicFileStoppedHolder.playButton.setOnClickListener(new OnClickListener() {

                    @Override
                    public void onClick(View arg0) {
                        Log.e("clicked", getItem(position).getName());
                        Bundle bun = new Bundle();
                        bun.putString(Constants.BUNDLE_KEYS.PLAY_FILES, getItem(position).getPath());
                        Message message = Message.obtain(null, Constants.OP_CODE.PLAY_FILES);
                        message.setData(bun);
                        try {
                            mActivity.mService.send(message);
                        }
                        catch (RemoteException re) {
                            re.printStackTrace();
                        }
                    }

                });
            }
            break;
        }

        if(!getItem(position).wasAnimatedIn()) {
            row.startAnimation(getItem(position).getGoingIn());
        }
        else if (!getItem(position).wasAnimatedOut()) {
            Animation outAnim = getItem(position).getGoingOut();
            outAnim.setAnimationListener(new AnimationListener() {

                @Override
                public void onAnimationEnd(Animation animation) {
                    data.remove(getItem(position));
                }

                @Override
                public void onAnimationRepeat(Animation animation) {}

                @Override
                public void onAnimationStart(Animation animation) {}
            });
            row.startAnimation(outAnim);
        }   
        return row;
    }

在此适配器上,服务可以调用的方法之一:

public void activatePlayingState(int positionInPage) {
        if(positionInPage < getCount()) {
            ListViewDataItem lvDataItem = getItem(positionInPage);
            lvDataItem.setState(Constants.MEDIA_FILE.TYPE.STATE.PLAYING);
            notifyDataSetChanged();
        }
    }
4

1 回答 1

1

位置并不意味着像 id 那样稳定。这方面的一个例子是选择模式。如果您的 id 不稳定,列表中的任何更改(移动/添加/删除项目)都会扰乱检查的位置,因为实际上没有办法存储所有项目以跟踪每个项目的去向。顺便说一句,尽管与您的问题没有真正的关系,但如果您确实有稳定的 id 并且一个项目移动了 20 多个项目左右,它们只会清除该项目的选中状态。在编写代码时,我假设他们认为遍历大约 20 个项目来检查位置 v.id 是所有可以以足够有效的方式执行的。

在您的情况下,虽然您可能不会在自己周围移动物品,但notifyDataSetChanged()在某种意义上,当您打电话时,内部物品确实会移动。AdapterView.AdapterDataSetObserver#onChanged准确显示了调用notifyDataSetChanged().

说到重点,您可以通过使用 stableIds 而不是位置来解决您的问题。要实现这一点,请更改您的getItemId(int position)方法以返回该位置的项目的唯一 ID。然后,覆盖hasStableIds()以返回 true。这是hasStableIds() BaseAdapter#hasStableIds()的文档。现在,您可以将 id 传递给您的服务。您已经将一个捆绑包传递给您的服务,所以只需将 id 放入其中。另请注意,您需要存储具有您需要跟踪的状态的项目的 ID。例如,这就像将 id 添加到 ArrayList 一样简单。当您的服务执行任何操作时,它可以activatePlayingState使用 id 而不是可能过时的位置调用您的方法(请记住将该参数从 int 更改为 long)。在getView,然后您可以将激活的 id 与当前的 getView 项目进行比较getItemId(int position),并按预期设置该项目的视图。

于 2013-10-24T00:22:39.323 回答