如何实现Android全局通用RecyclerView?基于Retrofit+JSON+MySQL
Hey there! I’ve been in your exact boat—copy-pasting RecyclerView adapter code, network call handlers, and response parsing across every Activity/Fragment is not just tedious, it’s a maintenance disaster down the line. Let’s build a clean, reusable solution that fits your tech stack (Retrofit + JSON + MySQL):
First, create a flexible adapter that works with any data type and list item layout. This eliminates writing a new adapter for every list:
import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView class GenericAdapter<T>( private val layoutId: Int, private val bindItem: (holder: RecyclerView.ViewHolder, item: T) -> Unit, private val onItemClick: ((T) -> Unit)? = null ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val itemList = mutableListOf<T>() fun updateItems(newItems: List<T>) { itemList.clear() itemList.addAll(newItems) notifyDataSetChanged() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val view = LayoutInflater.from(parent.context).inflate(layoutId, parent, false) return object : RecyclerView.ViewHolder(view) {} } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val item = itemList[position] bindItem(holder, item) holder.itemView.setOnClickListener { onItemClick?.invoke(item) } } override fun getItemCount() = itemList.size }
This adapter uses lambdas to handle view binding and item clicks, so you don’t need to create custom ViewHolders for each list.
Since all your lists use the same data source, create a centralized network handler to avoid duplicating Retrofit calls and response parsing:
Step 2.1: Create a Base Response Model
Wrap your JSON responses to standardize success/error handling:
data class BaseResponse<T>( val success: Boolean, val message: String?, val data: T? )
Step 2.2: Build a Generic Repository
Create a repository class to handle network requests for any list data:
import retrofit2.Call import retrofit2.Callback import retrofit2.Response class ListRepository(private val apiService: ApiService) { fun fetchListData<T>( onSuccess: (List<T>) -> Unit, onError: (String) -> Unit ) { apiService.fetchList<T>().enqueue(object : Callback<BaseResponse<List<T>>> { override fun onResponse( call: Call<BaseResponse<List<T>>>, response: Response<BaseResponse<List<T>>> ) { val body = response.body() if (response.isSuccessful && body != null && body.success) { body.data?.let(onSuccess) ?: onError("No data available") } else { onError(body?.message ?: "Request failed") } } override fun onFailure(call: Call<BaseResponse<List<T>>>, t: Throwable) { onError(t.message ?: "Network error") } }) } }
Step 2.3: Update Your Retrofit ApiService
Add a generic endpoint for your list data:
import retrofit2.Call import retrofit2.http.GET interface ApiService { @GET("your-mysql-backed-endpoint") // Replace with your actual endpoint fun fetchList<T>(): Call<BaseResponse<List<T>>> }
Build a base Fragment (or Activity) that handles all the common list logic—RecyclerView setup, loading states, network calls, and error handling. Subclasses only need to define the list item layout and view binding:
import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager abstract class BaseListFragment<T> : Fragment() { private lateinit var recyclerView: RecyclerView private lateinit var adapter: GenericAdapter<T> private val listRepository by lazy { ListRepository(RetrofitInstance.api) } // Abstract methods to be implemented by subclasses abstract fun getListItemLayoutId(): Int abstract fun bindListItem(holder: RecyclerView.ViewHolder, item: T) abstract fun onItemClicked(item: T) override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_base_list, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setupRecyclerView() loadListData() } private fun setupRecyclerView() { recyclerView = view.findViewById(R.id.recyclerView) recyclerView.layoutManager = LinearLayoutManager(context) adapter = GenericAdapter( layoutId = getListItemLayoutId(), bindItem = ::bindListItem, onItemClick = ::onItemClicked ) recyclerView.adapter = adapter } private fun loadListData() { // Show loading indicator here (e.g., progress bar) listRepository.fetchListData( onSuccess = { items -> // Hide loading indicator adapter.updateItems(items) }, onError = { errorMessage -> // Hide loading, show error message (e.g., toast or error view) } ) } }
The fragment_base_list.xml just needs a RecyclerView (and optional loading/error views):
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent"/> <!-- Add ProgressBar and Error TextView as needed --> </RelativeLayout>
Now creating a new list screen takes minutes—just subclass the base fragment and implement the abstract methods:
class UserListFragment : BaseListFragment<User>() { override fun getListItemLayoutId() = R.layout.item_user override fun bindListItem(holder: RecyclerView.ViewHolder, item: User) { // Bind your User data to the item views holder.itemView.tv_user_name.text = item.name holder.itemView.tv_user_email.text = item.email } override fun onItemClicked(item: User) { // Handle item click (e.g., navigate to user detail screen) } }
- Pagination: Add support for endless scrolling by extending the base fragment to handle load-more logic.
- Empty State: Add an empty view to the base layout and show it when the list is empty.
- Pull-to-Refresh: Wrap the RecyclerView in a
SwipeRefreshLayoutin the base fragment and add refresh logic. - ViewBinding: Replace
findViewByIdwith ViewBinding in the adapter and base fragment for safer view access.
This setup cuts out all repetitive code, keeps your codebase clean, and makes it easy to update list logic across the app in one place.
内容的提问来源于stack exchange,提问作者axar

