Создать меню настроек для элемента RecyclerView
Как создать меню параметров, как на следующем скриншоте:
![введите описание изображения здесь]()
Меню "Параметры" должно быть открыто, нажав кнопку "Больше" -Icon элемента RecyclerView!
Моя попытка:
@Override
public void onBindViewHolder(Holder holder, int position) {
holder.txvSongTitle.setText(sSongs[position].getTitle());
holder.txvSongInfo.setText(sSongs[position].getAlbum() + " - " + sSongs[position].getArtist());
holder.btnMore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, "More...", Toast.LENGTH_SHORT).show();
}
});
}
Но это вызывает проблемы, потому что нажата кнопка "Полный элемент", если я коснусь кнопки "Больше элементов" RecyclerView...
Здесь мой RecyclerViewOnTouchListener:
public class RecyclerViewOnTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector mGestureDetector;
private OnTouchCallback mOnTouchCallback;
public RecyclerViewOnTouchListener(Context context, final RecyclerView recyclerView, final OnTouchCallback onTouchCallback) {
mOnTouchCallback = onTouchCallback;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
@Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && onTouchCallback != null) {
onTouchCallback.onLongClick(child, recyclerView.getChildLayoutPosition(child));
}
super.onLongPress(e);
}
});
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && mOnTouchCallback != null && mGestureDetector.onTouchEvent(e)) {
mOnTouchCallback.onClick(child, rv.getChildLayoutPosition(child));
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public interface OnTouchCallback {
void onClick(View view, int position);
void onLongClick(View view, int position);
}
}
Я не смог найти подобную проблему, поэтому надеюсь, что вы сможете мне помочь!
Ответы
Ответ 1
Я узнал, что единственным меню, которое выглядит как меню выше, является PopupMenu
.
Итак, в onClick
:
@Override
public void onClick(View view, int position, MotionEvent e) {
ImageButton btnMore = (ImageButton) view.findViewById(R.id.item_song_btnMore);
if (RecyclerViewOnTouchListener.isViewClicked(btnMore, e)) {
PopupMenu popupMenu = new PopupMenu(view.getContext(), btnMore);
getActivity().getMenuInflater().inflate(R.menu.menu_song, popupMenu.getMenu());
popupMenu.show();
//The following is only needed if you want to force a horizontal offset like margin_right to the PopupMenu
try {
Field fMenuHelper = PopupMenu.class.getDeclaredField("mPopup");
fMenuHelper.setAccessible(true);
Object oMenuHelper = fMenuHelper.get(popupMenu);
Class[] argTypes = new Class[] {int.class};
Field fListPopup = oMenuHelper.getClass().getDeclaredField("mPopup");
fListPopup.setAccessible(true);
Object oListPopup = fListPopup.get(oMenuHelper);
Class clListPopup = oListPopup.getClass();
int iWidth = (int) clListPopup.getDeclaredMethod("getWidth").invoke(oListPopup);
clListPopup.getDeclaredMethod("setHorizontalOffset", argTypes).invoke(oListPopup, -iWidth);
clListPopup.getDeclaredMethod("show").invoke(oListPopup);
}
catch (NoSuchFieldException nsfe) {
nsfe.printStackTrace();
}
catch (NoSuchMethodException nsme) {
nsme.printStackTrace();
}
catch (InvocationTargetException ite) {
ite.printStackTrace();
}
catch (IllegalAccessException iae) {
iae.printStackTrace();
}
}
else {
MusicPlayer.playSong(position);
}
}
Вам нужно, чтобы ваш метод onClick прошел MotionEvent и, наконец, реализовал метод isViewClicked
в вашем RecyclerViewOnTouchListener:
public static boolean isViewClicked(View view, MotionEvent e) {
Rect rect = new Rect();
view.getGlobalVisibleRect(rect);
return rect.contains((int) e.getRawX(), (int) e.getRawY());
}
Ответ 2
Это очень легко создать меню опций, как это. Просто добавьте кнопку в свой дизайн элемента списка. Вы можете использовать следующую строку для отображения 3 вертикальных точек.
<TextView
android:id="@+id/textViewOptions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:text="⋮"
android:textAppearance="?android:textAppearanceLarge" />
Теперь в вашем адаптере внутри onBindViewHolder() используйте следующий код.
holder.buttonViewOption.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//creating a popup menu
PopupMenu popup = new PopupMenu(mCtx, holder.buttonViewOption);
//inflating menu from xml resource
popup.inflate(R.menu.options_menu);
//adding click listener
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu1:
//handle menu1 click
return true;
case R.id.menu2:
//handle menu2 click
return true;
case R.id.menu3:
//handle menu3 click
return true;
default:
return false;
}
}
});
//displaying the popup
popup.show();
}
});
Вот так.
Источник: Меню параметров для элемента RecyclerView
Ответ 3
Измените класс RecyclerViewOnTouchListener
, чтобы передать MotionEvent
в реализацию OnTouchCallback
.
В классе, реализующем onItemClick
, добавьте следующее:
@Override
public void onClick(final View view, int position, MotionEvent e) {
View menuButton = view.findViewById(R.id.menu);
if (isViewClicked(e, menuButton)) {
menuButton.setOnCreateContextMenuListener(this);
menuButton.showContextMenu();
return;
}
...
}
Где isViewClicked
следующее:
private boolean isViewClicked(MotionEvent e, View view) {
Rect rect = new Rect();
view.getGlobalVisibleRect(rect);
return rect.contains((int) e.getRawX(), (int) e.getRawY());
}
Чтобы показать список элементов, привязанных к представлению (кнопка меню), используйте ListPopupWindow
Ответ 4
step.1 добавить макет вида Recyclerview.
step.2 Макет строк recyclerview recycler_item.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
card_view:cardCornerRadius="4dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/itemTextView"
style="@style/Base.TextAppearance.AppCompat.Body2"
android:layout_width="wrap_content"
android:layout_height="?attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:layout_centerVertical="true"
android:padding="8dp" />
<ImageView
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@mipmap/more"
android:layout_alignParentRight="true"
android:text="Button"
android:padding="10dp"
android:layout_marginRight="10dp"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
Шаг 3. RecyclerAdapter
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<String> mItemList;
public RecyclerAdapter(List<String> itemList) {
mItemList = itemList;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
View view = LayoutInflater.from(context).inflate(R.layout.recycler_item, parent, false);
return RecyclerItemViewHolder.newInstance(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
RecyclerItemViewHolder holder = (RecyclerItemViewHolder) viewHolder;
String itemText = mItemList.get(position);
holder.setItemText(itemText);
}
@Override
public int getItemCount() {
return mItemList == null ? 0 : mItemList.size();
}
}
шаг .4 макет меню navigation_drawer_menu_items.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<item
android:id="@+id/navigation_drawer_item1"
android:icon="@android:drawable/ic_dialog_map"
android:title="Item 1" />
<item
android:id="@+id/navigation_drawer_item2"
android:icon="@android:drawable/ic_dialog_info"
android:title="Item 2" />
<item
android:id="@+id/navigation_drawer_item3"
android:icon="@android:drawable/ic_menu_share"
android:title="Item 3"/>
</menu>
step.5 добавить класс RecyclerItemClickListener.java
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private OnItemClickListener mListener;
public interface OnItemClickListener {
void onItemClick(View view, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
mListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
}
@Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
View childView = view.findChildViewUnder(e.getX(), e.getY());
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
}
return false;
}
@Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
@Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
// do nothing
}
}
step.6 добавьте ItemTouchListener в Recyclerview.
private void initRecyclerView() {
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
RecyclerAdapter recyclerAdapter = new RecyclerAdapter(createItemList());
recyclerView.setAdapter(recyclerAdapter);
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this, new RecyclerItemClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, final int position) {
ImageView moreImage = (ImageView) view.findViewById(R.id.button);
moreImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
openOptionMenu(v,position);
}
});
}
})
);
}
step.4 создать всплывающее меню.
public void openOptionMenu(View v,final int position){
PopupMenu popup = new PopupMenu(v.getContext(), v);
popup.getMenuInflater().inflate(R.menu.navigation_drawer_menu_items, popup.getMenu());
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
Toast.makeText(getBaseContext(), "You selected the action : " + item.getTitle()+" position "+position, Toast.LENGTH_SHORT).show();
return true;
}
});
popup.show();
}
Ответ 5
Все ответы выше великолепны. Я просто хочу добавить небольшой совет. Создание кнопки 'more' с textView также является хорошим решением, но есть более удобный способ сделать это. Вы можете получить векторный актив из меню векторного актива в студии android и использовать этот актив для любой кнопки или вида, где хотите.
//for instance
//ic_more_vert_black_24dp.xml
<vector android:height="24dp" android:tint="#cccccc"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-
0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2
-0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
</vector>
Ответ 6
-
Существует простой способ показать следующее меню:
ViewHolder:
определить поля
private ImageView menuBtn;
private PopupMenu popupMenu;
Создайте метод bind
с логикой создания меню при нажатии кнопки и закрытием его при повторном использовании вида:
if (popupMenu != null) {
popupMenu.dismiss();
}
menuBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
popupMenu = new PopupMenu(v.getContext(), v);
createMenu(popupMenu.getMenu());
popupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
@Override
public void onDismiss(PopupMenu menu) {
popupMenu = null;
}
});
popupMenu.show();
}
});
Метод createMenu(Menu menu)
зависит от вас, вот простой пример:
menu.add("Menu title")
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
// do whatever you want
}
});
-
Для обработки щелкните по другой части элемента списка, вам не нужно устанавливать OnItemTouchListener
на просмотр recycler, но простой в методе onBindViewHolder
сделать:
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//handle click here
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
//handle long click here
}
});
Ответ 7
переопределить веселье onClick (просмотр: просмотр) { showPopupMenu (вид) } частное веселье showPopupMenu (просмотр: просмотр) { // раздувать меню val popup = PopupMenu (view.context, view) val inflater = popup.menuInflater inflater.inflate(R.menu.event_default_menu, popup.menu) popup.setOnMenuItemClickListener(MenuItemClickListener()) popup.show() }
/**
* Click listener for popup menu items
*/
internal inner class MenuItemClickListener : PopupMenu.OnMenuItemClickListener {
override fun onMenuItemClick(menuItem: MenuItem): Boolean {
when (menuItem.itemId) {
R.id.def_msg1 -> {
Toast.makeText(context,context.getString(R.string.def_msg1),Toast.LENGTH_SHORT).show()
return true
}
R.id.def_msg2 -> {
Toast.makeText(context,context.getString(R.string.def_msg2),Toast.LENGTH_SHORT).show()
return true
}
R.id.def_msg3 -> {
Toast.makeText(context,context.getString(R.string.def_msg3),Toast.LENGTH_SHORT).show()
return true
}
}
return false
}
}