1:羽声新版本
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package com.xscm.moduleutil.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.xscm.moduleutil.R;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* 这是抢红包的自定义view
|
||||
*/
|
||||
public class CustomDialogView extends ConstraintLayout {
|
||||
public CustomDialogView(@NonNull @NotNull Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CustomDialogView(@NonNull @NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public CustomDialogView(@NonNull @NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
public CustomDialogView(@NonNull @NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
// 初始化视图
|
||||
// 设置背景色
|
||||
setBackground(ContextCompat.getDrawable(getContext(), R.drawable.bg_red_16_envel));
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
package com.xscm.moduleutil.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.RotateAnimation;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.GiftBean;
|
||||
import com.xscm.moduleutil.bean.blindboxwheel.BlindBoxBean;
|
||||
|
||||
public class QXMeetGiftView extends RelativeLayout {
|
||||
|
||||
private TextView giftNameLabel;
|
||||
private ImageView giftPriceBgView;
|
||||
private Button giftCoin;
|
||||
private ImageView bgImageView;
|
||||
private ImageView giftBgImageView;
|
||||
private ImageView giftImageView;
|
||||
|
||||
private boolean isLockGift;
|
||||
private Object model; // 这里用 Object 代替 QXDrawGiftModel
|
||||
|
||||
public QXMeetGiftView(Context context) {
|
||||
super(context);
|
||||
initSubviews(context);
|
||||
}
|
||||
|
||||
public QXMeetGiftView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initSubviews(context);
|
||||
}
|
||||
|
||||
public QXMeetGiftView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initSubviews(context);
|
||||
}
|
||||
|
||||
private void initSubviews(Context context) {
|
||||
// 创建背景图片视图
|
||||
bgImageView = new ImageView(context);
|
||||
bgImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
bgImageView.setImageResource(R.drawable.ac_left_gift_bg);
|
||||
LayoutParams bgParams = new LayoutParams(
|
||||
LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.MATCH_PARENT
|
||||
);
|
||||
addView(bgImageView, bgParams);
|
||||
|
||||
// 创建礼物图片视图(圆形)
|
||||
giftImageView = new ImageView(context);
|
||||
giftImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
LayoutParams giftImageParams = new LayoutParams(
|
||||
dpToPx(48), // 固定宽度
|
||||
dpToPx(62) // 固定高度
|
||||
);
|
||||
giftImageParams.addRule(CENTER_IN_PARENT);
|
||||
giftImageParams.topMargin = dpToPx(10);
|
||||
addView(giftImageView, giftImageParams);
|
||||
|
||||
// 创建礼物背景光效视图
|
||||
giftBgImageView = new ImageView(context);
|
||||
giftBgImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
giftBgImageView.setImageResource(R.drawable.ac_lock_gift_light_bg);
|
||||
giftBgImageView.setVisibility(View.GONE); // 初始隐藏
|
||||
LayoutParams giftBgParams = new LayoutParams(
|
||||
LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.MATCH_PARENT
|
||||
);
|
||||
addView(giftBgImageView, giftBgParams);
|
||||
|
||||
|
||||
|
||||
// 创建价格背景视图
|
||||
giftPriceBgView = new ImageView(context);
|
||||
giftPriceBgView.setImageResource(R.drawable.ac_meet_gift_name_bg);
|
||||
LayoutParams priceBgParams = new LayoutParams(
|
||||
LayoutParams.MATCH_PARENT,
|
||||
dpToPx(15)
|
||||
);
|
||||
priceBgParams.addRule(CENTER_HORIZONTAL);
|
||||
priceBgParams.addRule(ALIGN_PARENT_BOTTOM);
|
||||
priceBgParams.bottomMargin = dpToPx(10); // 在名称标签上方
|
||||
addView(giftPriceBgView, priceBgParams);
|
||||
|
||||
|
||||
// 创建金币按钮
|
||||
giftCoin = new Button(context);
|
||||
giftCoin.setTextColor(0xFFC7BF62); // 使用直接的颜色值
|
||||
// 设置按钮图标
|
||||
Drawable coinDrawable = getResources().getDrawable(R.mipmap.jinb);
|
||||
coinDrawable.setBounds(0, 0, dpToPx(1), dpToPx(1));
|
||||
giftCoin.setCompoundDrawables(coinDrawable, null, null, null);
|
||||
giftCoin.setTextSize(10);
|
||||
giftCoin.setBackgroundColor(0x00000000); // 透明背景
|
||||
LayoutParams coinParams = new LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
coinParams.addRule(CENTER_HORIZONTAL);
|
||||
coinParams.addRule(ALIGN_PARENT_BOTTOM);
|
||||
coinParams.bottomMargin = -dpToPx(2);
|
||||
addView(giftCoin, coinParams);
|
||||
|
||||
// 创建礼物名称标签
|
||||
giftNameLabel = new TextView(context);
|
||||
giftNameLabel.setTextColor(0xFFFFFFFF);
|
||||
giftNameLabel.setTextSize(12);
|
||||
giftNameLabel.setGravity(android.view.Gravity.CENTER);
|
||||
giftNameLabel.setSingleLine(true); // 设置为单行显示
|
||||
giftNameLabel.setEllipsize(android.text.TextUtils.TruncateAt.END); // 超出部分用省略号表示
|
||||
LayoutParams nameParams = new LayoutParams(
|
||||
LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
nameParams.addRule(CENTER_HORIZONTAL);
|
||||
nameParams.addRule(ALIGN_PARENT_BOTTOM);
|
||||
nameParams.bottomMargin = dpToPx(1);
|
||||
addView(giftNameLabel, nameParams);
|
||||
|
||||
// 调整视图层级 - 确保正确的层级关系
|
||||
// 按添加顺序已经确定层级,最晚添加的在最上层
|
||||
}
|
||||
|
||||
public void setIsLockGift(boolean isLockGift) {
|
||||
this.isLockGift = isLockGift;
|
||||
|
||||
// 设置背景图片
|
||||
int bgResource = isLockGift ?
|
||||
R.drawable.ac_lock_gift_bg : R.drawable.ac_left_gift_bg;
|
||||
bgImageView.setImageResource(bgResource);
|
||||
|
||||
// 显示/隐藏光效背景
|
||||
giftBgImageView.setVisibility(isLockGift ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (isLockGift) {
|
||||
// 重新设置礼物图片的约束
|
||||
LayoutParams params = (LayoutParams) giftImageView.getLayoutParams();
|
||||
params.width = dpToPx(36);
|
||||
params.height = dpToPx(36);
|
||||
params.addRule(CENTER_IN_PARENT);
|
||||
params.setMargins(0, 0, 0, 0);
|
||||
giftImageView.setLayoutParams(params);
|
||||
} else {
|
||||
// 恢复原始约束
|
||||
LayoutParams params = (LayoutParams) giftImageView.getLayoutParams();
|
||||
params.width = LayoutParams.MATCH_PARENT;
|
||||
params.height = LayoutParams.WRAP_CONTENT;
|
||||
params.addRule(CENTER_IN_PARENT);
|
||||
params.setMargins(dpToPx(6), dpToPx(6), dpToPx(6), 0);
|
||||
giftImageView.setLayoutParams(params);
|
||||
}
|
||||
}
|
||||
|
||||
public void setModel(BlindBoxBean.GiveGift model) {
|
||||
this.model = model;
|
||||
// 这里需要根据您的 QXDrawGiftModel 类来实现具体逻辑
|
||||
|
||||
if (model instanceof BlindBoxBean.GiveGift) {
|
||||
BlindBoxBean.GiveGift giftModel = (BlindBoxBean.GiveGift) model;
|
||||
|
||||
// 使用图片加载库加载图片
|
||||
Glide.with(getContext()).load(giftModel.getBase_image()).into(giftImageView);
|
||||
|
||||
giftNameLabel.setText(giftModel.getGift_name());
|
||||
giftCoin.setText(giftModel.getGift_price());
|
||||
}
|
||||
}
|
||||
|
||||
public void startAnimation() {
|
||||
// 礼物图片顺时针旋转动画
|
||||
RotateAnimation rotateAnimation = new RotateAnimation(
|
||||
0, 360, // 从 0 度旋转到 360 度
|
||||
Animation.RELATIVE_TO_SELF, 0.5f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f
|
||||
);
|
||||
rotateAnimation.setDuration(3000); // 3 秒
|
||||
rotateAnimation.setRepeatCount(Animation.INFINITE);
|
||||
rotateAnimation.setRepeatMode(Animation.RESTART);
|
||||
rotateAnimation.setInterpolator(getContext(), android.R.anim.linear_interpolator);
|
||||
giftImageView.startAnimation(rotateAnimation);
|
||||
|
||||
// 光效背景逆时针旋转动画
|
||||
RotateAnimation rotateAnimation1 = new RotateAnimation(
|
||||
0, -360, // 从 0 度旋转到 -360 度
|
||||
Animation.RELATIVE_TO_SELF, 0.5f,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f
|
||||
);
|
||||
rotateAnimation1.setDuration(3000); // 3 秒
|
||||
rotateAnimation1.setRepeatCount(Animation.INFINITE);
|
||||
rotateAnimation1.setRepeatMode(Animation.RESTART);
|
||||
rotateAnimation1.setInterpolator(getContext(), android.R.anim.linear_interpolator);
|
||||
giftBgImageView.startAnimation(rotateAnimation1);
|
||||
}
|
||||
|
||||
public void resetAnimation() {
|
||||
giftImageView.clearAnimation();
|
||||
giftBgImageView.clearAnimation();
|
||||
}
|
||||
|
||||
public void stopAnimation() {
|
||||
giftImageView.clearAnimation();
|
||||
giftBgImageView.clearAnimation();
|
||||
}
|
||||
|
||||
// 辅助方法:dp 转 px
|
||||
private int dpToPx(int dp) {
|
||||
float density = getResources().getDisplayMetrics().density;
|
||||
return Math.round(dp * density);
|
||||
}
|
||||
|
||||
// 缩放宽度方法(对应 ScaleWidth)
|
||||
private int scaleWidth(int value) {
|
||||
// 这里需要根据您的缩放逻辑实现
|
||||
// 通常可以根据屏幕密度进行缩放
|
||||
float scale = getResources().getDisplayMetrics().density;
|
||||
return (int) (value * scale);
|
||||
}
|
||||
|
||||
// Getter 方法
|
||||
public TextView getGiftNameLabel() {
|
||||
return giftNameLabel;
|
||||
}
|
||||
|
||||
public ImageView getGiftPriceBgView() {
|
||||
return giftPriceBgView;
|
||||
}
|
||||
|
||||
public Button getGiftCoin() {
|
||||
return giftCoin;
|
||||
}
|
||||
|
||||
public ImageView getBgImageView() {
|
||||
return bgImageView;
|
||||
}
|
||||
|
||||
public ImageView getGiftBgImageView() {
|
||||
return giftBgImageView;
|
||||
}
|
||||
|
||||
public ImageView getGiftImageView() {
|
||||
return giftImageView;
|
||||
}
|
||||
|
||||
public boolean isLockGift() {
|
||||
return isLockGift;
|
||||
}
|
||||
|
||||
public Object getModel() {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
package com.xscm.moduleutil.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
import com.xscm.moduleutil.bean.blindboxwheel.BlindBoxBean;
|
||||
import com.xscm.moduleutil.utils.ImageUtils;
|
||||
import com.xscm.moduleutil.widget.GifAvatarOvalView;
|
||||
|
||||
public class QXMeetUserView extends RelativeLayout {
|
||||
private static final String TAG = "QXMeetUserView";
|
||||
|
||||
private GifAvatarOvalView headerImageView; // 头像
|
||||
private ImageView dressImageView; // 装饰图
|
||||
private TextView tagLabel; // 标签
|
||||
private TextView nameLabel; // 名称
|
||||
|
||||
// 布局属性变量
|
||||
private int avatarSize; // 头像大小
|
||||
private int dressSize; // 装饰图大小
|
||||
private float nameTextSize; // 名字字体大小
|
||||
private int nameTextColor; // 名字颜色
|
||||
private float tagTextSize; // 标签字体大小
|
||||
private int tagTextColor; // 标签颜色
|
||||
private int tagMargin; // 标签与头像的水平间距
|
||||
|
||||
// 布局常量(dp)
|
||||
private static final int DEFAULT_AVATAR_SIZE = 60; // 默认头像大小
|
||||
private static final int DEFAULT_DRESS_SIZE = 70; // 默认装饰图大小
|
||||
private static final float DEFAULT_NAME_TEXT_SIZE = 12; // 默认名字字体大小
|
||||
private static final int DEFAULT_NAME_TEXT_COLOR = 0xFFFFFFFF; // 默认名字颜色
|
||||
private static final float DEFAULT_TAG_TEXT_SIZE = 12; // 默认标签字体大小
|
||||
private static final int DEFAULT_TAG_TEXT_COLOR = 0xFFFFE554; // 默认标签颜色
|
||||
private static final int DEFAULT_TAG_MARGIN = 0; // 默认标签间距
|
||||
private static final int NAME_MARGIN_TOP = 3; // 名字与标签间距
|
||||
|
||||
public QXMeetUserView(Context context) {
|
||||
super(context);
|
||||
initAttrs(null);
|
||||
initSubviews(context);
|
||||
}
|
||||
|
||||
public QXMeetUserView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initAttrs(attrs);
|
||||
initSubviews(context);
|
||||
}
|
||||
|
||||
public QXMeetUserView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initAttrs(attrs);
|
||||
initSubviews(context);
|
||||
}
|
||||
|
||||
// 初始化自定义属性
|
||||
private void initAttrs(AttributeSet attrs) {
|
||||
if (attrs == null) {
|
||||
// 设置默认值
|
||||
avatarSize = dpToPx(DEFAULT_AVATAR_SIZE);
|
||||
dressSize = dpToPx(DEFAULT_DRESS_SIZE);
|
||||
nameTextSize = DEFAULT_NAME_TEXT_SIZE;
|
||||
nameTextColor = DEFAULT_NAME_TEXT_COLOR;
|
||||
tagTextSize = DEFAULT_TAG_TEXT_SIZE;
|
||||
tagTextColor = DEFAULT_TAG_TEXT_COLOR;
|
||||
tagMargin = dpToPx(DEFAULT_TAG_MARGIN);
|
||||
return;
|
||||
}
|
||||
|
||||
// 从XML获取属性
|
||||
TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.QXMeetUserView);
|
||||
avatarSize = ta.getDimensionPixelSize(R.styleable.QXMeetUserView_avatarSize, dpToPx(DEFAULT_AVATAR_SIZE));
|
||||
dressSize = ta.getDimensionPixelSize(R.styleable.QXMeetUserView_dressSize, dpToPx(DEFAULT_DRESS_SIZE));
|
||||
nameTextSize = ta.getDimension(R.styleable.QXMeetUserView_nameTextSize, DEFAULT_NAME_TEXT_SIZE);
|
||||
nameTextColor = ta.getColor(R.styleable.QXMeetUserView_nameTextColor, DEFAULT_NAME_TEXT_COLOR);
|
||||
tagTextSize = ta.getDimension(R.styleable.QXMeetUserView_tagTextSize, DEFAULT_TAG_TEXT_SIZE);
|
||||
tagTextColor = ta.getColor(R.styleable.QXMeetUserView_tagTextColor, DEFAULT_TAG_TEXT_COLOR);
|
||||
tagMargin = ta.getDimensionPixelSize(R.styleable.QXMeetUserView_tagMargin, dpToPx(DEFAULT_TAG_MARGIN));
|
||||
ta.recycle();
|
||||
|
||||
// 日志输出属性值,便于调试
|
||||
Log.d(TAG, "属性初始化 - 头像大小: " + avatarSize + ", 装饰大小: " + dressSize);
|
||||
}
|
||||
|
||||
private void initSubviews(Context context) {
|
||||
setClipChildren(false);
|
||||
setClipToPadding(false);
|
||||
setWillNotDraw(false);
|
||||
// 为整个视图添加背景色,便于调试视图范围
|
||||
// setBackgroundColor(0x0A000000); // 极浅灰色背景
|
||||
|
||||
// 2. 头像(中间层)
|
||||
headerImageView = new GifAvatarOvalView(context);
|
||||
headerImageView.setId(View.generateViewId());
|
||||
headerImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
// 设置强制背景色,确保即使资源加载失败也能看到
|
||||
// headerImageView.setBackgroundColor(0x33FF0000); // 半透明红色
|
||||
|
||||
// 尝试加载默认头像资源
|
||||
try {
|
||||
int resId = R.mipmap.default_avatar;
|
||||
// headerImageView.setImageResource(resId);
|
||||
Log.d(TAG, "尝试加载默认头像资源: " + resId);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "默认头像资源加载失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
LayoutParams headerParams = new LayoutParams(avatarSize, avatarSize);
|
||||
headerParams.addRule(CENTER_IN_PARENT); // 头像在父容器居中
|
||||
addView(headerImageView, headerParams);
|
||||
Log.d(TAG, "头像已添加到视图,大小: " + avatarSize + "x" + avatarSize);
|
||||
|
||||
|
||||
|
||||
// 1. 装饰图(底层)- 优先初始化确保在最底层
|
||||
dressImageView = new ImageView(context);
|
||||
dressImageView.setId(View.generateViewId());
|
||||
dressImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
// 设置强制背景色,确保即使资源加载失败也能看到
|
||||
/// dressImageView.setBackgroundColor(0x330000FF); // 半透明蓝色
|
||||
|
||||
// 尝试加载装饰图资源
|
||||
try {
|
||||
int resId = R.mipmap.xlh_image;
|
||||
dressImageView.setImageResource(resId);
|
||||
Log.d(TAG, "尝试加载装饰图资源: " + resId);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "装饰图资源加载失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
LayoutParams dressParams = new LayoutParams(dressSize, dressSize);
|
||||
dressParams.addRule(CENTER_HORIZONTAL);
|
||||
dressParams.topMargin = dpToPx(5); // 稍微向下移动一点,确保可见
|
||||
addView(dressImageView, dressParams);
|
||||
Log.d(TAG, "装饰图已添加到视图,大小: " + dressSize + "x" + dressSize);
|
||||
|
||||
|
||||
// 3. 标签(顶层,与头像底部平齐)
|
||||
tagLabel = new TextView(context);
|
||||
tagLabel.setId(View.generateViewId());
|
||||
tagLabel.setTextColor(tagTextColor);
|
||||
tagLabel.setTextSize(tagTextSize);
|
||||
tagLabel.setGravity(Gravity.CENTER);
|
||||
tagLabel.setText("房主");
|
||||
tagLabel.setBackground(getRoundedRectBackground(0xFF8D6F28, dpToPx(8)));
|
||||
tagLabel.setPadding(dpToPx(6), 0, dpToPx(6), 0);
|
||||
|
||||
LayoutParams tagParams = new LayoutParams(LayoutParams.WRAP_CONTENT, dpToPx(16));
|
||||
tagParams.addRule(CENTER_HORIZONTAL);
|
||||
addView(tagLabel, tagParams);
|
||||
|
||||
// 4. 名字(顶层,在标签下方)
|
||||
nameLabel = new TextView(context);
|
||||
nameLabel.setId(View.generateViewId());
|
||||
nameLabel.setTextColor(nameTextColor);
|
||||
nameLabel.setTextSize(nameTextSize);
|
||||
nameLabel.setText("虚位以待");
|
||||
nameLabel.setGravity(Gravity.CENTER);
|
||||
nameLabel.setSingleLine(true);
|
||||
|
||||
LayoutParams nameParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
nameParams.addRule(CENTER_HORIZONTAL);
|
||||
nameParams.addRule(BELOW, tagLabel.getId());
|
||||
nameParams.topMargin = dpToPx(NAME_MARGIN_TOP);
|
||||
addView(nameLabel, nameParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
// 强制设置最小宽高,确保视图可见
|
||||
int minWidth = Math.max(avatarSize, dressSize);
|
||||
int minHeight = dressSize + dpToPx(40); // 装饰图高度 + 标签和名字的高度
|
||||
|
||||
int width = resolveSizeAndState(minWidth, widthMeasureSpec, 0);
|
||||
int height = resolveSizeAndState(minHeight, heightMeasureSpec, 0);
|
||||
|
||||
setMeasuredDimension(width, height);
|
||||
|
||||
// 测量子视图
|
||||
measureChildren(
|
||||
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
||||
);
|
||||
|
||||
Log.d(TAG, "onMeasure - 视图大小: " + width + "x" + height);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
|
||||
// 确保装饰图正确布局
|
||||
if (dressImageView != null) {
|
||||
int dressLeft = (getWidth() - dressSize) / 2;
|
||||
int dressTop = dpToPx(5); // 顶部留出一点空间
|
||||
dressImageView.layout(
|
||||
dressLeft,
|
||||
dressTop,
|
||||
dressLeft + dressSize,
|
||||
dressTop + dressSize
|
||||
);
|
||||
Log.d(TAG, "装饰图布局位置: " + dressLeft + "," + dressTop + "," +
|
||||
(dressLeft + dressSize) + "," + (dressTop + dressSize));
|
||||
}
|
||||
|
||||
// 确保头像正确布局
|
||||
if (headerImageView != null) {
|
||||
int avatarLeft = (getWidth() - avatarSize) / 2;
|
||||
int avatarTop = (dressSize - avatarSize) / 2 + dpToPx(5); // 居中显示在装饰图上
|
||||
headerImageView.layout(
|
||||
avatarLeft,
|
||||
avatarTop,
|
||||
avatarLeft + avatarSize,
|
||||
avatarTop + avatarSize
|
||||
);
|
||||
Log.d(TAG, "头像布局位置: " + avatarLeft + "," + avatarTop + "," +
|
||||
(avatarLeft + avatarSize) + "," + (avatarTop + avatarSize));
|
||||
}
|
||||
|
||||
// 标签布局(与头像底部平齐)
|
||||
if (headerImageView != null && tagLabel != null) {
|
||||
int avatarBottom = headerImageView.getBottom();
|
||||
int tagTop = avatarBottom + tagMargin;
|
||||
int tagLeft = (getWidth() - tagLabel.getMeasuredWidth()) / 2;
|
||||
|
||||
tagLabel.layout(
|
||||
tagLeft,
|
||||
tagTop,
|
||||
tagLeft + tagLabel.getMeasuredWidth(),
|
||||
tagTop + tagLabel.getMeasuredHeight()
|
||||
);
|
||||
}
|
||||
|
||||
// 名字布局(在标签下方)
|
||||
if (tagLabel != null && nameLabel != null) {
|
||||
int tagBottom = tagLabel.getBottom();
|
||||
int nameTop = tagBottom + dpToPx(NAME_MARGIN_TOP);
|
||||
int nameLeft = (getWidth() - nameLabel.getMeasuredWidth()) / 2;
|
||||
|
||||
nameLabel.layout(
|
||||
nameLeft,
|
||||
nameTop,
|
||||
nameLeft + nameLabel.getMeasuredWidth(),
|
||||
nameTop + nameLabel.getMeasuredHeight()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 以下方法保持不变
|
||||
public void setIsLuckUser(boolean isLuckUser) {
|
||||
if (tagLabel == null) return;
|
||||
|
||||
if (isLuckUser) {
|
||||
tagLabel.setTextColor(0xFFFFFFFF);
|
||||
tagLabel.setBackground(getRoundedRectBackground(0xFF6C49E4, dpToPx(8)));
|
||||
tagLabel.setText("幸运者");
|
||||
} else {
|
||||
tagLabel.setTextColor(tagTextColor);
|
||||
tagLabel.setBackground(getRoundedRectBackground(0xFF8D6F28, dpToPx(8)));
|
||||
tagLabel.setText("房主");
|
||||
}
|
||||
}
|
||||
|
||||
public void setModel(BlindBoxBean.xlhUser model) {
|
||||
|
||||
if (headerImageView == null || nameLabel == null) return;
|
||||
|
||||
if (model != null) {
|
||||
try {
|
||||
if (model.getAvatar().toString()!=""){
|
||||
ImageUtils.loadHeadCC(model.getAvatar(), headerImageView);
|
||||
}else {
|
||||
int resId = R.mipmap.default_avatar;
|
||||
headerImageView.setImageResource(resId);
|
||||
}
|
||||
|
||||
Log.d(TAG, "加载用户头像: " + model.getAvatar());
|
||||
} catch (Exception e) {
|
||||
int resId = R.mipmap.default_avatar;
|
||||
headerImageView.setImageResource(resId);
|
||||
Log.e(TAG, "加载用户头像失败: " + e.getMessage());
|
||||
//headerImageView.setBackgroundColor(0x33FF0000); // 保持红色背景以便识别
|
||||
}
|
||||
nameLabel.setText(model.getNickname() != null ? model.getNickname() : "虚位以待");
|
||||
} else {
|
||||
resetView();
|
||||
}
|
||||
}
|
||||
|
||||
public void resetView() {
|
||||
if (headerImageView != null) {
|
||||
try {
|
||||
headerImageView.setImageResource(R.mipmap.default_avatar);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "重置头像失败: " + e.getMessage());
|
||||
// headerImageView.setBackgroundColor(0x33FF0000);
|
||||
}
|
||||
}
|
||||
if (nameLabel != null) {
|
||||
nameLabel.setText("虚位以待");
|
||||
}
|
||||
setIsLuckUser(false);
|
||||
}
|
||||
|
||||
private GradientDrawable getRoundedRectBackground(int color, float radius) {
|
||||
GradientDrawable drawable = new GradientDrawable();
|
||||
drawable.setColor(color);
|
||||
drawable.setCornerRadius(radius);
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private int dpToPx(int dp) {
|
||||
float density = getResources().getDisplayMetrics().density;
|
||||
return Math.round(dp * density);
|
||||
}
|
||||
|
||||
// Getter方法
|
||||
public ImageView getHeaderImageView() { return headerImageView; }
|
||||
public ImageView getDressImageView() { return dressImageView; }
|
||||
public TextView getTagLabel() { return tagLabel; }
|
||||
public TextView getNameLabel() { return nameLabel; }
|
||||
}
|
||||
@@ -0,0 +1,860 @@
|
||||
package com.xscm.moduleutil.view;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.text.InputType;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
public class QXRedBagSendView extends FrameLayout {
|
||||
|
||||
// Properties
|
||||
private String redBagType = "1";
|
||||
private String redBagContentType = "1";
|
||||
private String redBagTime = "0";
|
||||
private int currentPage = 0;
|
||||
private boolean isFromRule = false;
|
||||
|
||||
// UI Components
|
||||
private LinearLayout mainContainer;
|
||||
private ImageView bgImageView;
|
||||
private TextView titleLabel;
|
||||
private Button helpBtn;
|
||||
private Button backBtn;
|
||||
private Button closeBtn;
|
||||
private Button nextBtn;
|
||||
private Button commitBtn;
|
||||
|
||||
private LinearLayout firstContentView;
|
||||
private Button normalRedBagBtn;
|
||||
private Button pwdRedBagBtn;
|
||||
private LinearLayout firstPwdView;
|
||||
private EditText pwdTextField;
|
||||
private LinearLayout firstTimeView;
|
||||
private View scrollBgView;
|
||||
private Button coinRedBagBtn;
|
||||
private Button diamondRedBagBtn;
|
||||
private Button selectedRedBagTimeBtn;
|
||||
|
||||
private LinearLayout nextContentView;
|
||||
private TextView moneyLabel;
|
||||
private TextView moneyUnitLabel;
|
||||
private EditText moneyTextField;
|
||||
private EditText countTextField;
|
||||
private EditText remarkTextField;
|
||||
private Button noDrawAuthBtn;
|
||||
private Button collectDrawAuthBtn;
|
||||
private Button upSeatDrawAuthBtn;
|
||||
|
||||
private LinearLayout ruleContentView;
|
||||
private WebView webView;
|
||||
|
||||
private final int[] timeArray = {0, 1, 2, 5, 10};
|
||||
private final int[] drawAuthArray = {0, 1, 2};
|
||||
|
||||
public QXRedBagSendView(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public QXRedBagSendView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public QXRedBagSendView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
setLayoutParams(new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
));
|
||||
setBackgroundColor(Color.parseColor("#80000000"));
|
||||
|
||||
initMainContainer(context);
|
||||
initTitleBar(context);
|
||||
initContentArea(context);
|
||||
initButtons(context);
|
||||
|
||||
// 默认选择普通红包
|
||||
selectedRedBagTypeAction(normalRedBagBtn);
|
||||
}
|
||||
|
||||
private void initMainContainer(Context context) {
|
||||
mainContainer = new LinearLayout(context);
|
||||
mainContainer.setOrientation(LinearLayout.VERTICAL);
|
||||
LayoutParams containerParams = new LayoutParams(
|
||||
dpToPx(345),
|
||||
dpToPx(454)
|
||||
);
|
||||
containerParams.gravity = Gravity.CENTER;
|
||||
mainContainer.setLayoutParams(containerParams);
|
||||
|
||||
// 背景图片
|
||||
bgImageView = new ImageView(context);
|
||||
bgImageView.setLayoutParams(new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
));
|
||||
// 设置背景资源,需要你在res/drawable中添加对应的图片
|
||||
bgImageView.setBackgroundResource(R.mipmap.red_en);
|
||||
// bgImageView.setBackgroundColor(Color.parseColor("#FFD700")); // 临时颜色
|
||||
|
||||
// 将背景图片添加到主容器
|
||||
mainContainer.addView(bgImageView);
|
||||
|
||||
addView(mainContainer);
|
||||
}
|
||||
|
||||
private void initTitleBar(Context context) {
|
||||
// 标题栏容器
|
||||
LinearLayout titleContainer = new LinearLayout(context);
|
||||
titleContainer.setOrientation(LinearLayout.HORIZONTAL);
|
||||
titleContainer.setGravity(Gravity.CENTER_VERTICAL);
|
||||
LinearLayout.LayoutParams titleContainerParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
titleContainerParams.setMargins(0, dpToPx(15), 0, 0);
|
||||
titleContainer.setLayoutParams(titleContainerParams);
|
||||
|
||||
// 帮助按钮
|
||||
helpBtn = createIconButton(context);
|
||||
helpBtn.setBackgroundColor(Color.TRANSPARENT);
|
||||
helpBtn.setOnClickListener(v -> helpAction());
|
||||
titleContainer.addView(helpBtn);
|
||||
|
||||
// 返回按钮
|
||||
backBtn = createIconButton(context);
|
||||
backBtn.setBackgroundColor(Color.TRANSPARENT);
|
||||
backBtn.setOnClickListener(v -> backAction());
|
||||
backBtn.setVisibility(View.GONE);
|
||||
titleContainer.addView(backBtn);
|
||||
|
||||
// 标题
|
||||
titleLabel = new TextView(context);
|
||||
titleLabel.setText("直播间红包");
|
||||
titleLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
|
||||
titleLabel.setTextColor(Color.WHITE);
|
||||
titleLabel.setTypeface(titleLabel.getTypeface(), android.graphics.Typeface.BOLD);
|
||||
LinearLayout.LayoutParams titleParams = new LinearLayout.LayoutParams(
|
||||
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1
|
||||
);
|
||||
titleLabel.setLayoutParams(titleParams);
|
||||
titleLabel.setGravity(Gravity.CENTER);
|
||||
titleContainer.addView(titleLabel);
|
||||
|
||||
// 关闭按钮
|
||||
closeBtn = createIconButton(context);
|
||||
closeBtn.setBackgroundColor(Color.TRANSPARENT);
|
||||
closeBtn.setOnClickListener(v -> closeAction());
|
||||
titleContainer.addView(closeBtn);
|
||||
|
||||
// 将标题栏添加到主容器(在背景图片之上)
|
||||
mainContainer.addView(titleContainer);
|
||||
}
|
||||
|
||||
private void initContentArea(Context context) {
|
||||
// 内容区域容器
|
||||
LinearLayout contentArea = new LinearLayout(context);
|
||||
contentArea.setOrientation(LinearLayout.VERTICAL);
|
||||
LinearLayout.LayoutParams contentParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
0, 1
|
||||
);
|
||||
contentParams.setMargins(dpToPx(15), dpToPx(15), dpToPx(15), dpToPx(15));
|
||||
contentArea.setLayoutParams(contentParams);
|
||||
|
||||
initFirstContentView(context, contentArea);
|
||||
initNextContentView(context, contentArea);
|
||||
initRuleView(context, contentArea);
|
||||
|
||||
mainContainer.addView(contentArea);
|
||||
}
|
||||
|
||||
private void initButtons(Context context) {
|
||||
// 按钮容器
|
||||
LinearLayout buttonContainer = new LinearLayout(context);
|
||||
buttonContainer.setOrientation(LinearLayout.VERTICAL);
|
||||
LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
buttonParams.setMargins(dpToPx(15), 0, dpToPx(15), dpToPx(15));
|
||||
buttonContainer.setLayoutParams(buttonParams);
|
||||
|
||||
// 下一步按钮
|
||||
nextBtn = createActionButton(context, "下一步");
|
||||
nextBtn.setOnClickListener(v -> nextAction());
|
||||
buttonContainer.addView(nextBtn);
|
||||
|
||||
// 提交按钮
|
||||
commitBtn = createActionButton(context, "发红包");
|
||||
commitBtn.setOnClickListener(v -> commitAction());
|
||||
commitBtn.setVisibility(View.GONE);
|
||||
buttonContainer.addView(commitBtn);
|
||||
|
||||
mainContainer.addView(buttonContainer);
|
||||
}
|
||||
|
||||
private void initFirstContentView(Context context, ViewGroup parent) {
|
||||
firstContentView = new LinearLayout(context);
|
||||
firstContentView.setOrientation(LinearLayout.VERTICAL);
|
||||
firstContentView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
));
|
||||
parent.addView(firstContentView);
|
||||
|
||||
// 顶部视图 - 参与领取限制
|
||||
LinearLayout firstTopView = createCardView(context);
|
||||
firstTopView.setOrientation(LinearLayout.VERTICAL);
|
||||
LinearLayout.LayoutParams topParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
dpToPx(88)
|
||||
);
|
||||
topParams.bottomMargin = dpToPx(12);
|
||||
firstTopView.setLayoutParams(topParams);
|
||||
firstContentView.addView(firstTopView);
|
||||
|
||||
TextView topTitleLabel = createTitleLabel(context, "参与领取限制");
|
||||
firstTopView.addView(topTitleLabel);
|
||||
|
||||
LinearLayout typeButtonContainer = new LinearLayout(context);
|
||||
typeButtonContainer.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
typeButtonContainer.setWeightSum(2);
|
||||
firstTopView.addView(typeButtonContainer);
|
||||
|
||||
normalRedBagBtn = createTypeButton(context, "普通红包");
|
||||
normalRedBagBtn.setOnClickListener(v -> selectedRedBagTypeAction(normalRedBagBtn));
|
||||
typeButtonContainer.addView(normalRedBagBtn);
|
||||
|
||||
pwdRedBagBtn = createTypeButton(context, "口令红包");
|
||||
pwdRedBagBtn.setOnClickListener(v -> selectedRedBagTypeAction(pwdRedBagBtn));
|
||||
typeButtonContainer.addView(pwdRedBagBtn);
|
||||
|
||||
// 口令输入视图
|
||||
firstPwdView = createInputCard(context, "口令", "请输入口令");
|
||||
pwdTextField = (EditText) ((LinearLayout) firstPwdView).getChildAt(1);
|
||||
firstContentView.addView(firstPwdView);
|
||||
|
||||
// 时间选择视图
|
||||
firstTimeView = createCardView(context);
|
||||
firstTimeView.setOrientation(LinearLayout.VERTICAL);
|
||||
LinearLayout.LayoutParams timeParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
dpToPx(88)
|
||||
);
|
||||
timeParams.bottomMargin = dpToPx(12);
|
||||
firstTimeView.setLayoutParams(timeParams);
|
||||
firstContentView.addView(firstTimeView);
|
||||
|
||||
TextView timeTitleLabel = createTitleLabel(context, "开奖倒计时");
|
||||
firstTimeView.addView(timeTitleLabel);
|
||||
|
||||
LinearLayout timeButtonContainer = new LinearLayout(context);
|
||||
timeButtonContainer.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
firstTimeView.addView(timeButtonContainer);
|
||||
|
||||
// 创建时间选择按钮
|
||||
for (int time : timeArray) {
|
||||
Button timeBtn = createTimeButton(context, time);
|
||||
timeBtn.setOnClickListener(v -> redBagTimeAction(timeBtn));
|
||||
if (time == 0) {
|
||||
timeBtn.setSelected(true);
|
||||
selectedRedBagTimeBtn = timeBtn;
|
||||
}
|
||||
timeButtonContainer.addView(timeBtn);
|
||||
}
|
||||
|
||||
// 红包类型选择
|
||||
LinearLayout bottomView = createCardView(context);
|
||||
bottomView.setOrientation(LinearLayout.HORIZONTAL);
|
||||
bottomView.setGravity(Gravity.CENTER_VERTICAL);
|
||||
LinearLayout.LayoutParams bottomParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
dpToPx(48)
|
||||
);
|
||||
bottomView.setLayoutParams(bottomParams);
|
||||
firstContentView.addView(bottomView);
|
||||
|
||||
TextView redBagTypeLabel = createTitleLabel(context, "红包类型");
|
||||
bottomView.addView(redBagTypeLabel);
|
||||
|
||||
// 红包类型切换容器
|
||||
RelativeLayout typeSwitchContainer = new RelativeLayout(context);
|
||||
LinearLayout.LayoutParams switchParams = new LinearLayout.LayoutParams(
|
||||
dpToPx(130),
|
||||
dpToPx(26)
|
||||
);
|
||||
switchParams.gravity = Gravity.CENTER_VERTICAL;
|
||||
typeSwitchContainer.setLayoutParams(switchParams);
|
||||
typeSwitchContainer.setBackgroundColor(Color.parseColor("#BA230A"));
|
||||
typeSwitchContainer.setPadding(dpToPx(2), dpToPx(2), dpToPx(2), dpToPx(2));
|
||||
bottomView.addView(typeSwitchContainer);
|
||||
|
||||
scrollBgView = new View(context);
|
||||
RelativeLayout.LayoutParams scrollParams = new RelativeLayout.LayoutParams(
|
||||
dpToPx(63),
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
);
|
||||
scrollBgView.setLayoutParams(scrollParams);
|
||||
scrollBgView.setBackgroundColor(Color.parseColor("#FDE8A3"));
|
||||
typeSwitchContainer.addView(scrollBgView);
|
||||
|
||||
coinRedBagBtn = createContentTypeButton(context, "金币红包");
|
||||
coinRedBagBtn.setSelected(true);
|
||||
coinRedBagBtn.setOnClickListener(v -> redBagContentTypeAction(coinRedBagBtn));
|
||||
typeSwitchContainer.addView(coinRedBagBtn);
|
||||
|
||||
diamondRedBagBtn = createContentTypeButton(context, "钻石红包");
|
||||
diamondRedBagBtn.setOnClickListener(v -> redBagContentTypeAction(diamondRedBagBtn));
|
||||
typeSwitchContainer.addView(diamondRedBagBtn);
|
||||
}
|
||||
|
||||
private void initNextContentView(Context context, ViewGroup parent) {
|
||||
nextContentView = new LinearLayout(context);
|
||||
nextContentView.setOrientation(LinearLayout.VERTICAL);
|
||||
nextContentView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
));
|
||||
nextContentView.setVisibility(View.GONE);
|
||||
parent.addView(nextContentView);
|
||||
|
||||
// 可用余额标签
|
||||
moneyLabel = new TextView(context);
|
||||
moneyLabel.setText("-金币可用");
|
||||
moneyLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
moneyLabel.setTextColor(Color.WHITE);
|
||||
LinearLayout.LayoutParams moneyLabelParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
moneyLabelParams.gravity = Gravity.END;
|
||||
moneyLabel.setLayoutParams(moneyLabelParams);
|
||||
nextContentView.addView(moneyLabel);
|
||||
|
||||
// 金额输入
|
||||
LinearLayout moneyBgView = createInputCard(context, "金额", "请输入红包金额");
|
||||
moneyTextField = (EditText) moneyBgView.getChildAt(1);
|
||||
|
||||
moneyUnitLabel = new TextView(context);
|
||||
moneyUnitLabel.setText("金币");
|
||||
moneyUnitLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
moneyUnitLabel.setTextColor(Color.parseColor("#666666"));
|
||||
LinearLayout.LayoutParams unitParams = new LinearLayout.LayoutParams(
|
||||
dpToPx(30),
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
moneyUnitLabel.setLayoutParams(unitParams);
|
||||
moneyBgView.addView(moneyUnitLabel);
|
||||
|
||||
nextContentView.addView(moneyBgView);
|
||||
|
||||
// 个数输入
|
||||
LinearLayout countBgView = createInputCard(context, "个数", "请输入红包数量");
|
||||
countTextField = (EditText) countBgView.getChildAt(1);
|
||||
|
||||
TextView countUnitLabel = new TextView(context);
|
||||
countUnitLabel.setText("个");
|
||||
countUnitLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
|
||||
countUnitLabel.setTextColor(Color.parseColor("#666666"));
|
||||
countUnitLabel.setLayoutParams(unitParams);
|
||||
countBgView.addView(countUnitLabel);
|
||||
|
||||
nextContentView.addView(countBgView);
|
||||
|
||||
// 领取条件
|
||||
LinearLayout drawAuthBgView = createCardView(context);
|
||||
drawAuthBgView.setOrientation(LinearLayout.VERTICAL);
|
||||
LinearLayout.LayoutParams authParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
dpToPx(88)
|
||||
);
|
||||
authParams.bottomMargin = dpToPx(12);
|
||||
drawAuthBgView.setLayoutParams(authParams);
|
||||
nextContentView.addView(drawAuthBgView);
|
||||
|
||||
TextView authTitleLabel = createTitleLabel(context, "条件");
|
||||
drawAuthBgView.addView(authTitleLabel);
|
||||
|
||||
LinearLayout authButtonContainer = new LinearLayout(context);
|
||||
authButtonContainer.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
drawAuthBgView.addView(authButtonContainer);
|
||||
|
||||
// 创建领取条件按钮
|
||||
noDrawAuthBtn = createAuthButton(context, "无");
|
||||
noDrawAuthBtn.setSelected(true);
|
||||
noDrawAuthBtn.setOnClickListener(v -> drawAuthAction(noDrawAuthBtn));
|
||||
authButtonContainer.addView(noDrawAuthBtn);
|
||||
|
||||
collectDrawAuthBtn = createAuthButton(context, "收藏房间");
|
||||
collectDrawAuthBtn.setOnClickListener(v -> drawAuthAction(collectDrawAuthBtn));
|
||||
authButtonContainer.addView(collectDrawAuthBtn);
|
||||
|
||||
upSeatDrawAuthBtn = createAuthButton(context, "仅麦上用户");
|
||||
upSeatDrawAuthBtn.setOnClickListener(v -> drawAuthAction(upSeatDrawAuthBtn));
|
||||
authButtonContainer.addView(upSeatDrawAuthBtn);
|
||||
|
||||
// 备注输入
|
||||
LinearLayout remarkBgView = createInputCard(context, "备注", "请输入备注");
|
||||
remarkTextField = (EditText) remarkBgView.getChildAt(1);
|
||||
nextContentView.addView(remarkBgView);
|
||||
}
|
||||
|
||||
private void initRuleView(Context context, ViewGroup parent) {
|
||||
ruleContentView = new LinearLayout(context);
|
||||
ruleContentView.setOrientation(LinearLayout.VERTICAL);
|
||||
ruleContentView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
));
|
||||
ruleContentView.setBackgroundColor(Color.WHITE);
|
||||
ruleContentView.setPadding(dpToPx(15), dpToPx(15), dpToPx(15), dpToPx(15));
|
||||
ruleContentView.setVisibility(View.GONE);
|
||||
parent.addView(ruleContentView);
|
||||
|
||||
webView = new WebView(context);
|
||||
webView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
));
|
||||
ruleContentView.addView(webView);
|
||||
}
|
||||
|
||||
// 工具方法
|
||||
private Button createIconButton(Context context) {
|
||||
Button button = new Button(context);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
dpToPx(40),
|
||||
dpToPx(40)
|
||||
);
|
||||
button.setLayoutParams(params);
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createActionButton(Context context, String text) {
|
||||
Button button = new Button(context);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
dpToPx(44)
|
||||
);
|
||||
button.setLayoutParams(params);
|
||||
button.setText(text);
|
||||
button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
|
||||
button.setTextColor(Color.parseColor("#F35248"));
|
||||
button.setTypeface(button.getTypeface(), android.graphics.Typeface.BOLD);
|
||||
button.setBackgroundColor(Color.parseColor("#FDE8A3")); // 临时背景色
|
||||
return button;
|
||||
}
|
||||
|
||||
private LinearLayout createCardView(Context context) {
|
||||
LinearLayout card = new LinearLayout(context);
|
||||
card.setBackgroundColor(Color.WHITE);
|
||||
card.setPadding(dpToPx(15), dpToPx(11), dpToPx(15), dpToPx(5));
|
||||
return card;
|
||||
}
|
||||
|
||||
private TextView createTitleLabel(Context context, String text) {
|
||||
TextView label = new TextView(context);
|
||||
label.setText(text);
|
||||
label.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
|
||||
label.setTextColor(Color.parseColor("#666666"));
|
||||
label.setTypeface(label.getTypeface(), android.graphics.Typeface.BOLD);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
dpToPx(26)
|
||||
);
|
||||
label.setLayoutParams(params);
|
||||
return label;
|
||||
}
|
||||
|
||||
private Button createTypeButton(Context context, String text) {
|
||||
Button button = new Button(context);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
0, dpToPx(36), 1
|
||||
);
|
||||
params.setMargins(0, 0, dpToPx(10), 0);
|
||||
button.setLayoutParams(params);
|
||||
button.setText(text);
|
||||
button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
|
||||
button.setTextColor(Color.WHITE);
|
||||
button.setTypeface(button.getTypeface(), android.graphics.Typeface.BOLD);
|
||||
button.setBackgroundColor(Color.parseColor("#FF9999")); // 临时背景色
|
||||
return button;
|
||||
}
|
||||
|
||||
private LinearLayout createInputCard(Context context, String title, String hint) {
|
||||
LinearLayout card = new LinearLayout(context);
|
||||
card.setOrientation(LinearLayout.HORIZONTAL);
|
||||
card.setGravity(Gravity.CENTER_VERTICAL);
|
||||
LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
dpToPx(48)
|
||||
);
|
||||
cardParams.bottomMargin = dpToPx(12);
|
||||
card.setLayoutParams(cardParams);
|
||||
card.setBackgroundColor(Color.WHITE);
|
||||
card.setPadding(dpToPx(15), 0, dpToPx(15), 0);
|
||||
|
||||
TextView titleLabel = createTitleLabel(context, title);
|
||||
card.addView(titleLabel);
|
||||
|
||||
EditText editText = new EditText(context);
|
||||
LinearLayout.LayoutParams editParams = new LinearLayout.LayoutParams(
|
||||
0, ViewGroup.LayoutParams.MATCH_PARENT, 1
|
||||
);
|
||||
editParams.setMargins(dpToPx(15), 0, dpToPx(15), 0);
|
||||
editText.setLayoutParams(editParams);
|
||||
editText.setHint(hint);
|
||||
editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
|
||||
editText.setTextColor(Color.parseColor("#666666"));
|
||||
editText.setGravity(Gravity.END);
|
||||
editText.setBackground(null);
|
||||
editText.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
card.addView(editText);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
private Button createTimeButton(Context context, int minutes) {
|
||||
Button button = new Button(context);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
dpToPx(49), dpToPx(36)
|
||||
);
|
||||
params.setMargins(0, 0, dpToPx(10), 0);
|
||||
button.setLayoutParams(params);
|
||||
|
||||
String text = minutes == 0 ? "立刻" : minutes + "分钟";
|
||||
button.setText(text);
|
||||
button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
||||
button.setTypeface(button.getTypeface(), android.graphics.Typeface.BOLD);
|
||||
button.setBackgroundColor(Color.parseColor("#EEEEEE")); // 临时背景色
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createContentTypeButton(Context context, String text) {
|
||||
Button button = new Button(context);
|
||||
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
|
||||
dpToPx(63), ViewGroup.LayoutParams.MATCH_PARENT
|
||||
);
|
||||
button.setLayoutParams(params);
|
||||
button.setText(text);
|
||||
button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
|
||||
button.setBackgroundColor(Color.TRANSPARENT);
|
||||
button.setTextColor(Color.parseColor("#FFC9C7"));
|
||||
return button;
|
||||
}
|
||||
|
||||
private Button createAuthButton(Context context, String text) {
|
||||
Button button = new Button(context);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
0, dpToPx(36), 1
|
||||
);
|
||||
params.setMargins(0, 0, dpToPx(10), 0);
|
||||
button.setLayoutParams(params);
|
||||
button.setText(text);
|
||||
button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
|
||||
button.setTypeface(button.getTypeface(), android.graphics.Typeface.BOLD);
|
||||
button.setBackgroundColor(Color.parseColor("#EEEEEE")); // 临时背景色
|
||||
return button;
|
||||
}
|
||||
|
||||
private int dpToPx(int dp) {
|
||||
return (int) TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
dp,
|
||||
getResources().getDisplayMetrics()
|
||||
);
|
||||
}
|
||||
|
||||
// Action Methods
|
||||
private void nextAction() {
|
||||
currentPage = 1;
|
||||
moneyUnitLabel.setText(redBagContentType.equals("1") ? "金币" : "钻石");
|
||||
|
||||
backBtn.setVisibility(View.GONE);
|
||||
helpBtn.setVisibility(View.VISIBLE);
|
||||
nextBtn.setVisibility(View.GONE);
|
||||
commitBtn.setVisibility(View.VISIBLE);
|
||||
|
||||
switchContentView(firstContentView, nextContentView);
|
||||
}
|
||||
|
||||
private void commitAction() {
|
||||
// 实现发红包逻辑
|
||||
}
|
||||
|
||||
private void helpAction() {
|
||||
isFromRule = true;
|
||||
webView.loadUrl("http://www.baidu.com");
|
||||
|
||||
backBtn.setVisibility(View.VISIBLE);
|
||||
helpBtn.setVisibility(View.GONE);
|
||||
nextBtn.setVisibility(View.GONE);
|
||||
commitBtn.setVisibility(View.GONE);
|
||||
|
||||
View currentView = currentPage == 1 ? nextContentView : firstContentView;
|
||||
switchContentView(currentView, ruleContentView);
|
||||
}
|
||||
|
||||
private void closeAction() {
|
||||
hide();
|
||||
}
|
||||
|
||||
private void backAction() {
|
||||
backBtn.setVisibility(View.GONE);
|
||||
helpBtn.setVisibility(View.VISIBLE);
|
||||
|
||||
View currentView = currentPage == 1 ? nextContentView : firstContentView;
|
||||
|
||||
nextBtn.setVisibility(currentPage == 0 ? View.VISIBLE : View.GONE);
|
||||
commitBtn.setVisibility(currentPage == 1 ? View.VISIBLE : View.GONE);
|
||||
|
||||
switchContentView(ruleContentView, currentView);
|
||||
isFromRule = false;
|
||||
}
|
||||
|
||||
private void selectedRedBagTypeAction(Button sender) {
|
||||
if (sender.isSelected()) return;
|
||||
|
||||
if (sender == normalRedBagBtn) {
|
||||
pwdRedBagBtn.setSelected(false);
|
||||
normalRedBagBtn.setSelected(true);
|
||||
firstPwdView.setVisibility(View.GONE);
|
||||
|
||||
// 调整时间视图位置
|
||||
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) firstTimeView.getLayoutParams();
|
||||
params.topMargin = 0;
|
||||
params.bottomMargin = dpToPx(12);
|
||||
firstTimeView.setLayoutParams(params);
|
||||
|
||||
redBagType = "1";
|
||||
} else {
|
||||
pwdRedBagBtn.setSelected(true);
|
||||
normalRedBagBtn.setSelected(false);
|
||||
firstPwdView.setVisibility(View.VISIBLE);
|
||||
|
||||
// 调整时间视图位置
|
||||
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) firstTimeView.getLayoutParams();
|
||||
params.topMargin = dpToPx(60);
|
||||
params.bottomMargin = dpToPx(12);
|
||||
firstTimeView.setLayoutParams(params);
|
||||
|
||||
redBagType = "2";
|
||||
}
|
||||
|
||||
// 更新按钮背景色
|
||||
updateButtonSelection(normalRedBagBtn, sender == normalRedBagBtn);
|
||||
updateButtonSelection(pwdRedBagBtn, sender == pwdRedBagBtn);
|
||||
}
|
||||
|
||||
private void redBagContentTypeAction(Button sender) {
|
||||
if (sender.isSelected()) return;
|
||||
|
||||
if (sender == coinRedBagBtn) {
|
||||
diamondRedBagBtn.setSelected(false);
|
||||
coinRedBagBtn.setSelected(true);
|
||||
redBagContentType = "1";
|
||||
animateSwitchBg(true);
|
||||
} else {
|
||||
coinRedBagBtn.setSelected(false);
|
||||
diamondRedBagBtn.setSelected(true);
|
||||
redBagContentType = "2";
|
||||
animateSwitchBg(false);
|
||||
}
|
||||
|
||||
// 更新按钮文字颜色
|
||||
updateContentTypeButtonColor(coinRedBagBtn, coinRedBagBtn.isSelected());
|
||||
updateContentTypeButtonColor(diamondRedBagBtn, diamondRedBagBtn.isSelected());
|
||||
}
|
||||
|
||||
private void drawAuthAction(Button sender) {
|
||||
if (sender == noDrawAuthBtn) {
|
||||
noDrawAuthBtn.setSelected(true);
|
||||
collectDrawAuthBtn.setSelected(false);
|
||||
upSeatDrawAuthBtn.setSelected(false);
|
||||
} else if (sender == collectDrawAuthBtn) {
|
||||
collectDrawAuthBtn.setSelected(!collectDrawAuthBtn.isSelected());
|
||||
if (upSeatDrawAuthBtn.isSelected() || collectDrawAuthBtn.isSelected()) {
|
||||
noDrawAuthBtn.setSelected(false);
|
||||
} else {
|
||||
noDrawAuthBtn.setSelected(true);
|
||||
}
|
||||
} else if (sender == upSeatDrawAuthBtn) {
|
||||
noDrawAuthBtn.setSelected(false);
|
||||
upSeatDrawAuthBtn.setSelected(!upSeatDrawAuthBtn.isSelected());
|
||||
if (upSeatDrawAuthBtn.isSelected() || collectDrawAuthBtn.isSelected()) {
|
||||
noDrawAuthBtn.setSelected(false);
|
||||
} else {
|
||||
noDrawAuthBtn.setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新按钮背景色
|
||||
updateAuthButtonSelection(noDrawAuthBtn, noDrawAuthBtn.isSelected());
|
||||
updateAuthButtonSelection(collectDrawAuthBtn, collectDrawAuthBtn.isSelected());
|
||||
updateAuthButtonSelection(upSeatDrawAuthBtn, upSeatDrawAuthBtn.isSelected());
|
||||
}
|
||||
|
||||
private void redBagTimeAction(Button sender) {
|
||||
if (sender.isSelected()) return;
|
||||
|
||||
if (selectedRedBagTimeBtn != null) {
|
||||
selectedRedBagTimeBtn.setSelected(false);
|
||||
updateTimeButtonSelection(selectedRedBagTimeBtn, false);
|
||||
}
|
||||
|
||||
sender.setSelected(true);
|
||||
selectedRedBagTimeBtn = sender;
|
||||
int minutes = 0;
|
||||
try {
|
||||
String text = sender.getText().toString();
|
||||
if (text.equals("立刻")) {
|
||||
minutes = 0;
|
||||
} else {
|
||||
minutes = Integer.parseInt(text.replace("分钟", ""));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
minutes = 0;
|
||||
}
|
||||
redBagTime = String.valueOf(minutes * 60);
|
||||
|
||||
updateTimeButtonSelection(sender, true);
|
||||
}
|
||||
|
||||
// Helper Methods
|
||||
private void switchContentView(View hideView, View showView) {
|
||||
hideView.setVisibility(View.GONE);
|
||||
showView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void updateButtonSelection(Button button, boolean selected) {
|
||||
button.setBackgroundColor(selected ?
|
||||
Color.parseColor("#FF5555") : Color.parseColor("#FF9999"));
|
||||
}
|
||||
|
||||
private void updateTimeButtonSelection(Button button, boolean selected) {
|
||||
button.setBackgroundColor(selected ?
|
||||
Color.parseColor("#FF5555") : Color.parseColor("#EEEEEE"));
|
||||
}
|
||||
|
||||
private void updateAuthButtonSelection(Button button, boolean selected) {
|
||||
button.setBackgroundColor(selected ?
|
||||
Color.parseColor("#FF5555") : Color.parseColor("#EEEEEE"));
|
||||
}
|
||||
|
||||
private void updateContentTypeButtonColor(Button button, boolean selected) {
|
||||
button.setTextColor(selected ?
|
||||
Color.parseColor("#D01717") : Color.parseColor("#FFC9C7"));
|
||||
}
|
||||
|
||||
private void animateSwitchBg(boolean toLeft) {
|
||||
int targetX = toLeft ? 0 : dpToPx(63);
|
||||
|
||||
ValueAnimator animator = ValueAnimator.ofInt(
|
||||
((RelativeLayout.LayoutParams) scrollBgView.getLayoutParams()).leftMargin,
|
||||
targetX
|
||||
);
|
||||
animator.addUpdateListener(animation -> {
|
||||
int value = (int) animation.getAnimatedValue();
|
||||
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) scrollBgView.getLayoutParams();
|
||||
params.leftMargin = value;
|
||||
scrollBgView.setLayoutParams(params);
|
||||
});
|
||||
animator.setDuration(300);
|
||||
animator.setInterpolator(new DecelerateInterpolator());
|
||||
animator.start();
|
||||
}
|
||||
|
||||
// Public Methods
|
||||
public void showInView(ViewGroup parent) {
|
||||
parent.addView(this);
|
||||
|
||||
// 添加入场动画
|
||||
mainContainer.setTranslationY(-getHeight());
|
||||
mainContainer.animate()
|
||||
.translationY(0)
|
||||
.setDuration(300)
|
||||
.setInterpolator(new DecelerateInterpolator())
|
||||
.start();
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
// 添加退场动画
|
||||
mainContainer.animate()
|
||||
.translationY(getHeight())
|
||||
.setDuration(300)
|
||||
.setInterpolator(new DecelerateInterpolator())
|
||||
.withEndAction(() -> {
|
||||
ViewGroup parent = (ViewGroup) getParent();
|
||||
if (parent != null) {
|
||||
parent.removeView(QXRedBagSendView.this);
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
// Getter methods
|
||||
public String getRedBagType() {
|
||||
return redBagType;
|
||||
}
|
||||
|
||||
public String getRedBagContentType() {
|
||||
return redBagContentType;
|
||||
}
|
||||
|
||||
public String getRedBagTime() {
|
||||
return redBagTime;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return pwdTextField.getText().toString();
|
||||
}
|
||||
|
||||
public String getMoney() {
|
||||
return moneyTextField.getText().toString();
|
||||
}
|
||||
|
||||
public String getCount() {
|
||||
return countTextField.getText().toString();
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remarkTextField.getText().toString();
|
||||
}
|
||||
|
||||
public int getDrawAuth() {
|
||||
if (noDrawAuthBtn.isSelected()) return 0;
|
||||
if (collectDrawAuthBtn.isSelected()) return 1;
|
||||
if (upSeatDrawAuthBtn.isSelected()) return 2;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package com.xscm.moduleutil.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.ScaleAnimation;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.xscm.moduleutil.R;
|
||||
|
||||
public class QXTimeDownView extends FrameLayout {
|
||||
|
||||
private ImageView bgImageView;
|
||||
private TextView titleLabel;
|
||||
private TextView timeLabel;
|
||||
private TextView bigTimeLabel;
|
||||
|
||||
private long endTime;
|
||||
private long startTime;
|
||||
private Handler timerHandler;
|
||||
private Runnable timerRunnable;
|
||||
|
||||
private TimeDownDelegate delegate;
|
||||
|
||||
public interface TimeDownDelegate {
|
||||
void timeDownStartAnimation();
|
||||
void timeDownUpdateAnimationWithTime(long time);
|
||||
void timeDownStopAnimation();
|
||||
void timeDownDidFinished();
|
||||
}
|
||||
|
||||
public QXTimeDownView(Context context) {
|
||||
super(context);
|
||||
initSubviews(context);
|
||||
}
|
||||
|
||||
public QXTimeDownView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initSubviews(context);
|
||||
}
|
||||
|
||||
public QXTimeDownView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initSubviews(context);
|
||||
}
|
||||
|
||||
private void initSubviews(Context context) {
|
||||
// 背景图片(最大的圆圈)
|
||||
bgImageView = new ImageView(context);
|
||||
bgImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
bgImageView.setImageResource(R.drawable.ac_time_down_bg);
|
||||
LayoutParams bgParams = new LayoutParams(
|
||||
LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.MATCH_PARENT
|
||||
);
|
||||
bgParams.gravity = Gravity.CENTER;
|
||||
addView(bgImageView, bgParams);
|
||||
|
||||
// 时间标签(居中显示在背景图上)
|
||||
timeLabel = new TextView(context);
|
||||
timeLabel.setTextSize(16);
|
||||
timeLabel.setTextColor(0xFFFFECA7);
|
||||
timeLabel.setGravity(Gravity.CENTER);
|
||||
timeLabel.setTypeface(android.graphics.Typeface.create("sans-serif-condensed", android.graphics.Typeface.NORMAL));
|
||||
LayoutParams timeParams = new LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
timeParams.gravity = Gravity.CENTER;
|
||||
timeParams.topMargin = dpToPx(-10);
|
||||
addView(timeLabel, timeParams);
|
||||
|
||||
// 标题标签(显示在时间标签下方)
|
||||
titleLabel = new TextView(context);
|
||||
titleLabel.setTextSize(12);
|
||||
titleLabel.setTextColor(0xFFFFECA7);
|
||||
titleLabel.setText("倒计时");
|
||||
titleLabel.setGravity(Gravity.CENTER);
|
||||
LayoutParams titleParams = new LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
titleParams.gravity = Gravity.CENTER;
|
||||
titleParams.topMargin = dpToPx(12); // 在timeLabel下方一定距离
|
||||
addView(titleLabel, titleParams);
|
||||
|
||||
// 大时间标签(初始隐藏,用于最后30秒显示)
|
||||
bigTimeLabel = new TextView(context);
|
||||
bigTimeLabel.setTextSize(20);
|
||||
bigTimeLabel.setText("-");
|
||||
bigTimeLabel.setTextColor(0xFFFFECA7);
|
||||
bigTimeLabel.setGravity(Gravity.CENTER);
|
||||
bigTimeLabel.setVisibility(View.GONE);
|
||||
bigTimeLabel.setTypeface(Typeface.create("semibold", Typeface.NORMAL));
|
||||
LayoutParams bigTimeParams = new LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
);
|
||||
bigTimeParams.gravity = Gravity.CENTER;
|
||||
addView(bigTimeLabel, bigTimeParams);
|
||||
|
||||
// 初始化定时器
|
||||
timerHandler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
public void setEndTime(long endTime) {
|
||||
this.endTime = endTime;
|
||||
|
||||
// 获取当前时间(秒)
|
||||
long currentTime = System.currentTimeMillis() / 1000;
|
||||
this.startTime = endTime - currentTime;
|
||||
|
||||
if (this.startTime <= 0) {
|
||||
// 时间错误不进行倒计时
|
||||
showBigTimeLabel("0");
|
||||
return;
|
||||
}
|
||||
|
||||
stopTimer();
|
||||
startTimer();
|
||||
}
|
||||
|
||||
private void startTimer() {
|
||||
timerRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
startTime--;
|
||||
|
||||
long min = (startTime % 3600) / 60;
|
||||
long second = startTime % 60;
|
||||
|
||||
if (startTime <= 30) {
|
||||
// 最后30秒显示大数字
|
||||
showBigTimeLabel(String.valueOf(startTime));
|
||||
|
||||
if (startTime == 30) {
|
||||
if (delegate != null) {
|
||||
delegate.timeDownStartAnimation();
|
||||
}
|
||||
} else {
|
||||
if (delegate != null) {
|
||||
delegate.timeDownUpdateAnimationWithTime(startTime);
|
||||
}
|
||||
}
|
||||
|
||||
// 弹性动画
|
||||
performScaleAnimation();
|
||||
} else {
|
||||
// 正常倒计时显示
|
||||
showNormalTime(String.format("%02d:%02d", min, second));
|
||||
|
||||
if (delegate != null) {
|
||||
delegate.timeDownStopAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
if (startTime <= 0) {
|
||||
stopTimer();
|
||||
showBigTimeLabel("0");
|
||||
|
||||
if (delegate != null) {
|
||||
delegate.timeDownDidFinished();
|
||||
delegate.timeDownStopAnimation();
|
||||
}
|
||||
} else {
|
||||
// 继续下一次计时
|
||||
timerHandler.postDelayed(this, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 立即开始第一次执行
|
||||
timerHandler.post(timerRunnable);
|
||||
}
|
||||
|
||||
public void stopTimer() {
|
||||
if (timerHandler != null && timerRunnable != null) {
|
||||
timerHandler.removeCallbacks(timerRunnable);
|
||||
timerRunnable = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void showBigTimeLabel(String text) {
|
||||
bigTimeLabel.setVisibility(View.VISIBLE);
|
||||
timeLabel.setVisibility(View.GONE);
|
||||
titleLabel.setVisibility(View.GONE);
|
||||
bigTimeLabel.setText(text);
|
||||
}
|
||||
|
||||
private void showNormalTime(String timeText) {
|
||||
bigTimeLabel.setVisibility(View.GONE);
|
||||
timeLabel.setVisibility(View.VISIBLE);
|
||||
titleLabel.setVisibility(View.VISIBLE);
|
||||
timeLabel.setText(timeText);
|
||||
}
|
||||
|
||||
private void performScaleAnimation() {
|
||||
ScaleAnimation scaleAnimation = new ScaleAnimation(
|
||||
1.0f, 1.5f, // X轴从1.0缩放到1.5
|
||||
1.0f, 1.5f, // Y轴从1.0缩放到1.5
|
||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f, // 缩放中心X
|
||||
ScaleAnimation.RELATIVE_TO_SELF, 0.5f // 缩放中心Y
|
||||
);
|
||||
|
||||
scaleAnimation.setDuration(600);
|
||||
scaleAnimation.setFillAfter(false);
|
||||
|
||||
bigTimeLabel.startAnimation(scaleAnimation);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
stopTimer();
|
||||
showNormalTime("00:00");
|
||||
bigTimeLabel.setVisibility(View.GONE);
|
||||
timeLabel.setVisibility(View.VISIBLE);
|
||||
titleLabel.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// 辅助方法:dp 转 px
|
||||
private int dpToPx(int dp) {
|
||||
float density = getResources().getDisplayMetrics().density;
|
||||
return Math.round(dp * density);
|
||||
}
|
||||
|
||||
// Getter 和 Setter 方法
|
||||
public void setDelegate(TimeDownDelegate delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
public long getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public TextView getTitleLabel() {
|
||||
return titleLabel;
|
||||
}
|
||||
|
||||
public TextView getTimeLabel() {
|
||||
return timeLabel;
|
||||
}
|
||||
|
||||
public TextView getBigTimeLabel() {
|
||||
return bigTimeLabel;
|
||||
}
|
||||
|
||||
public ImageView getBgImageView() {
|
||||
return bgImageView;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
stopTimer();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
package com.xscm.moduleutil.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.text.Html
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.TextPaint
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.*
|
||||
import android.util.Log
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.StringRes
|
||||
|
||||
/**
|
||||
* 封装了富文本展示功能的自定义TextView
|
||||
* 支持HTML格式、自定义样式文本和点击事件
|
||||
*/
|
||||
class RichTextView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : androidx.appcompat.widget.AppCompatTextView(context, attrs, defStyleAttr) {
|
||||
|
||||
// 富文本构建器
|
||||
private val spannableBuilder = SpannableStringBuilder()
|
||||
|
||||
init {
|
||||
// 初始化配置
|
||||
movementMethod = LinkMovementMethod.getInstance()
|
||||
highlightColor = Color.TRANSPARENT // 移除点击高亮
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置HTML格式的富文本
|
||||
*/
|
||||
fun setHtmlText(html: String) {
|
||||
val spanned = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
|
||||
Html.fromHtml(html, Html.FROM_HTML_MODE_COMPACT)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
Html.fromHtml(html)
|
||||
}
|
||||
text = spanned
|
||||
}
|
||||
|
||||
/**
|
||||
* 从资源文件设置HTML富文本
|
||||
*/
|
||||
fun setHtmlText(@StringRes resId: Int) {
|
||||
setHtmlText(context.getString(resId))
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始构建富文本
|
||||
*/
|
||||
fun beginBuild(): RichTextBuilder {
|
||||
spannableBuilder.clear()
|
||||
return RichTextBuilder()
|
||||
}
|
||||
|
||||
/**
|
||||
* 富文本构建器
|
||||
* 支持链式调用添加各种样式的文本
|
||||
*/
|
||||
inner class RichTextBuilder {
|
||||
|
||||
/**
|
||||
* 添加普通文本
|
||||
*/
|
||||
fun addText(text: String): RichTextBuilder {
|
||||
spannableBuilder.append(text)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加普通文本(从资源文件)
|
||||
*/
|
||||
fun addText(@StringRes resId: Int): RichTextBuilder {
|
||||
return addText(context.getString(resId))
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加加粗文本
|
||||
*/
|
||||
fun addBoldText(text: String): RichTextBuilder {
|
||||
return addStyledText(text, StyleSpan(Typeface.BOLD))
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加斜体文本
|
||||
*/
|
||||
fun addItalicText(text: String): RichTextBuilder {
|
||||
return addStyledText(text, StyleSpan(Typeface.ITALIC))
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加下划线文本
|
||||
*/
|
||||
fun addUnderlineText(text: String): RichTextBuilder {
|
||||
return addStyledText(text, UnderlineSpan())
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加删除线文本
|
||||
*/
|
||||
fun addStrikethroughText(text: String): RichTextBuilder {
|
||||
return addStyledText(text, StrikethroughSpan())
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加指定颜色的文本
|
||||
*/
|
||||
fun addColoredText(text: String, @ColorInt color: Int): RichTextBuilder {
|
||||
return addStyledText(text, ForegroundColorSpan(color))
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加指定背景色的文本
|
||||
*/
|
||||
fun addBackgroundColoredText(text: String, @ColorInt color: Int): RichTextBuilder {
|
||||
return addStyledText(text, BackgroundColorSpan(color))
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加指定大小的文本
|
||||
* @param proportion 相对于默认大小的比例
|
||||
*/
|
||||
fun addSizedText(text: String, proportion: Float): RichTextBuilder {
|
||||
return addStyledText(text, RelativeSizeSpan(proportion))
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加带有点击事件的文本
|
||||
*/
|
||||
fun addClickableText(
|
||||
text: String,
|
||||
@ColorInt linkColor: Int = Color.BLUE,
|
||||
isUnderline: Boolean = false,
|
||||
onClick: () -> Unit
|
||||
): RichTextBuilder {
|
||||
val start = spannableBuilder.length
|
||||
spannableBuilder.append(text)
|
||||
val end = spannableBuilder.length
|
||||
|
||||
val clickableSpan = object : ClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
onClick.invoke()
|
||||
}
|
||||
|
||||
override fun updateDrawState(ds: TextPaint) {
|
||||
super.updateDrawState(ds)
|
||||
ds.color = linkColor
|
||||
ds.isUnderlineText = isUnderline
|
||||
}
|
||||
}
|
||||
|
||||
spannableBuilder.setSpan(
|
||||
clickableSpan,
|
||||
start,
|
||||
end,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加换行
|
||||
*/
|
||||
fun addLineBreak(): RichTextBuilder {
|
||||
spannableBuilder.append("\n")
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加自定义样式的文本
|
||||
*/
|
||||
fun addStyledText(text: String, vararg spans: Any): RichTextBuilder {
|
||||
val start = spannableBuilder.length
|
||||
spannableBuilder.append(text)
|
||||
val end = spannableBuilder.length
|
||||
|
||||
spans.forEach { span ->
|
||||
spannableBuilder.setSpan(
|
||||
span,
|
||||
start,
|
||||
end,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成构建并应用到TextView
|
||||
*/
|
||||
fun build() {
|
||||
text = spannableBuilder
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user