Як створити контекстне меню для RecyclerView


97

Як реалізувати контекстне меню для RecyclerView?Очевидно, що виклик registerForContextMenu(recyclerView)не працює. Я називаю це з фрагмента. Хтось мав якийсь успіх у реалізації цього?


Я в одному човні. Пробував різноманітні програми ListView за допомогою AdapterContextMenuInfo - але не вдається отримати інфопозицію
RoundSparrow hilltx

Я думаю, що дні контекстного меню закінчились.
Біной Бабу,

4
Гей , - я отримав це працює;) refeerence: stackoverflow.com/questions/2321332 / ... - ViewHolder для мене є OnClick слухач - я зробив це OnCreateContextMenuListener. Ключовим у всьому цьому є те, що я зрозумів, що одночасно може бути відкрито лише ОДНЕ Меню - тому адаптеру просто потрібна вкладка, про яку було сказано, який був останній пункт списку RecyclerView, у якому було натиснуто меню ... тоді фрагмент / активність може запитайте адаптер, коли він отримає фактичний клік пункту меню.
RoundSparrow hilltx

Відповіді:


92

Ви не можете безпосередньо реалізувати ці методи, такі як onClickListener , OnContextMenuListener тощо, оскільки RecycleView розширює android.view.ViewGroup . Тому ми не можемо безпосередньо використовувати цей метод. Ми можемо реалізувати ці методи в класі адаптера ViewHolder . Ми можемо використовувати контекстне меню в RecycleView так:

public static class ViewHolder extends RecyclerView.ViewHolder implements OnCreateContextMenuListener {

    TextView tvTitle;
    ImageView ivImage;

    public ViewHolder(View v) {
        super(v);
        tvTitle =(TextView)v.findViewById(R.id.item_title);
        v.setOnCreateContextMenuListener(this);


    }

Тепер ми дотримуємося тієї ж процедури, реалізуючи контекстне меню.

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {

    menu.setHeaderTitle("Select The Action");    
    menu.add(0, v.getId(), 0, "Call");//groupId, itemId, order, title   
    menu.add(0, v.getId(), 0, "SMS"); 

}

Якщо у вас виникли труднощі, запитайте у коментарі.


7
Гаразд, і тоді ми реалізуємо onContextItemSelectedна рівні активності / фрагмента. getTitleпрацює, getItemIdпрацює, але getMenuInfoвидає нуль. Отже, як дістатись до ViewHolder?
Michael Schmidt

@MichaelSchmidt можеш показати мені свій код, де ти реалізуєш інформацію контекстного меню.
Прабхакар

7
Трохи експериментуючи, я теж не можу змусити це працювати, getMenuInfo()повертає null onContextItemSelected(). Можливо, у людей, які змусили його працювати, трапляється onCreateContextMenu()метод з метою подальшої ієрархії ( RecyclerViewабо Fragment)? Це може спрацювати, але потім переходить до інших відповідей на це питання.
NeilS

3
Прабхакбар, ви не згадували, як отримати клікнутий елемент, ні вигляд?
Бенджамін Штурмер,

6
Ви можете використовувати, menu.add(this.getAdapterPosition(), v.getId(), 0, "Call");а потім у вашому методі зворотного виклику тест для item.getGroupId()отримання позиції
JacobPariseau

99

Дякуємо за інформацію та коментарі. Мені вдалося досягти ContextMenuдля предметів у Recyclerview.

Ось що я зробив

у onViewCreatedметоді фрагмента або методу діяльності onCreate:

registerForContextMenu(mRecyclerView);

Потім в Adapter додайте

private int position;

public int getPosition() {
    return position;
}

public void setPosition(int position) {
    this.position = position;
}

змусити ViewHolderклас реалізуватиOnCreateContextMenuListener

public static class ViewHolder extends RecyclerView.ViewHolder 
        implements View.OnCreateContextMenuListener {

    public ImageView icon;

    public TextView fileName;
    public ImageButton menuButton;


    public ViewHolder(View v) {
        super(v);
        icon = (ImageView)v.findViewById(R.id.file_icon);
        fileName = (TextView)v.findViewById(R.id.file_name);
        menuButton = (ImageButton)v.findViewById(R.id.menu_button);
        v.setOnCreateContextMenuListener(this);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, 
        ContextMenu.ContextMenuInfo menuInfo) {
        //menuInfo is null
        menu.add(Menu.NONE, R.id.ctx_menu_remove_backup, 
            Menu.NONE, R.string.remove_backup);
        menu.add(Menu.NONE, R.id.ctx_menu_restore_backup,
            Menu.NONE, R.string.restore_backup);
    }
}

onBindViewHolderметод add OnLongClickListenerна holder.itemView, щоб зафіксувати позицію до завантаження контекстного меню:

holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        setPosition(holder.getPosition());
        return false;
    }
});

Потім onViewRecycledвидаліть слухач, щоб не було проблем із посиланнями. (може не знадобитися).

@Override
public void onViewRecycled(ViewHolder holder) {
    holder.itemView.setOnLongClickListener(null);
    super.onViewRecycled(holder);
}

Нарешті, у фрагменті / дії перевизначте наступне onContextItemSelected:

@Override
public boolean onContextItemSelected(MenuItem item) {
    int position = -1;
    try {
        position = ((BackupRestoreListAdapter)getAdapter()).getPosition();
    } catch (Exception e) {
        Log.d(TAG, e.getLocalizedMessage(), e);
        return super.onContextItemSelected(item);
    }
    switch (item.getItemId()) {
        case R.id.ctx_menu_remove_backup:
            // do your stuff
            break;
        case R.id.ctx_menu_restore_backup:
            // do your stuff
            break;
    }
    return super.onContextItemSelected(item);
}

