安卓recyclerView的findViewHolderForAdapterPosition方法返回null

32

我希望能够以编程方式点击recyclerView中的项目,我发现有一种方式可以实现:

recyclerView.findViewHolderForAdapterPosition(0).itemView.performClick();

但是对我来说不起作用,findViewHolderForAdapterPosition只会返回null。

我的代码有什么问题吗?

HistoryListAdapter:

public class HistoryListAdapter extends RecyclerView.Adapter<HistoryListAdapter.ViewHolder> {
private static ArrayList<RecordItem> recordItems;
private static FragmentActivity activity;
private static RecordList recordList;

public HistoryListAdapter(ArrayList<RecordItem> recordItems, FragmentActivity FA, RecordList FRL) {
    this.recordItems = recordItems;
    this.activity = FA;
    this.recordList = FRL;
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    // create a new view
    View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.recorditem, parent, false);
    return new ViewHolder(itemLayoutView);
}

@Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
    // TextView setText ...
}

// Return the size of the itemsData (invoked by the layout manager)
@Override
public int getItemCount() {
    return recordItems.size();
}

// inner class to hold a reference to each item of RecyclerView
public static class ViewHolder extends RecyclerView.ViewHolder{
    public TextView result, datetime;

    public ViewHolder(View itemLayoutView) {
        super(itemLayoutView);
        itemLayoutView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (activity != null) {
                    // do something...
                }
            }
        });
        result = (TextView) itemLayoutView.findViewById(R.id.result);
        heartrate = (TextView) itemLayoutView.findViewById(R.id.heartrate);
        datetime = (TextView) itemLayoutView.findViewById(R.id.datetime);
    }
}
}

记录列表:

RecyclerView listView = (RecyclerView) layoutView.findViewById(R.id.listView);
HistoryListAdapter listadapter = new HistoryListAdapter(itemsToShow, getActivity(), RecordList.this);
listView.swapAdapter(listadapter, false);
listView.findViewHolderForAdapterPosition(0).itemView.performClick();

我省略了一些代码,但这不应该影响我的代码的整体结构。


请查看此答案,以获得在RecyclerView中实现onclick函数的好方法:https://dev59.com/HYzda4cB1Zd3GeqPoJTK#31176508 - Smashing
6个回答

30
根据官方文档
如果已经调用了notifyDataSetChanged()但新的布局尚未计算,此方法将返回null,因为在计算布局之前,视图的新位置是未知的。
使用findViewHolderForAdapterPosition()是不安全的。
当你在listView.swapAdapter(listadapter, false);之后调用该方法时,由于notifyDataSetChanged()方法被调用,你将始终得到一个null作为结果。请注意保留HTML标记。

8
那么你要用什么?@Rami - ericn
2
@eric 在适配器外部管理viewHolder是一种不好的做法。 - Rami
24
你没有给出任何解决这个问题的答案,为什么?你只是说它不起作用...好吧...我们已经发现了。 - user25
2
在调用 notifyDataSetChanged 方法之后,可以使用 recyclerView.post() 方法。 - invisbo
2
这个答案没有给出任何解决问题的方案。 - Azizjon Kholmatov
显示剩余3条评论

29

你可以这样做:

   listView.postDelayed(new Runnable()
            {
                @Override
                public void run()
                {
                    if(listView.findViewHolderForAdapterPosition(0)!=null )
                    {

                        listView.findViewHolderForAdapterPosition(0).itemView.performClick();
                    }
                }
            },50);

1
感谢@Dharmaraj。非常好用。根据yigit的评论,这是因为位置没有布局(例如越界或已删除)。 - Faheem Kalsekar
9
永远不要这样做。你确定50毫秒对于老旧的设备来说足够吗?当你看到postDelayed时,请忽略答案(而且不仅限于这个问题,我在其他问题中也看到了很多类似的答案和评论)。这很愚蠢... - user25
2
@user25 虽然我理解你的意思,但即使没有延迟,调用 Handler.post() 在这种情况下也可以正常工作,因为它会将获取ViewHolder的任务放入 UI 线程队列的末尾,所以该任务将在UI设置完成 RecyclerViewViewHolder 及其他所有内容后才被调用。 - AxiomaticNexus
@user25,你说的不对。在许多情况下,即使我们不确定旧设备上的超时是否足够,postDelayed也会有所帮助。有时候甚至1毫秒的延迟效果最好。有时候没有其他解决方案。如果不确定,我们可以设置1000毫秒的延迟,在多种情况下都适用。 - CoolMind

5

虽然已经很晚了,不过或许对 Kotlin 时代的其他人有所帮助:

 binding.recycler.post {
     val view = binding.recycler.findViewHolderForAdapterPosition(position)?.itemView?.performClick()
 }

1
你可以这样做:

你能做到这一点:

postAndNotifyAdapter(new Handler(),mRecyclerView);



