You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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):

1. Build a Generic RecyclerView Adapter

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.

2. Abstract Your Network Layer

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>>>
}
3. Create a Reusable List Container (Base Fragment/Activity)

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>
4. Use the Reusable Components in Your App

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)
    }
}
Bonus Optimizations
  • 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 SwipeRefreshLayout in the base fragment and add refresh logic.
  • ViewBinding: Replace findViewById with 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:28:16