1
position = ((BackupRestoreListAdapter) getAdapter ()). getPosition (); -> отримати помилку: метод getAdapter () не визначений для типу ..., чи можете ви показати метод
getAdapter

1
Чудова пропозиція, спасибі. Незначне: viewHolder.getPosition () застаріле. Яка ваша порада щодо вдосконалення?
tm1701

10
використовувати viewHolder.getAdapterPosition () замість getPosition ()
Сагар Чавада

2
Для тих, хто отримує помилку getAdapter (), я вирішив її, збереживши посилання на мій RecyclerView, а потім використав її як: ((BackupRestoreListAdapter) recyclerView.getAdapter ()). GetPosition ();
Kevin Amorim

1
А де саме R.id.ctx_menu_remove_backup?
akubi

29

Поточна відповідь неправильна. Ось робоча реалізація:

public class ContextMenuRecyclerView extends RecyclerView {

  private RecyclerViewContextMenuInfo mContextMenuInfo;

  @Override
  protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
    return mContextMenuInfo;
  }

  @Override
  public boolean showContextMenuForChild(View originalView) {
    final int longPressPosition = getChildPosition(originalView);
    if (longPressPosition >= 0) {
        final long longPressId = getAdapter().getItemId(longPressPosition);
        mContextMenuInfo = new RecyclerViewContextMenuInfo(longPressPosition, longPressId);
        return super.showContextMenuForChild(originalView);
    }
    return false;
  }

  public static class RecyclerViewContextMenuInfo implements ContextMenu.ContextMenuInfo {

    public RecyclerViewContextMenuInfo(int position, long id) {
        this.position = position;
        this.id = id;
    }

    final public int position;
    final public long id;
  }
}

У вашому фрагменті (або діяльності):

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    mRecyclerView = view.findViewById(R.id.recyclerview);
    registerForContextMenu(mRecyclerView);
}

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    // inflate menu
    MenuInflater inflater = getActivity().getMenuInflater();
    inflater.inflate(R.menu.my_context_menu, menu);
}

@Override
public boolean onContextItemSelected(MenuItem item) {
    return super.onContextItemSelected(item);
    RecyclerViewContextMenuInfo info = (RecyclerViewContextMenuInfo) item.getMenuInfo();
    // handle menu item here
}

І нарешті, у вашому ViewHolder:

class MyViewHolder extends RecyclerView.View.ViewHolder {
    ...
    private void onLongClick() {
        itemView.showContextMenu();
    }
}

Будь ласка, додайте конструктор RecyclerView, який вам потрібно було реалізувати на yout ContextMenuRecyclerView. А також, getChildPosition()зараз застаріло. Я використовував getChildAdapterPosition()замість цього.
Хуан Хосе Мелеро Гомес

1
Примітка: Вибачте, я забув додати: getChildPosition()є застарілим у com.android.support:recyclerview-v7:22.0.0.
Хуан Хосе Мелеро Гомес

1
це не працює. ви не можете отримати доступ до getView (). showContextMenu () з RecyclerView.View.ViewHolder.
Mulgard

4
Це працює для мене. Але немає необхідності мати onLongClick () у MyViewHolder, досить встановити itemView.setLongClickable (true) у конструкторі, контекстне меню з'явиться, коли OnLongClickListener не зареєстрований.
вільхи

2
Важливо також: розширюючи RecyclerView до ContextMenuRecyclerView, не забудьте ДОДАТИ КОНСТРУКТОРІВ, запропоновані IDE. Зокрема, якщо ви не реалізуєте двоаргументний конструктор, який приймає Context та AttributeSet, Android не зможе надути ваш XML-макет.
lidkxx

24

Спробуйте це для Viewелемента в recycleView

.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
        @Override
        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
            menu.add("delete").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
                @Override
                public boolean onMenuItemClick(MenuItem item) {

                    //do what u want
                    return true;
                }
            });
        }
    });

Ви можете використовувати їх для встановлення даних для ViewHolderелемента


Саме те, що мені потрібно для контекстного меню у поданні зображення всередині переробленого подання
Фемос

2
Для тих, хто дивиться на це зараз, це працює лише на API 23 і вище.
SWoo

16

Відповідь Прабхакара правильна, але він не пояснив, як отримати дані, що стосуються натиснутого елемента, коли вибрано пункт контекстного меню. Ми можемо використовувати onContextItemSelectedзворотний виклик, але в цьому випадку ContextMenuInfoнедоступний ( null) (якщо getContextMenuInfo()метод не замінено для натиснутого подання). Отже, найпростішим рішенням є додавання OnMenuItemClickListenerбезпосередньо до MenuItem.

private class ViewHolder extends RecyclerView.ViewHolder {
    private final TextView mTitleTextView;
    private MyItemData mData;

    public ViewHolder(View view) {
        super(view);

        mTitleTextView = (TextView)view.findViewById(R.id.title);

        view.setOnCreateContextMenuListener(mOnCreateContextMenuListener);
    }

    public void bind(@NonNull MyItemData data) {
         mData = data;

         String title = mData.getTitle();
         mTitleTextView.setText(title);
    }

