1:将道具商城做成原生的,添加点击的时候展示动效
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
package com.xscm.moduleutil.bean
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import java.util.ArrayList
|
||||
|
||||
/**
|
||||
* 项目名称:羽声语音
|
||||
* 时间:2026/1/4 10:22
|
||||
* 用途:装扮价格详情
|
||||
*/
|
||||
class DecorateDetailBean {
|
||||
// 用户信息(服务端返回的user_info字段)
|
||||
@SerializedName("user_info")
|
||||
var userInfo: UserInfoDecorate? = UserInfoDecorate()
|
||||
|
||||
// 装饰商品核心信息(服务端返回的decorate字段)
|
||||
@SerializedName("decorate")
|
||||
var decorate: Decorate? = null
|
||||
|
||||
/**
|
||||
* 用户信息内部类(对应服务端user_info)
|
||||
*/
|
||||
class UserInfoDecorate {
|
||||
@SerializedName("user_id")
|
||||
var userId: Int = 0
|
||||
|
||||
@SerializedName("user_coin")
|
||||
var userCoin: String = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* 装饰商品核心信息(对应服务端decorate,直接解析服务端返回数据)
|
||||
* 注意:与之前适配器的Item模型解耦,该类仅负责接收服务端数据,不承担适配器布局类型职责
|
||||
*/
|
||||
class Decorate {
|
||||
@SerializedName("title")
|
||||
var title: String = "" // 商品名称(如“粉色花头”)
|
||||
@SerializedName("price")
|
||||
var price : String =""
|
||||
@SerializedName("base_image")
|
||||
var base_image : String =""
|
||||
@SerializedName("price_list")
|
||||
var priceList: List<PriceListBean> = ArrayList() // 价格/时长列表(服务端返回数组)
|
||||
}
|
||||
|
||||
/**
|
||||
* 价格/时长明细bean(对应服务端price_list中的单个对象)
|
||||
* 可直接解析服务端返回的每个价格项数据,同时适配适配器的PriceItem模型
|
||||
*/
|
||||
class PriceListBean {
|
||||
@SerializedName("price")
|
||||
var price: String = "" // 现价
|
||||
|
||||
@SerializedName("original_price")
|
||||
var originalPrice: String = "" // 原价
|
||||
|
||||
@SerializedName("discount")
|
||||
var discount: String = "" // 折扣(如“5.0”)
|
||||
|
||||
@SerializedName("day")
|
||||
var day: Int = 0 // 有效天数
|
||||
|
||||
@SerializedName("month")
|
||||
var month: String = "" // 有效月数
|
||||
|
||||
@SerializedName("end_time")
|
||||
var endTime: String = "" // 有效期截止时间
|
||||
}
|
||||
|
||||
// ---------------------- 适配器适配相关:转换方法 + 适配器所需模型 ----------------------
|
||||
/**
|
||||
* 适配器的Item数据模型(密封类,区分单行/多选项)
|
||||
* 与服务端数据模型解耦,专门用于RecyclerView适配器布局
|
||||
*/
|
||||
sealed class DecorateAdapterItem {
|
||||
// 单行信息类型(如“商品价格”“有效期至”)
|
||||
data class SingleItem(
|
||||
val label: String, // 左侧标签文字
|
||||
val content: String // 右侧内容文字
|
||||
) : DecorateAdapterItem()
|
||||
|
||||
// 购买时长多选项类型(承载所有时长选项)
|
||||
data class MultiOptionItem(
|
||||
val options: List<PriceListBean> // 直接复用PriceListBean(已适配服务端数据)
|
||||
) : DecorateAdapterItem()
|
||||
|
||||
// type=12专用:购买次数(加减按钮)
|
||||
data class BuyCountItem(
|
||||
val initialCount: Int,
|
||||
val unitPrice: String
|
||||
) : DecorateAdapterItem()
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换方法:将服务端数据(Decorate)转换为适配器所需数据列表(List<DecorateAdapterItem>)
|
||||
* 实现服务端数据与适配器的桥接,方便适配器直接使用
|
||||
* @param defaultSelectedPos 默认选中的时长选项下标(默认0,即第一个选项)
|
||||
*/
|
||||
fun convertToAdapterData(
|
||||
decorate: Decorate?,
|
||||
defaultSelectedPos: Int = 0
|
||||
): List<DecorateAdapterItem> {
|
||||
val adapterDataList = mutableListOf<DecorateAdapterItem>()
|
||||
if (decorate == null ) {
|
||||
return adapterDataList
|
||||
}
|
||||
|
||||
if ( decorate.priceList.isEmpty()){
|
||||
// ---------- type=12:解析单个字段(无price_list),新增购买次数、商品总价 ----------
|
||||
val unitPrice = decorate.price// 直接取Decorate的singlePrice(服务端返回的单价)
|
||||
val unitPriceStr = unitPrice // 格式化单价,避免小数异常
|
||||
|
||||
|
||||
// 2. 商品单价(单行项,取decorate.singlePrice)
|
||||
adapterDataList.add(
|
||||
DecorateAdapterItem.SingleItem(
|
||||
label = "商品单价",
|
||||
content = unitPriceStr
|
||||
)
|
||||
)
|
||||
|
||||
// 3. 购买次数(type=12专用,初始数量1,传入单价用于计算总价)
|
||||
adapterDataList.add(
|
||||
DecorateAdapterItem.BuyCountItem(
|
||||
initialCount = 1,
|
||||
unitPrice = unitPrice
|
||||
)
|
||||
)
|
||||
|
||||
// 4. 商品总价(单行项,初始:单价×1)
|
||||
adapterDataList.add(
|
||||
DecorateAdapterItem.SingleItem(
|
||||
label = "商品总价",
|
||||
content = unitPriceStr // 初始总价=单价×1
|
||||
)
|
||||
)
|
||||
|
||||
}else {
|
||||
|
||||
// 安全获取默认选中项(防止下标越界)
|
||||
val selectedPos = if (defaultSelectedPos in decorate.priceList.indices) {
|
||||
defaultSelectedPos
|
||||
} else {
|
||||
0
|
||||
}
|
||||
val selectedPriceItem = decorate.priceList[selectedPos]
|
||||
|
||||
// 1. 添加“商品价格”单行项(取选中项的现价)
|
||||
adapterDataList.add(
|
||||
DecorateAdapterItem.SingleItem(
|
||||
label = "商品价格",
|
||||
content = "${selectedPriceItem.price}" // 拼接货币符号,优化展示
|
||||
)
|
||||
)
|
||||
|
||||
// 2. 添加“有效期至”单行项(取选中项的截止时间)
|
||||
adapterDataList.add(
|
||||
DecorateAdapterItem.SingleItem(
|
||||
label = "有效期至",
|
||||
content = selectedPriceItem.endTime
|
||||
)
|
||||
)
|
||||
|
||||
// 3. 添加“购买时长”多选项(承载所有价格/时长列表)
|
||||
adapterDataList.add(
|
||||
DecorateAdapterItem.MultiOptionItem(
|
||||
options = decorate.priceList
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
return adapterDataList
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ class PersonaltyListBean {
|
||||
var price: Int = 0 // 实际价格(金币)
|
||||
var special_num: Int = 0 // 靓号
|
||||
var original_price: Int = 0 // 原价
|
||||
var discount: Int = 0 // 折扣
|
||||
var discount: Double = 0.0 // 折扣
|
||||
var discount_str: String = "" // 折扣字段
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import com.xscm.moduleutil.widget.Constants;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Field;
|
||||
@@ -470,6 +469,13 @@ public interface ApiServer {
|
||||
@GET(Constants.GET_DECORATE)
|
||||
Call<BaseModel<List<ZhuangBanShangChengBean>>> getDecorateList(@Query("type") String type);
|
||||
|
||||
@GET(Constants.GET_DECORATE_DETAIL)
|
||||
Call<BaseModel<DecorateDetailBean>> getDecorateDetail(@Query("did") String id);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST(Constants.POST_PAY_DECORATE)
|
||||
Call<BaseModel<String>> payDecorate(@Field("did") String id, @Field("day") String day,@Field("num") String num);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST(Constants.POST_GZ)
|
||||
Call<BaseModel<String>> userGuanz(@Field("user_id") String userId, @Field("type") String type);
|
||||
|
||||
@@ -41,7 +41,6 @@ import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EventListener;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -2721,6 +2720,53 @@ public class RetrofitClient {
|
||||
});
|
||||
}
|
||||
|
||||
public void payDecorate(String did, String day, String num, BaseObserver<String> observer) {
|
||||
sApiServer.payDecorate(did, day, num).enqueue(new Callback<BaseModel<String>>() {
|
||||
|
||||
@Override
|
||||
public void onResponse(Call<BaseModel<String>> call, Response<BaseModel<String>> response) {
|
||||
onNextRetu(response, observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<BaseModel<String>> call, Throwable t) {
|
||||
LogUtils.e("payDecorate", t.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void getDecorateDetail(String did, BaseObserver<DecorateDetailBean> observer) {
|
||||
sApiServer.getDecorateDetail(did).enqueue(new Callback<BaseModel<DecorateDetailBean>>() {
|
||||
|
||||
@Override
|
||||
public void onResponse(Call<BaseModel<DecorateDetailBean>> call, Response<BaseModel<DecorateDetailBean>> response) {
|
||||
if (response.code() == 200) {
|
||||
BaseModel<DecorateDetailBean> listBaseModel = response.body();
|
||||
if (listBaseModel.getCode() == 1) {
|
||||
observer.onNext(listBaseModel.getData());
|
||||
} else if (listBaseModel.getCode() == 301) {
|
||||
setCode301(listBaseModel.getMsg());
|
||||
} else if (listBaseModel.getCode() == 0) {
|
||||
observer.onNext(new DecorateDetailBean());
|
||||
ToastUtils.showShort(listBaseModel.getMsg());
|
||||
}
|
||||
|
||||
} else {
|
||||
observer.onNext(new DecorateDetailBean());
|
||||
ToastUtils.showLong("请求装饰详情错误", response.code());
|
||||
LogUtils.e("getDecorateDetail", response.message());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<BaseModel<DecorateDetailBean>> call, Throwable t) {
|
||||
LogUtils.e("getDecorateDetail", t.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void getDecorateList(String type, BaseObserver<List<ZhuangBanShangChengBean>> observer) {
|
||||
sApiServer.getDecorateList(type).enqueue(new Callback<BaseModel<List<ZhuangBanShangChengBean>>>() {
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.xscm.moduleutil.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
/**
|
||||
* 项目名称:羽声语音
|
||||
* 时间:2026/1/4 16:09
|
||||
* 用途:自定义组件:购买数量,带减少增加按钮
|
||||
*/
|
||||
public class AmountView extends LinearLayout implements View.OnClickListener, TextWatcher {
|
||||
|
||||
private static final String TAG = "AmountView";
|
||||
private int amount = 1; //购买数量
|
||||
private int goods_storage = Integer.MAX_VALUE; //商品库存
|
||||
|
||||
private OnAmountChangeListener mListener;
|
||||
|
||||
private EditText etAmount;
|
||||
private Button btnDecrease;
|
||||
private Button btnIncrease;
|
||||
|
||||
public AmountView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public AmountView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.view_amount, this);
|
||||
etAmount = (EditText) findViewById(R.id.etAmount);
|
||||
btnDecrease = (Button) findViewById(R.id.btnDecrease);
|
||||
btnIncrease = (Button) findViewById(R.id.btnIncrease);
|
||||
btnDecrease.setOnClickListener(this);
|
||||
btnIncrease.setOnClickListener(this);
|
||||
etAmount.addTextChangedListener(this);
|
||||
|
||||
TypedArray obtainStyledAttributes = getContext().obtainStyledAttributes(attrs, R.styleable.AmountView);
|
||||
int btnWidth = obtainStyledAttributes.getDimensionPixelSize(R.styleable.AmountView_btnWidth, LayoutParams.WRAP_CONTENT);
|
||||
int tvWidth = obtainStyledAttributes.getDimensionPixelSize(R.styleable.AmountView_tvWidth, 80);
|
||||
int tvTextSize = obtainStyledAttributes.getDimensionPixelSize(R.styleable.AmountView_tvTextSize, 0);
|
||||
int btnTextSize = obtainStyledAttributes.getDimensionPixelSize(R.styleable.AmountView_btnTextSize, 0);
|
||||
obtainStyledAttributes.recycle();
|
||||
|
||||
LayoutParams btnParams = new LayoutParams(btnWidth, LayoutParams.MATCH_PARENT);
|
||||
btnDecrease.setLayoutParams(btnParams);
|
||||
btnIncrease.setLayoutParams(btnParams);
|
||||
if (btnTextSize != 0) {
|
||||
btnDecrease.setTextSize(TypedValue.COMPLEX_UNIT_PX, btnTextSize);
|
||||
btnIncrease.setTextSize(TypedValue.COMPLEX_UNIT_PX, btnTextSize);
|
||||
}
|
||||
|
||||
LayoutParams textParams = new LayoutParams(tvWidth, LayoutParams.MATCH_PARENT);
|
||||
etAmount.setLayoutParams(textParams);
|
||||
if (tvTextSize != 0) {
|
||||
etAmount.setTextSize(tvTextSize);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnAmountChangeListener(OnAmountChangeListener onAmountChangeListener) {
|
||||
this.mListener = onAmountChangeListener;
|
||||
}
|
||||
|
||||
public void setGoods_storage(int goods_storage) {
|
||||
this.goods_storage = goods_storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int i = v.getId();
|
||||
if (i == R.id.btnDecrease) {
|
||||
if (amount > 1) {
|
||||
amount--;
|
||||
etAmount.setText(amount + "");
|
||||
}
|
||||
} else if (i == R.id.btnIncrease) {
|
||||
if (amount < goods_storage) {
|
||||
amount++;
|
||||
etAmount.setText(amount + "");
|
||||
}
|
||||
}
|
||||
|
||||
etAmount.clearFocus();
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.onAmountChange(this, amount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (s.toString().isEmpty())
|
||||
return;
|
||||
amount = Integer.valueOf(s.toString());
|
||||
if (amount > goods_storage) {
|
||||
etAmount.setText(goods_storage + "");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.onAmountChange(this, amount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface OnAmountChangeListener {
|
||||
void onAmountChange(View view, int amount);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -259,6 +259,8 @@ public class Constants {
|
||||
public static final String LIKE_ALBUM = "/api/User/like_album";//相册点赞
|
||||
public static final String GET_PERSONALTY = "/api/Decorate/get_type_list";//装扮类型列表
|
||||
public static final String GET_DECORATE = "/api/Decorate/user_decorate";//装扮详情
|
||||
public static final String GET_DECORATE_DETAIL = "/api/Decorate/get_decorate_detail";//装饰价格详情
|
||||
public static final String POST_PAY_DECORATE = "/api/Decorate/pay_decorate";//购买装扮
|
||||
public static final String SET_USER_DECORATE = "/api/Decorate/set_user_decorate";//用户装扮
|
||||
public static final String JOIN_ROOM = "/api/Room/join_room";//加入房间
|
||||
public static final String BEFORE_JOIN_ROOM_CHECK = "/api/Room/before_join_room_check";//加入房间前检查
|
||||
|
||||
12
BaseModule/src/main/res/drawable/bg_amount_layout.xml
Normal file
12
BaseModule/src/main/res/drawable/bg_amount_layout.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#FFFFFF" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/divider" />
|
||||
<padding
|
||||
android:bottom="1dp"
|
||||
android:left="1dp"
|
||||
android:right="1dp"
|
||||
android:top="1dp" />
|
||||
</shape>
|
||||
7
BaseModule/src/main/res/drawable/btn_amount.xml
Normal file
7
BaseModule/src/main/res/drawable/btn_amount.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:drawable="@color/divider" />
|
||||
<item android:state_enabled="false" android:drawable="@color/divider" />
|
||||
<item android:drawable="@android:color/white" />
|
||||
</selector>
|
||||
8
BaseModule/src/main/res/drawable/divider.xml
Normal file
8
BaseModule/src/main/res/drawable/divider.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<size
|
||||
android:width="0.5dp"/>
|
||||
<solid android:color="@color/divider"/>
|
||||
</shape>
|
||||
39
BaseModule/src/main/res/layout/view_amount.xml
Normal file
39
BaseModule/src/main/res/layout/view_amount.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:divider="@drawable/divider"
|
||||
android:background="@drawable/bg_amount_layout"
|
||||
android:showDividers="middle"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnDecrease"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/btn_amount"
|
||||
android:text="-"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etAmount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:minWidth="60dp"
|
||||
android:layout_weight="2"
|
||||
android:background="@null"
|
||||
android:inputType="number"
|
||||
android:gravity="center"
|
||||
android:text="1"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnIncrease"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/btn_amount"
|
||||
android:text="+"/>
|
||||
</LinearLayout>
|
||||
@@ -487,6 +487,14 @@
|
||||
<attr name="tagMargin" format="dimension" />
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
<declare-styleable name="AmountView">
|
||||
<!-- 左右2边+-按钮的宽度 -->
|
||||
<attr name="btnWidth" format="dimension"/>
|
||||
<!-- 中间TextView的宽度 -->
|
||||
<attr name="tvWidth" format="dimension"/>
|
||||
<!-- <attr name="tvColor" format="color"/> -->
|
||||
<attr name="tvTextSize" format="dimension"/>
|
||||
<attr name="btnTextSize" format="dimension"/>
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
@@ -12,7 +12,7 @@
|
||||
<color name="color_DADADA">#DADADA</color>
|
||||
<color name="color_BB8BE2">#BB8BE2</color>
|
||||
<color name="color_999999">#999999</color>
|
||||
|
||||
<color name="divider">#ffd2d2d2</color>
|
||||
<color name="colorPrimaryDark">#FFFFBB00</color>
|
||||
<color name="colorAccent">#FFFFBB00</color>
|
||||
<color name="color_666666">#666666</color>
|
||||
|
||||
@@ -58,10 +58,11 @@ public class PersonalityActivity extends BaseMvpActivity<PersonalityPresenter, A
|
||||
mBinding.topBar.setRightText(getResources().getString(com.xscm.moduleutil.R.string.shopping));
|
||||
mBinding.topBar.setRightColor(ColorManager.getInstance().getPrimaryColorInt());
|
||||
mBinding.topBar.getTvRight().setOnClickListener(v -> {
|
||||
Intent intent=new Intent(this, WebViewActivity.class);
|
||||
intent.putExtra("url", String.format(WebUrlConstants.INSTANCE.getWEB_PROP_MALL_URL(),SpUtil.getToken()));
|
||||
intent.putExtra("title", "道具商城");
|
||||
startActivity(intent);
|
||||
// Intent intent=new Intent(this, WebViewActivity.class);
|
||||
// intent.putExtra("url", String.format(WebUrlConstants.INSTANCE.getWEB_PROP_MALL_URL(),SpUtil.getToken()));
|
||||
// intent.putExtra("title", "道具商城");
|
||||
// startActivity(intent);
|
||||
startActivity(new Intent(this, PropMallActivity.class));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,19 +2,13 @@ package com.xscm.modulemain.activity.user.activity.ui.main
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.stx.xhb.xbanner.XBanner
|
||||
import com.blankj.utilcode.util.ActivityUtils
|
||||
import com.xscm.modulemain.R
|
||||
import com.xscm.modulemain.activity.user.conacts.PersonalityConacts
|
||||
import com.xscm.modulemain.activity.user.presenter.PersonalityPresenter
|
||||
import com.xscm.modulemain.databinding.FragmentPropMallBinding
|
||||
import com.xscm.modulemain.dialog.PurchaseOutfitsDialog
|
||||
import com.xscm.moduleutil.base.BaseMvpFragment
|
||||
import com.xscm.moduleutil.bean.BannerModel
|
||||
import com.xscm.moduleutil.bean.PersonaltyBean
|
||||
@@ -31,6 +25,7 @@ class PlaceholderFragment : BaseMvpFragment<PersonalityPresenter,FragmentPropMal
|
||||
private lateinit var personaltyAdapter: PersonaltyAdapter
|
||||
|
||||
var type: String =""
|
||||
var purchaseOutfitsDialog: PurchaseOutfitsDialog?=null
|
||||
|
||||
// 标记数据是否已加载
|
||||
private var isDataLoaded = false
|
||||
@@ -89,6 +84,10 @@ class PlaceholderFragment : BaseMvpFragment<PersonalityPresenter,FragmentPropMal
|
||||
|
||||
personaltyAdapter.setOnItemClickListener(object : PersonaltyAdapter.OnItemClickListener{
|
||||
override fun onItemClick(item: PersonaltyListBean?) {
|
||||
if (purchaseOutfitsDialog==null){
|
||||
purchaseOutfitsDialog = PurchaseOutfitsDialog(ActivityUtils.getTopActivity())
|
||||
}
|
||||
purchaseOutfitsDialog?.show( item)
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
package com.xscm.modulemain.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.xscm.modulemain.R
|
||||
import com.xscm.moduleutil.bean.DecorateDetailBean
|
||||
import com.xscm.moduleutil.bean.DecorateDetailBean.PriceListBean
|
||||
import com.xscm.moduleutil.widget.AmountView
|
||||
import com.xscm.moduleutil.widget.AmountView.OnAmountChangeListener
|
||||
|
||||
|
||||
/**
|
||||
* 项目名称:羽声语音
|
||||
* 时间:2026/1/4 11:16
|
||||
* 用途:装饰价格详情列表(兼容普通类型/type=12类型)
|
||||
*/
|
||||
class PurchaseOutfitsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
// 定义Item类型(补充TYPE_BUY_COUNT,对应购买次数项)
|
||||
private companion object {
|
||||
const val TYPE_SINGLE = 1
|
||||
const val TYPE_MULTI_OPTION = 2
|
||||
const val TYPE_BUY_COUNT = 3 // 新增:购买次数项类型(type=12专用)
|
||||
}
|
||||
|
||||
// 适配器数据列表
|
||||
private var dataList: MutableList<DecorateDetailBean.DecorateAdapterItem> = mutableListOf()
|
||||
// 记录选中的时长选项位置(普通类型专用,默认选中第一个)
|
||||
private var selectedOptionPos = 0
|
||||
|
||||
// 新增:购买次数相关变量(type=12专用,支撑加减逻辑和总价计算)
|
||||
private var currentBuyCount = 1 // 当前购买次数,最小值1
|
||||
private var currentUnitPrice = "" // 商品单价(type=12专用,从实体类获取)
|
||||
private var currentDecorateType = false // 标记是否为type=12(true=type12,false=普通类型)
|
||||
|
||||
// ---------------------- 新增:定义回调接口 ----------------------
|
||||
/**
|
||||
* 时长选项点击回调接口(普通类型专用)
|
||||
* 用于将选中的整体数据传递给调用者(Activity/Fragment)
|
||||
*/
|
||||
interface OnDurationOptionSelectedListener {
|
||||
/**
|
||||
* 时长选项被选中时的回调方法
|
||||
* @param selectedPos 选中的选项下标
|
||||
* @param selectedPriceItem 选中的完整价格/时长数据(核心整体数据)
|
||||
* @param allPriceOptions 所有价格/时长选项列表(可选,满足调用者额外需求)
|
||||
*/
|
||||
fun onDurationSelected(
|
||||
selectedPos: Int,
|
||||
selectedPriceItem: PriceListBean,
|
||||
allPriceOptions: List<PriceListBean>,
|
||||
)
|
||||
|
||||
fun onBuyCountChange(count: Int)
|
||||
}
|
||||
|
||||
// 暴露回调实例(可空)
|
||||
private var mListener: OnDurationOptionSelectedListener? = null
|
||||
|
||||
/**
|
||||
* 给调用者提供的setter方法,用于绑定回调
|
||||
*/
|
||||
fun setOnDurationOptionSelectedListener(listener: OnDurationOptionSelectedListener?) {
|
||||
this.mListener = listener
|
||||
}
|
||||
|
||||
// 新增:获取当前选中的价格/时长项(安全判空,防止数组越界,普通类型专用)
|
||||
private fun getSelectedPriceItem(): PriceListBean? {
|
||||
// 先找到MultiOptionItem(承载所有options),再根据selectedOptionPos获取选中项
|
||||
val multiItem = dataList.find { it is DecorateDetailBean.DecorateAdapterItem.MultiOptionItem }
|
||||
as? DecorateDetailBean.DecorateAdapterItem.MultiOptionItem
|
||||
val options = multiItem?.options ?: return null
|
||||
return if (selectedOptionPos in options.indices) {
|
||||
options[selectedOptionPos]
|
||||
} else {
|
||||
// 兜底返回第一个选项
|
||||
options.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 新增:获取所有价格/时长选项(普通类型专用) ----------------------
|
||||
private fun getAllPriceOptions(): List<DecorateDetailBean.PriceListBean> {
|
||||
val multiItem = dataList.find { it is DecorateDetailBean.DecorateAdapterItem.MultiOptionItem }
|
||||
as? DecorateDetailBean.DecorateAdapterItem.MultiOptionItem
|
||||
return multiItem?.options ?: emptyList()
|
||||
}
|
||||
|
||||
// 新增:根据选中项更新dataList中的“商品价格”“有效期至”(普通类型专用)
|
||||
// 优化:仅精准更新第0、1项,不遍历整个dataList,提升效率
|
||||
private fun updateSingleItemData() {
|
||||
val selectedItem = getSelectedPriceItem() ?: return
|
||||
|
||||
// 1. 精准更新第0项:商品价格(直接定位,无需遍历)
|
||||
if (dataList.size > 0 && dataList[0] is DecorateDetailBean.DecorateAdapterItem.SingleItem) {
|
||||
val priceItem = dataList[0] as DecorateDetailBean.DecorateAdapterItem.SingleItem
|
||||
dataList[0] = DecorateDetailBean.DecorateAdapterItem.SingleItem(
|
||||
label = priceItem.label,
|
||||
content = "${selectedItem.price}"
|
||||
)
|
||||
}
|
||||
|
||||
// 2. 精准更新第1项:有效期至(直接定位,无需遍历)
|
||||
if (dataList.size > 1 && dataList[1] is DecorateDetailBean.DecorateAdapterItem.SingleItem) {
|
||||
val endTimeItem = dataList[1] as DecorateDetailBean.DecorateAdapterItem.SingleItem
|
||||
dataList[1] = DecorateDetailBean.DecorateAdapterItem.SingleItem(
|
||||
label = endTimeItem.label,
|
||||
content = selectedItem.endTime
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新数据(从外部传入数据源,组装成Adapter需要的Item,兼容type=12)
|
||||
fun setData(decorateData: MutableList<DecorateDetailBean.DecorateAdapterItem> , type: Boolean) {
|
||||
dataList.clear()
|
||||
dataList.addAll(decorateData)
|
||||
currentDecorateType = type // 记录当前类型,区分是否为type=12
|
||||
|
||||
// 仅普通类型(非12,即type=false):校准购买时长的默认选中位置(依赖price_list)
|
||||
if (!type) {
|
||||
val multiItem = dataList.find { it is DecorateDetailBean.DecorateAdapterItem.MultiOptionItem }
|
||||
as? DecorateDetailBean.DecorateAdapterItem.MultiOptionItem
|
||||
val optionsSize = multiItem?.options?.size ?: 0
|
||||
selectedOptionPos = if (optionsSize > 0) 0 else 0
|
||||
} else {
|
||||
// type=12:重置购买次数和单价为初始值
|
||||
currentBuyCount = 1
|
||||
currentUnitPrice = ""
|
||||
}
|
||||
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
// 完善:补充TYPE_BUY_COUNT的视图类型判断
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (dataList[position]) {
|
||||
is DecorateDetailBean.DecorateAdapterItem.SingleItem -> TYPE_SINGLE
|
||||
is DecorateDetailBean.DecorateAdapterItem.MultiOptionItem -> TYPE_MULTI_OPTION
|
||||
is DecorateDetailBean.DecorateAdapterItem.BuyCountItem -> TYPE_BUY_COUNT // 新增:匹配购买次数项
|
||||
}
|
||||
}
|
||||
|
||||
// 完善:补充TYPE_BUY_COUNT的ViewHolder创建,加载对应的布局
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return when (viewType) {
|
||||
TYPE_SINGLE -> {
|
||||
val view = inflater.inflate(R.layout.item_single_info, parent, false)
|
||||
SingleInfoViewHolder(view)
|
||||
}
|
||||
TYPE_MULTI_OPTION -> {
|
||||
val view = inflater.inflate(R.layout.item_multi_option, parent, false)
|
||||
MultiOptionViewHolder(view)
|
||||
}
|
||||
TYPE_BUY_COUNT -> {
|
||||
// 新增:加载购买次数专属布局(需确保项目中有该布局文件)
|
||||
val view = inflater.inflate(R.layout.item_buy_count, parent, false)
|
||||
BuyCountViewHolder(view)
|
||||
}
|
||||
else -> throw IllegalArgumentException("未知的Item类型:$viewType")
|
||||
}
|
||||
}
|
||||
|
||||
// 完善:补充BuyCountViewHolder的绑定逻辑,兼容三种ViewHolder类型
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is SingleInfoViewHolder -> {
|
||||
val item = dataList[position] as DecorateDetailBean.DecorateAdapterItem.SingleItem
|
||||
holder.tvLabel.text = item.label
|
||||
holder.tvContent.text = item.content
|
||||
// 核心修改:仅第0项(商品价格/商品单价)显示图标,其他项隐藏
|
||||
holder.ivPriceIcon.visibility = if (position == 0) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
}
|
||||
is MultiOptionViewHolder -> {
|
||||
val item = dataList[position] as DecorateDetailBean.DecorateAdapterItem.MultiOptionItem
|
||||
holder.bindOptions(item.options)
|
||||
}
|
||||
is BuyCountViewHolder -> {
|
||||
// 新增:绑定购买次数数据(type=12专用)
|
||||
val item = dataList[position] as DecorateDetailBean.DecorateAdapterItem.BuyCountItem
|
||||
holder.bindData(item.initialCount, item.unitPrice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = dataList.size
|
||||
|
||||
// 单行信息的ViewHolder(保持原有逻辑,已绑定价格图标)
|
||||
inner class SingleInfoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val tvLabel: TextView = itemView.findViewById(R.id.tv_label)
|
||||
val tvContent: TextView = itemView.findViewById(R.id.tv_content)
|
||||
val ivPriceIcon: ImageView = itemView.findViewById(R.id.iv_price_icon) // 价格图标(仅第0项显示)
|
||||
}
|
||||
|
||||
// ---------------------- 完善:补全BuyCountViewHolder,实现加减交互和总价联动 ----------------------
|
||||
inner class BuyCountViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val ivMinus: AmountView = itemView.findViewById(R.id.amount_view) // 减号按钮
|
||||
// val tvCount: TextView = itemView.findViewById(R.id.tv_count) // 次数显示
|
||||
|
||||
fun bindData(initialCount: Int, unitPrice: String) {
|
||||
// 初始化数据(单价直接从BuyCountItem传入,来自decorate.singlePrice)
|
||||
currentBuyCount = initialCount
|
||||
currentUnitPrice = unitPrice
|
||||
// tvCount.text = currentBuyCount.toString()
|
||||
|
||||
ivMinus.setOnAmountChangeListener(object : OnAmountChangeListener {
|
||||
override fun onAmountChange(view: View?, amount: Int) {
|
||||
currentBuyCount = amount
|
||||
// 联动更新商品总价
|
||||
// tvCount.text = currentBuyCount.toString()
|
||||
// 联动更新商品总价
|
||||
updateTotalPrice()
|
||||
|
||||
this@PurchaseOutfitsAdapter.mListener?.onBuyCountChange(
|
||||
currentBuyCount
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算并更新商品总价(单价 × 购买次数),兼容type=12(无price_list)
|
||||
* 局部刷新,避免页面闪烁
|
||||
*/
|
||||
private fun updateTotalPrice() {
|
||||
// 计算总价并格式化(保留2位小数,避免小数位数混乱)
|
||||
val totalPrice = currentUnitPrice.toInt() * currentBuyCount
|
||||
val totalPriceStr =totalPrice
|
||||
|
||||
// 遍历找到“商品总价”项,更新数据源并局部刷新
|
||||
dataList.forEachIndexed { index, adapterItem ->
|
||||
if (adapterItem is DecorateDetailBean.DecorateAdapterItem.SingleItem
|
||||
&& adapterItem.label == "商品总价") {
|
||||
// 更新数据源(替换SingleItem,保持label不变,更新content)
|
||||
dataList[index] = DecorateDetailBean.DecorateAdapterItem.SingleItem(
|
||||
label = adapterItem.label,
|
||||
content = totalPriceStr.toString()
|
||||
)
|
||||
// 局部刷新,仅更新总价项,避免页面闪烁
|
||||
notifyItemChanged(index)
|
||||
return // 找到后直接返回,无需继续遍历,提升效率
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 多选项(购买时长)的ViewHolder(重构:视图复用,普通类型专用,无闪烁)
|
||||
inner class MultiOptionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val llContainer: LinearLayout = itemView.findViewById(R.id.ll_options_container)
|
||||
private val optionViews = mutableListOf<TextView>() // 缓存选项视图,实现复用
|
||||
private var currentOptions: List<DecorateDetailBean.PriceListBean> = emptyList() // 缓存当前选项数据
|
||||
|
||||
// 重构bindOptions:视图复用,仅更新数据和状态,不重建视图
|
||||
fun bindOptions(options: List<DecorateDetailBean.PriceListBean>) {
|
||||
currentOptions = options
|
||||
// 1. 移除多余的视图(当新选项数量少于缓存视图数量时)
|
||||
if (optionViews.size > options.size) {
|
||||
val removeViews = optionViews.subList(options.size, optionViews.size)
|
||||
llContainer.removeViews(options.size, removeViews.size)
|
||||
removeViews.clear()
|
||||
}
|
||||
|
||||
// 2. 复用已有视图,或创建新视图(仅当新选项数量多于缓存视图数量时)
|
||||
options.forEachIndexed { index, priceItem ->
|
||||
val optionView = if (index < optionViews.size) {
|
||||
// 复用已有视图,避免重复创建
|
||||
optionViews[index]
|
||||
} else {
|
||||
// 创建新视图并缓存,仅绑定一次点击事件
|
||||
val newView = LayoutInflater.from(itemView.context)
|
||||
.inflate(R.layout.item_time_tag, llContainer, false) as TextView
|
||||
// 仅绑定一次点击事件(避免重复绑定导致多次触发)
|
||||
newView.setOnClickListener {
|
||||
onOptionItemClick(index)
|
||||
}
|
||||
optionViews.add(newView)
|
||||
llContainer.addView(newView)
|
||||
newView
|
||||
}
|
||||
|
||||
// 3. 仅更新视图的文本和选中状态(核心:不重建视图,只更新必要UI,无闪烁)
|
||||
optionView.text = "${priceItem.day}天"
|
||||
optionView.isSelected = (index == selectedOptionPos)
|
||||
}
|
||||
}
|
||||
|
||||
// 抽取选项点击事件,统一处理(普通类型专用)
|
||||
private fun onOptionItemClick(clickedIndex: Int) {
|
||||
if (clickedIndex == selectedOptionPos) {
|
||||
return // 点击已选中项,不做任何处理,避免无效刷新
|
||||
}
|
||||
|
||||
// 1. 记录上一个选中位置(用于仅更新上一个选中项的UI)
|
||||
val lastSelectedPos = selectedOptionPos
|
||||
// 2. 更新当前选中位置
|
||||
selectedOptionPos = clickedIndex
|
||||
|
||||
// 3. 仅更新上一个和当前选中项的UI(局部刷新,不刷新整个容器)
|
||||
updateOptionItemStatus(lastSelectedPos, false)
|
||||
updateOptionItemStatus(clickedIndex, true)
|
||||
|
||||
// 4. 核心:更新dataList中第0、1项的数据源(仅更新对应数据,不改动其他项)
|
||||
this@PurchaseOutfitsAdapter.updateSingleItemData()
|
||||
|
||||
// 5. 精准刷新:仅刷新商品价格(0)和有效期至(1),不刷新其他视图
|
||||
notifyItemChanged(0) // 仅刷新商品价格
|
||||
notifyItemChanged(1) // 仅刷新有效期至
|
||||
|
||||
// ---------------------- 触发回调,传递整体数据(普通类型专用) ----------------------
|
||||
val selectedItem = this@PurchaseOutfitsAdapter.getSelectedPriceItem()
|
||||
val allOptions = this@PurchaseOutfitsAdapter.getAllPriceOptions()
|
||||
// 非空判断:避免回调实例或选中数据为空导致崩溃
|
||||
if (selectedItem != null && allOptions.isNotEmpty()) {
|
||||
this@PurchaseOutfitsAdapter.mListener?.onDurationSelected(
|
||||
selectedPos = clickedIndex,
|
||||
selectedPriceItem = selectedItem,
|
||||
allPriceOptions = allOptions,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 仅更新单个选项的选中状态(局部UI更新,无视图重建)
|
||||
private fun updateOptionItemStatus(pos: Int, isSelected: Boolean) {
|
||||
if (pos in optionViews.indices) {
|
||||
optionViews[pos].isSelected = isSelected
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package com.xscm.modulemain.dialog
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.blankj.utilcode.util.ActivityUtils.startActivity
|
||||
import com.blankj.utilcode.util.ToastUtils
|
||||
import com.xscm.modulemain.activity.user.activity.RechargeActivity
|
||||
import com.xscm.modulemain.adapter.PurchaseOutfitsAdapter
|
||||
import com.xscm.modulemain.databinding.DialogPurchaseOutfitsBinding
|
||||
import com.xscm.moduleutil.bean.DecorateDetailBean
|
||||
import com.xscm.moduleutil.bean.PersonaltyListBean
|
||||
import com.xscm.moduleutil.color.ThemeableDrawableUtils
|
||||
import com.xscm.moduleutil.http.BaseObserver
|
||||
import com.xscm.moduleutil.http.RetrofitClient
|
||||
import com.xscm.moduleutil.utils.ColorManager
|
||||
import com.xscm.moduleutil.utils.ImageUtils
|
||||
import com.xscm.moduleutil.widget.GiftAnimView
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
||||
/**
|
||||
* 项目名称:羽声语音
|
||||
* 时间:2026/1/4 9:36
|
||||
* 用途:装饰详情
|
||||
*/
|
||||
class PurchaseOutfitsDialog(context: Context) :
|
||||
Dialog(context, com.xscm.moduleutil.R.style.BaseDialogStyleH) {
|
||||
private var mBinding: DialogPurchaseOutfitsBinding =
|
||||
DialogPurchaseOutfitsBinding.inflate(LayoutInflater.from(context))
|
||||
var did: String = ""
|
||||
var personaltyListBean: PersonaltyListBean? = null
|
||||
var adapter: PurchaseOutfitsAdapter = PurchaseOutfitsAdapter()
|
||||
|
||||
private var mSelectedPriceBean: DecorateDetailBean.PriceListBean? = null
|
||||
private var mCurrentBuyCount: String? = "1"
|
||||
|
||||
var imageBg2: GiftAnimView? = null
|
||||
|
||||
init {
|
||||
setContentView(mBinding.root)
|
||||
|
||||
setupWindow()
|
||||
|
||||
}
|
||||
|
||||
private fun setupViews() {
|
||||
RetrofitClient.getInstance()
|
||||
.getDecorateDetail(did, object : BaseObserver<DecorateDetailBean>() {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
}
|
||||
|
||||
override fun onNext(t: DecorateDetailBean) {
|
||||
if (t != null && t.decorate != null && t.userInfo != null) {
|
||||
mBinding.tvRewardGift.text = t.userInfo!!.userCoin.toString()
|
||||
mBinding.tvOutfitsN.text = t.decorate!!.title
|
||||
// 2. 初始化RecyclerView
|
||||
mBinding.rvOutfits.layoutManager = LinearLayoutManager(context)
|
||||
val adapterDataList = t.convertToAdapterData(t.decorate, 0)
|
||||
mBinding.rvOutfits.adapter = adapter
|
||||
adapter.setData(
|
||||
adapterDataList as MutableList<DecorateDetailBean.DecorateAdapterItem>,
|
||||
t.decorate!!.priceList.isEmpty()
|
||||
)
|
||||
|
||||
} else {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
fun show(item: PersonaltyListBean?) {
|
||||
personaltyListBean = item
|
||||
did = item?.did.toString()
|
||||
super.show()
|
||||
initView()
|
||||
setupViews()
|
||||
}
|
||||
|
||||
private fun setupWindow() {
|
||||
val window = window ?: return
|
||||
window.setGravity(Gravity.BOTTOM)
|
||||
window.setLayout(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
window.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
|
||||
val params = window.attributes
|
||||
params.windowAnimations = com.xscm.moduleutil.R.style.BaseDialogStyleH
|
||||
window.attributes = params
|
||||
|
||||
}
|
||||
|
||||
fun initView() {
|
||||
setCancelable(true)
|
||||
setCanceledOnTouchOutside(true)
|
||||
ThemeableDrawableUtils.setThemeableRoundedBackground(
|
||||
mBinding.tvOutfitsQd,
|
||||
ColorManager.getInstance().primaryColorInt,
|
||||
53
|
||||
)
|
||||
mBinding.tvOutfitsQd.setTextColor(ColorManager.getInstance().buttonColorInt)
|
||||
|
||||
imageBg2 = GiftAnimView(context)
|
||||
val parentLayout = mBinding.ccl.parent as ViewGroup // 或者其他合适的父布局
|
||||
parentLayout.post {
|
||||
val parentWidth = parentLayout.width
|
||||
val parentHeight = parentLayout.height
|
||||
imageBg2!!.layoutParams = ViewGroup.LayoutParams(parentWidth, parentHeight)
|
||||
}
|
||||
|
||||
|
||||
imageBg2!!.visibility = View.GONE
|
||||
|
||||
|
||||
parentLayout.addView(imageBg2)
|
||||
// 设置确认按钮点击事件
|
||||
mBinding.tvOutfitsQd.setOnClickListener {
|
||||
|
||||
RetrofitClient.getInstance().payDecorate(
|
||||
did,
|
||||
mSelectedPriceBean?.day.toString(),
|
||||
mCurrentBuyCount,
|
||||
object : BaseObserver<String>() {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
}
|
||||
|
||||
override fun onNext(t: String) {
|
||||
ToastUtils.showLong("购买成功")
|
||||
dismiss()
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
mBinding.cz.setOnClickListener {
|
||||
//充值
|
||||
startActivity(Intent(context, RechargeActivity::class.java))
|
||||
}
|
||||
if (personaltyListBean != null) {
|
||||
if (personaltyListBean?.type == 12) {
|
||||
mBinding.ivOutfits.visibility = View.GONE
|
||||
mBinding.imJsk.visibility = View.VISIBLE
|
||||
mBinding.imageHeadPortrait.visibility= View.GONE
|
||||
mBinding.imQp.visibility=View.GONE
|
||||
ImageUtils.loadHead(personaltyListBean?.base_image, mBinding.imJsk)
|
||||
}else if (personaltyListBean?.type == 9) {
|
||||
mBinding.ivOutfits.visibility = View.GONE
|
||||
mBinding.imJsk.visibility = View.GONE
|
||||
mBinding.imageHeadPortrait.visibility= View.GONE
|
||||
mBinding.imQp.visibility=View.VISIBLE
|
||||
ImageUtils.loadHead(personaltyListBean?.base_image, mBinding.imQp)
|
||||
} else {
|
||||
mBinding.ivOutfits.visibility = View.VISIBLE
|
||||
mBinding.imJsk.visibility = View.GONE
|
||||
mBinding.imQp.visibility=View.GONE
|
||||
ImageUtils.loadHead(personaltyListBean?.base_image, mBinding.ivOutfits)
|
||||
if (personaltyListBean?.type == 1) {
|
||||
mBinding.imageHeadPortrait.visibility= View.VISIBLE
|
||||
imageBg2!!.visibility = View.GONE
|
||||
mBinding.imageHeadPortrait.stopAll()
|
||||
mBinding.imageHeadPortrait.setSource(personaltyListBean?.play_image, 2)
|
||||
}else if (personaltyListBean?.type == 2) {
|
||||
mBinding.imageHeadPortrait.visibility= View.GONE
|
||||
imageBg2!!.visibility = View.VISIBLE
|
||||
imageBg2!!.previewEffectWith(personaltyListBean?.play_image)
|
||||
}
|
||||
}
|
||||
mBinding.tvOutfitsName.text = personaltyListBean?.title
|
||||
}
|
||||
|
||||
|
||||
|
||||
adapter.setOnDurationOptionSelectedListener(object :
|
||||
PurchaseOutfitsAdapter.OnDurationOptionSelectedListener {
|
||||
|
||||
override fun onDurationSelected(
|
||||
selectedPos: Int,
|
||||
selectedPriceItem: DecorateDetailBean.PriceListBean,
|
||||
allPriceOptions: List<DecorateDetailBean.PriceListBean>,
|
||||
) {
|
||||
mSelectedPriceBean = selectedPriceItem
|
||||
}
|
||||
|
||||
|
||||
override fun onBuyCountChange(count: Int) {
|
||||
mCurrentBuyCount = count.toString()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
29
MainModule/src/main/res/drawable/bg_time_tag_selector.xml
Normal file
29
MainModule/src/main/res/drawable/bg_time_tag_selector.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- 选中状态 -->
|
||||
<item android:state_selected="true">
|
||||
<shape android:shape="rectangle">
|
||||
<!-- 红色背景 -->
|
||||
<solid android:color="#F3FDF5"/>
|
||||
<!-- 绿色边框 -->
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#4CAF50"/>
|
||||
<!-- 圆角 -->
|
||||
<corners android:radius="10dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
<!-- 未选中状态 -->
|
||||
<item android:state_selected="false">
|
||||
<shape android:shape="rectangle">
|
||||
<!-- 白色背景 -->
|
||||
<solid android:color="#ffffff"/>
|
||||
<!-- 绿色边框 -->
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#4CAF50"/>
|
||||
<!-- 圆角 -->
|
||||
<corners android:radius="10dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
6
MainModule/src/main/res/drawable/text_color_selector.xml
Normal file
6
MainModule/src/main/res/drawable/text_color_selector.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- res/color/text_color_selector.xml -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_selected="true" android:color="#4CAF50"/>
|
||||
<item android:color="#000000"/>
|
||||
</selector>
|
||||
171
MainModule/src/main/res/layout/dialog_purchase_outfits.xml
Normal file
171
MainModule/src/main/res/layout/dialog_purchase_outfits.xml
Normal file
@@ -0,0 +1,171 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_r16_fff"
|
||||
android:paddingVertical="@dimen/dp_15">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/ll"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/im_jsk"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:src="@drawable/ic_launcher_background" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/im_qp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="60dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:src="@drawable/ic_launcher_background" />
|
||||
|
||||
|
||||
<com.xscm.moduleutil.widget.CircularImage
|
||||
android:id="@+id/iv_outfits"
|
||||
android:layout_width="@dimen/dp_70"
|
||||
android:layout_height="@dimen/dp_70"
|
||||
tools:src="@drawable/ic_launcher_background" />
|
||||
|
||||
<com.xscm.moduleutil.widget.AvatarFrameView
|
||||
android:id="@+id/image_headPortrait"
|
||||
android:layout_width="@dimen/dp_72"
|
||||
android:layout_height="@dimen/dp_72"
|
||||
app:layout_constraintTop_toTopOf="@+id/iv_outfits"
|
||||
app:layout_constraintStart_toStartOf="@+id/iv_outfits"
|
||||
app:layout_constraintEnd_toEndOf="@+id/iv_outfits"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/iv_outfits"
|
||||
|
||||
/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_outfits_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp_5"
|
||||
android:textColor="@color/color_FF333333"
|
||||
android:textSize="@dimen/sp_13"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/ll"
|
||||
tools:text="套装名称" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_commodity_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_26"
|
||||
android:layout_marginTop="@dimen/dp_5"
|
||||
android:text="商品名称"
|
||||
android:textColor="@color/color_FF333333"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_outfits_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_outfits_n"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/dp_26"
|
||||
android:textColor="@color/color_FF333333"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/tv_commodity_name"
|
||||
tools:text="粉色头花" />
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_outfits"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/dp_10"
|
||||
android:layout_marginVertical="@dimen/dp_10"
|
||||
android:paddingBottom="@dimen/dp_10"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tv_commodity_name" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_reward_gift"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/rv_outfits">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_reward_gift"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dp_16"
|
||||
android:layout_marginStart="@dimen/dp_2"
|
||||
android:drawableStart="@mipmap/jinb"
|
||||
android:textColor="@color/color_FF333333"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/rv_outfits"
|
||||
tools:text="23" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cz"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_18"
|
||||
android:text="去充值"
|
||||
android:textColor="#0DFFB9"
|
||||
android:textSize="@dimen/sp_12"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/tv_reward_gift"
|
||||
app:layout_constraintStart_toEndOf="@+id/tv_reward_gift"
|
||||
app:layout_constraintTop_toTopOf="@+id/tv_reward_gift" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_outfits_qd"
|
||||
android:layout_width="@dimen/dp_158"
|
||||
android:layout_height="@dimen/dp_42"
|
||||
android:layout_marginTop="@dimen/dp_10"
|
||||
android:layout_marginBottom="@dimen/dp_14"
|
||||
android:background="@drawable/bg_r53_0dffb9"
|
||||
android:gravity="center"
|
||||
android:text="确认支付"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/sp_14"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/ll_reward_gift" />
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/ccl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_350"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
||||
36
MainModule/src/main/res/layout/item_buy_count.xml
Normal file
36
MainModule/src/main/res/layout/item_buy_count.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- 根布局:横向排列,适配RecyclerView列表项,与item_single_info.xml布局风格保持一致 -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<!-- 左侧:购买次数标签(与item_single_info.xml的标签样式保持一致,保证视觉统一) -->
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="start"
|
||||
android:text="购买次数"
|
||||
android:textColor="#333333"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<!-- 右侧:加减按钮 + 数量显示(横向排列,居中对齐) -->
|
||||
<com.xscm.moduleutil.widget.AmountView
|
||||
android:id="@+id/amount_view"
|
||||
app:btnTextSize="14sp"
|
||||
app:btnWidth="36dp"
|
||||
app:tvWidth="50dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="0.7"
|
||||
android:layout_height="36dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_gravity="right"
|
||||
/>
|
||||
</LinearLayout>
|
||||
22
MainModule/src/main/res/layout/item_multi_option.xml
Normal file
22
MainModule/src/main/res/layout/item_multi_option.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="购买时长"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_options_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
/>
|
||||
</LinearLayout>
|
||||
43
MainModule/src/main/res/layout/item_single_info.xml
Normal file
43
MainModule/src/main/res/layout/item_single_info.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="商品价格"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<!-- 右侧内容 + 图标按钮(横向排列) -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- 右侧内容文字 -->
|
||||
<TextView
|
||||
android:id="@+id/tv_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textColor="@color/color_FF333333"
|
||||
android:textSize="16sp"
|
||||
tools:text="25"/>
|
||||
|
||||
<!-- 商品价格专属图标按钮(默认隐藏) -->
|
||||
<ImageView
|
||||
android:id="@+id/iv_price_icon"
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:src="@mipmap/jinb"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"/> <!-- 默认隐藏 -->
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
13
MainModule/src/main/res/layout/item_time_tag.xml
Normal file
13
MainModule/src/main/res/layout/item_time_tag.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginHorizontal="@dimen/dp_10"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:gravity="center"
|
||||
tools:text="30天"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@drawable/text_color_selector"
|
||||
android:background="@drawable/bg_time_tag_selector"
|
||||
android:clickable="true"
|
||||
android:focusable="true" />
|
||||
Reference in New Issue
Block a user