左晓为主开发手持机充值管理机
refactor(CardReplaceActivity): 重构补卡流程并优化界面显示- 重新设计卡片信息展示布局,增加新卡卡地址、客户编号等字段
- 优化补卡逻辑,支持通过卡号或客户编号获取卡片信息
-调整工本费和返回金额输入框位置
- 更新API调用路径,使用新的卡片查询接口
4个文件已修改
494 ■■■■ 已修改文件
generallibrary/src/main/java/com/dayu/general/activity/CardReplaceActivity.kt 178 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/CardUnlossActivity.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/net/NetConstans.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/res/layout/activity_card_replace.xml 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
generallibrary/src/main/java/com/dayu/general/activity/CardReplaceActivity.kt
@@ -38,7 +38,7 @@
    private var cardInfo: CardInfoResult? = null
    private var cardInfoByClient: CardInfoByClientResult? = null
    private var userCard: UserCard? = null
    private var clientNum: String? = null
    private var cardNum: String? = null
    private var newCardNumber: String? = null // 新卡卡号
    // 支付方式相关属性
@@ -50,11 +50,9 @@
        /**
         * 启动补卡Activity
         */
        fun start(context: Context, clientNum: String? = null) {
        fun start(context: Context, clientNum: String) {
            val intent = Intent(context, CardReplaceActivity::class.java)
            clientNum?.let {
                intent.putExtra("clientNum", it)
            }
            intent.putExtra("cardNum", clientNum)
            context.startActivity(intent)
        }
    }
@@ -65,12 +63,12 @@
        setContentView(binding.root)
        // 获取传递的clientNum参数
        clientNum = intent.getStringExtra("clientNum")
        cardNum = intent.getStringExtra("cardNum")
        initView()
        // 获取支付方式
        getPaymentMethods()
        // 无论是否有clientNum,都先显示读卡界面,等待用户刷新卡
        resetToReadingState()
    }