    private final View.OnCreateContextMenuListener mOnCreateContextMenuListener = new View.OnCreateContextMenuListener() {
        @Override
        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
            if (mData!= null) {
                MenuItem myActionItem = menu.add("My Context Action");
                myActionItem.setOnMenuItemClickListener(mOnMyActionClickListener);
            }
        }
    };

    private final MenuItem.OnMenuItemClickListener mOnMyActionClickListener = new MenuItem.OnMenuItemClickListener() {
        @Override
        public boolean onMenuItemClick(MenuItem item) {
            //todo: process item click, mData is available here!!!
            return true;
        }
    };
}

1
Чітко поясніть, що і як робить ваш фрагмент коду. Недостатньо лише скопійованого коду, навіть якщо він вартий.
Петер

Це видається розумним компромісом, якщо ви не хочете створювати власні підкласи RecyclerViewпросто для заміни getContextMenuInfoтощо, навіть якщо це не настільки ефективно, як дозволяти фрагменту / дії обробляти кліки. Слухачі матимуть доступ до даних у власнику, тому вам не потрібно знати місце. І теоретично ви в будь-якому випадку можете кешувати позицію при прив'язці у своєму адаптері та використати делегування для виклику власника, якщо потрібно, хоча Contextіноді може бути достатньо використання одного з пов'язаних подань.
qix

10

Відповідь @ Renaud спрацювала для мене, але спочатку потрібно було кілька виправлень коду. Це наче він розмістив фрагменти з декількох різних ітерацій свого коду. Зміни, які необхідно внести:

  • RecyclerContextMenuInfoі RecyclerViewContextMenuInfoє одним класом. Виберіть ім’я та дотримуйтесь його.
  • ViewHolderПовинен реалізувати View.OnLongClickListener, і НЕ забудьте зателефонувати setOnLongClickListener()з цього питання в конструкторі.
  • У onLongClick()слухача getView().showContextMenu()це абсолютно неправильно. Ви повинні зателефонувати showContextMenuForChild()у свій ContextMenuRecyclerView, інакше ContextMenuInfoви потрапите onCreateContextMenu()і onContextItemSelected()буде нульовим.

Мій відредагований код нижче:

ContextMenuRecyclerView:

public class ContextMenuRecyclerView extends RecyclerView {

    private RecyclerViewContextMenuInfo mContextMenuInfo;

    @Override
    protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
        return mContextMenuInfo;
    }

    @Override
    public boolean showContextMenuForChild(View originalView) {
        final int longPressPosition = getChildPosition(originalView);
        if (longPressPosition >= 0) {
            final long longPressId = getAdapter().getItemId(longPressPosition);
                mContextMenuInfo = new RecyclerViewContextMenuInfo(longPressPosition, longPressId);
            return super.showContextMenuForChild(originalView);
        }
        return false;
    }

    public static class RecyclerViewContextMenuInfo implements ContextMenu.ContextMenuInfo {

        public RecyclerViewContextMenuInfo(int position, long id) {
            this.position = position;
            this.id = id;
        }

        final public int position;
        final public long id;
    }
}

У вашому фрагменті:

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    mRecyclerView = view.findViewById(R.id.recyclerview);
    registerForContextMenu(mRecyclerView);
}

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    // inflate menu here
    // If you want the position of the item for which we're creating the context menu (perhaps to add a header or something):
    int itemIndex = ((ContextMenuRecyclerView.RecyclerViewContextMenuInfo) menuInfo).position;
}

@Override
public boolean onContextItemSelected(MenuItem item) {
    ContextMenuRecyclerView.RecyclerViewContextMenuInfo info = (ContextMenuRecyclerView.RecyclerViewContextMenuInfo) item.getMenuInfo();
    // handle menu here - get item index or ID from info
    return super.onContextItemSelected(item);
}

У вашому ViewHolder:

class MyViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener {

    public MyViewHolder( View itemView ) {
        super( itemView );
        itemView.setOnLongClickListener( this );
    }

    @Override public boolean onLongClick() {
        recyclerView.showContextMenuForChild( v );
        return true;
    }
}

Також переконайтеся, що ви заміните RecyclerViewна ContextMenuRecyclerViewу своєму макеті!


1
Дякую @ josh2112, що вказав мені на помилки, я щойно їх виправив - АЛЕ щодо останньої точки, ви можете замінити recyclerView.showContextMenuForChild(itemView);на itemView.showContextMenu().
Рено Серрато

1
Важливо також: розширюючи RecyclerView до ContextMenuRecyclerView, не забудьте ДОДАТИ КОНСТРУКТОРІВ, запропоновані IDE. Зокрема, якщо ви не реалізуєте двоаргументний конструктор, який приймає Context та AttributeSet, Android не зможе надути ваш XML-макет.
lidkxx

5

Ось чистий спосіб використовувати контекст меню для елементів RecyclerView

По-перше, вам потрібна позиція предмета

У класі Adapter:

 /**
 * Custom on long click item listener.
 */
onLongItemClickListener mOnLongItemClickListener;

public void setOnLongItemClickListener(onLongItemClickListener onLongItemClickListener) {
    mOnLongItemClickListener = onLongItemClickListener;
}

public interface onLongItemClickListener {
    void ItemLongClicked(View v, int position);
}

У onBindViewHolderзачепити призначений для користувача слухач:

        // Hook our custom on long click item listener to the item view.
        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                if (mOnLongItemClickListener != null) {
                    mOnLongItemClickListener.ItemLongClicked(v, position);
                }

                return true;
            }
        });

У MainActivity (Activity / Fragment) створіть поле:

private int mCurrentItemPosition;

