Android中如何结合Room与CoinGecko API在RecyclerView展示加密货币组合
加密货币组合展示与盈亏计算实现方案
1. 定义组合数据模型
先创建一个整合本地与API数据的实体类,把需要展示的字段都包含进去,同时预留盈亏计算结果的字段:
data class CryptoPortfolioItem( val id: String, val name: String, val symbol: String, val buyPrice: Double, val currentPrice: Double, val profitLoss: Double, val profitLossPercentage: Double )
2. 合并本地数据与API价格
步骤拆解:
- 从Room数据库取出所有持仓的加密货币记录
- 收集所有加密货币ID,调用CoinGecko的批量价格接口(推荐用
/simple/price?ids=xxx,xxx&vs_currencies=usd,减少请求次数)获取当前价格 - 遍历本地数据,匹配对应ID的当前价格,计算盈亏:
- 盈亏金额 = 当前价格 - 买入价
- 盈亏百分比 = ((当前价格 - 买入价) / 买入价) * 100(注意处理买入价为0的异常情况)
核心代码示例:
// 从Room拉取本地持仓数据 val localCryptos = cryptoDao.getAllCryptos() // 拼接ID列表用于批量请求 val cryptoIds = localCryptos.joinToString(",") { it.id } // 调用API获取批量价格(假设已封装CoinGecko API请求类) val priceMap = coinGeckoApi.getBatchPrices(cryptoIds, "usd") // 合并数据并计算盈亏 val portfolioList = localCryptos.map { localItem -> val currentPrice = priceMap[localItem.id]?.get("usd") ?: 0.0 val profitLoss = currentPrice - localItem.buyPrice val profitLossRate = if (localItem.buyPrice != 0.0) { (profitLoss / localItem.buyPrice) * 100 } else 0.0 CryptoPortfolioItem( id = localItem.id, name = localItem.name, symbol = localItem.symbol, buyPrice = localItem.buyPrice, currentPrice = currentPrice, profitLoss = profitLoss, profitLossPercentage = profitLossRate ) }
3. RecyclerView适配展示
创建对应的Adapter,把组合数据绑定到UI上,同时给盈亏设置不同颜色区分正负:
class CryptoPortfolioAdapter(private val portfolioList: List<CryptoPortfolioItem>) : RecyclerView.Adapter<CryptoPortfolioAdapter.ViewHolder>() { inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val tvName: TextView = itemView.findViewById(R.id.tv_crypto_name) val tvSymbol: TextView = itemView.findViewById(R.id.tv_crypto_symbol) val tvCurrentPrice: TextView = itemView.findViewById(R.id.tv_current_price) val tvProfitLoss: TextView = itemView.findViewById(R.id.tv_profit_loss) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val itemView = LayoutInflater.from(parent.context) .inflate(R.layout.item_crypto_portfolio, parent, false) return ViewHolder(itemView) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = portfolioList[position] holder.tvName.text = item.name holder.tvSymbol.text = item.symbol.uppercase() holder.tvCurrentPrice.text = "$${String.format("%.2f", item.currentPrice)}" // 格式化盈亏文本,正负用不同颜色 val (profitText, textColor) = if (item.profitLoss >= 0) { Pair( "+$${String.format("%.2f", item.profitLoss)} (${String.format("%.2f", item.profitLossPercentage)}%)", holder.itemView.context.getColor(R.color.green_positive) ) } else { Pair( "-$${String.format("%.2f", -item.profitLoss)} (${String.format("%.2f", -item.profitLossPercentage)}%)", holder.itemView.context.getColor(R.color.red_negative) ) } holder.tvProfitLoss.text = profitText holder.tvProfitLoss.setTextColor(textColor) } override fun getItemCount() = portfolioList.size }
4. 数据更新与生命周期管理
- 把数据获取逻辑放到ViewModel中,用LiveData或者Flow暴露处理好的组合数据,在Activity/Fragment中观察数据变化,刷新RecyclerView
- 处理API请求异常:比如网络不通时,显示本地缓存的价格或者提示用户检查网络
- 可以添加下拉刷新功能,触发重新拉取价格并更新列表
内容的提问来源于stack exchange,提问作者Purab Modi
相关产品推荐
相关产品推荐

