diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..518c6bc
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 24e8552..8994e32 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,8 +1,6 @@
-#Wed May 07 09:31:48 CST 2025
+#Mon Sep 22 21:05:11 CST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-#distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.7-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-#distributionUrl=file:///D:/Greadle/gradle-8.10.2-all.zip
-distributionUrl=file:///D:/Gradle/gradle-8.10.2-bin.zip
+zipStorePath=wrapper/distsl.
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/view/AvatarWithDecoration.java b/moduleUtil/src/main/java/com/xscm/moduleutil/view/AvatarWithDecoration.java
new file mode 100644
index 0000000..3112883
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/view/AvatarWithDecoration.java
@@ -0,0 +1,119 @@
+package com.xscm.moduleutil.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import androidx.appcompat.widget.AppCompatImageView;
+
+import com.xscm.moduleutil.R;
+
+public class AvatarWithDecoration extends AppCompatImageView {
+ // 挂件图片
+ private Drawable decoration;
+ // 挂件位置偏移量
+ private int decorationOffsetX = 0;
+ private int decorationOffsetY = 0;
+ // 挂件大小比例(相对于头像)
+ private float decorationScale = 0.3f;
+
+ public AvatarWithDecoration(Context context) {
+ super(context);
+ init();
+ }
+
+ public AvatarWithDecoration(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public AvatarWithDecoration(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AvatarWithDecoration);
+ decoration = a.getDrawable(R.styleable.AvatarWithDecoration_decoration);
+ decorationScale = a.getFloat(R.styleable.AvatarWithDecoration_decorationScale, 0.3f);
+ decorationOffsetX = a.getInt(R.styleable.AvatarWithDecoration_decorationOffsetX, 0);
+ decorationOffsetY = a.getInt(R.styleable.AvatarWithDecoration_decorationOffsetY, 0);
+ a.recycle();
+ }
+
+ private void init() {
+ // 可以在这里设置默认的挂件
+ decoration = getResources().getDrawable(R.mipmap.xlh_image);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // 如果有挂件,则绘制挂件
+ if (decoration != null) {
+ drawDecoration(canvas);
+ }
+ }
+
+ private void drawDecoration(Canvas canvas) {
+ // 获取头像的宽高
+ int avatarWidth = getWidth();
+ int avatarHeight = getHeight();
+
+ // 计算挂件的大小
+ int decorationWidth = (int) (avatarWidth * decorationScale);
+ int decorationHeight = (int) (avatarHeight * decorationScale);
+
+ // 计算挂件的位置(右下角)
+ int left = avatarWidth - decorationWidth + decorationOffsetX;
+ int top = avatarHeight - decorationHeight + decorationOffsetY;
+ int right = left + decorationWidth;
+ int bottom = top + decorationHeight;
+
+ // 设置挂件的绘制边界
+ decoration.setBounds(left, top, right, bottom);
+
+ // 绘制挂件
+ decoration.draw(canvas);
+ }
+
+ /**
+ * 设置挂件图片
+ */
+ public void setDecoration(Drawable decoration) {
+ this.decoration = decoration;
+ invalidate();
+ }
+
+ /**
+ * 设置挂件图片(通过Bitmap)
+ */
+ public void setDecoration(Bitmap bitmap) {
+ if (bitmap != null) {
+ this.decoration = new BitmapDrawable(getResources(), bitmap);
+ invalidate();
+ }
+ }
+
+ /**
+ * 设置挂件位置偏移量
+ */
+ public void setDecorationOffset(int offsetX, int offsetY) {
+ this.decorationOffsetX = offsetX;
+ this.decorationOffsetY = offsetY;
+ invalidate();
+ }
+
+ /**
+ * 设置挂件大小比例
+ */
+ public void setDecorationScale(float scale) {
+ if (scale > 0 && scale <= 1) {
+ this.decorationScale = scale;
+ invalidate();
+ }
+ }
+}
+
diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/view/FashionAvatarView.java b/moduleUtil/src/main/java/com/xscm/moduleutil/view/FashionAvatarView.java
new file mode 100644
index 0000000..e0ab8c0
--- /dev/null
+++ b/moduleUtil/src/main/java/com/xscm/moduleutil/view/FashionAvatarView.java
@@ -0,0 +1,373 @@
+package com.xscm.moduleutil.view;
+
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.target.SimpleTarget;
+import com.bumptech.glide.request.transition.Transition;
+import com.xscm.moduleutil.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class FashionAvatarView extends View {
+ // 头像相关属性
+ private Bitmap mAvatarBitmap;
+ private String mAvatarUrl;
+ private Drawable mPlaceholderAvatar;
+ private int mAvatarRadius;
+ private int mAvatarBorderWidth;
+ private int mAvatarBorderColor;
+
+ // 顶部标签属性
+ private String mTagText;
+ private int mTagTextColor;
+ private float mTagTextSize;
+ private int mTagBackgroundColor;
+ private float mTagCornerRadius;
+ private int mTagPadding;
+ private int mTagOffsetY; // 标签Y轴偏移量
+
+ // 底部文字属性
+ private String mBottomText;
+ private int mBottomTextColor;
+ private float mBottomTextSize;
+ private int mBottomTextOffsetY; // 底部文字Y轴偏移量
+
+ // 爱心装饰属性
+ private Drawable mHeartIcon;
+ private int mHeartCount;
+ private int mHeartSize;
+ private List mHeartPositions = new ArrayList<>();
+ private Random mRandom = new Random();
+
+ // 画笔
+ private Paint mAvatarPaint;
+ private Paint mTextPaint;
+ private Paint mTagBgPaint;
+
+ public FashionAvatarView(Context context) {
+ this(context, null);
+ }
+
+ public FashionAvatarView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FashionAvatarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initAttrs(context, attrs);
+ initPaints();
+ }
+
+ private void initAttrs(Context context, AttributeSet attrs) {
+ TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.FashionAvatarView);
+
+ // 头像属性
+ mAvatarUrl = ta.getString(R.styleable.FashionAvatarView_avatarUrl);
+ mPlaceholderAvatar = ta.getDrawable(R.styleable.FashionAvatarView_placeholderAvatar);
+ mAvatarRadius = ta.getDimensionPixelSize(R.styleable.FashionAvatarView_avatarRadius, dp2px(60));
+ mAvatarBorderWidth = ta.getDimensionPixelSize(R.styleable.FashionAvatarView_avatarBorderWidth, dp2px(2));
+ mAvatarBorderColor = ta.getColor(R.styleable.FashionAvatarView_avatarBorderColor, Color.parseColor("#FFD700"));
+
+ // 顶部标签属性
+ mTagText = ta.getString(R.styleable.FashionAvatarView_tagText);
+ mTagTextColor = ta.getColor(R.styleable.FashionAvatarView_tagTextColor, Color.WHITE);
+ mTagTextSize = ta.getDimension(R.styleable.FashionAvatarView_tagTextSize, sp2px(12));
+ mTagBackgroundColor = ta.getColor(R.styleable.FashionAvatarView_tagBackgroundColor, Color.parseColor("#FFA500"));
+ mTagCornerRadius = ta.getDimension(R.styleable.FashionAvatarView_tagCornerRadius, dp2px(4));
+ mTagPadding = ta.getDimensionPixelSize(R.styleable.FashionAvatarView_tagPadding, dp2px(4));
+ mTagOffsetY = ta.getDimensionPixelSize(R.styleable.FashionAvatarView_tagOffsetY, 0);
+
+ // 底部文字属性
+ mBottomText = ta.getString(R.styleable.FashionAvatarView_bottomText);
+ mBottomTextColor = ta.getColor(R.styleable.FashionAvatarView_bottomTextColor, Color.WHITE);
+ mBottomTextSize = ta.getDimension(R.styleable.FashionAvatarView_bottomTextSize, sp2px(14));
+ mBottomTextOffsetY = ta.getDimensionPixelSize(R.styleable.FashionAvatarView_bottomTextOffsetY, dp2px(10));
+
+ // 爱心装饰属性
+ mHeartIcon = ta.getDrawable(R.styleable.FashionAvatarView_heartIcon);
+ if (mHeartIcon == null) {
+ try {
+ mHeartIcon = context.getResources().getDrawable(R.mipmap.xlh_image);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ mHeartCount = ta.getInt(R.styleable.FashionAvatarView_heartCount, 6);
+ mHeartSize = ta.getDimensionPixelSize(R.styleable.FashionAvatarView_heartSize, dp2px(16));
+
+ ta.recycle();
+
+ // 加载头像
+ if (mAvatarUrl != null && !mAvatarUrl.isEmpty()) {
+ loadAvatarFromNetwork();
+ } else if (mPlaceholderAvatar != null) {
+ mAvatarBitmap = drawableToBitmap(mPlaceholderAvatar);
+ }
+ }
+
+ private void initPaints() {
+ // 头像画笔
+ mAvatarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mAvatarPaint.setDither(true);
+
+ // 文字画笔
+ mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mTextPaint.setTextAlign(Paint.Align.CENTER);
+
+ // 标签背景画笔
+ mTagBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mTagBgPaint.setColor(mTagBackgroundColor);
+ }
+
+ private void loadAvatarFromNetwork() {
+ Glide.with(this)
+ .asBitmap()
+ .load(mAvatarUrl)
+ .into(new SimpleTarget() {
+ @Override
+ public void onResourceReady(Bitmap resource, Transition super Bitmap> transition) {
+ mAvatarBitmap = resource;
+ invalidate();
+ }
+
+ @Override
+ public void onLoadFailed(@Nullable Drawable errorDrawable) {
+ super.onLoadFailed(errorDrawable);
+ if (mPlaceholderAvatar != null) {
+ mAvatarBitmap = drawableToBitmap(mPlaceholderAvatar);
+ }
+ invalidate();
+ }
+ });
+ }
+
+ private Bitmap drawableToBitmap(Drawable drawable) {
+ if (drawable instanceof BitmapDrawable) {
+ return ((BitmapDrawable) drawable).getBitmap();
+ }
+
+ if (drawable == null) {
+ return null;
+ }
+
+ int width = drawable.getIntrinsicWidth();
+ int height = drawable.getIntrinsicHeight();
+
+ if (width <= 0) width = mAvatarRadius * 2;
+ if (height <= 0) height = mAvatarRadius * 2;
+
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ }
+
+ private void initHeartPositions() {
+ mHeartPositions.clear();
+
+ if (mHeartIcon == null || mHeartCount <= 0 || mHeartSize <= 0) {
+ return;
+ }
+
+ int centerX = getWidth() / 2;
+ int centerY = getAvatarCenterY();
+ // 爱心围绕的半径,比头像大一些
+ int radius = mAvatarRadius + mAvatarBorderWidth + mHeartSize / 2;
+
+ for (int i = 0; i < mHeartCount; i++) {
+ // 随机分布在头像周围
+ double angle = 2 * Math.PI * mRandom.nextDouble();
+ // 稍微随机调整距离,让分布更自然
+ float distanceFactor = 0.8f + mRandom.nextFloat() * 0.4f;
+
+ float x = (float) (centerX + radius * distanceFactor * Math.cos(angle));
+ float y = (float) (centerY + radius * distanceFactor * Math.sin(angle));
+ mHeartPositions.add(new PointF(x, y));
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // 计算宽度:直径 + 左右可能的爱心空间
+ int width = 2 * (mAvatarRadius + mAvatarBorderWidth + mHeartSize / 2);
+
+ // 计算高度:头像直径 + 标签高度 + 底部文字高度 + 间距
+ int tagHeight = (int) (mTagTextSize + mTagPadding * 2);
+ int bottomTextHeight = (int) mBottomTextSize;
+ int height = 2 * (mAvatarRadius + mAvatarBorderWidth)
+ + tagHeight / 2 // 标签一半在头像内
+ + bottomTextHeight + mBottomTextOffsetY
+ + dp2px(10);
+
+ setMeasuredDimension(resolveSize(width, widthMeasureSpec),
+ resolveSize(height, heightMeasureSpec));
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ // 视图大小确定后计算爱心位置
+ initHeartPositions();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (getWidth() == 0 || getHeight() == 0) {
+ return;
+ }
+
+ int centerX = getWidth() / 2;
+ int avatarCenterY = getAvatarCenterY();
+
+ // 1. 绘制爱心装饰
+ drawHearts(canvas);
+
+ // 2. 绘制头像边框
+ mAvatarPaint.setColor(mAvatarBorderColor);
+ mAvatarPaint.setStyle(Paint.Style.FILL);
+ canvas.drawCircle(centerX, avatarCenterY, mAvatarRadius + mAvatarBorderWidth, mAvatarPaint);
+
+ // 3. 绘制头像
+ if (mAvatarBitmap != null) {
+ mAvatarPaint.setShader(new BitmapShader(mAvatarBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
+ canvas.drawCircle(centerX, avatarCenterY, mAvatarRadius, mAvatarPaint);
+ mAvatarPaint.setShader(null);
+ }
+
+ // 4. 绘制顶部标签
+ drawTag(canvas, centerX, avatarCenterY);
+
+ // 5. 绘制底部文字
+ drawBottomText(canvas, centerX);
+ }
+
+ private int getAvatarCenterY() {
+ // 计算头像中心Y坐标,考虑标签的高度
+ int tagHeight = (int) (mTagTextSize + mTagPadding * 2);
+ return mAvatarRadius + mAvatarBorderWidth + tagHeight / 2 + mTagOffsetY;
+ }
+
+ private void drawHearts(Canvas canvas) {
+ if (mHeartIcon == null || mHeartPositions.isEmpty()) {
+ return;
+ }
+
+ canvas.save();
+ for (PointF point : mHeartPositions) {
+ int left = (int) (point.x - mHeartSize / 2);
+ int top = (int) (point.y - mHeartSize / 2);
+ int right = left + mHeartSize;
+ int bottom = top + mHeartSize;
+
+ // 只绘制在视图范围内的爱心
+ if (right > 0 && bottom > 0 && left < getWidth() && top < getHeight()) {
+ mHeartIcon.setBounds(left, top, right, bottom);
+ mHeartIcon.draw(canvas);
+ }
+ }
+ canvas.restore();
+ }
+
+ private void drawTag(Canvas canvas, int centerX, int avatarCenterY) {
+ if (mTagText == null || mTagText.isEmpty()) {
+ return;
+ }
+
+ // 计算标签文字宽度
+ mTextPaint.setTextSize(mTagTextSize);
+ float textWidth = mTextPaint.measureText(mTagText);
+
+ // 计算标签背景矩形
+ float tagLeft = centerX - textWidth / 2 - mTagPadding;
+ float tagRight = centerX + textWidth / 2 + mTagPadding;
+ // 标签底部与头像顶部对齐
+ float tagBottom = avatarCenterY - mAvatarRadius - mAvatarBorderWidth;
+ float tagTop = tagBottom - mTagTextSize - mTagPadding * 2;
+
+ RectF tagRect = new RectF(tagLeft, tagTop, tagRight, tagBottom);
+
+ // 绘制标签背景
+ canvas.drawRoundRect(tagRect, mTagCornerRadius, mTagCornerRadius, mTagBgPaint);
+
+ // 绘制标签文字
+ mTextPaint.setColor(mTagTextColor);
+ Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
+ float baseLineY = tagBottom - mTagPadding - fontMetrics.bottom;
+ canvas.drawText(mTagText, centerX, baseLineY, mTextPaint);
+ }
+
+ private void drawBottomText(Canvas canvas, int centerX) {
+ if (mBottomText == null || mBottomText.isEmpty()) {
+ return;
+ }
+
+ // 计算文字位置:头像底部下方
+ int textY = getAvatarCenterY() + mAvatarRadius + mAvatarBorderWidth + mBottomTextOffsetY;
+
+ mTextPaint.setColor(mBottomTextColor);
+ mTextPaint.setTextSize(mBottomTextSize);
+
+ Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
+ float baseLineY = textY - fontMetrics.top;
+
+ canvas.drawText(mBottomText, centerX, baseLineY, mTextPaint);
+ }
+
+ // dp转px
+ private int dp2px(float dp) {
+ return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
+ getResources().getDisplayMetrics());
+ }
+
+ // sp转px
+ private float sp2px(float sp) {
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
+ getResources().getDisplayMetrics());
+ }
+
+ // 设置器方法
+ public void setAvatarUrl(String url) {
+ this.mAvatarUrl = url;
+ loadAvatarFromNetwork();
+ }
+
+ public void setTagText(String text) {
+ this.mTagText = text;
+ invalidate();
+ }
+
+ public void setBottomText(String text) {
+ this.mBottomText = text;
+ invalidate();
+ }
+
+ public void setHeartIcon(Drawable heartIcon) {
+ this.mHeartIcon = heartIcon;
+ initHeartPositions();
+ invalidate();
+ }
+}
+
diff --git a/moduleUtil/src/main/res/drawable/bg_person.xml b/moduleUtil/src/main/res/drawable/bg_person.xml
new file mode 100644
index 0000000..a7b3cf2
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_person.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/drawable/bg_round_corner.xml b/moduleUtil/src/main/res/drawable/bg_round_corner.xml
new file mode 100644
index 0000000..298a617
--- /dev/null
+++ b/moduleUtil/src/main/res/drawable/bg_round_corner.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/moduleUtil/src/main/res/layout/fragment_tour_club_dialog.xml b/moduleUtil/src/main/res/layout/fragment_tour_club_dialog.xml
index 2ed1185..687dbf4 100644
--- a/moduleUtil/src/main/res/layout/fragment_tour_club_dialog.xml
+++ b/moduleUtil/src/main/res/layout/fragment_tour_club_dialog.xml
@@ -62,6 +62,7 @@
app:layout_constraintTop_toBottomOf="@+id/tv_gz" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -281,7 +432,7 @@
android:layout_marginBottom="@dimen/dp_15"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- app:layout_constraintTop_toBottomOf="@+id/cl_gift"
+ app:layout_constraintTop_toBottomOf="@+id/gift_l5"
app:layout_constraintBottom_toTopOf="@+id/exchange_layout"
/>
@@ -325,6 +476,7 @@
app:layout_constraintEnd_toStartOf="@+id/tv_option"
app:layout_constraintTop_toTopOf="@+id/exchange_layout"
app:layout_constraintBottom_toBottomOf="@+id/exchange_layout"
+ android:visibility="gone"
/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/moduleroom/src/main/res/layout/activity_room.xml b/moduleroom/src/main/res/layout/activity_room.xml
index 15786cf..772b416 100644
--- a/moduleroom/src/main/res/layout/activity_room.xml
+++ b/moduleroom/src/main/res/layout/activity_room.xml
@@ -482,7 +482,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:visibility="visible" />
-
+
+