У вашому об’єкті Adapter встановіть спеціальний слухач:

    mAdapter.setOnLongItemClickListener(new FileAdapter.onLongItemClickListener() {
        @Override
        public void ItemLongClicked(View v, int position) {
            mCurrentItemPosition = position;
        }
    });

Тепер у вас є смачна позиція для будь-якого елемента, на якому ви довго натискали 😋

По-друге, створіть своє меню

У меню res -> Створіть файл із вашим пунктом меню context_menu_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/delete" android:title="Delete"/>
<item android:id="@+id/share" android:title="Share"/>
</menu>

В MainActivity: реалізуйте і те, onCreateContextMenuі onContextItemSelected:

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.context_menu_main, menu);
}

@Override
public boolean onContextItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.delete) {

    }

    if (id == R.id.share) {

    }

    return true;
}

По-третє, поверніться до об’єкта Adapter

  1. Зареєструйте своє контекстне меню.
  2. показати контекстне меню.

    registerForContextMenu(mRecyclerView);
    mAdapter.setOnLongItemClickListener(new FileAdapter.onLongItemClickListener() {
        @Override
        public void ItemLongClicked(View v, int position) {
            mCurrentItemPosition = position;
            v.showContextMenu();
        }
    });

Сподіваюся, я нічого не забуваю 🤔

Більше інформації в меню Документація


Дякую! Як передати дані в контекстне меню? Наприклад, ідентифікатор товару, текст елемента тощо.
CoolMind

4

Ось простіший спосіб зробити це з Котліном, який працював у мене. Основною проблемою є з’ясування положення предмета, на який було натиснуто. Всередині адаптера ви можете розмістити цей фрагмент коду, і він зможе зафіксувати позицію елемента, для якого відображається контекстне меню; це все.

override fun onBindViewHolder(holder: YourViewHolder, position: Int) {

...     

    holder.view.setOnCreateContextMenuListener { contextMenu, _, _ -> 
            contextMenu.add("Add").setOnMenuItemClickListener {
                    longToast("I'm pressed for the item at position => $position")
                    true    
            }       
    }       

} 

2
Це найприродніший і контрольований спосіб зробити це
Німа

3

Я поєднав своє рішення з рішенням від @Hardik Shah:

У діяльності я маю:

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    if (v.getId() == R.id.rvQuests) {
        getMenuInflater().inflate(R.menu.list_menu, menu);
    }
}

В адаптері я маю:

private MainActivity context;
private int position;

public int getPosition() {
    return position;
}

public void setPosition(int position) {
    this.position = position;
}

public QuestsAdapter(MainActivity context, List<Quest> objects) {
    this.context = context;
    this.quests.addAll(objects);
}

public class QuestViewHolder extends RecyclerView.ViewHolder {
    private QuestItemBinding questItemBinding;

    public QuestViewHolder(View v) {
        super(v);
        questItemBinding = DataBindingUtil.bind(v);
        v.setOnCreateContextMenuListener(context);
    }
}

@Override
public void onBindViewHolder(final QuestViewHolder holder, int position) {
    Quest quest = quests.get(position);
    holder.questItemBinding.setQuest(quest);
    holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            setPosition(holder.getAdapterPosition());
            return false;
        }
    });
}

@Override
public void onViewRecycled(QuestViewHolder holder) {
    holder.itemView.setOnLongClickListener(null);
    super.onViewRecycled(holder);
}

У фрагменті я маю:

@Override
public boolean onContextItemSelected(MenuItem item) {
    int position = ((QuestsAdapter) questsList.getAdapter()).getPosition();
    switch (item.getItemId()) {
        case R.id.menu_delete:
            Quest quest = questsAdapter.getItem(position);
            App.getQuestManager().deleteQuest(quest);
            questsAdapter.remove(quest);
            checkEmptyList();
            return true;
        default:
            return super.onContextItemSelected(item);
    }
}

2

Я, можливо, спізнююся на вечірку, але у мене є робоче рішення . Я зробив для цього суть.

Додати контекстне меню в RecyclerView

ActivityName.java

//Import Statements

public class ActivityName extends AppCompatActivity {
    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_birthdays);


        //Recycle View
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
        mLayoutManager = new LinearLayoutManager(getApplicationContext());
        mRecyclerView.setLayoutManager(mLayoutManager);
        mAdapter = new BirthdaysListAdapter(data, this);
        mRecyclerView.setAdapter(mAdapter);


    }

RecyclerAdapter.java

//Import Statements


public class BirthdaysListAdapter extends RecyclerView.Adapter<BirthdaysListAdapter.ViewHolder> {
    static Context ctx;

    private List<typeOfData> Data;


    public BirthdaysListAdapter(List<typeOfData> list, Context context) {
        Data = list;
        this.ctx = context;

    }

    BirthdaysListAdapter() {
    }

