当前所选项目在屏幕外时,RecyclerView项目选择器崩溃

时间:2015-09-16 18:22:12

标签: android android-recyclerview

对于作业,我必须使用一些预定义的项目进行回收查看,并且点击其中的每一项都会在另一个视图中执行某些操作。这一切都运行良好,可能对我的任务来说已经足够好了,但我对它并不满意,因为没有任何东西显示选择的项目等等。

因此,无论何时选择列表项,我都希望对其进行背景突出显示。经过一番搜索,我发现了一个类似问题的答案:https://stackoverflow.com/a/28617619/2437682

我按照了答案,得到了我非常满意的结果,直到我注意到如果当前选择的项目在点击新项目时屏幕外,则会导致应用程序与NPE崩溃。

造成此次崩溃的特定代码是:

void onItemClick(int position) {

        ListItemViewHolder yourViewHolder;

        int oldSelectedPosition = mSelectedIndex;

        if (position != mSelectedIndex) {
            mSelectedIndex = position;


            yourViewHolder = (ListItemViewHolder) recyclerView.findViewHolderForAdapterPosition(oldSelectedPosition);

            //this is the line that causes the crash when the view is off-screen
            yourViewHolder.itemView.setSelected(false);


            yourViewHolder = (ListItemViewHolder) recyclerView.findViewHolderForAdapterPosition(mSelectedIndex);
            yourViewHolder.itemView.setSelected(true);
        }
    }

我明白发生了什么。如果项目在屏幕外,则findViewHolderForAdapterPosition方法返回null,因此我无法更改setSelected属性。

我不确定如何解决这个问题。我试着说:

if (yourViewHolder != null) {
    yourViewHolder.itemView.setSelected(false);
}

这至少可以防止应用程序崩溃,但是下次该列表项滚动回到屏幕上时,它仍会突出显示,就像它被选中一样。

有什么想法吗?

更新

这是更新的适配器和视图持有者类:

public class ColorsAdaptor extends RecyclerView.Adapter<ListItemViewHolder>{

    @Override
    public ListItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        Context context = viewGroup.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);

        View listItemView = inflater.inflate(R.layout.list_item, viewGroup, false);

        ColorDrawable colorDrawableSelected =
                new ColorDrawable(getResources().getColor(R.color.selected));

        StateListDrawable stateListDrawable = new StateListDrawable();
        stateListDrawable.addState(new int[]{android.R.attr.state_selected}, colorDrawableSelected);
        stateListDrawable.addState(StateSet.WILD_CARD, getResources().getDrawable(R.drawable.border));

        listItemView.setBackgroundDrawable(stateListDrawable);

        ListItemViewHolder viewHolder = new ListItemViewHolder(listItemView);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ListItemViewHolder listItemViewHolder, int position) {
        listItemViewHolder.updateView();

        MyColor color = items.get(position);

        TextView colorText = listItemViewHolder.getColorText();
        colorText.setText(color.getName());

        ImageView colorBlock = listItemViewHolder.getColorBlock();
        colorBlock.setBackgroundColor(color.getColorID());
    }

    @Override
    public int getItemCount() {
        return items.size();
    }
}

public class ListItemViewHolder extends RecyclerView.ViewHolder{
    private TextView colorText;
    private ImageView colorBlock;
    private View itemView;

    public ListItemViewHolder(View view) {
        super(view);
        itemView = view;
        this.colorBlock = (ImageView) view.findViewById(R.id.colorBlock);
        this.colorText = (TextView) view.findViewById(R.id.colorText);

        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = getAdapterPosition();

                if (position != mSelectedIndex) {

                   ListItemViewHolder oldViewHolder = (ListItemViewHolder) recyclerView.findViewHolderForAdapterPosition(mSelectedIndex);
                   if (oldViewHolder != null) {
                       oldViewHolder.itemView.setSelected(false);
                   }

                    itemView.setSelected(true);
                    mSelectedIndex = position;
                }



                ImageView temp = (ImageView) v.findViewById(R.id.colorBlock);
                colorViewer.setBackground(temp.getBackground());
            }
        });
    }

    public void updateView(){
        Log.d("", "updateView SelectedIndex: " + mSelectedIndex + " adapterPosition: " + getAdapterPosition());
        this.itemView.setSelected(getAdapterPosition() == mSelectedIndex);
    }

    public TextView getColorText() {
        return colorText;
    }

    public ImageView getColorBlock() {
        return colorBlock;
    }

}

哦,以及updateView()方法的调试输出。

首次运行时:

SelectedIndex: 0 adapterPosition: 0
SelectedIndex: 0 adapterPosition: 1
SelectedIndex: 0 adapterPosition: 2
SelectedIndex: 0 adapterPosition: 3
SelectedIndex: 0 adapterPosition: 4
SelectedIndex: 0 adapterPosition: 5
SelectedIndex: 0 adapterPosition: 6

然后当我向上和向下滚动几次时,当新视图返回到屏幕时会调用updateView:

SelectedIndex: 0 adapterPosition: 7
SelectedIndex: 0 adapterPosition: 9
SelectedIndex: 0 adapterPosition: 2
SelectedIndex: 0 adapterPosition: 0
SelectedIndex: 0 adapterPosition: 7
SelectedIndex: 0 adapterPosition: 9
SelectedIndex: 0 adapterPosition: 2
SelectedIndex: 0 adapterPosition: 0
SelectedIndex: 0 adapterPosition: 7
SelectedIndex: 0 adapterPosition: 9

滚动时看看它是如何跳过adapterPositions的?因此,如果在滚动期间adapterPosition 8永远不会恢复,那么在updateView()方法中,adapterPosition8上的视图永远不会被关闭。

1 个答案:

答案 0 :(得分:0)

您必须将项目状态保留在“onBindViewHolder”方法中。 此外,你应该管理视图持有者内部的onItemClick,因为在那里,你正好在你想要的位置,所以基本上你会有:

YourAdapter.java

@Override
public void onBindViewHolder(YourViewHolder yourViewHolder, int i) {
    yourViewHolder.updateView();
}

YourViewHolder.java

class YourViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ 
    View itemView;
    public YourViewHolder(View itemView){
        this.itemView = itemView;
    }

    public void updateView(){
        //Do whatever you need to do regarding the item view
        this.itemView.setSelected(getAdapterPosition() == mSelectedIndex);
    }

    @Override
    public void onClick(View v) {
        int position = getAdapterPosition();
        if (position == mSelectedIndex){
            //It's the same view, deselect it or just leave it as it is
        } else {
           this.itemView.setSelected(true);
           mSelectedIndex = position;
        }
    }
}

请记住,不应将mSelectedIndex放在ViewHolder中,而应放在单独的类中。