protected void postAndNotifyAdapter(final Handler handler, final RecyclerView recyclerView) {
    handler.post(new Runnable() {
        @Override
        public void run() {
            if ( recyclerView.findViewHolderForLayoutPosition(0)!=null) {
                // This will call first item by calling "performClick()" of view.
                recyclerView.findViewHolderForLayoutPosition(0).itemView.performClick();
            } else {
                //   
                postAndNotifyAdapter(handler, recyclerView);
            }
        }
    });
}

0

我不建议使用post delay。虽然它可以工作,但时间是任意的。

在recyclerview上使用OnPreDrawListener,时间将与下一个绘制周期对齐,并且可访问可见项。


0

嘿,我是这样解决问题的:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

private TextView mTotalSum;
protected int mSum;
protected ArrayList<BasketRVAdapter.ViewHolder> mViewHolders;

public BasketRVAdapter(JSONArray recyclerItems, Context context, TextView totalSum) {
    super(recyclerItems, context);
    mTotalSum = totalSum;
    mViewHolders = new ArrayList<>();
}

@Override
public RecyclerView.ViewHolder onCreateSwipeViewHolder(ViewGroup parent, int i) {
    View view = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.view_basket_rv_item, parent, true);

    return new ViewHolder(view);
}

@Override
public void onBindSwipeViewHolder(final RecyclerView.ViewHolder viewHolder, int position) {
    final BasketRVAdapter.ViewHolder holder = (BasketRVAdapter.ViewHolder) viewHolder;

    JSONObject object;
    try {
        object = mRecyclerItems.getJSONObject(position);
        final int priceValue = object.getInt("price");
        final int quantityValue = object.getInt("quantity");

        holder.setId(object.getInt("id"));
        holder.title.setText(object.getString("title"));
        holder.price.setText(String.format(mContext.getString(R.string.price), priceValue));
        holder.quantity.setText(String.valueOf(quantityValue));
        holder.sum.setText(String.valueOf(priceValue * quantityValue));

        holder.quantity.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void afterTextChanged(Editable editable) {
                String value = holder.quantity.getText().toString();

                if (value.equals("")) {
                    return;
                }

                if (value.equals("0")) {
                    Toast.makeText(mContext, R.string.null_value_error, Toast.LENGTH_SHORT).show();
                    return;
                }
                holder.sum.setText(String.valueOf(priceValue * Integer.valueOf(value)));
                setTotalSum();
            }

        });

        mViewHolders.add(holder);
        mSum += priceValue * quantityValue;

        if (position == getItemCount() - 1) {
            mTotalSum.setText(String.format(mContext.getString(R.string.total_sum), mSum));
        }
    } catch (JSONException e) {
        e.printStackTrace();
    }
}

@Override
public SwipeConfiguration onCreateSwipeConfiguration(Context context, int i) {
    return new SwipeConfiguration.Builder(context)
                                 .setLeftSwipeBehaviour(SwipeConfiguration.SwipeBehaviour.NORMAL_SWIPE)
                                 .setRightSwipeBehaviour(SwipeConfiguration.SwipeBehaviour.NO_SWIPE)
                                 .setLeftBackgroundColorResource(R.color.dt_basket_deleted)
                                 .setLeftUndoDescription(R.string.deleted)
                                 .setDescriptionTextColorResource(R.color.dt_white)
                                 .setLeftUndoable(true)
                                 .build();
}

@Override
public void onSwipe(int i, int i1, RecyclerView.ViewHolder holder) {
    try {
        if (i1 == SWIPE_LEFT) {
            remove(i);
            notifyItemRemoved(i);
            mViewHolders.remove(i);
            setTotalSum();
        }
    } catch (JSONException e) {
        e.printStackTrace();
    }
}

public int getSum() {
    return mSum;
}

public void setTotalSum() {
    int totalSum = 0;

    for (BasketRVAdapter.ViewHolder holder : mViewHolders) {
        totalSum += Integer.valueOf(holder.sum.getText().toString());
    }

    mSum = totalSum;
    mTotalSum.setText(String.format(mContext.getString(R.string.total_sum), totalSum));
}

public static class ViewHolder extends RecyclerView.ViewHolder {

    @Bind(R.id.trade_title)
    public TextView title;
    @Bind(R.id.trade_price)
    public TextView price;
    @Bind(R.id.trade_quantity)
    public EditText quantity;
    @Bind(R.id.sum)
    public TextView sum;

    private int mId;

    public ViewHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);
    }

    public int getId() {
        return mId;
    }

    public void setId(int id) {
        mId = id;
    }
}}
  1. 在类中添加了ArrayList mViewholders字段
  2. 在onBindSwipeViewHolder方法中将每个viewholder实例添加到mViewHolders arraylist中
  3. 在setTotalSum方法中使用mViewholders arraylist代替findViewHolderForadapterposition方法

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接