    public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener {
        public TextView name;
        public TextView Birthday;
        public ImageView colorAlphabet;
        public TextView textInImg;


        public ViewHolder(View v) {
            super(v);
            name = (TextView) v.findViewById(R.id.name);
            Birthday = (TextView) v.findViewById(R.id.Birthday);
            colorAlphabet = (ImageView) v.findViewById(R.id.colorAlphabet);
            textInImg = (TextView) v.findViewById(R.id.textInImg);


            v.setOnCreateContextMenuListener(this); //REGISTER ONCREATE MENU LISTENER
        }

        @Override
        public void onCreateContextMenu(ContextMenu menu, View v                         //CREATE MENU BY THIS METHOD
                                        ContextMenu.ContextMenuInfo menuInfo) {
            new BirthdaysListAdapter().info = (AdapterView.AdapterContextMenuInfo) menuInfo;
            MenuItem Edit = menu.add(Menu.NONE, 1, 1, "Edit");
            MenuItem Delete = menu.add(Menu.NONE, 2, 2, "Delete");
            Edit.setOnMenuItemClickListener(onEditMenu);
            Delete.setOnMenuItemClickListener(onEditMenu);


        }
//ADD AN ONMENUITEM LISTENER TO EXECUTE COMMANDS ONCLICK OF CONTEXT MENU TASK
        private final MenuItem.OnMenuItemClickListener onEditMenu = new MenuItem.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {


                DBHandler dbHandler = new DBHandler(ctx);
                List<WishMen> data = dbHandler.getWishmen();

                switch (item.getItemId()) {
                    case 1:
                        //Do stuff
                        break;

                    case 2:
                       //Do stuff

                        break;
                }
                return true;
            }
        };


    }


    public List<ViewBirthdayModel> getData() {
        return Data;
    }


    @Override
    public long getItemId(int position) {

        return super.getItemId(position);
    }


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

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        holder.name.setText(Data.get(position).getMan().getName());
        holder.Birthday.setText(Data.get(position).getMan().getBday());
        holder.colorAlphabet.setBackgroundColor(Color.parseColor(Data.get(position).getColor()));
        holder.textInImg.setText(String.valueOf(Data.get(position).getMan().getName().toUpperCase().charAt(0)));
           }


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

    private int position;

    public int getPosition() {

        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }

}

1

Привіт, хлопці, вийшла одна альтернатива, яка працює для мене. Я просто реєструю свій itemView за допомогою registerContextMenu y конструкторі ViewHolder, а також встановлюю onLongClikcListener на те саме представлення. У реалізації onLongClick (View v) я просто отримую клацану позицію за допомогою getLayoutPosition () і зберігаю у змінній екземпляра (я створив клас для представлення цих даних, як і ContextMenuInfo, як очікується, що буде працювати), але більш важливим є зробити переконайтеся, що ви повертаєте помилкове в цьому методі. Все, що вам зараз потрібно зробити, це у вас на onContextItemSelected (пункт MenuItem), прочитайте дані, які ви зберігаєте у своїй змінній екземпляра, і якщо це дійсно, ви продовжите свої дії. Ось фрагмент.

  public MyViewHolder(View itemView){
super(itemView);
registerForContextMenu(itemView);
itemView.setOnLongClickListener(this);
}

Я роблю ViewHolder реалізовує OnLongClickListener, але ви можете зробити це будь-яким способом, який вам більше подобається.

@Override
public boolean onLongClick(View v){
    mCurrentLongItem = new ListItemInfo(v.getId(), getLayoutPosition());
    return false; // REMEMBER TO RETURN FALSE.
  }

Ви також можете встановити це в адаптері або в іншому поданні, яке ви маєте у ViewHolder (тобто TextView). Важливою є реалізація onLongClik ().

@Override
public boolean onContextItemSelected(MenuItem item) {
    switch (item.getItemId()){
        case R.id.client_edit_context_menu:
            if(mCurrentLongItem != null){
                 int position = mCurrentLongItem.position;
                //TAKE SOME ACTIONS.
                mCurrentLongItem = null;
            }
            return true;
    }
    return super.onContextItemSelected(item);
}

Найкраще те, що ви все одно можете обробити подію LongClick, яка повертає істину у випадках, коли ви цього хочете, і conextMenu не з'явиться.

Цей метод працює, оскільки registerForContextView робить View LongClickable, і коли настає час обробити ContextMenu, система викликає perforLongClick, який спочатку викликає реалізацію onLongClick, а якщо повертає false, то викликає showContextMenu.


1

Я вже деякий час використовую це рішення і дуже добре для мене спрацювало.

