baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/BaseNfcReadHelper.java
@@ -52,7 +52,7 @@ * @return */ public String getCradTypeAndCardNumber(){ return getCradTypeAndCardNumber(1); return getCradTypeAndCardNumber(1,0,0); }; /** @@ -60,7 +60,7 @@ * * @return */ public String getCradTypeAndCardNumber(int sectorIndex){ public String getCradTypeAndCardNumber(int sectorIndex,int blockIndex,int cardTypeIndex){ return null; }; @@ -82,12 +82,18 @@ public abstract List<byte[]> getOnesectorData(); public BaseUserCardCard getUserCardData(BaseUserCardCard userCardCard){ return getUserCardData(1,userCardCard); }; /** * 同步获取用户卡信息 * * @return */ public abstract BaseUserCardCard getUserCardData(BaseUserCardCard userCardCard); public BaseUserCardCard getUserCardData(int sectorIndex,BaseUserCardCard userCardCard){ return null; } public BaseManagerToUserCard getManagerToUserCardData(BaseManagerToUserCard baseManagerToUserCard) { baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NativeNfcReadHelper.java
@@ -53,13 +53,18 @@ return helper; } public BaseUserCardCard getUserCardData( BaseUserCardCard userCardCard) { return getUserCardData(1, userCardCard); } /** * 获取用户卡信息 * * @return */ @Override public BaseUserCardCard getUserCardData(BaseUserCardCard userCardCard) { public BaseUserCardCard getUserCardData(int sectorIndex, BaseUserCardCard userCardCard) { if (userCardCard != null) { BaseUserCardCard userCard = null; Map<String, List<byte[]>> map = new HashMap<>(); @@ -68,26 +73,31 @@ try { //链接NFC mfc.connect(); //获取扇区数量 int count = mfc.getSectorCount(); //存储空间 int size = mfc.getSize(); //用于判断时候有内容读取出来 boolean flag = false; List<byte[]> list = new ArrayList<>(); //验证扇区密码,否则会报错(链接失败错误) boolean isOpen = false; for (int i = 0; i < listKeyA.size(); i++) { if (mfc.authenticateSectorWithKeyA(1, listKeyA.get(i))) { if (!listKeyA.isEmpty()) { for (int i = 0; i < listKeyA.size(); i++) { if (mfc.authenticateSectorWithKeyA(sectorIndex, listKeyA.get(i))) { isOpen = true; break; } } } else if (!listA_PS.isEmpty()) { if (mfc.authenticateSectorWithKeyA(sectorIndex, defauleKey)) { isOpen = true; break; } else if (mfc.authenticateSectorWithKeyA(sectorIndex, listA_PS.get(sectorIndex))) { isOpen = true; } } if (isOpen) { //获取扇区里面块的数量 int bCount = mfc.getBlockCountInSector(1); int bCount = mfc.getBlockCountInSector(sectorIndex); //获取扇区第一个块对应芯片存储器的位置(我是这样理解的,因为第0扇区的这个值是4而不是0) int bIndex = mfc.sectorToBlock(1); int bIndex = mfc.sectorToBlock(sectorIndex); //String data1 = ""; for (int j = 0; j < bCount; j++) { //读取数据 @@ -482,7 +492,7 @@ @Override public String getCradTypeAndCardNumber() { return getCradTypeAndCardNumber(1); return getCradTypeAndCardNumber(1, 0, 0); } @@ -492,7 +502,7 @@ * @return */ public String getCradTypeAndCardNumber(int sectorIndex) { public String getCradTypeAndCardNumber(int sectorIndex, int blockIndex, int cardTypeIndex) { MifareClassic mfc = MifareClassic.get(tag); if (null != mfc) { @@ -544,9 +554,9 @@ } if (isOpen) { int bIndex = mfc.sectorToBlock(sectorIndex); byte[] data = mfc.readBlock(bIndex + sectorIndex); byte[] data = mfc.readBlock(bIndex + 0); if (data != null && data.length > 0) { String hex = HexUtil.byteToHex(data[0]); String hex = HexUtil.byteToHex(data[cardTypeIndex]); strData.append(hex); Log.i("NFCWreatActivity", "hex===" + hex); return strData.toString().toUpperCase(); @@ -556,6 +566,7 @@ return BaseCommon.CARD_TYPE_ERROR2; } } catch (IOException e) { e.printStackTrace(); return BaseCommon.CARD_TYPE_ERROR1; } finally { try { @@ -693,8 +704,6 @@ } return null; } } baselibrary/src/main/java/com/dayu/baselibrary/tools/nfc/NfcReadAdapter.java
@@ -66,11 +66,11 @@ } } @Override public String getCardNumber() { return getCardNumber(false); } public String getCardNumber(boolean isChangePS) { @@ -101,10 +101,10 @@ } @Override public String getCradTypeAndCardNumber(int sectorIndex) { public String getCradTypeAndCardNumber(int sectorIndex, int blockIndex, int cardTypeIndex) { switch (BaseNfcActivity.adapterType) { case ModelUtils.defaultType: return nativeNfcReadHelper.getCradTypeAndCardNumber(sectorIndex); return nativeNfcReadHelper.getCradTypeAndCardNumber(sectorIndex, blockIndex, cardTypeIndex); } return ""; } @@ -138,7 +138,12 @@ return null; } @Override public BaseUserCardCard getUserCardData(int sectorIndex, BaseUserCardCard userCardCard) { switch (BaseNfcActivity.adapterType) { case ModelUtils.defaultType: return nativeNfcReadHelper.getUserCardData(sectorIndex, userCardCard); } return null; } } generallibrary/src/main/AndroidManifest.xml
@@ -92,7 +92,18 @@ <!-- 写卡成功页面 --> <activity android:name=".activity.CardWriteSuccessActivity" /> <!-- 挂失页面 --> <activity android:name=".activity.LossCardActivity"/> <activity android:name=".activity.LossCardActivity" /> <!-- 读卡页面 --> <activity android:name=".activity.CardReadActivity" android:exported="false" android:launchMode="singleTop"> <intent-filter> <action android:name="android.nfc.action.ACTION_NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> </activity> <meta-data android:name="BUGLY_APP_VERSION" generallibrary/src/main/java/com/dayu/general/activity/BSCardFragment.kt
@@ -51,6 +51,9 @@ } startActivity(intent) } binding.homeRedCard.setOnClickListener { context?.let { CardReadActivity.start(it) } } } } generallibrary/src/main/java/com/dayu/general/activity/CardReadActivity.kt
New file @@ -0,0 +1,324 @@ package com.dayu.general.activity import android.content.Context import android.content.Intent import android.os.Bundle import com.dayu.baselibrary.net.subscribers.SubscriberListener import com.dayu.baselibrary.utils.ToastUtil import com.dayu.baselibrary.view.TipDialog import com.dayu.baselibrary.view.TitleBar import com.dayu.general.bean.net.CardInfoResult import com.dayu.general.databinding.ActivityCardReadBinding import com.dayu.general.net.ApiManager import com.dayu.general.net.BaseResponse import com.dayu.general.tool.NfcReadHelper import com.dayu.general.bean.card.UserCard import com.dayu.general.tool.CardCommon /** * @author: zuo * @desc: 读卡Activity * @since: 2025/3/6 */ class CardReadActivity : BaseNfcActivity() { private lateinit var binding: ActivityCardReadBinding private var cardNumber: String? = null companion object { /** * 启动读卡Activity */ fun start(context: Context) { val intent = Intent(context, CardReadActivity::class.java) context.startActivity(intent) } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityCardReadBinding.inflate(layoutInflater) setContentView(binding.root) initView() } private fun initView() { // 设置标题 binding.titleBar.setCenterText("读卡") // 设置TitleBar的返回按钮点击事件 binding.titleBar.setOnItemclickListner(TitleBar.ClickType_LEFT_IMAGE) { finish() } } /** * 重置到读卡状态 */ private fun resetToReadingState() { binding.cardReadLL.visibility = android.view.View.VISIBLE binding.cardInfoContainer.visibility = android.view.View.GONE cardNumber = null } /** * 显示确认对话框 */ private fun showConfirmDialog(message: String, onConfirm: () -> Unit) { val confirmDialog = TipDialog(this, message) { onConfirm() } confirmDialog.show() } override fun onNfcBack(intent: Intent?) { intent?.let { handleNfcIntent(it) } ?: run { showConfirmDialog("NFC数据异常,请重新刷卡") { } } } /** * 处理NFC刷卡信息 */ private fun handleNfcIntent(intent: Intent) { try { // 检查intent中是否包含NFC Tag if (intent.getParcelableExtra<android.nfc.Tag>(android.nfc.NfcAdapter.EXTRA_TAG) == null) { showConfirmDialog("未检测到NFC卡片,请确保卡片已正确放置") { } return } val nfcAdapter = NfcReadHelper.getInstance(intent, this) val cardTypeAndCardNumber = nfcAdapter.getCardTypeAndCardNumber() if (cardTypeAndCardNumber.isNullOrBlank() || !cardTypeAndCardNumber.contains(",")) { showConfirmDialog("卡片信息读取失败,请重新刷卡") { } return } val parts = cardTypeAndCardNumber.split(",") if (parts.size < 2) { showConfirmDialog("卡片信息格式异常,请重新刷卡") { } return } val cardNumber = parts[0] val cardType = parts[1] this.cardNumber = cardNumber if (cardNumber.isBlank()) { showConfirmDialog("卡号为空,无法进行操作,请重新刷卡") { } return } // 根据卡片类型进行不同处理 when (cardType) { CardCommon.USER_CARD_TYPE_1, CardCommon.USER_CARD_TYPE_2, CardCommon.USER_CARD_TYPE_3 -> { // 用户卡:解析卡内数据并调用接口 handleUserCard(cardNumber, cardType, nfcAdapter) } else -> { // 管理类卡:显示卡片类型信息 handleManagementCard(cardNumber, cardType) } } } catch (e: Exception) { showConfirmDialog("读卡异常:${e.message}") { } e.printStackTrace() } } /** * 处理用户卡 */ private fun handleUserCard(cardNumber: String, cardType: String, nfcAdapter: NfcReadHelper) { // 解析用户卡数据 val userCard = nfcAdapter.getUserCardData() if (userCard == null) { showConfirmDialog("解析卡片数据失败,请重新刷卡") { } return } // 根据卡号获取卡片详细信息 getCardInfo(cardNumber, cardType, userCard) } /** * 处理管理类卡片 */ private fun handleManagementCard(cardNumber: String, cardType: String) { val cardTypeName = getCardTypeName(cardType) // 显示管理卡信息 showManagementCardInfo(cardNumber, cardTypeName) } /** * 显示管理类卡片信息 */ private fun showManagementCardInfo(cardNumber: String, cardTypeName: String) { // 隐藏读卡提示,显示信息区域 binding.cardReadLL.visibility = android.view.View.GONE binding.cardInfoContainer.visibility = android.view.View.VISIBLE // 显示基本信息 binding.tvCardNumber.text = cardNumber binding.tvCardType.text = cardTypeName // 隐藏用户卡特有的字段 binding.llCardBalance.visibility = android.view.View.GONE binding.llUserNumber.visibility = android.view.View.GONE binding.llCardStatus.visibility = android.view.View.GONE binding.llLastUseTime.visibility = android.view.View.GONE } /** * 获取卡片类型名称 */ private fun getCardTypeName(cardType: String): String { return when (cardType) { CardCommon.USER_CARD_TYPE_1 -> "用户卡(开泵前)" CardCommon.USER_CARD_TYPE_2 -> "用户卡(开泵后)" CardCommon.USER_CARD_TYPE_3 -> "用户卡(叠加充值)" CardCommon.REGION_CARD -> "区域表号卡" CardCommon.ELECTRIC_PRICE_CARD -> "取数卡(需要刷卡取数)" CardCommon.MANAGE_CRAD -> "取数卡(刷卡取数返写成功)" CardCommon.CHECK_CARD -> "检查卡" CardCommon.DEBUG_CARD -> "调试卡" CardCommon.CLEAN_CARD_TYPE -> "清零卡" CardCommon.IP_CARD -> "IP地址设置卡" CardCommon.AREA_CARD -> "区域设置卡" CardCommon.GPS_CARD -> "GPS设置卡" CardCommon.VALVE_TIME_CARD -> "开关阀时间配置卡" else -> "未知类型卡片($cardType)" } } /** * 获取卡片详细信息(用户卡专用) */ private fun getCardInfo(cardNumber: String, cardType: String, userCard: UserCard) { val map = mutableMapOf<String, Any>() map["cardAddr"] = cardNumber ApiManager.getInstance().requestGetLoading( this, "terminal/card/readCard", CardInfoResult::class.java, map, object : SubscriberListener<BaseResponse<CardInfoResult>>() { override fun onNext(t: BaseResponse<CardInfoResult>) { if (t.success) { // 读卡成功,显示用户卡详细信息 showUserCardInfo(t.content, cardNumber, cardType, userCard) } else { // 处理获取失败的情况 handleCardInfoError(t.code, t.msg) } } override fun onError(e: Throwable?) { super.onError(e) showConfirmDialog("获取卡信息失败: ${e?.message ?: "网络异常,请检查网络连接"}") { } } } ) } /** * 显示用户卡片信息(包含卡内数据和接口返回数据) */ private fun showUserCardInfo(cardInfo: CardInfoResult?, cardNumber: String, cardType: String, userCard: UserCard) { // 隐藏读卡提示,显示信息区域 binding.cardReadLL.visibility = android.view.View.GONE binding.cardInfoContainer.visibility = android.view.View.VISIBLE val cardTypeName = getCardTypeName(cardType) // 显示卡内数据 binding.tvCardNumber.text = cardNumber binding.tvCardType.text = cardTypeName // 显示用户卡特有字段 binding.llCardBalance.visibility = android.view.View.VISIBLE binding.llUserNumber.visibility = android.view.View.VISIBLE binding.llCardStatus.visibility = android.view.View.VISIBLE userCard.let { card -> // 余额转换为元(原始数据可能是分) val balanceInYuan = if (card.balance > 1000) { String.format("%.2f", card.balance / 100.0) } else { card.balance.toString() } binding.tvCardBalance.text = "${balanceInYuan}元" // 使用完整的用户编号 binding.tvUserNumber.text = card.getMyUserCode() // 卡片状态(假设正常状态,因为UserCard中没有状态字段) binding.tvCardStatus.text = "正常" binding.tvCardStatus.setTextColor(android.graphics.Color.parseColor("#4CAF50")) // 充值时间作为最后使用时间 if (card.rechargeDate != null) { binding.llLastUseTime.visibility = android.view.View.VISIBLE val dateFormat = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm", java.util.Locale.getDefault()) binding.tvLastUseTime.text = dateFormat.format(card.rechargeDate!!.time) } else { binding.llLastUseTime.visibility = android.view.View.GONE } } // 显示服务器数据 cardInfo?.let { info -> binding.tvUserName.text = info.userName ?: "未知" binding.tvPhone.text = info.phone ?: "未绑定" binding.tvIdCard.text = info.userCode ?: "未录入" // 使用userCode作为身份证号的替代 // 根据status字段显示状态 val statusText = when (info.status) { 1 -> "正常" 2 -> "挂失" 3 -> "锁定" else -> "未知" } } } /** * 处理卡信息获取错误 */ private fun handleCardInfoError(code: String?, msg: String?) { val errorMessage: String = when (code) { "1001" -> { "该卡片未在系统中注册,请先进行开卡操作。" } else -> { when { msg.isNullOrBlank() -> "获取卡信息失败,请重新刷卡重试。" msg.contains("数据不存在") -> "该卡片未在系统中注册,请先进行开卡操作。" msg.contains("网络") -> "网络连接异常,请检查网络连接后重新刷卡。" msg.contains("超时") -> "网络请求超时,请重新刷卡重试。" else -> "获取卡信息失败:$msg\n\n请重新刷卡重试。" } } } // 显示错误信息的对话框 showConfirmDialog(errorMessage) { resetToReadingState() } } } generallibrary/src/main/java/com/dayu/general/activity/NfcWreatActivity.kt
@@ -30,6 +30,10 @@ var cardType = "" var cardAddr = "" var cardFee = 0 // 充值相关金额 private var rechargeAmount = 0.0 private var bonusAmount = 0.0 //订单编号 var orderNumber = "" @@ -53,6 +57,11 @@ operationTypeCode = intent?.getIntExtra("operationTypeCode", -1) ?: -1 orderNumber = intent?.getStringExtra("orderNumber") ?: "" operationType = CardOperationType.fromCode(operationTypeCode) // 获取充值相关金额 rechargeAmount = intent?.getDoubleExtra("rechargeAmount", 0.0) ?: 0.0 bonusAmount = intent?.getDoubleExtra("bonusAmount", 0.0) ?: 0.0 if (intent?.hasExtra("cardFee") == true) { cardFee = intent?.getIntExtra("cardFee", 0) ?: 0 } @@ -86,12 +95,23 @@ CardOperationType.Recharge -> { var textData = StringBuilder() textData.append("用户充值\n") textData.append("订单号:" + orderNumber + "\n") if (userCard.balance != 0) { val balanceInYuan = userCard.balance / 100.0 // 转换为元 textData.append("充值金额:" + String.format("%.2f", balanceInYuan) + "元") // 显示充值金额 if (rechargeAmount > 0) { textData.append("充值金额:" + String.format("%.2f", rechargeAmount) + "元\n") } // 显示赠送金额 if (bonusAmount > 0) { textData.append("赠送金额:" + String.format("%.2f", bonusAmount) + "元\n") } // 显示总金额(写入卡内的总余额) if (userCard.balance != 0) { val totalBalanceInYuan = userCard.balance / 100.0 // 转换为元 textData.append("卡内总余额:" + String.format("%.2f", totalBalanceInYuan) + "元") } binding?.cardData?.text = textData.toString() } generallibrary/src/main/java/com/dayu/general/activity/RechargeDetailActivity.kt
@@ -25,6 +25,7 @@ import com.dayu.general.databinding.ActivityRechargeDetailBinding import com.dayu.general.net.ApiManager import com.dayu.general.net.BaseResponse import com.dayu.general.tool.CardCommon.Companion.USER_CARD_TYPE_1 import com.dayu.general.tool.CardOperationType class RechargeDetailActivity : AppCompatActivity() { @@ -395,7 +396,13 @@ // 设置用户卡信息 cardInfo?.let { info -> userCode = info.cardNum ?: "" balance = ((rechargeAmount + bonusAmount) * 100).toInt() // 转换为分 // 计算新余额:原有余额 + 充值金额 + 赠送金额 val originalBalance = this@RechargeDetailActivity.userCard?.balance ?: 0 // 原有余额(分) val rechargeAmountInCents = (rechargeAmount * 100).toInt() // 充值金额转分 val bonusAmountInCents = (bonusAmount * 100).toInt() // 赠送金额转分 balance = originalBalance + rechargeAmountInCents + bonusAmountInCents } // 设置其他必要信息 @@ -406,22 +413,16 @@ // 启动写卡Activity val intent = Intent(this, NfcWreatActivity::class.java).apply { putExtra("cardType", "USER_CARD") // 用户卡类型 putExtra("cardType", USER_CARD_TYPE_1) // 用户卡类型 putExtra("cardAddr", cardAddress) putExtra("operationTypeCode", CardOperationType.Recharge.code) putExtra("orderNumber", rechargeResult.orderNo) putExtra("userCard", userCard) putExtra("rechargeAmount", rechargeAmount) // 传递充值金额 putExtra("bonusAmount", bonusAmount) // 传递赠送金额 } startActivity(intent) // 显示成功信息 val formattedRecharge = String.format("%.2f", rechargeAmount) val formattedBonus = String.format("%.2f", bonusAmount) val formattedTotal = String.format("%.2f", rechargeAmount + bonusAmount) ToastUtil.show("充值订单创建成功\n订单号: ${rechargeResult.orderNo}\n充值金额: ${formattedRecharge}元\n赠送金额: ${formattedBonus}元\n总金额: ${formattedTotal}元\n请贴卡进行写卡操作") } catch (e: Exception) { ToastUtil.show("启动写卡界面失败: ${e.message}") } generallibrary/src/main/java/com/dayu/general/tool/NfcReadHelper.kt
@@ -166,7 +166,7 @@ */ fun getCardTypeAndCardNumber(): String { return try { adapter.getCradTypeAndCardNumber(7) adapter.getCradTypeAndCardNumber(7,0,8) } catch (e: Exception) { e.printStackTrace() "" @@ -324,7 +324,7 @@ fun getUserCardData(): UserCard? { return try { // 获取基础卡数据 val baseCard = adapter.getUserCardData(UserCard()) val baseCard = adapter.getUserCardData(7,UserCard()) // 如果获取成功且是UserCard类型,则返回 if (baseCard is UserCard) { baseCard generallibrary/src/main/res/layout/activity_card_read.xml
New file @@ -0,0 +1,375 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/base_green_bg"> <com.dayu.baselibrary.view.TitleBar android:id="@+id/titleBar" android:layout_width="match_parent" android:layout_height="@dimen/dimen_title_height" android:background="@color/title_bar_bg" android:elevation="4dp" app:centerText="读卡" /> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/titleBar" android:fillViewport="true"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <!-- 读卡提示区域 --> <LinearLayout android:id="@+id/card_read_LL" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:visibility="gone"> <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="16dp" app:cardCornerRadius="8dp" app:cardElevation="2dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:padding="16dp"> <TextView android:id="@+id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp" android:gravity="center" android:text="读卡操作" android:textColor="@color/base_blue_bg" android:textSize="@dimen/big_text_size" android:textStyle="bold" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="20dp" android:gravity="center" android:text="请将卡贴在设备上进行读卡" android:textColor="#333333" android:textSize="@dimen/text_size" android:textStyle="bold" /> <ImageView android:layout_width="120dp" android:layout_height="120dp" android:layout_gravity="center" android:layout_marginBottom="20dp" android:scaleType="fitCenter" android:src="@mipmap/nfc_write" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="请保持手持机和卡片不要移动" android:textColor="#666666" android:textSize="@dimen/new_card_size" /> </LinearLayout> </androidx.cardview.widget.CardView> </LinearLayout> <!-- 卡片信息显示区域 --> <LinearLayout android:id="@+id/card_info_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:visibility="visible"> <!-- 卡内数据区域 --> <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp" app:cardCornerRadius="8dp" app:cardElevation="2dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <!-- 持卡人 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:orientation="horizontal" android:gravity="center_vertical" android:background="#F8F9FA" android:padding="12dp"> <TextView android:layout_width="105dp" android:layout_height="wrap_content" android:text="持卡人:" android:textColor="#333333" android:textSize="@dimen/text_size" /> <TextView android:id="@+id/tv_user_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="--" android:textColor="#666666" android:textSize="@dimen/text_size" /> </LinearLayout> <!-- 卡号 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:orientation="horizontal" android:gravity="center_vertical" android:background="#FFFFFF" android:padding="12dp"> <TextView android:layout_width="105dp" android:layout_height="wrap_content" android:text="卡号:" android:textColor="#333333" android:textSize="@dimen/text_size" /> <TextView android:id="@+id/tv_card_number" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="--" android:textColor="#666666" android:textSize="@dimen/text_size" /> </LinearLayout> <!-- 卡片类型 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:orientation="horizontal" android:gravity="center_vertical" android:background="#F8F9FA" android:padding="12dp"> <TextView android:layout_width="105dp" android:layout_height="wrap_content" android:text="卡类型:" android:textColor="#333333" android:textSize="@dimen/text_size" /> <TextView android:id="@+id/tv_card_type" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="--" android:textColor="#666666" android:textSize="@dimen/text_size" /> </LinearLayout> <!-- 卡内余额 --> <LinearLayout android:id="@+id/ll_card_balance" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:orientation="horizontal" android:visibility="visible" android:gravity="center_vertical" android:background="#FFFFFF" android:padding="12dp"> <TextView android:layout_width="105dp" android:layout_height="wrap_content" android:text="卡余额:" android:textColor="#333333" android:textSize="@dimen/text_size" /> <TextView android:id="@+id/tv_card_balance" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="0.00元" android:textColor="#FF6B35" android:textSize="@dimen/text_size" android:textStyle="bold" /> </LinearLayout> <!-- 用户编号 --> <LinearLayout android:id="@+id/ll_user_number" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:orientation="horizontal" android:visibility="visible" android:gravity="center_vertical" android:background="#F8F9FA" android:padding="12dp"> <TextView android:layout_width="105dp" android:layout_height="wrap_content" android:text="用户编号:" android:textColor="#333333" android:textSize="@dimen/text_size" /> <TextView android:id="@+id/tv_user_number" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="--" android:textColor="#666666" android:textSize="@dimen/text_size" /> </LinearLayout> <!-- 手机号 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:orientation="horizontal" android:gravity="center_vertical" android:background="#FFFFFF" android:padding="12dp"> <TextView android:layout_width="105dp" android:layout_height="wrap_content" android:text="手机号:" android:textColor="#333333" android:textSize="@dimen/text_size" /> <TextView android:id="@+id/tv_phone" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="--" android:textColor="#666666" android:textSize="@dimen/text_size" /> </LinearLayout> <!-- 身份证号 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:orientation="horizontal" android:gravity="center_vertical" android:background="#F8F9FA" android:padding="12dp"> <TextView android:layout_width="105dp" android:layout_height="wrap_content" android:text="身份证号:" android:textColor="#333333" android:textSize="@dimen/text_size" /> <TextView android:id="@+id/tv_id_card" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="--" android:textColor="#666666" android:textSize="@dimen/text_size" /> </LinearLayout> <!-- 卡片状态 --> <LinearLayout android:id="@+id/ll_card_status" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:orientation="horizontal" android:visibility="visible" android:gravity="center_vertical" android:background="#FFFFFF" android:padding="12dp"> <TextView android:layout_width="105dp" android:layout_height="wrap_content" android:text="卡片状态:" android:textColor="#333333" android:textSize="@dimen/text_size" /> <TextView android:id="@+id/tv_card_status" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="正常" android:textColor="#4CAF50" android:textSize="@dimen/text_size" /> </LinearLayout> <!-- 最后使用时间 --> <LinearLayout android:id="@+id/ll_last_use_time" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:orientation="horizontal" android:visibility="visible" android:gravity="center_vertical" android:background="#F8F9FA" android:padding="12dp"> <TextView android:layout_width="105dp" android:layout_height="wrap_content" android:text="最后充值时间:" android:textColor="#333333" android:textSize="@dimen/text_size" /> <TextView android:id="@+id/tv_last_use_time" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="--" android:textColor="#666666" android:textSize="@dimen/text_size" /> </LinearLayout> </LinearLayout> </androidx.cardview.widget.CardView> </LinearLayout> </LinearLayout> </ScrollView> </RelativeLayout>