在 recyclerview 片段中实现上下文操作模式的问题

Posted

技术标签:

【中文标题】在 recyclerview 片段中实现上下文操作模式的问题【英文标题】:Problems with implementing contextual action mode in recyclerview fragment 【发布时间】:2015-08-29 03:24:14 【问题描述】:

我从一堆教程中破解了我的第一个应用程序。在其中一个的帮助下,我在主要活动使用的片段中实现了 RecyclerView。现在我找到了another tutorial,我想用它来实现上下文动作模式的多选。最大的问题是,本教程没有使用片段。我试图重写代码以适应片段。我现在在 android Studio 中没有任何错误并且应用程序运行,但是当我实际执行 LongClick 来选择一个项目时,它会崩溃并出现 NullPointerException:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.view.ActionMode.setTitle(java.lang.CharSequence)' on a null object reference

我完全不知道为什么会发生这种情况以及整个多选的东西。

我认为每个涉及的类的相关部分是:

Adapter.java:

public class Adapter extends SelectableAdapter<Adapter.ViewHolder> 

private ArrayList<Meal> items = new ArrayList<>();

private Context context;
private ViewHolder.ClickListener clickListener;

public Adapter(ViewHolder.ClickListener clickListener) 
    super();
    this.clickListener = clickListener;



@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.fragment_today_items, parent, false);
    return new ViewHolder(v, clickListener);


@Override
public void onBindViewHolder(final ViewHolder holder, int position) 
    Meal currentMeal = items.get(position);


    holder.meal_typeicon.setImageResource(currentMeal.getBadgeIcon());
    holder.meal_price.setText(currentMeal.getPriceOutput());

    holder.meal_name.setText(isSelected(position) ? "SELECTED" : "NOT SELECTED");




public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener 
    @SuppressWarnings("unused")
    private static final String TAG = ViewHolder.class.getSimpleName();

    private RelativeLayout meal_item;

    private ClickListener listener;

    private TextView meal_name;
    private TextView meal_price;
    private TextView meal_contents;
    private TextView meal_contents_spelledout;
    private ImageView meal_typeicon;

    public ViewHolder(View itemView, ClickListener listener) 
        super(itemView);
        meal_item = (RelativeLayout) itemView.findViewById(R.id.meal_item);

        this.listener = listener;

        itemView.setOnClickListener(this);
        itemView.setOnLongClickListener(this);


        meal_name = (TextView) itemView.findViewById(R.id.meal_name);
        meal_price = (TextView) itemView.findViewById(R.id.meal_price);
        meal_contents = (TextView) itemView.findViewById(R.id.meal_contents);
        meal_contents_spelledout = (TextView) itemView.findViewById(R.id.meal_contents_spelledout);
        meal_typeicon = (ImageView) itemView.findViewById(R.id.meal_typeicon);
    

    @Override
    public void onClick(View v) 
        if (listener != null) 
            listener.onItemClicked(getAdapterPosition());
        
    

    @Override
    public boolean onLongClick(View v) 
        if (listener != null) 
            return listener.onItemLongClicked(getAdapterPosition());
        
        return false;
    

    public interface ClickListener 
        void onItemClicked(int position);

        boolean onItemLongClicked(int position);
    
 

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


public void setMealList(ArrayList<Meal> listMeals) 
    this.items = listMeals;
    notifyDataSetChanged();
    notifyItemRangeChanged(0, listMeals.size());


SelectableAdapter.java:

public abstract class SelectableAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> 
@SuppressWarnings("unused")
private static final String TAG = SelectableAdapter.class.getSimpleName();

private SparseBooleanArray selectedItems;


public SelectableAdapter() 
    selectedItems = new SparseBooleanArray();



/**
 * Indicates if the item at position position is selected
 * @param position Position of the item to check
 * @return true if the item is selected, false otherwise
 */
public boolean isSelected(int position) 
    return getSelectedItems().contains(position);



/**
 * Toggle the selection status of the item at a given position
 * @param position Position of the item to toggle the selection status for
 */
public void toggleSelection(int position) 

    if (selectedItems.get(position, false)) 
        selectedItems.delete(position);
     else 
        selectedItems.put(position, true);
    
    notifyItemChanged(position);


/**
 * Clear the selection status for all items
 */