public class CUSTOMVIEWNAME extends RecyclerView { 

public CUSTOMVIEWNAME(Context context) {
    super(context);
}

public CUSTOMVIEWNAME (Context context, AttributeSet attrs) {
    super(context, attrs);
}

public CUSTOMVIEWNAME (Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

private RecyclerContextMenuInfo mContextMenuInfo;

@Override
protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
    return mContextMenuInfo;
}

@Override
public boolean showContextMenuForChild(View originalView) {
    final int longPressPosition = getChildAdapterPosition(originalView);
    if (longPressPosition >= 0) {
        final long longPressId = getAdapter().getItemId(longPressPosition);
        mContextMenuInfo = new RecyclerContextMenuInfo(longPressPosition,    `           longPressId);
        return super.showContextMenuForChild(originalView);
    }
    return false;
}

public class RecyclerContextMenuInfo implements ContextMenu.ContextMenuInfo             {

    public RecyclerContextMenuInfo(int position, long id) {
        this.position = position;
        this.id = id;
    }

    final public int position;
    final public long id;
}
}

Тепер у своєму фрагменті або Дії застосуйте такі методи.

  @Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);

    // Inflate Menu from xml resource
    MenuInflater menuInflater = getMenuInflater();
    menuInflater.inflate(R.menu.context_menu, menu);
}

@Override
public boolean onContextItemSelected(MenuItem item) {
    ContextMenuRecyclerView.RecyclerContextMenuInfo info = (ContextMenuRecyclerView.RecyclerContextMenuInfo) item.getMenuInfo();
    Toast.makeText(InstanceOfContext , " User selected  " + info.position, Toast.LENGTH_LONG).show();

    return false;
}

Нарешті, зареєструйтесь для контекстного меню на перегляді утилізатора

   //for showing a popup on LongClick of items in recycler.
    registerForContextMenu(recyclerView);

Це має працювати!


1

Ось як можна реалізувати контекстне меню для RecyclerView та отримати позицію елемента, для якого вибрано пункт контекстного меню:

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

... 

@Override
public void onBindViewHolder(@NonNull final ViewHolder viewHolder, int position) {

    ...

    viewHolder.itemView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
        @Override
        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
            menu.add(0, R.id.mi_context_disable, 0, R.string.text_disable)
                    .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
                        @Override
                        public boolean onMenuItemClick(MenuItem item) {
                            // can do something with item at position given below,
                            // viewHolder is final
                            viewHolder.getAdapterPosition();
                            return true;
                        }
                    });
            menu.add(0, R.id.mi_context_remove, 1, R.string.text_remove)
                    .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
                        @Override
                        public boolean onMenuItemClick(MenuItem item) {                                
                            // can do something with item at position given below,
                            // viewHolder is final
                            viewHolder.getAdapterPosition();
                            return true;
                        }
                    });
        }
    });
}

static class ViewHolder extends RecyclerView.ViewHolder {
    private View itemView;

    private ViewHolder(@NonNull View itemView) {
        super(itemView);
        this.itemView = itemView;
    }
}

}


1

Рішення для тих, хто хоче отримати ідентифікатор товару під час дзвінка ContextMenu.

Якщо у вас є RecyclerViewтакі предмети (що містять інтерактивні елементи ImageView):

список

тоді вам слід отримувати зворотні дзвінки від onClickListener.

Перехідник

class YourAdapter(private val contextMenuCallback: ContextMenuCallback) :
    RecyclerView.Adapter<YourAdapter.ViewHolder>() {

    private var items: MutableList<Item> = mutableListOf()

    ...

    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
        val item = items[position] as Item
        updateItem(viewHolder, item)

        setOnClickListener(viewHolder.itemView, items[position].id, items[position].title)
    }

    private fun setOnClickListener(view: View, id: Int, title: String) {
//        view.setOnClickListener { v ->  }
        // A click listener for ImageView `more`.
        view.more.setOnClickListener {
            // Here we pass item id, title, etc. to Fragment.
            contextMenuCallback.onContextMenuClick(view, id, title)
        }
    }


    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val titleTextView: TextView = itemView.title
    }

    class Item(
        val id: Int,
        val title: String
    )

    interface ContextMenuCallback {
        fun onContextMenuClick(view: View, id: Int, title: String)
    }
}

Фрагмент

class YourFragment : Fragment(), YourAdapter.ContextMenuCallback {

    private var adapter: YourAdapter? = null
    private var linearLayoutManager: LinearLayoutManager? = null
    private var selectedItemId: Int = -1
    private lateinit var selectedItemTitle: String


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        adapter = YourAdapter(this)
        view.recycler_view.apply {
            layoutManager = linearLayoutManager
            adapter = this@YourFragment.adapter
            setHasFixedSize(true)
        }

        registerForContextMenu(view.recycler_view)
    }

    override fun onCreateContextMenu(menu: ContextMenu?, v: View?,
                                     menuInfo: ContextMenu.ContextMenuInfo?) {
        activity?.menuInflater?.inflate(R.menu.menu_yours, menu)
    }

    override fun onContextItemSelected(item: MenuItem?): Boolean {
        super.onContextItemSelected(item)
        when (item?.itemId) {
            R.id.action_your -> yourAction(selectedItemId, selectedItemTitle)
            ...
        }
        return true
    }

    override fun onContextMenuClick(view: View, id: Int, title: String) {
        // Here we accept item id, title from adapter and show context menu.
        selectedItemId = id
        selectedItemTitle = title
        view.showContextMenu()
    }
}

Увага!

Якщо ви використовуєте на ViewPagerоснові одного фрагмента (усі сторінки мають подібні списки), ви зіткнетеся з проблемою. Коли ви перевизначите, onContextItemSelectedщоб зрозуміти, який пункт меню був обраний, ви отримаєте ідентифікатор елемента списку з першої сторінки! Щоб подолати цю проблему, див. Неправильний фрагмент у ViewPager отримує виклик onContextItemSelected .


0

Я боровся з цим, оскільки Android не справляється з цим добре в RecyclerView, який дуже добре працював для ListView.

Найскладніший фрагмент - це те, що фрагмент ContextMenuInfo вбудований всередину подання, яке ви не можете легко прикріпити, крім перевизначення подання.

Тож вам знадобиться обгортка, яка допоможе вам доставити інформацію про позицію в Activity.

public class RecyclerContextMenuInfoWrapperView extends FrameLayout {
private RecyclerView.ViewHolder mHolder;
private final View mView;

public RecyclerContextMenuInfoWrapperView(View view) {
    super(view.getContext());
    setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
    mView = view;
    addView(mView);
}

public void setHolder(RecyclerView.ViewHolder holder) {
    mHolder = holder;
}

@Override
protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
    return new RecyclerContextMenuInfo(mHolder.getPosition(), mHolder.getItemId());
}

public static class RecyclerContextMenuInfo implements ContextMenu.ContextMenuInfo {

    public RecyclerContextMenuInfo(int position, long id) {
        this.position = position;
        this.id = id;
    }

    final public int position;
    final public long id;
}

}

Потім у вашому RecyclerAdapter, коли ви створюєте ViewHolders, вам потрібно встановити Wrapper як кореневий вигляд та зареєструвати contextMenu у кожному поданні.

public static class AdapterViewHolder extends RecyclerView.ViewHolder {
    public AdapterViewHolder(  View originalView) {
        super(new RecyclerContextMenuInfoWrapperView(originalView);
        ((RecyclerContextMenuInfoWrapperView)itemView).setHolder(this);
        yourActivity.registerForContextMenu(itemView);
        itemView.setOnCreateContextMenuListener(yourListener);
    }

}

І нарешті, у своїй діяльності ви зможете робити те, що зазвичай робите:

@Override
public boolean onContextItemSelected(MenuItem item) {
    int position = ((RecyclerContextMenuInfoWrapperView.RecyclerContextMenuInfo)item.getMenuInfo()).position;
    // do whatever you need as now you have access to position and id and everything

0

Найкращим було використовувати контекстне меню з поданням утилізатора, якщо ви створили власний вигляд утилізатора та перевизначили getContextMenuInfo()метод та повернули власний екземпляр інформаційного об'єкта контекстного меню, щоб ви могли отримувати позиції, коли він був створений та коли клацнуло меню:

@Override
protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
    return mContextMenuInfo;
}

Погляньте на суть, яку я створив:

https://gist.github.com/resengupta/2b2e26c949b28f8973e5


0

Трохи розширивши деякі з наведених вище відповідей, якщо ви хочете уникнути ручного визначення меню в коді в Adapter / ViewHolder, тоді ви можете використовувати PopupMenu і надути параметри меню зі стандартного файлу ресурсу menu.xml.

Приклад нижче показує це, включаючи можливість передавати слухачеві, який ви можете реалізувати у своєму фрагменті / діяльності, щоб відповідати на кліки контекстного меню.

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

private List<CustomObject> objects;
private OnItemSelectedListener listener;
private final boolean withContextMenu;

class ViewHolder extends RecyclerView.ViewHolder
        implements View.OnClickListener, View.OnCreateContextMenuListener, PopupMenu.OnMenuItemClickListener {

    @BindView(R.id.custom_name)
    TextView name;

    @BindView(R.id.custom_value)
    TextView value;

    ViewHolder(View view) {
        super(view);
        ButterKnife.bind(this, view);
        view.setOnClickListener(this);
        if (withContextMenu) {
            view.setOnCreateContextMenuListener(this);
        }
    }

    @Override
    public void onClick(View v) {
        int position = getAdapterPosition();
        if (listener != null) {
            listener.onCustomerSelected(objects.get(position));
        }
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        PopupMenu popup = new PopupMenu(v.getContext(), v);
        popup.getMenuInflater().inflate(R.menu.custom_menu, popup.getMenu());
        popup.setOnMenuItemClickListener(this);
        popup.show();
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        if (listener != null) {
            CustomObject object = objects.get(getAdapterPosition());
            listener.onCustomerMenuAction(object, item);
        }
        return false;
    }
}

public CustomerAdapter(List<CustomObject> objects, OnItemSelectedListener listener, boolean withContextMenu) {
    this.listener = listener;
    this.objects = objects;
    this.withContextMenu = withContextMenu;
}

public interface OnItemSelectedListener {

    void onSelected(CustomObject object);

    void onMenuAction(CustomObject object, MenuItem item);
}

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

@Override
public void onBindViewHolder(CustomAdapter.ViewHolder holder, int position) {
    CustomObject object = objects.get(position);
    holder.name.setText(object.getName());
    holder.value.setText(object.getValue());
}

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

Повний зміст тут https://gist.github.com/brettwold/45039b7f02ce752ae0d32522a8e2ad9c


0

Ви можете передати OnCreateContextMenuListener у ViewHolder на прив'язку. Цей слухач може створити власне меню для кожного елемента даних. Просто додайте setOnCreateContextMenuListener у ваш ViewHolder і зателефонуйте йому під час прив'язки.

     public static class ItemViewHolder extends RecyclerView.ViewHolder
     {


        public ItemViewHolder(View itemView) {
            super(itemView);


        }
        void setOnCreateContextMenuListener(View.OnCreateContextMenuListener listener) {
            itemView.setOnCreateContextMenuListener(listener);
        }

}

У адаптері:

      @Override
     public void onBindViewHolder(ItemViewHolder viewHolder,
                    int position) {
         final MyObject myObject = mData.get(position);
         viewHolder.setOnCreateContextMenuListener(new OnCreateContextMenuListener(){

                    @Override
                    public void onCreateContextMenu(ContextMenu menu,
                            View v, ContextMenuInfo menuInfo) {
                        switch (myObject.getMenuVariant() {
                            case MNU_VARIANT_1:
                                menu.add(Menu.NONE, CTX_MNU_1, 
                                        Menu.NONE,R.string.ctx_menu_item_1);    
                                menu.add(Menu.NONE, CTX_MNU_2,Menu.NONE, R.string.ctx_menu_item_2); 
                            break;
                            case MNU_VARIANT_2:
                                menu.add(Menu.NONE, CTX_MNU_3,Menu.NONE, R.string.ctx_menu_item_3); 
                            break;
                            default:
                                menu.add(Menu.NONE, CTX_MNU_4, 
                                        Menu.NONE, R.string.ctx_menu_item_4);   

                        }
                    }

                });
     }

0

У моєму випадку мені довелося використовувати дані з мого фрагмента в onContextItemSelected()методі. Рішення, з яким я в підсумку пішов, було передати екземпляр фрагмента в мій адаптер і зареєструвати елемент перегляду у власнику перегляду:

@Override
public void onBindViewHolder(final MyListAdapter.ViewHolder viewHolder, int position) {
    final Object rowObject = myListItems.get(position);

    // Do your data binding here

    viewHolder.itemView.setTag(position);
    fragment.registerForContextMenu(viewHolder.itemView);
}

Тоді в onCreateContextMenu()ви можете зберегти індекс до локальної змінної:

selectedViewIndex = (int)v.getTag();

і отримати його в onContextItemSelected()


0

Перший раз, коли я зіткнувся з цією проблемою із звичайними адаптерами, я в кінцевому підсумку створив власний власний підклас View та зберігав у ньому потрібні речі. Мені справді не сподобалось таке рішення, і я витратив багато часу, розглядаючи чудові ідеї, які пропонували люди, і вирішив, що вони мені не подобаються більше. Тож я якось зібрав усе, потрусив це деякий час і вийшов із чимось новим, що мені подобається.

Ми починаємо з декількох класів корисності. ContextMenuHandler - це інтерфейс для будь-якого об'єкта, який збирається обробляти контекстне меню. На практиці це буде підклас ViewHolder, але теоретично це може бути майже все

/**
 * Interface for objects that wish to create and handle selections from a context
 * menu associated with a view
 */
public interface ContextMenuHandler extends View.OnCreateContextMenuListener {

  boolean onContextItemSelected(MenuItem item);
}

Далі - інтерфейс, який повинен бути реалізований будь-яким видом, який буде використовуватися як безпосередній дочірній елемент RecyclerView.

public interface ViewWithContextMenu {
  public void setContextMenuHandler(FragmentWithContextMenu fragment, ContextMenuHandler handler);

  public ContextMenuHandler getContextMenuHandler();
}

Далі, будь-який подання, яке збирається створити контекстне меню як дочірній RecylcerView, повинно реалізувати ViewWIthContextMenu. У моєму випадку мені потрібен був лише підклас LinearLayout.

public class LinearLayoutWithContextMenu extends LinearLayout implements ViewWithContextMenu {

  public LinearLayoutWithContextMenu(Context context) {
    super(context);
   }

  public LinearLayoutWithContextMenu(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  private ContextMenuHandler handler;

  @Override
  public void setContextMenuHandler(FragmentWithContextMenu fragment, ContextMenuHandler handler) {
    this.handler = handler;
    setOnCreateContextMenuListener(fragment);
  }

  @Override
  public ContextMenuHandler getContextMenuHandler() {
    return handler;
  }
}

І, нарешті, нам потрібен перероблений клас Fragment, щоб перехопити виклики контекстного меню і перенаправити їх на відповідний обробник.

public class FragmentWithContextMenu extends Fragment {

  ContextMenuHandler handler = null;

  @Override
  public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, view, menuInfo);
    handler = null;
    if (view instanceof ViewWithContextMenu) {
      handler = ((ViewWithContextMenu)view).getContextMenuHandler();
      if (handler != null) handler.onCreateContextMenu(menu, view, menuInfo);
    }
  }

  @Override
  public boolean onContextItemSelected(MenuItem item) {
    if (handler != null) {
      if (handler.onContextItemSelected(item)) return true;
    }
    return super.onContextItemSelected(item);
  }
}

З усім цим на місці остаточна реалізація досить проста. Основний фрагмент повинен мати підклас FragmentWithContextMenu. Він нормально налаштовує головне RecylerWindow і переходить до підкласу Adapter. Підклас Adapter виглядає так

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

  private final FragmentWithContextMenu fragment;

  Adapter(FragmentWithContextMenu fragment) {
    this.fragment = fragment;
  }

  @Override
  public Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(context)
        .inflate(R.layout.child_view, parent, false);
    return new ViewHolder(view);
  }

  @Override
  public void onBindViewHolder(final Adapter.ViewHolder holder, int position) {
    // Logic needed to bind holder to specific position
    // ......
  }

  @Override
  public int getItemCount() {
    // Logic to return current item count
    // ....
  }

  public class ViewHolder extends RecyclerView.ViewHolder implements ContextMenuHandler {

    ViewHolder(View view) {
      super(view);
      ((ViewWithContextMenu)view).setContextMenuHandler(fragment, this);

      view.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {

          // Do stuff to handle simple clicks on child views
          // .......
        }
      });
    }

   @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {

      // Logic to set up context menu goes here
      // .... 
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {

      // Logic to handle context menu item selections goes here
      // ....

      return true;
    }
  }
}

Ось про це. Здається, все це працює. Він помістив усі класи утиліти в окремий пакет контекстного меню, щоб я міг дати імена класів, які відповідають класам, що існують у підкласифікації, але я думав, це буде більш заплутаним.


-1

Гаразд, на основі відповіді @ Flexo, я впорядкую mPosition ...

protected class ExampleViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener {

    int mPosition;

    public KWViewHolder(View itemView) {
        super(itemView);
        itemView.setOnCreateContextMenuListener(this);
    }

    public void setPosition(int position) {
        mPosition = position;
    }

    @Override
    public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) {
        contextMenu.setHeaderTitle(R.string.menu_title_context);
        contextMenu.add(0, R.id.menu_delete, mPosition, R.string.delete);
    }
}

то в onContextItemSelected я використовую

item.getOrder() 

І все добре працює, я легко отримую позицію масиву

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.