简单的 Android RecyclerView 示例
Posted
技术标签:
【中文标题】简单的 Android RecyclerView 示例【英文标题】:Simple Android RecyclerView example 【发布时间】:2017-03-27 20:02:50 【问题描述】:我用 android 的RecyclerView
做了几次项目列表,但这是一个相当复杂的过程。浏览众多在线教程之一(this、this 和 this 很好),但我正在寻找一个可以复制和粘贴以快速启动和运行的简单示例。只需要以下功能:
因为这个愿望我已经好几次了,所以我最终决定做出下面的答案,以供我和你的参考。
【问题讨论】:
查看tutorial with source code 对于未来的小伙伴们,这里我写了一篇详细的文章,有视频教程handyopinion.com/… youtu.be/UZwiKdrm768:您可以查看此视频教程,因为它详细解释了 recyclerview: 检查这个要点.. github.com/AtifAbbAsi19/-RecyclerView 【参考方案1】:以下是一个类似于下图的最小示例。
从一个空的活动开始。您将执行以下任务来添加 RecyclerView。您需要做的就是复制并粘贴每个部分的代码。稍后您可以对其进行自定义以满足您的需求。
向 gradle 添加依赖项 为 Activity 和 RecyclerView 行添加 xml 布局文件 制作 RecyclerView 适配器 在您的活动中初始化 RecyclerView更新 Gradle 依赖项
确保您的应用程序gradle.build
文件中有以下依赖项:
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
您可以将版本号更新为the most current。如果您仍在使用 Android Studio 2.x,请使用 compile
而不是 implementation
。
创建活动布局
将RecyclerView
添加到您的 xml 布局中。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_>
<android.support.v7.widget.RecyclerView
android:id="@+id/rvAnimals"
android:layout_
android:layout_/>
</RelativeLayout>
创建行布局
RecyclerView
中的每一行只有一个 TextView
。创建一个新的布局资源文件。
recyclerview_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="@+id/tvAnimalName"
android:layout_
android:layout_
android:textSize="20sp"/>
</LinearLayout>
创建适配器
RecyclerView
需要一个适配器来用您的数据填充每一行中的视图。创建一个新的 java 文件。
MyRecyclerViewAdapter.java
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder>
private List<String> mData;
private LayoutInflater mInflater;
private ItemClickListener mClickListener;
// data is passed into the constructor
MyRecyclerViewAdapter(Context context, List<String> data)
this.mInflater = LayoutInflater.from(context);
this.mData = data;
// inflates the row layout from xml when needed
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
View view = mInflater.inflate(R.layout.recyclerview_row, parent, false);
return new ViewHolder(view);
// binds the data to the TextView in each row
@Override
public void onBindViewHolder(ViewHolder holder, int position)
String animal = mData.get(position);
holder.myTextView.setText(animal);
// total number of rows
@Override
public int getItemCount()
return mData.size();
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
TextView myTextView;
ViewHolder(View itemView)
super(itemView);
myTextView = itemView.findViewById(R.id.tvAnimalName);
itemView.setOnClickListener(this);
@Override
public void onClick(View view)
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
// convenience method for getting data at click position
String getItem(int id)
return mData.get(id);
// allows clicks events to be caught
void setClickListener(ItemClickListener itemClickListener)
this.mClickListener = itemClickListener;
// parent activity will implement this method to respond to click events
public interface ItemClickListener
void onItemClick(View view, int position);
注意事项
虽然不是绝对必要的,但我包含了侦听行上的点击事件的功能。这在旧的ListViews
中可用,是一种常见的需求。如果不需要,可以删除此代码。
在Activity中初始化RecyclerView
将以下代码添加到您的主要活动中。
MainActivity.java
public class MainActivity extends AppCompatActivity implements MyRecyclerViewAdapter.ItemClickListener
MyRecyclerViewAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// data to populate the RecyclerView with
ArrayList<String> animalNames = new ArrayList<>();
animalNames.add("Horse");
animalNames.add("Cow");
animalNames.add("Camel");
animalNames.add("Sheep");
animalNames.add("Goat");
// set up the RecyclerView
RecyclerView recyclerView = findViewById(R.id.rvAnimals);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new MyRecyclerViewAdapter(this, animalNames);
adapter.setClickListener(this);
recyclerView.setAdapter(adapter);
@Override
public void onItemClick(View view, int position)
Toast.makeText(this, "You clicked " + adapter.getItem(position) + " on row number " + position, Toast.LENGTH_SHORT).show();
注意事项
请注意,该活动实现了我们在适配器中定义的ItemClickListener
。这使我们能够处理onItemClick
中的行点击事件。
完成
就是这样。您现在应该能够运行您的项目并获得类似于顶部图像的内容。
进行中
在行之间添加分隔符
你可以像这样添加一个简单的分隔符
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);
如果您想要更复杂一点的东西,请查看以下答案:
How to add dividers and spaces between items in RecyclerView? How to indent the divider in a linear layout RecyclerView (ie, add padding, margin, or an inset only to the ItemDecoration)点击时更改行颜色
请参阅this answer,了解如何更改背景颜色并在单击一行时添加波纹效果。
更新行
请参阅this answer,了解如何添加、删除和更新行。
进一步阅读
CodePath YouTube tutorials Android RecyclerView Example(堆栈提示教程) RecyclerView in Android: Tutorial【讨论】:
非常好。这一直有效,直到我添加一个按钮并尝试设置它的点击监听器。有什么想法可能会干扰吗? @johnktejik,从您在此处提供的信息中很难确切知道您在说什么。这可能最好作为一个新问题提出。我的猜测是您的按钮正在处理动作事件,因此其他事情不会得到它。 抱歉.. 添加动物描述?例如:新 TextView 中的骆驼:是动物 // 新 TextVire 中的羊:是动物。我需要帮助..T_T @coder,不客气。是的,我已经将它用于水平输出,并且效果相同。 这个答案需要对 androidx 进行一些更新。【参考方案2】:准备好使用 Kotlin 模板的最小回收器视图:
垂直布局 每行有一个 TextView 响应点击事件(Single 和 LongPress)我知道这是一个旧线程,所以在这里回答。添加此答案以供将来参考:
在构建依赖中添加回收视图
implementation 'com.google.android.material:material:1.4.0-alpha02'
// RecyclerView
implementation "androidx.recyclerview:recyclerview:1.2.0"
在布局中添加回收视图
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/wifiList"
android:layout_
android:layout_
/>
创建布局以显示列表项 (list_item.xml)
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_>
<LinearLayout
android:padding="5dp"
android:layout_
android:orientation="vertical"
android:layout_>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/ssid"
android:text="@string/app_name"
android:layout_
android:textSize="17sp"
android:layout_ />
</LinearLayout>
</androidx.cardview.widget.CardView>
现在创建一个最小的适配器来保存数据,这里的代码是不言自明的
class WifiAdapter(private val wifiList: ArrayList<ScanResult>) : RecyclerView.Adapter<WifiAdapter.ViewHolder>()
// holder class to hold reference
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view)
//get view reference
var ssid: TextView = view.findViewById(R.id.ssid) as TextView
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder
// create view holder to hold reference
return ViewHolder( LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false))
override fun onBindViewHolder(holder: ViewHolder, position: Int)
//set values
holder.ssid.text = wifiList[position].SSID
override fun getItemCount(): Int
return wifiList.size
// update your data
fun updateData(scanResult: ArrayList<ScanResult>)
wifiList.clear()
notifyDataSetChanged()
wifiList.addAll(scanResult)
notifyDataSetChanged()
添加此类以处理列表项上的单击和长按事件
import android.content.Context;
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener
public interface ClickListener
void onClick(View view, int position);
void onLongClick(View view, RecyclerView recyclerView, int position);
private GestureDetector gestureDetector;
private ClickListener clickListener;
public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener)
this.clickListener = clickListener;
gestureDetector = 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 && clickListener != null)
clickListener.onLongClick(child,recyclerView, recyclerView.getChildPosition(child));
);
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e)
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e))
clickListener.onClick(child, rv.getChildPosition(child));
return false;
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e)
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)
最后将您的适配器设置为 Recycler View 并添加 Touch Listener 以开始拦截单击或双击列表项的触摸事件
wifiAdapter = WifiAdapter(ArrayList())
wifiList.apply
// vertical layout
layoutManager = LinearLayoutManager(applicationContext)
// set adapter
adapter = wifiAdapter
// Touch handling
wifiList.addOnItemTouchListener(RecyclerTouchListener(applicationContext, wifiList, object : RecyclerTouchListener.ClickListener
override fun onClick(view: View?, position: Int)
Toast.makeText(applicationContext, "RV OnCLickj " + position, Toast.LENGTH_SHORT).show()
override fun onLongClick(view: View, recyclerView: RecyclerView, position: Int)
Toast.makeText(applicationContext, "RV OnLongCLickj " + position, Toast.LENGTH_SHORT).show()
))
奖励:更新数据
wifiAdapter.updateData(mScanResults as ArrayList<ScanResult>)
结果:
【讨论】:
【参考方案3】:这将是 RecyclerView 实现的最简单版本。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:layout_
android:layout_
android:id="@+id/recycler_view"/>
</FrameLayout>
list_item_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_>
<TextView
android:layout_
android:layout_
android:id="@+id/textview"
android:text="TextView"
android:textSize="16dp" />
</LinearLayout>
CustomAdapter.java
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder>
private List<String> data;
public CustomAdapter (List<String> data)
this.data = data;
@Override
public CustomAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
View rowItem = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_view, parent, false);
return new ViewHolder(rowItem);
@Override
public void onBindViewHolder(CustomAdapter.ViewHolder holder, int position)
holder.textView.setText(this.data.get(position));
@Override
public int getItemCount()
return this.data.size();
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
private TextView textView;
public ViewHolder(View view)
super(view);
view.setOnClickListener(this);
this.textView = view.findViewById(R.id.textview);
@Override
public void onClick(View view)
Toast.makeText(view.getContext(), "position : " + getLayoutPosition() + " text : " + this.textView.getText(), Toast.LENGTH_SHORT).show();
MainActivity.java
public class MainActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new CustomAdapter(generateData()));
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
private List<String> generateData()
List<String> data = new ArrayList<>();
for (int i = 0; i < 100; i++)
data.add(String.valueOf(i) + "th Element");
return data;
【讨论】:
【参考方案4】:从添加 recyclerview 库开始。
implementation 'androidx.recyclerview:recyclerview:1.1.0'
创建模型类。
public class UserModel implements Serializable
private String userName;
public UserModel(String userName)
this.userName = userName;
public String getUserName()
return userName;
public void setUserName(String userName)
this.userName = userName;
创建适配器类。
public class UsersAdapter extends RecyclerView.Adapter<UsersAdapter.UsersAdapterVh> implements Filterable
private List<UserModel> userModelList;
private List<UserModel> getUserModelListFiltered;
private Context context;
private SelectedUser selectedUser;
public UsersAdapter(List<UserModel> userModelList,SelectedUser selectedUser)
this.userModelList = userModelList;
this.getUserModelListFiltered = userModelList;
this.selectedUser = selectedUser;
@NonNull
@Override
public UsersAdapter.UsersAdapterVh onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
context = parent.getContext();
return new UsersAdapterVh(LayoutInflater.from(context).inflate(R.layout.row_users,null));
@Override
public void onBindViewHolder(@NonNull UsersAdapter.UsersAdapterVh holder, int position)
UserModel userModel = userModelList.get(position);
String username = userModel.getUserName();
String prefix = userModel.getUserName().substring(0,1);
holder.tvUsername.setText(username);
holder.tvPrefix.setText(prefix);
@Override
public int getItemCount()
return userModelList.size();
@Override
public Filter getFilter()
Filter filter = new Filter()
@Override
protected FilterResults performFiltering(CharSequence charSequence)
FilterResults filterResults = new FilterResults();
if(charSequence == null | charSequence.length() == 0)
filterResults.count = getUserModelListFiltered.size();
filterResults.values = getUserModelListFiltered;
else
String searchChr = charSequence.toString().toLowerCase();
List<UserModel> resultData = new ArrayList<>();
for(UserModel userModel: getUserModelListFiltered)
if(userModel.getUserName().toLowerCase().contains(searchChr))
resultData.add(userModel);
filterResults.count = resultData.size();
filterResults.values = resultData;
return filterResults;
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults)
userModelList = (List<UserModel>) filterResults.values;
notifyDataSetChanged();
;
return filter;
public interface SelectedUser
void selectedUser(UserModel userModel);
public class UsersAdapterVh extends RecyclerView.ViewHolder
TextView tvPrefix;
TextView tvUsername;
ImageView imIcon;
public UsersAdapterVh(@NonNull View itemView)
super(itemView);
tvPrefix = itemView.findViewById(R.id.prefix);
tvUsername = itemView.findViewById(R.id.username);
imIcon = itemView.findViewById(R.id.imageView);
itemView.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
selectedUser.selectedUser(userModelList.get(getAdapterPosition()));
);
创建布局 row_uses.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_
android:layout_>
<RelativeLayout
android:layout_
android:padding="10dp"
android:layout_>
<RelativeLayout
android:layout_
android:background="@drawable/users_bg"
android:layout_>
<TextView
android:id="@+id/prefix"
android:layout_
android:textSize="16sp"
android:textColor="@color/headerColor"
android:text="T"
android:layout_centerInParent="true"
android:layout_/>
</RelativeLayout>
<TextView
android:id="@+id/username"
android:layout_
android:textSize="16sp"
android:textColor="@color/headerColor"
android:text="username"
android:layout_marginStart="90dp"
android:layout_centerVertical="true"
android:layout_/>
<ImageView
android:layout_
android:id="@+id/imageView"
android:layout_margin="10dp"
android:layout_alignParentEnd="true"
android:src="@drawable/ic_navigate_next_black_24dp"
android:layout_/>
</RelativeLayout>
</LinearLayout>
查找 recyclerview 并填充数据。
Toolbar toolbar;
RecyclerView recyclerView;
List<UserModel> userModelList = new ArrayList<>();
String[] names = "Richard","Alice","Hannah","David";
UsersAdapter usersAdapter;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerview);
toolbar = findViewById(R.id.toolbar);
this.setSupportActionBar(toolbar);
this.getSupportActionBar().setTitle("");
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));
for(String s:names)
UserModel userModel = new UserModel(s);
userModelList.add(userModel);
usersAdapter = new UsersAdapter(userModelList,this);
recyclerView.setAdapter(usersAdapter);
在这里找到完整的教程和源代码:
Android RecyclerView
【讨论】:
为什么要将上下文发送给用户适配器类usersAdapter = new UsersAdapter(userModelList,this);
?.
@OmarBoshra this
指的是SelectedUser
接口的实现。【参考方案5】:
这是一个更新的 Kotlin 解决方案,它比这里写的许多答案简单得多,它使用匿名类。
val items = mutableListOf<String>()
inner class ItemHolder(view: View): RecyclerView.ViewHolder(view)
var textField: TextView = view.findViewById(android.R.id.text1) as TextView
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
rvitems.layoutManager = LinearLayoutManager(context)
rvitems.adapter = object : RecyclerView.Adapter<ItemHolder>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder
return ItemHolder(LayoutInflater.from(parent.context).inflate(android.R.layout.simple_list_item_1, parent, false))
override fun getItemCount(): Int
return items.size
override fun onBindViewHolder(holder: ItemHolder, position: Int)
holder.textField.text = items[position]
holder.textField.setOnClickListener
Toast.makeText(context, "Clicked $position", Toast.LENGTH_SHORT).show()
我冒昧地使用了 android.R.layout.simple_list_item_1,因为它更简单。我想进一步简化它并将 ItemHolder 作为内部类,但无法完全弄清楚如何在外部类参数的类型中引用它。
【讨论】:
我几乎放弃了 kotlin 和 android 开发,因为没有简单的解决方案,直到我看到你的。谢谢! 有趣。您如何响应点击? @SMBiggs 你是什么意思?处理点击的代码也出现在 onBindViewHolder 内的这个示例中。 我现在看到了——我的错。我不知何故跳过了 onBindViewHolder 的那部分。【参考方案6】:基于不同的来源,我使用简单库创建了 RecyclerView 的简单实现。
在build.gradle
中添加这一行
implementation 'com.hereshem.lib:awesomelib:2.0.1'
添加通过在activity_main.xml中添加MyRecyclerView来创建一个RecyclerView
<com.hereshem.lib.recycler.MyRecyclerView
android:id="@+id/recycler"
app:layoutManager="LinearLayoutManager"
android:layout_
android:layout_/>
现在在 MainActivity 中,通过传递需要绑定的 Class 的名称来创建一个 ViewHolder
public static class EVHolder extends MyViewHolder<Events>
TextView date, title, summary;
public EVHolder(View v)
super(v);
date = v.findViewById(R.id.date);
title = v.findViewById(R.id.title);
summary = v.findViewById(R.id.summary);
@Override
public void bindView(Events c)
date.setText(c.date);
title.setText(c.title);
summary.setText(c.summary);
通过在适配器中传递项目、类和布局来创建项目列表变量和适配器
List<Events> items = new ArrayList<>();
MyRecyclerView recycler = findViewById(R.id.recycler);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(this, items, EVHolder.class, R.layout.row_event);
recycler.setAdapter(adapter);
ClickListener 可以通过以下几行添加
recycler.setOnItemClickListener(new MyRecyclerView.OnItemClickListener()
@Override
public void onItemClick(int position)
Toast.makeText(MainActivity.this, "Recycler Item Clicked " + position, Toast.LENGTH_SHORT).show();
);
一切都搞定了。
更多示例和实现可以在here 找到。 希望这会有所帮助!!!
【讨论】:
什么是单一活动 :错误:找不到符号摘要 = v.findViewById(R.id.summary); ^ 符号:变量摘要位置:类 id EVHolder.java:15:错误:方法未覆盖或实现超类型 @Override 的方法 ^ EVHolder.java:17:错误:找不到符号 date.setText(c.date) ; @johnktejik R.id.summary 是为布局资源文件提供的 ID。请查看此布局文件github.com/hereshem/Easy-RecyclerView-Library/blob/master/app/…【参考方案7】:开始,只是在 Recycler 视图中查看一些东西
recycler view adapter 可以是这样的。
class CustomAdapter: RecyclerView.Adapter<CustomAdapter.ViewHolder>()
var data = listOf<String>()
set(value)
field = value
notifyDataSetChanged()
override fun getItemCount() =data.size
override fun onBindViewHolder(holder: ViewHolder, position: Int)
holder.txt.text= data[position]
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder
return ViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_view, parent, false)
)
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
val txt: TextView = itemView.findViewById(R.id.item_text_view)
并将适配器附加到回收器视图并将数据附加到适配器
val view = findViewById<RecyclerView>(R.id.recycler_view)
val adapter = CustomAdapter()
val data = listOf("text1", "text2", "text3")
adapter.data = data
view.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
view.adapter = adapter
【讨论】:
【参考方案8】:现在你需要1 adapter 来代表所有RecyclerView
一个适配器可用于所有 RecyclerView。所以 NO onBindViewHolder
, No onCreateViewHolder
处理。
没有用于从 Java/Kotlin 类设置适配器的代码。检查sample class。
您可以使用Binding Adapters为每个列表设置事件和自定义数据。
我在这里展示了通过 1 个适配器设置两个不同的 RecyclerView
-
activity_home.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="listOne"
type="java.util.List"/>
<variable
name="listTwo"
type="java.util.List"/>
<variable
name="onItemClickListenerOne"
type="com.ks.nestedrecyclerbindingexample.callbacks.OnItemClickListener"/>
<variable
name="onItemClickListenerTwo"
type="com.ks.nestedrecyclerbindingexample.callbacks.OnItemClickListener"/>
</data>
<LinearLayout
android:layout_
android:layout_
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
rvItemLayout="@@layout/row_one"
rvList="@listOne"
rvOnItemClick="@onItemClickListenerOne"
android:layout_
android:layout_
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
/>
<android.support.v7.widget.RecyclerView
rvItemLayout="@@layout/row_two"
rvList="@listTwo"
rvOnItemClick="@onItemClickListenerTwo"
android:layout_
android:layout_
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
/>
</LinearLayout>
</layout>
你可以看到我从布局中传递了列表、项目布局id和点击监听器。
rvItemLayout="@@layout/row_one"
rvList="@listOne"
rvOnItemClick="@onItemClickListenerOne"
此自定义属性由 BindingAdapter 创建。
public class BindingAdapters
@BindingAdapter(value = "rvItemLayout", "rvList", "rvOnItemClick", requireAll = false)
public static void setRvAdapter(RecyclerView recyclerView, int rvItemLayout, List rvList, @Nullable OnItemClickListener onItemClickListener)
if (rvItemLayout != 0 && rvList != null && rvList.size() > 0)
recyclerView.setAdapter(new GeneralAdapter(rvItemLayout, rvList, onItemClickListener));
现在从Activity,你传递列表,点击监听器
HomeActivity.java
public class HomeActivity extends AppCompatActivity
ActivityHomeBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_home);
binding.setListOne(new ArrayList()); // pass your list or set list from response of API
binding.setListTwo(new ArrayList());
binding.setOnItemClickListenerOne(new OnItemClickListener()
@Override
public void onItemClick(View view, Object object)
if (object instanceof ModelParent)
// TODO: your action here
);
binding.setOnItemClickListenerTwo(new OnItemClickListener()
@Override
public void onItemClick(View view, Object object)
if (object instanceof ModelChild)
// TODO: your action here
);
你不想读太多,直接从我的github repo克隆/下载full example。自己试试吧。
你可以在上面的 repo 中看到GeneralAdapter.java
。
如果您在设置数据绑定时遇到问题,请参阅this answer。
【讨论】:
【参考方案9】:依赖关系
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:design:25.3.1'
compile 'com.android.support:multidex:1.0.1'
compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.android.support:support-v4:25.3.1'
compile 'com.lguipeng.bubbleview:library:1.0.0'
compile 'com.larswerkman:HoloColorPicker:1.5'
compile 'com.mcxiaoke.volley:library-aar:1.0.0'
点击项的一个类
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
public 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.getChildPosition(childView));
return true;
return false;
@Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent)
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)
二等 RecyclerView
import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
public class SLByTopics extends Fragment
public static ArrayList<MByTopics> byTopicsMainArrayList=new ArrayList<>();
TabRefreshReceiver tabRefreshReceiver;
RecyclerView recyclerView;
SAdpByTopics sAdpByTopics;
public ArrayList<MByTopics> mByTopicsArrayList=new ArrayList<>();
ProgressDialog progressDialog;
public SLByTopics()
@Override
public void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
View view = inflater.inflate(R.layout.sl_fragment_by_topics, container, false);
progressDialog = new ProgressDialog(getActivity());
if (IsOnline.isNetworkAvailable(getActivity()))
getCategoryTree();
else
IsOnline.showNoInterNetMessage(getActivity());
tabRefreshReceiver = new TabRefreshReceiver();
LocalBroadcastManager.getInstance(getContext()).registerReceiver(tabRefreshReceiver, new IntentFilter("BY_TOPICS"));
setUpView(view);
return view;
private void setUpView(View view)
recyclerView=(RecyclerView)view.findViewById(R.id.by_topics_list_recyclerView);
LinearLayoutManager linearLayoutManager=new LinearLayoutManager(getActivity());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
@Override
public void onResume()
super.onResume();
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener()
@Override
public void onItemClick(View view, final int position)
if (mByTopicsArrayList.get(position).getChild().size()>0)
Intent intent = new Intent(getActivity(), SByTopicCategory.class);
intent.putExtra("selectedCategoryName",mByTopicsArrayList.get(position).getCatname());
intent.putExtra("jsonData",mByTopicsArrayList.get(position).getMainTopicJson());
startActivity(intent);
getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out);
else
Intent intent = new Intent(getActivity(), SByCategoryQuestionList.class);
intent.putExtra("selectedSubCategoryName",mByTopicsArrayList.get(position).getCatname());
intent.putExtra("catID",mByTopicsArrayList.get(position).getId());
startActivity(intent);
getActivity().overridePendingTransition(R.anim.activity_in, R.anim.activity_out);
));
private class TabRefreshReceiver extends BroadcastReceiver
@Override
public void onReceive(Context context, Intent intent)
try
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.detach(SLByTopics.this).attach(SLByTopics.this).commit();
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(tabRefreshReceiver);
catch (Exception e)
e.printStackTrace();
private void getCategoryTree()
progressDialog.setMessage("Please Wait...");
progressDialog.setCancelable(false);
progressDialog.show();
StringRequest stringRequest = new StringRequest(Request.Method.POST, Const.HOSTNAME + Const.STUDENT_GET_CATEGORY_TREE,
new Response.Listener<String>()
@SuppressLint("LongLogTag")
@Override
public void onResponse(String response)
try
JSONObject object = new JSONObject(response);
String status = object.getString("status");
int i = Integer.parseInt(status);
switch (i)
case 0:
progressDialog.dismiss();
// Toast.makeText(getActivity(), "getCategorySuccess", Toast.LENGTH_SHORT).show();
Log.e("getCategoryTree Response", "getCategoryTree Response : " + response);
try
byTopicsMainArrayList.clear();
JSONArray info = object.getJSONArray("info");
if (info.length() > 0)
for (i = 0; i < info.length(); i++)
JSONObject data = info.getJSONObject(i);
MByTopics mByTopics = new MByTopics();
mByTopics.setId(data.getString("id"));
mByTopics.setCatname(data.getString("catname"));
mByTopics.setMainTopicJson(data.toString());
JSONArray topicChildren = data.getJSONArray("children");
ArrayList<SMByTopicCategory> byChildrenArrayList = new ArrayList<>();
for (int j = 0; j < topicChildren.length(); j++)
JSONObject topicChildrenData = topicChildren.getJSONObject(j);
SMByTopicCategory smByTopicCategory = new SMByTopicCategory();
smByTopicCategory.setId(topicChildrenData.getString("id"));
smByTopicCategory.setCatname(topicChildrenData.getString("catname"));
smByTopicCategory.setChildTopicJson(topicChildrenData.toString());
JSONArray topicChildrenQuestion = topicChildrenData.getJSONArray("children");
ArrayList<SMByTopicSubCategory> byChildrenSubArrayList = new ArrayList<>();
for (int k = 0; k < topicChildrenQuestion.length(); k++)
JSONObject topicChildrenSubData = topicChildrenQuestion.getJSONObject(k);
SMByTopicSubCategory smByTopicSubCategory = new SMByTopicSubCategory();
smByTopicSubCategory.setId(topicChildrenSubData.getString("id"));
smByTopicSubCategory.setCatname(topicChildrenSubData.getString("catname"));
smByTopicSubCategory.setChildSubTopicJson(topicChildrenSubData.toString());
byChildrenSubArrayList.add(smByTopicSubCategory);
smByTopicCategory.setQuestions(byChildrenSubArrayList);
byChildrenArrayList.add(smByTopicCategory);
mByTopics.setChild(byChildrenArrayList);
byTopicsMainArrayList.add(mByTopics);
mByTopicsArrayList.clear();
mByTopicsArrayList=byTopicsMainArrayList;
sAdpByTopics=new SAdpByTopics(mByTopicsArrayList,getActivity());
recyclerView.setAdapter(sAdpByTopics);
sAdpByTopics.notifyDataSetChanged();
catch (Exception e)
e.printStackTrace();
break;
default:
progressDialog.dismiss();
// Toast.makeText(getActivity(), "getCategoryError : " + response, Toast.LENGTH_SHORT).show();
Log.e("getCategoryTree Not Response", "getCategoryTree Uploading Not Response : " + response);
catch (JSONException e)
e.printStackTrace();
,
new Response.ErrorListener()
@Override
public void onErrorResponse(VolleyError error)
progressDialog.dismiss();
Log.e("getCategoryTree Error :","getCategoryTree Error :"+error.getMessage());
// Toast.makeText(getActivity(), error.getMessage(), Toast.LENGTH_LONG).show();
)
;/*
@Override
protected Map<String, String> getParams() throws AuthFailureError
Map<String, String> map = new HashMap<String, String>();
// map.put("uid", String.valueOf(ConfigManager.getUserId()));
return map;
;*/
stringRequest.setRetryPolicy(new DefaultRetryPolicy(
0,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
RequestQueue requestQueue = Volley.newRequestQueue(getActivity());
requestQueue.add(stringRequest);
回收站项目的适配器类
import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
public class SAdpByTopics extends RecyclerView.Adapter<RecyclerView.ViewHolder>
ArrayList<MByTopics> topicsArrayList=new ArrayList<>();
Activity activity;
public SAdpByTopics(ArrayList<MByTopics> topicsArrayList,Activity activity)
this.topicsArrayList=topicsArrayList;
this.activity=activity;
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
View itemeView= LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_by_topic_list,parent,false);
RecyclerView.ViewHolder holder=new Holder(itemeView);
holder.setIsRecyclable(false);
return holder;
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
final Holder classHolder = (Holder) holder;
try
classHolder.txt_topic_name.setText(topicsArrayList.get(position).getCatname());
catch (Exception e)
e.printStackTrace();
@Override
public int getItemCount()
return topicsArrayList.size();
class Holder extends RecyclerView.ViewHolder implements View.OnClickListener
TextView txt_topic_name;
public Holder(View itemView)
super(itemView);
txt_topic_name = (TextView) itemView.findViewById(R.id.txt_topic_name);
@Override
public void onClick(View v)
模块类
public class MByTopics
String id;
String topicName;
String catname;
String MainTopicJson;
ArrayList<SMByTopicCategory> child;
ArrayList<SMByTopicSubCategory> questions;
public void setId(String id)
this.id=id;
public String getId()
return id;
public void setCatname(String catname)
this.catname = catname;
public String getCatname()
return catname;
public void setTopicName(String topicName)
this.topicName = topicName;
public String getTopicName()
return topicName;
public void setChild(ArrayList<SMByTopicCategory> child)
this.child = child;
public String getMainTopicJson()
return MainTopicJson;
public void setMainTopicJson(String mainTopicJson)
MainTopicJson = mainTopicJson;
public ArrayList<SMByTopicCategory> getChild()
return child;
public void setQuestions(ArrayList<SMByTopicSubCategory> questions)
this.questions = questions;
public ArrayList<SMByTopicSubCategory> getQuestions()
return questions;
public ArrayList<MByTopics> getByTopicList()
ArrayList<MByTopics> mByTopicsArrayList = new ArrayList<>();
for (int i=0;i<11;i++)
MByTopics mQuestionBankCategory=new MByTopics();
if (i==1 || i== 5|| i==9)
mQuestionBankCategory.setTopicName("Microeconomics");
else if (i==2 || i== 10|| i==6)
mQuestionBankCategory.setTopicName("Macroeconomics");
else
mQuestionBankCategory.setTopicName("Current Isssues");
mByTopicsArrayList.add(mQuestionBankCategory);
return mByTopicsArrayList;
【讨论】:
解释会很好 代码不干净,没有解释!【参考方案10】:由于我无法发表评论,但我将发布链接作为答案。我在 recyclerview 上找到了一个简单、组织良好的教程 http://www.androiddeft.com/2017/10/01/recyclerview-android/
除此之外,当您要在活动中添加回收站视图时,您想要执行的操作如下,链接中已经描述了您应该如何执行此操作
将 RecyclerView 组件添加到您的布局文件中 创建一个要显示为列表行的类 制作一个布局文件,它是你列表中一行的布局 现在我们需要一个自定义适配器,所以通过扩展创建一个自定义适配器 来自父类 RecyclerView.Adapter 将 recyclerview 添加到您的 mainActivity oncreate 添加分隔符 添加触摸监听器【讨论】:
【参考方案11】:您可以将抽象适配器与差异工具和过滤器一起使用
SimpleAbstractAdapter.kt
abstract class SimpleAbstractAdapter<T>(private var items: ArrayList<T> = arrayListOf()) : RecyclerView.Adapter<SimpleAbstractAdapter.VH>()
protected var listener: OnViewHolderListener<T>? = null
private val filter = ArrayFilter()
private val lock = Any()
protected abstract fun getLayout(): Int
protected abstract fun bindView(item: T, viewHolder: VH)
protected abstract fun getDiffCallback(): DiffCallback<T>?
private var onFilterObjectCallback: OnFilterObjectCallback? = null
private var constraint: CharSequence? = ""
override fun onBindViewHolder(vh: VH, position: Int)
getItem(position)?.let bindView(it, vh)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH
return VH(parent, getLayout())
override fun getItemCount(): Int = items.size
protected abstract class DiffCallback<T> : DiffUtil.Callback()
private val mOldItems = ArrayList<T>()
private val mNewItems = ArrayList<T>()
fun setItems(oldItems: List<T>, newItems: List<T>)
mOldItems.clear()
mOldItems.addAll(oldItems)
mNewItems.clear()
mNewItems.addAll(newItems)
override fun getOldListSize(): Int
return mOldItems.size
override fun getNewListSize(): Int
return mNewItems.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean
return areItemsTheSame(
mOldItems[oldItemPosition],
mNewItems[newItemPosition]
)
abstract fun areItemsTheSame(oldItem: T, newItem: T): Boolean
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean
return areContentsTheSame(
mOldItems[oldItemPosition],
mNewItems[newItemPosition]
)
abstract fun areContentsTheSame(oldItem: T, newItem: T): Boolean
class VH(parent: ViewGroup, @LayoutRes layout: Int) : RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(layout, parent, false))
interface OnViewHolderListener<T>
fun onItemClick(position: Int, item: T)
fun getItem(position: Int): T?
return items.getOrNull(position)
fun getItems(): ArrayList<T>
return items
fun setViewHolderListener(listener: OnViewHolderListener<T>)
this.listener = listener
fun addAll(list: List<T>)
val diffCallback = getDiffCallback()
when
diffCallback != null && !items.isEmpty() ->
diffCallback.setItems(items, list)
val diffResult = DiffUtil.calculateDiff(diffCallback)
items.clear()
items.addAll(list)
diffResult.dispatchUpdatesTo(this)
diffCallback == null && !items.isEmpty() ->
items.clear()
items.addAll(list)
notifyDataSetChanged()
else ->
items.addAll(list)
notifyDataSetChanged()
fun add(item: T)
items.add(item)
notifyDataSetChanged()
fun add(position:Int, item: T)
items.add(position,item)
notifyItemInserted(position)
fun remove(position: Int)
items.removeAt(position)
notifyItemRemoved(position)
fun remove(item: T)
items.remove(item)
notifyDataSetChanged()
fun clear(notify: Boolean=false)
items.clear()
if (notify)
notifyDataSetChanged()
fun setFilter(filter: SimpleAdapterFilter<T>): ArrayFilter
return this.filter.setFilter(filter)
interface SimpleAdapterFilter<T>
fun onFilterItem(contains: CharSequence, item: T): Boolean
fun convertResultToString(resultValue: Any): CharSequence
return filter.convertResultToString(resultValue)
fun filter(constraint: CharSequence)
this.constraint = constraint
filter.filter(constraint)
fun filter(constraint: CharSequence, listener: Filter.FilterListener)
this.constraint = constraint
filter.filter(constraint, listener)
fun getFilter(): Filter
return filter
interface OnFilterObjectCallback
fun handle(countFilterObject: Int)
fun setOnFilterObjectCallback(objectCallback: OnFilterObjectCallback)
onFilterObjectCallback = objectCallback
inner class ArrayFilter : Filter()
private var original: ArrayList<T> = arrayListOf()
private var filter: SimpleAdapterFilter<T> = DefaultFilter()
private var list: ArrayList<T> = arrayListOf()
private var values: ArrayList<T> = arrayListOf()
fun setFilter(filter: SimpleAdapterFilter<T>): ArrayFilter
original = items
this.filter = filter
return this
override fun performFiltering(constraint: CharSequence?): Filter.FilterResults
val results = Filter.FilterResults()
if (constraint == null || constraint.isBlank())
synchronized(lock)
list = original
results.values = list
results.count = list.size
else
synchronized(lock)
values = original
val result = ArrayList<T>()
for (value in values)
if (constraint!=null && constraint.trim().isNotEmpty() && value != null)
if (filter.onFilterItem(constraint, value))
result.add(value)
else
value?.let result.add(it)
results.values = result
results.count = result.size
return results
override fun publishResults(constraint: CharSequence, results: Filter.FilterResults)
items = results.values as? ArrayList<T> ?: arrayListOf()
notifyDataSetChanged()
onFilterObjectCallback?.handle(results.count)
class DefaultFilter<T> : SimpleAdapterFilter<T>
override fun onFilterItem(contains: CharSequence, item: T): Boolean
val valueText = item.toString().toLowerCase()
if (valueText.startsWith(contains.toString()))
return true
else
val words = valueText.split(" ".toRegex()).dropLastWhile it.isEmpty() .toTypedArray()
for (word in words)
if (word.contains(contains))
return true
return false
并通过实现方法扩展抽象适配器
TasksAdapter.kt
import android.annotation.SuppressLint
import kotlinx.android.synthetic.main.task_item_layout.view.*
class TasksAdapter(private val listener:TasksListener? = null) : SimpleAbstractAdapter<Task>()
override fun getLayout(): Int
return R.layout.task_item_layout
override fun getDiffCallback(): DiffCallback<Task>?
return object : DiffCallback<Task>()
override fun areItemsTheSame(oldItem: Task, newItem: Task): Boolean
return oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Task, newItem: Task): Boolean
return oldItem.items == newItem.items
@SuppressLint("SetTextI18n")
override fun bindView(item: Task, viewHolder: VH)
viewHolder.itemView.apply
val position = viewHolder.adapterPosition
val customer = item.customer
val customerName = if (customer != null) customer.name else ""
tvTaskCommentTitle.text = customerName + ", #" + item.id
tvCommentContent.text = item.taskAddress
ivCall.setOnClickListener
listener?.onCallClick(position, item)
setOnClickListener
listener?.onItemClick(position, item)
interface TasksListener : SimpleAbstractAdapter.OnViewHolderListener<Task>
fun onCallClick(position: Int, item: Task)
初始化适配器
mAdapter = TasksAdapter(object : TasksAdapter.TasksListener
override fun onCallClick(position: Int, item:Task)
override fun onItemClick(position: Int, item:Task)
)
rvTasks.adapter = mAdapter
填充
mAdapter?.addAll(tasks)
添加自定义过滤器
mAdapter?.setFilter(object : SimpleAbstractAdapter.SimpleAdapterFilter<MoveTask>
override fun onFilterItem(contains: CharSequence, item:Task): Boolean
return contains.toString().toLowerCase().contains(item.id?.toLowerCase().toString())
)
过滤数据
mAdapter?.filter("test")
【讨论】:
使用 diff utils 添加一些更改,添加类型支持gist.github.com/arnyigor/568035c6db9bdbeaf609b68a71834349【参考方案12】:实现 androidx.recyclerview:recyclerview:.... 建议更新到这里的 androidx 库:
https://developer.android.com/jetpack/androidx/releases/recyclerview
然后必须将布局文件 Widget XML 标签更新为: androidx.recyclerview.widget.RecyclerView
【讨论】:
你是对的。我需要用 AndroidX 更新我的答案。以上是关于简单的 Android RecyclerView 示例的主要内容,如果未能解决你的问题,请参考以下文章
Android实战----RecyclerView下拉刷新和上拉加载的简单实现
Android实战----RecyclerView下拉刷新和上拉加载的简单实现
使用带有 GridLayoutManager 的 RecyclerView 的简单 Android 网格示例(如旧的 GridView)