public void clearSelection() 
    List<Integer> selection = getSelectedItems();
    selectedItems.clear();
    for (Integer i : selection) 
        notifyItemChanged(i);
    


/**
 * Count the selected items
 * @return Selected items count
 */
public int getSelectedItemCount() 
    return selectedItems.size();


/**
 * Indicates the list of selected items
 * @return List of selected items ids
 */
public List<Integer> getSelectedItems() 
    List<Integer> items = new ArrayList<>(selectedItems.size());
    for (int i = 0; i < selectedItems.size(); ++i) 
        items.add(selectedItems.keyAt(i));
    
    return items;


FragmentToday.java:

public class FragmentToday extends Fragment implements     Adapter.ViewHolder.ClickListener
private RequestQueue requestQueue;
public ArrayList<Meal> listMeals = new ArrayList<>();
private TextView textVolleyError;
private MealSorter mSorter = new MealSorter();
private Adapter adapter;
private ActionModeCallback actionModeCallback = new ActionModeCallback();
private ActionMode actionMode;

public static FragmentToday newInstance() 
    return new FragmentToday();


public FragmentToday() 


@Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    VolleySingleton volleySingleton = VolleySingleton.getInstance();
    requestQueue = volleySingleton.getRequestQueue();
    sendJsonRequest();



@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) 
    View view = inflater.inflate(R.layout.fragment_today, container, false);
    textVolleyError = (TextView) view.findViewById(R.id.textVolleyError);

    adapter = new Adapter(this);

    RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
    recyclerView.setAdapter(adapter);
    recyclerView.setItemAnimator(new DefaultItemAnimator());
    recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

    sendJsonRequest();
    return view;


@Override
public void onItemClicked(int position) 
    if (actionMode != null) 
        toggleSelection(position);
    


@Override
public boolean onItemLongClicked(int position) 
    if (actionMode != null) 
        ((AppCompatActivity) getActivity()).startSupportActionMode(actionModeCallback);
    

    toggleSelection(position);

    return true;


private void toggleSelection(int position) 
    adapter.toggleSelection(position);
    int count = adapter.getSelectedItemCount();

    if (count == 0) 
        actionMode.finish();
     else 
        actionMode.setTitle(String.valueOf(count));
        actionMode.invalidate();
    


private class ActionModeCallback implements ActionMode.Callback 
    @SuppressWarnings("unused")
    private final String TAG = ActionModeCallback.class.getSimpleName();

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) 
        mode.getMenuInflater().inflate (R.menu.menu_cam, menu);
        return true;
    

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) 
        return false;
    

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) 
        switch (item.getItemId()) 
            case R.id.favorite:
                // TODO: actually remove items
                Log.d(TAG, "menu_remove");
                mode.finish();
                return true;

            default:
                return false;
        
    

    @Override
    public void onDestroyActionMode(ActionMode mode) 
        adapter.clearSelection();
        actionMode = null;
    



【问题讨论】:

非常感谢你拯救了我的一天。 不错的示例已经获取 api 数据并将其设置为适配器 UI,如何通过删除此示例实现操作模式缺少一些代码,我认为请提供完整或示例链接 动作模式已填充,当我按下第二个项目时,一次只选择一个项目作为上下文动作模式,它没有被选中,请帮助我 如果我的活动在我长按显示不幸错误时没有 recyclerview 数据,请帮助我 【参考方案1】:

问题似乎来自FragmentToday.java 类的onItemLongClicked 方法。应该是:

@Override
public boolean onItemLongClicked(int position) 
    if (actionMode == null) 
        actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(actionModeCallback);
    

    toggleSelection(position);

    return true;

代替:

@Override
public boolean onItemLongClicked(int position) 
    if (actionMode != null) 
        ((AppCompatActivity) getActivity()).startSupportActionMode(actionModeCallback);
    

    toggleSelection(position);

    return true;

【讨论】:

以上是关于在 recyclerview 片段中实现上下文操作模式的问题的主要内容,如果未能解决你的问题,请参考以下文章

片段内的 RecyclerView 的 Kotlin OnItemClickListener

Android:RecyclerView 不显示片段中的列表项

RecyclerView onClickListener 适用于所有片段

如何在 RecyclerView 的 CardView 中实现对 Item Click 的操作以显示结果

如何在 RecyclerView 中实现 StartActivityForResult

在滚动列表中实现视频的播放(ListView & RecyclerView)