Выделите выбранный элемент внутри RecyclerView
У меня проблемы с выделением элемента в RecyclerView, аналогично настройке выбранного элемента в ListView.
На данный момент я настроил RecyclerView, у меня есть LayoutManager по умолчанию и есть адаптер, который отображает все данные. Я только недавно получил onClickListener()
работает (хотя я не уверен, если это должно идти в onCreateViewHolder()
или же onBindViewHolder*(
- не уверен, когда onBindViewHolder()
называется).
Я попытался искать, и самый близкий, который я получил, является вопросом здесь. Тем не менее, я полностью потерян в этом вопросе. Где это onClick()
метод (внутри адаптера, внутри содержащего Activity/ фрагмента и т. д.?). Что это такое viewHolderListener
? Что такое getPosition()
от чего и что конкретно делает? По сути, я ничего не получил от этого вопроса, и это был лучший ресурс, который я мог найти до сих пор.
Вот мой текущий код для настройки RecyclerView:
// Sets up the main navigation
private void setupMainNav() {
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mMainRecyclerNav.setHasFixedSize(true);
// use a linear layout manager
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
mMainRecyclerNav.setLayoutManager(layoutManager);
// Create and set the adapter for this recyclerView
MainNavAdapter adapter = new MainNavAdapter(getActivity());
mMainRecyclerNav.setAdapter(adapter);
}
Вот мой текущий код для адаптера:
class MainNavAdapter extends RecyclerView.Adapter<MainNavAdapter.ViewHolder> {
Context mContext;
// Holds the titles of every row
String[] rowTitles;
// Default constructor
MainNavAdapter(Context context) {
this.mContext = context;
// Get the rowTitles - the necessary data for now - from resources
rowTitles = context.getResources().getStringArray(R.array.nav_items);
}
// Simple class that holds all the views that need to be reused
class ViewHolder extends RecyclerView.ViewHolder{
View parentView; // The view which holds all the other views
TextView rowTitle; // The title of this item
// Default constructor, itemView holds all the views that need to be saved
public ViewHolder(View itemView) {
super(itemView);
// Save the entire itemView, for setting listeners and usch later
this.parentView = itemView;
// Save the TextView- all that's supported at the moment
this.rowTitle = (TextView) itemView.findViewById(R.id.row_title);
}
}
// Create new views (invoked by the layout manager)
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
// Create the view for this row
View row = LayoutInflater.from(mContext)
.inflate(R.layout.list_navmain_row, viewGroup, false);
// Create a new viewHolder which caches all the views that needs to be saved
ViewHolder viewHolder = new ViewHolder(row);
return viewHolder;
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
viewHolder.rowTitle.setText(rowTitles[i]);
// TODO: Make a better workaround for passing in the position to the listener
final int position = i;
// Set a listener for this entire view
viewHolder.parentView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getActivity(), "Clicked on row: " + position, Toast.LENGTH_SHORT).show();
}
});
}
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
return rowTitles.length;
}
}
Буду признателен за любую помощь. Спасибо.
4 ответа
RecyclerView
не обрабатывает выбор элемента или состояния как ListView
делает. Вместо этого вы должны обработать это вручную в вашем держателе представления.
Первое, что вы можете сделать, это объявить ваш элемент как интерактивный в вашем конструкторе `ViewHolder:
public ViewHolder(View itemView) {
super(itemView);
// Make this view clickable
itemView.setClickable(true);
// ...
}
Затем, если вы хотите выделить выделение, вы должны отслеживать выбранные строки в вашем адаптере (например, с помощью списка индексов) и в вашем методе onBindViewHolder:
@Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
// mark the view as selected:
viewHolder.parentview.setSelected(mSelectedRows.contains(i));
}
В качестве примечания вы должны установить onClickListener в родительском представлении в onCreateViewHolder вместо onBindViewHolder. Метод onBindViewHolder будет вызываться много раз для одного и того же владельца представления, и вы будете выполнять больше операций, чем необходимо
Есть простой способ сделать это:
int row_index=0;
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
Movie movie = moviesList.get(position);
holder.title.setText(movie.getTitle());
holder.genre.setText(movie.getGenre());
holder.year.setText(movie.getYear());
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
row_index=position;
notifyDataSetChanged();
}
});
if(row_index==position){
holder.itemView.setBackgroundColor(Color.parseColor("#567845"));
}
else
{
holder.itemView.setBackgroundColor(Color.parseColor("#ffffff"));
}
}
Я расширяю ответ @XGouchet, потому что, несмотря на то, что он поставил меня на правильный путь, мне все равно нужно было проделать дополнительную работу для поддержки выбора И НЕ ВЫБРАННЫХ рядов (например, переключателя). И @ScottBiggs, и мне нужна была такая поддержка.
Код держит на mSelectedPosition
что позволяет правильно выбрать выбор в onBindViewHolder
а также mSelectedView
что позволяет правильно переключать выбор (как включенный, так и выключенный) при нажатии.
private class AsanaAdapter extends RecyclerView.Adapter<AsanaViewHolder> {
private RecyclerView mRecyclerView;
private final List<Asana> mAsanas;
private int mSelectedPosition;
private View mSelectedView;
public AsanaAdapter(RecyclerView recyclerView, List<Asana> items) {
mRecyclerView = recyclerView;
mAsanas = items;
setHasStableIds(true);
setSelected(0);
}
@Override
public int getItemCount() {
return mAsanas.size();
}
@Override
public AsanaViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View newView = getLayoutInflater().inflate(
R.layout.performance_builder_list_content, parent, false);
newView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!v.isSelected()) {
// We are selecting the view clicked
if (mSelectedView != null) {
// deselect the previously selected view
mSelectedView.setSelected(false);
}
mSelectedPosition = mRecyclerView.getChildAdapterPosition(v);
mSelectedView = v;
setTitle(mAsanas.get(mSelectedPosition).getName());
} else {
// We are deselecting the view clicked
setTitle("");
mSelectedPosition = -1;
mSelectedView = null;
}
// toggle the item clicked
v.setSelected(!v.isSelected());
}
});
return new AsanaViewHolder(newView);
}
@Override
public void onBindViewHolder(AsanaViewHolder holder, int position) {
if (position == mSelectedPosition) {
holder.itemView.setSelected(true);
// keep track of the currently selected view when recycled
mSelectedView = holder.itemView;
} else {
holder.itemView.setSelected(false);
}
}
}
Попробуйте это решение, если хотите выделить выбранный элемент только на мгновение после щелчка. Я использую retrolambda, но вы также можете использовать обычный объект Runnable.
1.
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
@BindView(R.id.appIcon)
ImageView appIcon;
@BindView(R.id.appName)
TextView appName;
public ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
ButterKnife.bind(this, itemView);
}
@Override
public void onClick(View v) {
v.setSelected(true);
new Handler().postDelayed(() -> v.setSelected(false), 100);
//or new Handler().postDelayed(new Runnable...
//your onClick action
}
}
создайте list_item_selector.xml в вашем каталоге Drawable
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true" android:drawable="@color/colorPrimary" /> <item android:drawable="@android:color/transparent" /> </selector>
Установите селектор в корневой макет вашего единственного элемента
android:background="@drawable/list_item_selector"