@@ -194,7 +192,7 @@
        cardInfoByClient = null
        newCardNumber = null
        binding.etCardCost.setText("")
        binding.etReturnAmount.setText("0")
        binding.etReturnAmount.setText("")
    }
    /**
@@ -244,7 +242,7 @@
            }
            val readCardNumber = parts[0]
            val cardType = parts[1]
            if (readCardNumber.isBlank()) {
                showConfirmDialog("卡号为空,无法进行操作,请重新刷卡") {
                }
@@ -254,27 +252,33 @@
            // 保存新卡卡号
            this.newCardNumber = readCardNumber
            // 根据是否有clientNum参数决定获取卡信息的方式
            if (clientNum != null) {
                // 如果有clientNum,使用clientNum获取卡信息
                getCardInfoByClientNum(clientNum!!)
            } else {
                // 如果没有clientNum,根据卡片类型进行处理
                when (cardType) {
                    CardCommon.USER_CARD_TYPE_1,
                    CardCommon.USER_CARD_TYPE_2,
                    CardCommon.USER_CARD_TYPE_3 -> {
                        // 用户卡:解析卡内数据并调用接口
                        showConfirmDialog("该卡片类型不支持补卡操作") {
            when (cardType) {
                "00" -> {
                    // 白卡才可以补卡
                    if (cardNum != null) {
                        // 如果有clientNum,使用clientNum获取卡信息
                        getCardInfoByClientNum(cardNum!!)
                    } else {
                        showConfirmDialog("获取旧卡信息数据失败") {
                            resetToReadingState()
                        }
                    }
                    else -> {
                        // 管理类卡不支持补卡
                        handleUserCard(readCardNumber, cardType, nfcAdapter)
                }
                CardCommon.USER_CARD_TYPE_1,
                CardCommon.USER_CARD_TYPE_2,
                CardCommon.USER_CARD_TYPE_3 -> {
                    // 用户卡:解析卡内数据并调用接口
                    showConfirmDialog("用户卡不支持补卡") {
                        resetToReadingState()
                    }
                }
                else -> {
                }
            }
        } catch (e: Exception) {
            showConfirmDialog("读卡异常:${e.message}") {
            }
@@ -282,102 +286,6 @@
        }
    }
    /**
     * 处理用户卡
     */
    private fun handleUserCard(cardNumber: String, cardType: String, nfcAdapter: NfcReadHelper) {
        // 解析用户卡数据
        val userCard = nfcAdapter.getUserCardData()
        if (userCard == null) {
            showConfirmDialog("解析卡片数据失败,请重新刷卡") {
            }
            return
        }
        // 输出用户卡内所有信息到日志
        android.util.Log.d("CardReplaceActivity", "=== 用户卡信息 ===")
        android.util.Log.d("CardReplaceActivity", "卡号: $cardNumber")
        android.util.Log.d("CardReplaceActivity", "卡片类型: $cardType")
        android.util.Log.d("CardReplaceActivity", "卡内余额: ${userCard.balance}")
        android.util.Log.d("CardReplaceActivity", "==================")
        this.userCard = userCard
        // 根据卡号获取卡片详细信息
        getCardInfo(cardNumber, cardType, userCard)
    }
    /**
     * 获取卡片详细信息(用户卡专用)
     */
    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.scrollReadCard.visibility = android.view.View.GONE
        binding.cardInfoContainer.visibility = android.view.View.VISIBLE
        binding.bottomButtonContainer.visibility = android.view.View.VISIBLE
        this.cardInfo = cardInfo
        this.cardNumber = cardNumber
        // 显示新卡卡号
        binding.tvCurrentCardAddress.text = newCardNumber ?: cardNumber
        // 显示服务器数据
        cardInfo?.let { info ->
            binding.tvUserName.text = info.userName ?: "未知"
            binding.tvPhone.text = info.phone ?: "未绑定"
            // 根据state字段显示状态
            val (statusText, statusColor) = when (info.state) {
                1 -> Pair("正常", android.graphics.Color.parseColor("#4CAF50")) // 绿色
                2 -> Pair("已注销", android.graphics.Color.parseColor("#FF5722")) // 深橙色
                3 -> Pair("已挂失", android.graphics.Color.parseColor("#FF9800")) // 橙色
                4 -> Pair("无效卡片", android.graphics.Color.parseColor("#F44336")) // 红色
                else -> Pair("未知状态", android.graphics.Color.parseColor("#9E9E9E")) // 灰色
            }
            binding.tvCardStatus.text = statusText
            binding.tvCardStatus.setTextColor(statusColor)
        }
        userCard.let { card ->
            // 余额转换为元(原始数据可能是分)
            binding.tvCardBalance.text = MornyUtil.changeF2Y(card.balance) + "元"
        }
    }
    /**
     * 处理卡信息获取错误
@@ -463,7 +371,7 @@
        val map = mutableMapOf<String, Any>()
        map["cardAddr"] = newCardNumber!! // 使用新卡卡号
        // 根据数据源选择cardNum参数
        val cardNum = when {
            cardInfo != null -> cardInfo!!.cardNum.toString()
@@ -471,7 +379,7 @@
            else -> cardNumber ?: newCardNumber!!
        }
        map["cardNum"] = cardNum
        map["cardCost"] = cardCost
        map["returnAmount"] = returnAmount  // 使用返回金额
        map["paymentId"] = paymentId // 使用选中的支付方式ID
@@ -488,7 +396,12 @@
                override fun onNext(t: BaseResponse<CardReplaceResult>) {
                    if (t.success && t.content != null) {
                        // 补卡成功,跳转到写卡界面
                        startWriteCardActivity(t.content!!, cardCost, returnAmount, createUserCardFromData())
                        startWriteCardActivity(
                            t.content!!,
                            cardCost,
                            returnAmount,
                            createUserCardFromData()
                        )
                    } else {
                        // 补卡失败
                        val errorMsg = if (t.msg.isNullOrBlank()) "补卡失败,请重试" else t.msg
@@ -520,6 +433,7 @@
                    phoneNumber = cardInfoByClient!!.phone.toString()
                }
            }
            else -> UserCard() // 返回空的UserCard对象
        }
    }
@@ -568,13 +482,13 @@
    /**
     * 根据客户编号获取卡信息
     */
    private fun getCardInfoByClientNum(clientNum: String) {
    private fun getCardInfoByClientNum(cardNum: String) {
        val map = mutableMapOf<String, Any>()
        map["clientNum"] = clientNum
        map["cardNum"] = cardNum
        ApiManager.getInstance().requestGetLoading(
            this,
            "terminal/card/getcardbyclientnum",
            "terminal/card/getcardbycardnum",
            CardInfoByClientResult::class.java,
            map,
            object : SubscriberListener<BaseResponse<CardInfoByClientResult>>() {
@@ -610,11 +524,17 @@
        binding.bottomButtonContainer.visibility = android.view.View.VISIBLE
        // 显示新卡卡号(如果已读取到新卡)
        binding.tvCurrentCardAddress.text = newCardNumber ?: cardInfo.cardNum
        binding.tvCurrentCardAddress.text = newCardNumber ?: ""
        // 显示卡信息
        binding.tvUserName.text = cardInfo.clientName
        binding.tvPhone.text = cardInfo.phone.toString()
        // 显示卡号
        binding.tvCardNumber.text = cardInfo.cardNum ?: "--"
        // 显示客户编号
        binding.tvCustomerId.text = cardInfo.clientNum ?: "--"
        // 根据cardState字段显示状态
        val (statusText, statusColor) = when (cardInfo.cardState) {
@@ -629,7 +549,7 @@
        // 显示余额
        binding.tvCardBalance.text = "${cardInfo.money}元"
        // 设置cardNumber用于后续API调用
        cardNumber = cardInfo.cardNum
    }
generallibrary/src/main/java/com/dayu/general/activity/CardUnlossActivity.kt
@@ -99,7 +99,7 @@
        // 设置补卡按钮点击事件  
        cardAdapter?.setOnReplaceClickListener { card ->
            CardReplaceActivity.start(this, card.clientNum)
            card.cardNum?.let { CardReplaceActivity.start(this, it) }
        }
    }
generallibrary/src/main/java/com/dayu/general/net/NetConstans.kt
@@ -8,8 +8,8 @@
class NetConstans {
    companion object {
//        const val BASE_URL: String = "https://no253541tf71.vicp.fun/"
//        const val BASE_URL: String = "http://192.168.40.166:54321/"
        const val BASE_URL: String = "http://192.168.10.87:54321/"
        const val BASE_URL: String = "http://192.168.40.166:54321/"
//        const val BASE_URL: String = "http://192.168.10.87:54321/"
        const val TOKEN_INVALID: String = "0000"
    }
generallibrary/src/main/res/layout/activity_card_replace.xml
@@ -21,7 +21,7 @@
        android:layout_height="match_parent"
        android:layout_below="@+id/titleBar"
        android:fillViewport="true"
        android:visibility="visible">
        android:visibility="gone">
        <LinearLayout
            android:layout_width="match_parent"
@@ -107,7 +107,7 @@
        android:layout_above="@+id/bottom_button_container"
        android:layout_below="@+id/titleBar"
        android:orientation="vertical"
        android:visibility="gone">
        android:visibility="visible">
        <ScrollView
            android:layout_width="match_parent"
@@ -121,66 +121,29 @@
                android:layout_height="wrap_content"
                android:orientation="vertical">
                <!-- 当前卡地址显示区域 -->
                <!-- 卡片信息区域 -->
                <androidx.cardview.widget.CardView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="12dp"
                    app:cardCornerRadius="8dp"
                    app:cardElevation="2dp">
                    app:cardCornerRadius="6dp"
                    app:cardElevation="1dp">
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        android:padding="16dp">
                        android:padding="14dp">
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="12dp"
                            android:text="卡号"
                            android:layout_marginBottom="10dp"
                            android:text="卡片信息"
                            android:textColor="@color/base_blue_bg"
                            android:textSize="18sp"
                            android:textSize="17sp"
                            android:textStyle="bold" />
                        <TextView
                            android:id="@+id/tv_current_card_address"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:background="#F5F5F5"
                            android:padding="12dp"
                            android:text="--"
                            android:textColor="#333333"
                            android:textSize="16sp"
                            android:textIsSelectable="true" />
                    </LinearLayout>
                </androidx.cardview.widget.CardView>
                <!-- 旧卡信息区域 -->
                <androidx.cardview.widget.CardView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:cardCornerRadius="8dp"
                    app:cardElevation="2dp">
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        android:padding="16dp">
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="12dp"
                            android:text="旧卡信息"
                            android:textColor="@color/base_blue_bg"
                            android:textSize="18sp"
                            android:textStyle="bold" />
                        <!-- 持卡人 -->
                        <!-- 新卡卡地址 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
@@ -188,27 +151,123 @@
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="12dp">
                            android:padding="8dp">
                            <TextView
                                android:layout_width="100dp"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="持卡人:"
                                android:text="新卡卡地址:"
                                android:textColor="#333333"
                                android:textSize="16sp"
                                android:textSize="15sp"
                                android:textStyle="bold" />
                            <TextView
                                android:id="@+id/tv_user_name"
                                android:id="@+id/tv_current_card_address"
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:background="#F5F5F5"
                                android:padding="8dp"
                                android:text="--"
                                android:textColor="#333333"
                                android:textSize="15sp"
                                android:textIsSelectable="true" />
                        </LinearLayout>
                        <View
                            android:layout_width="match_parent"
                            android:layout_height="1dp"
                            android:layout_marginTop="2dp"
                            android:background="@color/base_blue_bg" />
                        <!-- 持卡人和卡片状态 - 同一行显示 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginTop="2dp"
                            android:layout_marginBottom="1dp"
                            android:background="#FFFFFF"
                            android:orientation="horizontal"
                            android:padding="10dp">
                            <!-- 持卡人 -->
                            <LinearLayout
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:orientation="vertical">
                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:text="持卡人"
                                    android:textColor="#333333"
                                    android:textSize="13sp"
                                    android:textStyle="bold" />
                                <TextView
                                    android:id="@+id/tv_user_name"
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:layout_marginTop="3dp"
                                    android:text="--"
                                    android:textColor="#666666"
                                    android:textSize="15sp" />
                            </LinearLayout>
                            <!-- 卡片状态 -->
                            <LinearLayout
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:orientation="vertical">
                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:text="卡片状态"
                                    android:textColor="#333333"
                                    android:textSize="13sp"
                                    android:textStyle="bold" />
                                <TextView
                                    android:id="@+id/tv_card_status"
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:layout_marginTop="3dp"
                                    android:text="正常"
                                    android:textColor="#4CAF50"
                                    android:textSize="15sp" />
                            </LinearLayout>
                        </LinearLayout>
                        <!-- 卡号 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="1dp"
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="8dp">
                            <TextView
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="卡号:"
                                android:textColor="#333333"
                                android:textSize="14sp"
                                android:textStyle="bold" />
                            <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="16sp" />
                                android:textSize="14sp" />
                        </LinearLayout>
                        <!-- 卡片状态 -->
                        <!-- 客户编号 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
@@ -216,53 +275,24 @@
                            android:background="#FFFFFF"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="12dp">
                            android:padding="8dp">
                            <TextView
                                android:layout_width="100dp"
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="卡片状态:"
                                android:text="客户编号:"
                                android:textColor="#333333"
                                android:textSize="16sp"
                                android:textSize="14sp"
                                android:textStyle="bold" />
                            <TextView
                                android:id="@+id/tv_card_status"
                                android:id="@+id/tv_customer_id"
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:text="正常"
                                android:textColor="#4CAF50"
                                android:textSize="16sp" />
                        </LinearLayout>
                        <!-- 卡内余额 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="1dp"
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="12dp">
                            <TextView
                                android:layout_width="100dp"
                                android:layout_height="wrap_content"
                                android:text="卡内余额:"
                                android:textColor="#333333"
                                android:textSize="16sp"
                                android:textStyle="bold" />
                            <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="16sp"
                                android:textStyle="bold" />
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="14sp" />
                        </LinearLayout>
                        <!-- 手机号 -->
@@ -272,14 +302,14 @@
                            android:background="#FFFFFF"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="12dp">
                            android:padding="8dp">
                            <TextView
                                android:layout_width="100dp"
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="手机号:"
                                android:textColor="#333333"
                                android:textSize="16sp"
                                android:textSize="14sp"
                                android:textStyle="bold" />
                            <TextView
@@ -289,7 +319,35 @@
                                android:layout_weight="1"
                                android:text="--"
                                android:textColor="#666666"
                                android:textSize="16sp" />
                                android:textSize="14sp" />
                        </LinearLayout>
                        <!-- 卡内余额 -->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginBottom="1dp"
                            android:background="#F8F9FA"
                            android:gravity="center_vertical"
                            android:orientation="horizontal"
                            android:padding="8dp">
                            <TextView
                                android:layout_width="80dp"
                                android:layout_height="wrap_content"
                                android:text="卡内余额:"
                                android:textColor="#333333"
                                android:textSize="14sp"
                                android:textStyle="bold" />
                            <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="14sp"
                                android:textStyle="bold" />
                        </LinearLayout>
                    </LinearLayout>
@@ -309,7 +367,7 @@
        android:elevation="4dp"
        android:orientation="vertical"
        android:padding="16dp"
        android:visibility="gone">
        android:visibility="visible">
        <!-- 支付方式选择区域 -->
        <LinearLayout
@@ -343,6 +401,36 @@
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="16dp">
            <!-- 返回金额输入 -->
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginEnd="8dp"
                android:orientation="vertical">
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="6dp"
                    android:text="返回金额(元)"
                    android:textColor="#333333"
                    android:textSize="14sp"
                    android:textStyle="bold" />
                <EditText
                    android:id="@+id/et_return_amount"
                    android:layout_width="match_parent"
                    android:layout_height="48dp"
                    android:background="@drawable/edit_text_bg"
                    android:hint="返回金额"
                    android:inputType="numberDecimal"
                    android:padding="12dp"
                    android:text=""
                    android:textColor="#333333"
                    android:textColorHint="#999999"
                    android:textSize="16sp" />
            </LinearLayout>
            <!-- 工本费输入 -->
            <LinearLayout
@@ -375,36 +463,6 @@
                    android:textSize="16sp" />
            </LinearLayout>
            <!-- 返回金额输入 -->
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginStart="8dp"
                android:orientation="vertical">
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="6dp"
                    android:text="返回金额(元)"
                    android:textColor="#333333"
                    android:textSize="14sp"
                    android:textStyle="bold" />
                <EditText
                    android:id="@+id/et_return_amount"
                    android:layout_width="match_parent"
                    android:layout_height="48dp"
                    android:background="@drawable/edit_text_bg"
                    android:hint="返回金额"
                    android:inputType="numberDecimal"
                    android:padding="12dp"
                    android:text="0"
                    android:textColor="#333333"
                    android:textColorHint="#999999"
                    android:textSize="16sp" />
            </LinearLayout>
        </LinearLayout>