分析onXXX事件监听器中的两个参数position和id

本文详细解析了Android中ListView点击事件的position与id参数的区别及使用方法,通过源码分析了不同Adapter(如SimpleAdapter与CursorAdapter)下两者的具体实现。
 Android API中有部分事件监听器的方法中都会有参数position和id,它们是用来描述一个视图当中的某一项(item,当然你也可以理解成其它的意思)的属性。position描述该项在视图中所处的位置;而id描述的是该项的id,确切点说是该项在视图中所在的行位置。 
        这就造成了一种困惑,我们在实际开发中,到底怎么去理解去这两个参数?我们又该怎么去使用?两者又何区别? 
        下面以ListView中的onItemClickListener事件监听器为例,分析一下两者的区别及其如何使用。 
一、区别 
        貌似有点复杂,分两种场景来讨论: 
        1.ListView用SimpleAdapter展示数据 
        position:表示该项在视图中的位置,起始为0。 
        id:同上。 
        2.ListView用CursorAdapter展示数据 
        position: 同上。 
        id:表示该项在视图中实际位置,说白了就是起始为1。在视图上是第几行,就是第几行。举例:某个ListView共有5行数据,我选中了第3行,那么onItemClickListener()中的id就为3。 
        3.源码分析 
        上面简短的总结了两种场景下的含义,说再多也顶不上来自源码的分析: 
        同样也分两种场景,我们先来分析CursorAdapter场景: 
        是谁给参数position和id赋的值? 
        android.widget.AbsListView里面定义了一个私有内部类,用来处理ListView单击事件,参见下面源码: 
Java代码  收藏代码
  1. private class PerformClick extends WindowRunnnable implements Runnable {  
  2.         View mChild;//子视图  
  3.         int mClickMotionPosition;//单击事件的所在位置(该位置应理解为某集合中的位置。)  
  4.         //执行主方法  
  5.         public void run() {  
  6.             if (mDataChanged) return;  
  7.             //这个是告知Android我们用哪个场景来展示数据。  
  8.             //SimpleAdapter还是CursorAdapter?  
  9.             //阐述策略模式的一个真实场景。  
  10.             final ListAdapter adapter = mAdapter;  
  11.             //这个就是我们经常看到的position。  
  12.             final int motionPosition = mClickMotionPosition;  
  13.             if (adapter != null && mItemCount > 0 &&  
  14.                 motionPosition != INVALID_POSITION &&  
  15.                 motionPosition < adapter.getCount() && sameWindow()) {  
  16.                //positon还是原来的position,但id就不一样了,它是依靠position来计算得出的值。  
  17.                //adapter getItemId(motionPosition)也说明了两种场景下的实现方式也是不一样的。  
  18.                performItemClick(mChild, motionPosition, adapter.getItemId(motionPosition));  
  19.             }  
  20.         }  
  21.     }  

CursorAdapter场景中的id是怎样实现的? 
Java代码  收藏代码
  1. public long getItemId(int position) {  
  2.         if (mDataValid && mCursor != null) {  
  3.             //游标挪到了position的位置上。  
  4.             if (mCursor.moveToPosition(position)) {  
  5.                 //参数mRowIDColumn就是主键ID位置,也就是上文所说的带有”_id”的字段的位置。  
  6.                 return mCursor.getLong(mRowIDColumn);  
  7.             } else {  
  8.                 return 0;  
  9.             }  
  10.         } else {  
  11.             return 0;  
  12.         }  
  13.  }  

        android.database.CursorWindow定义并实现了getLong(),参见下面源码: 
Java代码  收藏代码
  1. public long getLong(int row, int col) {  
  2.         acquireReference();  
  3.         try {  
  4.             // getLong_native是本地方法,底层计算得出视图中我们单击的那个实际行位置  
  5.             //参数col就是上面提到的那个mRowIDColumn。  
  6.             return getLong_native(row - mStartPos, col);  
  7.         } finally {  
  8.             releaseReference();  
  9.         }  
  10.     }  

SimpleAdapter场景中的id是怎样实现的? 
        了解了CursorAdapter场景的id实现方式,那么SimpleAdapter的实现方式就更容易了解了,非常简单,参见下面源码: 
Java代码  收藏代码
  1. public long getItemId(int position) {  
  2.     //返回的就是position      
  3.     return position;  
  4. }  

二、使用方式 
        分两种场景,以代码的形式来展示使用方式,以下均选中第2行: 
        1.SimpleAdapter 
Java代码  收藏代码
  1. //模拟数据,其中_id类似于数据库中的主键,主键名一定要带有”_id”,Android好这口。虽然不是从数据库获取的数据,但最好也要遵从这种习惯。  
  2. ArrayList<HashMap<String, String>> classic = new ArrayList<HashMap<String, String>>();  
  3. HashMap<String, String> englishMap = new HashMap<String, String>();  
  4. englishMap.put(“classic_id”,1l);  
  5. englishMap.put(“name”,lileilei);  
  6. englishMap.put(“gender”,male);  
  7. englishMap.put(“classic _id”,2l);  
  8. englishMap.put(“name”,hanmeimei);  
  9. englishMap.put(“gender”,female);  
  10. englishMap.put(“classic _id”,3l);  
  11. englishMap.put(“name”,poly);  
  12. englishMap.put(“gender”,male);  
  13.   
  14. //创建SimpleAdater  
  15. SimpleAdapter simpleAdapter = new SimpleAdapter(this, data, R.layout.classic,new String[] { " classic _id""name""age" }, new int[] {R.id.id_text, R.id.name_text, R.id.age_text });  
  16.   
  17. //设置监听器  
  18. ListView lv = this.findViewById(R.id.listview);  
  19. lv.setAdapter(simpleAdapter);  
  20. lv.setOnItemClickListener(new OnItemClickListener() {  
  21.     @Override  
  22.     public void onItemClick(AdapterView<?> parent, View view,  
  23.         int position, long id) {  
  24.         Log.i(LOG_TAG, "position:" + position);//输出1  
  25.         Log.i(LOG_TAG, "id:" + id);//输出1  
  26.         Log.i(LOG_TAG, "item class : "+ ((ListView) parent).getItemAtPosition(position).getClass().getSimpleName());//输出item class : HashMap  
  27.       
  28.         //由于上面第三条日志信息输出的类型为HashMap,所以采用HashMap的方式获取数据。  
  29.        HashMap<String, String>  englishMap = (HashMap<String, String>) ((ListView) parent).getItemAtPosition(position);  
  30.        if (englishMap!= null && englishMap.size()> 0) {  
  31.              //做其他操作  
  32.        }  
  33.     }  
  34. });  


2. CursorAdapter 
Java代码  收藏代码
  1. //从数据库中获取数据,同样,返回的Cursor中必须含有”_id”的字段。  
  2. Cursor cursor = .....;//写一个查询数据的方法并返回一个Cursor对象;  
  3. //创建SimpleCursorAdapter  
  4. SimpleCursorAdapter simpleCursorAdapter = new SimpleCursorAdapter(this,R.layout.person, cursor, new String[] { " classic _id ""name""age" },new int[] { R.id.id_text, R.id.name_text, R.id.age_text });  
  5.   
  6. //设置监听器  
  7. ListView lv = this.findViewById(R.id.listview);  
  8. lv.setAdapter(simpleCursorAdapter);  
  9. lv.setOnItemClickListener(newOnItemClickListener() {  
  10.     @Override  
  11.     public void onItemClick(AdapterView<?> parent, View view,int position, long id) {  
  12.         Log.i(LOG_TAG, "position:" + position);//输出1  
  13.         Log.i(LOG_TAG, "id:" + id);//输出2  
  14.         Log.i(LOG_TAG, "item class : "+ ((ListView) parent).getItemAtPosition(position).getClass().getSimpleName());//输出item class : SQLiteCursor  
  15.       
  16.         //由于上面第三条日志信息输出的类型为SQLiteCursor,所以采用Cursor的方式获取数据。  
  17.        Cursor cursor = (Cursor) ((ListView) parent).getItemAtPosition(position);  
  18.        if (cursor != null && cursor.moveToPosition(position)) {  
  19.               //做其他操作  
  20.        }  
  21.     }  
  22. });  


四、总结 
        目前还是要重点使用positon为上策,更复合程序员的编程习惯(下标以0开始);而id存在一定的变数,也许还未领会其实际用途吧,并且不太理解为什么id要是long类型的。 
        onItemClickListener()中的position和id就是这样实现的,那与之相类似的监听器也是如此。毕竟监听器是实际开发中经常用到的,所以弄懂细枝末节还是有必要的。 
import uiBase from "./uiBase.js"; import { Container, Graphics, Text, Assets, Sprite, Texture } from "pixi.js"; import gameData from "../Frame/gameDataMgr.js"; import networkMgr from "../Frame/networkMgr.js"; import { dataPersistenceMgr } from "../Frame/dataPersistenceMgr.js"; import { UIManager } from "../Frame/uiManager.js"; class uiShop extends uiBase { mainContainer; // 主界面容器 overlay; // 背景遮罩 shopBg3Container; // 弹窗容器 shopBgButton; // 按钮 buttonText; // 按钮文字 closeButton; // 叉叉按钮 introduceText; // 介绍背景 isInist = false; isPivotInitialized = false; CONTAINER_WIDTH = 650; CONTAINER_HEIGHT = 400; shopGoodsList = []; _rpLabel = "Rp"; // 记录当前选中的索引金额值 selectedIndex = -1; // 当前选中的金额框索引 selectedAmountValue = 0; // 当前选中的金额数值 selectedStarCount = 0; // 当前赠送的星数 id = 0; // 当前选中的id inputDisplayText; // 显示选中金额的文本 rechargeYes; // 可点击亮色图 rechargeNo; // 不可点击灰色图 depositHistory; // 历史记录按钮 depositHistoryText; // 历史记录文本 // =====银行选择弹窗容器 ===== bankPopupContainer; // 充值支付银行弹窗容器 bankList = []; // 银行列表数据 bankSprites = []; // 银行图标精灵数组 bankShineSprites = []; // 银行图标发光边框数组 selectedBankIndex = -1; // 当前选中的银行索引 selectedBankData = null; // 当前选中的银行数据 bankConfirmYes; // 银行确认按钮亮色 bankConfirmNo; // 银行确认按钮灰色 // =====支付成功展示 ===== paymentSuccessContainer; // 支付成功展示容器 paymentAmountText; // 支付金额文本 paymentConfirmButton; // 支付成功确认按钮 scrollContainer;//创建滚动容器 // =====历史记录弹窗容器 ===== historyListContainer; recordList= []; historyTextObjects = [];// 用于存储每条记录的所有文本对象(因为要每条记录都参与本地化) async init() { super.init(); // 创建主界面容器 this.mainContainer = new Container(); this.mainContainer.position.set(330, 210); this.container.addChild(this.mainContainer); // 预加载资源 await Assets.load([ "assets/UIShop/+.png", "assets/UIShop/复制.png", "assets/UIShop/商店底1_1.png", "assets/UIShop/商店底2.png", "assets/UIShop/商店底3.png", "assets/UIShop/赠送底.png", "assets/UIShop/赠送金额底.png", "assets/UIShop/UI1_02.png", "assets/UIShop/UI1_03.png", "assets/UIShop/UI1_05.png", "assets/UIShop/UI1_07.png", "assets/UIShop/UI1_09_2.png", "assets/UIShop/UI1_09.png", "assets/UIShop/UI1_11.png", "assets/UIShop/UI1_14.png", "assets/UIShop/UI1_17.png", "assets/UIShop/UI1_22.png", "assets/UIShop/商店底4.png", "assets/UIShop/UI8_6.png", "assets/UIShop/WithdrawalBankIcon_DANA.png", "assets/UIShop/WithdrawalBankIcon_OVO.png", "assets/UIShop/WithdrawalBankIcon_QRIS.png", "assets/UIShop/WithdrawalBankIcon_BNI.png", "assets/UIShop/WithdrawalBankIcon_BRI.png", "assets/UIShop/WithdrawalBankIcon_ALLIANCE.png", "assets/UIShop/WithdrawalBankIcon_BCA.png", "assets/UIShop/WithdrawalBankIcon_BIMB.png", "assets/UIShop/商店底5.png", "assets/UIShop/复制.png", ]); // 获取商店数据 // await Promise.all([ // await this.SendShopData(), // ]); await this.SendShopData(); // === 主界面内容 === // 创建黑幕 const app = this.app; const blackMask = new Graphics(); blackMask.beginFill(0x000000, 0.6); const width = window.innerWidth; const height = window.innerHeight; const widthRatio = app.screen.width / 896; const heightRatio = app.screen.height / 414; const offsetX = -450 * widthRatio; const offsetY = -220 * heightRatio; blackMask.drawRect(offsetX, offsetY, width, height); blackMask.endFill(); this.mainContainer.addChild(blackMask); const shopBg1 = new Sprite(Texture.from("assets/UIShop/商店底2.png")); shopBg1.width = 650; shopBg1.height = 55; shopBg1.position.set(-330, -184); const shopBg2 = new Sprite(Texture.from("assets/UIShop/商店底1_1.png")); shopBg2.width = 650; shopBg2.height = 390; shopBg2.position.set(-330, -210); this.closeButton = new Sprite(Texture.from("assets/UIShop/UI1_03.png"));//主界面叉叉按钮 this.closeButton.width = 50; this.closeButton.height = 50; this.closeButton.anchor.set(1, 0); this.closeButton.position.set(335 - 20, -190 + 20); // 右上偏移 this.closeButton.eventMode = "static"; this.closeButton.cursor = "pointer"; this.closeButton.on("pointerdown", () => this.hide()); // 主界面装饰文本 this.depositText = Object.assign( new Text("存款deposit", {fontFamily: "Arial",fontSize: 35,fill: 0xffff84,align: "center", stroke: "#FFA500", strokeThickness: 2, fontWeight: "bold", }), { anchor: { x: 0.5, y: 0 }, position: { x: -25, y: -170 } } ); this.topUpAmountText = Object.assign( new Text("haha", {fontFamily: "Arial",fontSize: 14,fill: 0xcf9f6c,align: "center",fontWeight: "bold", }), { anchor: { x: 0.5, y: 0 }, position: { x: -110, y: -100 } } ); this.inputAmountText = Object.assign( new Text("haha", {fontFamily: "Arial",fontSize: 14.5,fill: 0xd37744,align: "center", }), { anchor: { x: 0.5, y: 0 }, position: { x: -90, y: -70 } } ); const inputBox = new Sprite(Texture.from("assets/UIShop/UI1_02.png")); // 输入框图 inputBox.width = 275; inputBox.height *= this.app.rat; inputBox.anchor.set(0.5, 0.5); inputBox.position.set(-80, -60); // 显示选中金额的文本 this.inputDisplayText = new Text("", { fontSize: 18, fill: 0xb18b5b, fontFamily: "Arial", fontWeight: "bold", }); this.inputDisplayText.anchor.set(0.5); this.inputDisplayText.position.set(-80, -60); this.inputDisplayText.visible = false; // 初始不显示 const clearInput = new Sprite(Texture.from("assets/UIShop/UI1_17.png")); // 输入框里的叉叉 clearInput.width *= this.app.rat; clearInput.height *= this.app.rat; clearInput.anchor.set(0.5, 0.5); clearInput.position.set(40, -60); clearInput.eventMode = "static"; clearInput.cursor = "pointer"; // 绑定叉叉点击事件:清除输入框中的金额 clearInput.on("pointerdown", () => { if (this.inputDisplayText.visible) { this.clearSelection(); // 清除状态 } }); const addPicture = new Sprite(Texture.from("assets/UIShop/+.png")); // 加号图 addPicture.width *= this.app.rat; addPicture.height *= this.app.rat; addPicture.anchor.set(0.5, 0.5); addPicture.position.set(80, -60); const givePicture = new Sprite(Texture.from("assets/UIShop/赠送金额底.png")); // 赠送金额底 givePicture.width *= this.app.rat; givePicture.height *= this.app.rat; givePicture.anchor.set(0.5, 0.5); givePicture.position.set(180, -60); const givePicture2 = new Sprite(Texture.from("assets/UIShop/赠送底.png")); // 赠送底 givePicture2.width *= this.app.rat; givePicture2.height *= this.app.rat; givePicture2.anchor.set(0.5, 0.5); givePicture2.position.set(180, -60); this.freeAmountText = Object.assign( new Text("haha", { fontFamily: "Arial", fontSize: 14, fill: 0xcf9f6c, align: "center", fontWeight: "bold", }), { anchor: { x: 0.5, y: 0.5 }, position: { x: 180, y: -80 } } ); this.rpText = Object.assign( new Text("haha", { fontFamily: "Arial", fontSize: 25, fill: 0xffffff, align: "center", fontWeight: "bold", }), { anchor: { x: 0.5, y: 0.5 }, position: { x: 185, y: -50 } } ); this.selectAmountText = Object.assign( new Text("haha", { fontFamily: "Arial", fontSize: 18, fill: 0xcf9f6c, align: "center", fontWeight: "bold", }), { anchor: { x: 0.5, y: 0.5 }, position: { x: -10, y: -20 } } ); //添加到舞台 画面上 this.mainContainer.addChild(shopBg2,shopBg1,this.closeButton,inputBox,clearInput,addPicture,givePicture,givePicture2); this.mainContainer.addChild(this.depositText, this.topUpAmountText,this.inputAmountText,this.freeAmountText,this.rpText,this.selectAmountText,this.inputDisplayText ); console.log("看一下有没有赋值进去~~~", this.shopGoodsList); // 创建金额框图8个、小红角星8张、发光边框8个 this.amountSprites = []; // 存储所有金额图的数组 this.starSprites = []; // 存储所有五角星的数组 this.shineSprites = []; // 存储所有发光边框的数组 this.selectedIndex = -1; // 当前选中的索引,-1表示没有选中 // === 创建滚动容器 === this.scrollContainer = new Container(); this.scrollContainer.x = 0; this.scrollContainer.y = 0; this.mainContainer.addChild(this.scrollContainer); // === 设置滚动参数 === const visibleRows = 2; // 显示2排 const colsPerRow = 3; // 每排3个 const totalRows = Math.ceil(this.shopGoodsList.length / colsPerRow); // 总排数 const scrollableHeight = (totalRows - visibleRows) * 60; // 可滚动的高度 // === 设置遮罩,限制显示区域 === const mask = new Graphics(); mask.beginFill(0xFFFFFF); mask.drawRect(-280, 0, 560, 120); mask.endFill(); this.scrollContainer.mask = mask; this.mainContainer.addChild(mask); // 调整金额框的间距参数 const startX = -120; // 起始X坐标 const startY = 30; // 起始Y坐标 const spacingX = 100; // X轴间距(左右间隔) const spacingY = 60; // Y轴间距(上下间隔) for (let i = 0; i < this.shopGoodsList.length; i++) { // 1. 创建发光边框图 const shine = new Sprite(Texture.from("assets/UIShop/UI1_11.png")); shine.width *= this.app.rat; shine.height *= this.app.rat; shine.anchor.set(0.5, 0.5); shine.visible = false; // 初始隐藏,只有选中时才显示 // 2. 创建金额框图 const amount = new Sprite(Texture.from("assets/UIShop/UI1_22.png")); amount.width *= this.app.rat; amount.height *= this.app.rat; amount.anchor.set(0.5, 0.5); amount.interactive = true; // 启用点击交互 amount.buttonMode = true; // 鼠标移到上面变成手型 // 3. 创建小红角星图 const star = new Sprite(Texture.from("assets/UIShop/UI1_14.png")); star.width *= this.app.rat; star.height *= this.app.rat; star.anchor.set(0.5, 0.5); // 4. 创建文本显示 - 金额 const amountText = new Text("", { fontSize: 15, fill: 0xff7e36, // 橘色 fontFamily: "Arial", }); amountText.anchor.set(0.5, 0.5); amountText.interactive = true; // 启用点击交互 amountText.buttonMode = true; // 鼠标移到上面变成手型 amountText.cursor = "pointer"; // 设置鼠标指针为手型 // 5. 创建文本显示 - 星星 const starText = new Text("", { fontSize: 11, fill: 0xffffff, // 白色 fontWeight: "normal", fontFamily: "Arial, sans-serif", }); starText.anchor.set(0.5, 0.5); starText.interactive = true; // 启用点击交互 starText.buttonMode = true; // 鼠标移到上面变成手型 starText.cursor = "pointer"; // 设置鼠标指针为手型 // 计算位置(每排3个,多排) const row = Math.floor(i / colsPerRow); const col = i % colsPerRow; const xPos = startX + col * spacingX; const yPos = startY + row * spacingY; // 设置所有元素的位置 shine.position.set(xPos, yPos); // 发光边框在底层 amount.position.set(xPos, yPos); // 金额框在中间 star.position.set(xPos + 18, yPos - 22); // 五角星在金额框右上方 amountText.position.set(xPos, yPos + 2); // 金额文本在金额框中心偏下 starText.position.set(xPos + 18, yPos - 25); // 星星数量文本在五角星中心 // shopGoodsList获取数据 let item, extraItem, count, starCount, id; if (this.shopGoodsList && this.shopGoodsList[i]) { item = this.shopGoodsList[i].items[0]; extraItem = this.shopGoodsList[i].extraItems[0]; count = item ? item.count : 0; starCount = extraItem ? extraItem.count : 0; id = this.shopGoodsList[i].id; } else { count = 0; starCount = 0; id = 0; } // 设置金额文本 const label = this._rpLabel || "Rp"; amountText.text = count > 0 ? `${label} ${count}` : "0"; // 只有当starCount大于0时才显示星星图片文本 if (starCount > 0) { starText.text = `+${starCount}`; star.visible = true; starText.visible = true; star.interactive = true; star.buttonMode = true; } else { starText.text = ""; star.visible = false; starText.visible = false; star.interactive = false; star.buttonMode = false; } // 创建点击处理函数 const handleItemClick = () => { if (this.bankPopupContainer && this.bankPopupContainer.visible) { // 如果支付弹窗已显示 return; } // 先隐藏所有发光边框 for (let j = 0; j < this.shineSprites.length; j++) { this.shineSprites[j].visible = false; } // 显示当前点击的发光边框 this.shineSprites[i].visible = true; this.selectedIndex = i; this.selectedAmountValue = count; this.selectedStarCount = starCount; this.id = id; this.inputDisplayText.text = `${label} ${count}`;//// 输入框显示为"Rp" this.inputDisplayText.visible = true; this.inputAmountText.visible = false; // 隐藏提示文字 // rpText 显示为纯数字 this.rpText.text = `${this._rpLabel} ${this.formatNumberWithUnit(starCount)}`; console.log("选中了第", i + 1, "个金额图,金额:", count, "赠送星:", starCount,"id:", id); // 选中后切换为亮色可点击按钮 if (this.rechargeYes && this.rechargeNo) { this.rechargeYes.visible = true; this.rechargeNo.visible = false; this.rechargeYes.eventMode = "static"; this.rechargeYes.cursor = "pointer"; } }; // 给金额图添加点击事件 amount.on("pointerdown", handleItemClick); // 给金额文本添加点击事件 amountText.on("pointerdown", handleItemClick); // 当starCount大于0时,才给星星图片文本添加点击事件 if (starCount > 0) { star.on("pointerdown", handleItemClick); starText.on("pointerdown", handleItemClick); } // 将创建的元素存储到数组 this.amountSprites.push(amount); this.starSprites.push(star); this.shineSprites.push(shine); // 按层级顺序添加到滚动容器(从下到上) this.scrollContainer.addChild(amount); // 最底层:金额图 this.scrollContainer.addChild(shine); // 中间层:发光边框 // 只有当starCount大于0时,才添加星星图片文本到容器 if (starCount > 0) { this.scrollContainer.addChild(star); // 最上层:五角星 this.scrollContainer.addChild(starText); // 星星数量文本 } this.scrollContainer.addChild(amountText); // 金额文本 } // === 添加滚动功能 === if (scrollableHeight > 0) { // 只有需要滚动时才添加滚动功能 let isDragging = false; let lastY = 0; let currentScrollY = 0; // 鼠标/触摸按下事件 this.scrollContainer.interactive = true; this.scrollContainer.on('mousedown', (event) => { isDragging = true; lastY = event.data.global.y; }); this.scrollContainer.on('touchstart', (event) => { isDragging = true; lastY = event.data.global.y; }); // 鼠标/触摸移动事件 this.scrollContainer.on('mousemove', (event) => { if (isDragging) { const deltaY = event.data.global.y - lastY; lastY = event.data.global.y; // 更新滚动位置,限制在可滚动范围内 currentScrollY += deltaY; currentScrollY = Math.max(-scrollableHeight, Math.min(0, currentScrollY)); this.scrollContainer.y = currentScrollY; } }); this.scrollContainer.on('touchmove', (event) => { if (isDragging) { const deltaY = event.data.global.y - lastY; lastY = event.data.global.y; // 更新滚动位置,限制在可滚动范围内 currentScrollY += deltaY; currentScrollY = Math.max(-scrollableHeight, Math.min(0, currentScrollY)); this.scrollContainer.y = currentScrollY; } }); // 鼠标/触摸释放事件 this.scrollContainer.on('mouseup', () => { isDragging = false; }); this.scrollContainer.on('touchend', () => { isDragging = false; }); this.scrollContainer.on('mouseupoutside', () => { isDragging = false; }); this.scrollContainer.on('touchendoutside', () => { isDragging = false; }); // 鼠标滚轮事件 this.scrollContainer.on('wheel', (event) => { const scrollAmount = event.deltaY > 0 ? 30 : -30; // 滚轮滚动量 // 更新滚动位置,限制在可滚动范围内 currentScrollY += scrollAmount; currentScrollY = Math.max(-scrollableHeight, Math.min(0, currentScrollY)); this.scrollContainer.y = currentScrollY; }); } // === 充值按钮:亮色(可点)与灰色(不可点)切换 === const rechargeYes = new Sprite(Texture.from("assets/UIShop/UI1_09.png")); // 可点击亮色图 rechargeYes.width *= this.app.rat; rechargeYes.height *= this.app.rat; rechargeYes.anchor.set(0.5, 0.5); rechargeYes.position.set(-20, 140); rechargeYes.visible = false; // 初始隐藏 rechargeYes.eventMode = "none"; // 初始禁用交互 rechargeYes.cursor = "default"; const rechargeNo = new Sprite(Texture.from("assets/UIShop/UI1_09_2.png")); // 不可点击灰色图 rechargeNo.width *= this.app.rat; rechargeNo.height *= this.app.rat; rechargeNo.anchor.set(0.5, 0.5); rechargeNo.position.set(-20, 140); rechargeNo.visible = true; // 初始显示 rechargeNo.eventMode = "none"; rechargeNo.cursor = "default"; // 创建按钮文字 this.depositText2 = Object.assign( new Text("嘟嘟嘟", { fontFamily: "Arial", fontSize: 20, fill: 0xffffff, align: "center", stroke: "#FFA500", }), { anchor: { x: 0.5, y: 0.5 }, position: { x: -20, y: 135 } } ); // 添加到容器 this.mainContainer.addChild(rechargeYes, rechargeNo, this.depositText2); this.rechargeYes = rechargeYes; this.rechargeNo = rechargeNo; // 绑定点击事件:只有亮色按钮才可点击 rechargeYes.on("pointerdown", async () => { // 如果支付弹窗已显示,则忽略点击 if (this.bankPopupContainer && this.bankPopupContainer.visible) { return; } if (this.selectedIndex !== -1) { console.log("充值发起支付请求,金额:", this.selectedAmountValue, "赠送星数:", this.selectedStarCount,"id:", this.id); // 获取支付银行接口 await this.SendShopBankData(false); // 传入false表示不是初始化调用 } }); // =======点击充值银行弹窗开始========= this.bankPopupContainer = new Container(); this.bankPopupContainer.visible = false; // 初始隐藏 this.bankPopupContainer.zIndex = 100; // 确保置顶 this.bankPopupContainer.position.set(-270, -110); // 居中对齐主界面 // 创建弹窗背景 const bank = new Sprite(Texture.from("assets/UIShop/商店底4.png")); bank.width = 550; bank.height = 320; bank.position.set(0, -40); this.bankPopupContainer.addChild(bank); this.tipText = new Text("请选择支付方式", {fontFamily: "Arial",fontSize: 25,fill: 0xfff1a5,align: "center" }); this.tipText.anchor.set(0.5,0.5); this.tipText.position.set(275, -10); this.bankPopupContainer.addChild(this.tipText); // 创建银行介绍文本 this.bankIntroduceText = new Text("银行介绍", {fontFamily: "Arial",fontSize: 13,fill: 0xd18b5c,align: "center", }); this.bankIntroduceText.anchor.set(0.5, 0.5); this.bankIntroduceText.position.set(260, 190); this.bankPopupContainer.addChild(this.bankIntroduceText); // 创建银行选择区域 this.createBankSelectionArea(); // 创建银行确认按钮 this.createBankConfirmButton(); // 关闭充值银行弹窗按钮 const closeBankBtn = new Sprite(Texture.from("assets/UIShop/UI8_6.png")); closeBankBtn.width *= this.app.rat; closeBankBtn.height *= this.app.rat; closeBankBtn.position.set(510, -22); closeBankBtn.eventMode = "static"; closeBankBtn.cursor = "pointer"; closeBankBtn.on("pointerdown", () => { this.bankPopupContainer.visible = false; if (this.overlay) this.overlay.visible = false;//恢复主界面亮度 if (this.closeButton) {//主界面叉叉按钮可点击 this.closeButton.eventMode = "static"; this.closeButton.cursor = "pointer"; } // 恢复主界面的交互性 this.enableMainContainerInteractivity(true); }); this.bankPopupContainer.addChild(closeBankBtn); this.container.addChild(this.bankPopupContainer); // =======充值弹窗结束========= // =======支付成功展示容器开始========= this.paymentSuccessContainer = new Container(); this.paymentSuccessContainer.visible = false; // 初始隐藏 this.paymentSuccessContainer.zIndex = 200; // 最高层级 this.paymentSuccessContainer.eventMode = "static"; // 启用交互 // 创建全屏透明背景 const successOverlay = new Graphics(); successOverlay.beginFill(0x000000, 0.01); // 透明 successOverlay.drawRect(-375, -667, 750, 1334); successOverlay.endFill(); this.paymentSuccessContainer.addChild(successOverlay); // 创建支付金额文本 this.paymentAmountText = new Text("", { fontFamily: "Arial", fontSize: 60, fill: 0xffff00, // 黄色 align: "center", fontWeight: "bold", stroke: "#000000", strokeThickness: 4 }); this.paymentAmountText.anchor.set(0.5); this.paymentAmountText.position.set(0, 0); this.paymentSuccessContainer.addChild(this.paymentAmountText); // 创建确认按钮 this.createPaymentConfirmButton(); // 点击任意位置隐藏支付成功展示 this.paymentSuccessContainer.on("pointerdown", (event) => { // 检查点击的是否是确认按钮,如果不是则隐藏 if (!event.target || !event.target.parent || event.target.parent !== this.paymentConfirmButton) { this.hidePaymentSuccess(); } }); // 将支付成功容器添加到根容器 this.container.addChild(this.paymentSuccessContainer); // =======支付成功展示容器结束========= this.depositHistory = new Sprite(Texture.from("assets/UIShop/UI1_05.png")); //历史记录图按钮 this.depositHistory.width *= this.app.rat; this.depositHistory.height *= this.app.rat; this.depositHistory.anchor.set(0.5, 0.5); this.depositHistory.position.set(220, 140); this.depositHistory.interactive = true; this.depositHistory.buttonMode = true; this.mainContainer.addChild(this.depositHistory); this.depositHistory.on('pointerdown', async () => { // 添加点击事件监听器 // 如果支付弹窗已显示,忽略点击 if (this.bankPopupContainer && this.bankPopupContainer.visible) { return; } console.log('depositHistory 被点击了'); await this.SendHistoryData(); }); const threeStripes = new Sprite(Texture.from("assets/UIShop/UI1_07.png")); //三条杠图(历史) threeStripes.width *= this.app.rat; threeStripes.height *= this.app.rat; threeStripes.anchor.set(0.5, 0.5); threeStripes.position.set(180, 140); this.depositHistoryText = Object.assign(//提示历史文字(主界面点击的历史) new Text("haha", { fontFamily: "Arial", fontSize: 12, fill: 0xd87910, align: "center" }), { anchor: { x: 0.5, y: 0.5 }, position: { x: 235, y: 139 } } ); this.mainContainer.addChild(threeStripes, this.depositHistoryText); // === 历史记录弹窗开始=== this.historyListContainer = new Container(); this.historyListContainer.visible = false; // 默认隐藏 this.historyListContainer.zIndex = 100; this.historyListContainer.position.set(-270, -110); // 居中对齐主界面 // 背景图 const recordBg = new Sprite(Texture.from("assets/UIShop/商店底5.png")); recordBg.width *= this.app.rat; recordBg.height *= this.app.rat; recordBg.anchor.set(0.5, 0.5); recordBg.position.set(260, 125); this.historyText = Object.assign( new Text("haha", { fontFamily: "Arial", fontSize: 23, fill: 0xfff1a5, align: "center" }), { anchor: { x: 0.5, y: 0.5 }, position: { x: 250, y: -44 } } ); this.historyListContainer.addChild(recordBg, this.historyText ); // 关闭按钮 const closeHistory = new Sprite(Texture.from("assets/UIShop/UI1_03.png")); closeHistory.width *= this.app.rat; closeHistory.height *= this.app.rat; closeHistory.position.set(500, -30); closeHistory.eventMode = "static"; closeHistory.cursor = "pointer"; closeHistory.on("pointerdown", () => { this.historyListContainer.visible = false; if (this.overlay) this.overlay.visible = false; this.enableMainContainerInteractivity(true); // 恢复主界面操作 }); this.historyListContainer.addChild(closeHistory); this.container.addChild(this.historyListContainer); // === 历史记录弹窗结束 === // === 弹窗与阴影遮罩 === this.overlay = new Graphics(); this.overlay.beginFill(0x000000, 0.6); // this.overlay.drawRect(-450, -220, app.screen.width, app.screen.height); const width2 = window.innerWidth; const height2 = window.innerHeight; const widthRatio2 = app.screen.width / 896; const heightRatio2 = app.screen.height / 414; const offsetX2 = -450 * widthRatio; const offsetY2 = -220 * heightRatio; this.overlay.drawRect(offsetX2, offsetY2, width2, height2); this.overlay.endFill(); this.overlay.visible = true; this.container.addChild(this.overlay); const shopBg3 = new Sprite(Texture.from("assets/UIShop/商店底3.png")); shopBg3.width = 450; shopBg3.height = 300; shopBg3.position.set(0, 0); this.shopBg3Container = new Container(); this.shopBg3Container.position.set(-240, -140); this.shopBg3Container.addChild(shopBg3); this.container.addChild(this.shopBg3Container); this.introduceText = new Text("介绍背景", { fontFamily: "Arial", fontSize: 13, fill: 0xffffff, align: "center", stroke: " #000000", strokeThickness: 0, lineHeight: 25, }); this.introduceText.anchor.set(0.5); this.introduceText.position.set(shopBg3.width / 2 + 48, shopBg3.height / 2 - 10); this.shopBg3Container.addChild(this.introduceText); this.shopBgButton = new Sprite(Texture.from("assets/UIShop/UI1_09.png")); this.shopBgButton.width = 210; this.shopBgButton.height = 60; this.shopBgButton.position.set(-80, 100); this.shopBgButton.eventMode = "static"; this.shopBgButton.cursor = "pointer"; this.buttonText = new Text("哈咯", { fontSize: 28, fill: 0xffffff, align: "center", stroke: "#000000", strokeThickness: 2, }); this.buttonText.anchor.set(0.5); this.buttonText.position.set(this.shopBgButton.width / 2, this.shopBgButton.height / 2); this.shopBgButton.addChild(this.buttonText); this.shopBgButton.on("pointerdown", () => { // 点击按钮后隐藏弹窗 if (this.shopBg3Container) this.shopBg3Container.visible = false; if (this.shopBgButton) this.shopBgButton.visible = false; if (this.overlay) this.overlay.visible = false; if (this.closeButton) { this.closeButton.eventMode = "static"; this.closeButton.cursor = "pointer"; } this.enableMainContainerInteractivity(true); }); this.container.addChild(this.shopBgButton); // 只设置一次 pivot:围绕内容中心缩放 if (!this.isPivotInitialized) { this.mainContainer.pivot.set(this.CONTAINER_WIDTH / 2, this.CONTAINER_HEIGHT / 2); this.isPivotInitialized = true; } // 初始化状态 this.resetState(); this.localize(); this.isInist = true; } // 创建银行选择区域 createBankSelectionArea() { // 清除现有的银行选项 this.bankSprites.forEach(sprite => sprite.destroy()); this.bankShineSprites.forEach(sprite => sprite.destroy()); this.bankSprites = []; this.bankShineSprites = []; // 银行选项布局参数 - 每排5个 const startX = 100; const startY = 100; const spacingX = 90; // 左右间隔 const rowHeight = 70; // 行高 const maxPerRow = 4; // 每排显示的数量 // 定义 bankCode 到纹理路径的映射表 const bankIconMap = { "DANA": "assets/UIShop/WithdrawalBankIcon_DANA.png", "OVO": "assets/UIShop/WithdrawalBankIcon_OVO.png", "QRIS": "assets/UIShop/WithdrawalBankIcon_QRIS.png", "BNI": "assets/UIShop/WithdrawalBankIcon_BNI.png", "BRI": "assets/UIShop/WithdrawalBankIcon_BRI.png" }; // === 创建银行选择的滚动容器 === this.bankScrollContainer = new Container(); this.bankScrollContainer.x = 0; this.bankScrollContainer.y = 0; this.bankPopupContainer.addChild(this.bankScrollContainer); // === 设置滚动参数 === const visibleRows = 1; // 只显示1排 const totalRows = Math.ceil(this.bankList.length / maxPerRow); // 总排数 const scrollableHeight = (totalRows - visibleRows) * rowHeight; // 可滚动的高度 // === 设置遮罩,限制显示区域 === const bankMask = new Graphics(); bankMask.beginFill(0xFFFFFF); // 遮罩高度只显示一排,宽度足够显示5个图标 bankMask.drawRect(50, 50, 450, rowHeight); bankMask.endFill(); this.bankScrollContainer.mask = bankMask; this.bankPopupContainer.addChild(bankMask); for (let i = 0; i < this.bankList.length; i++) { const bankData = this.bankList[i]; // 获取当前银行的 bankCode const bankCode = bankData.bankCode; const iconPath = bankIconMap[bankCode]; if (!iconPath) { continue; } // 1. 创建发光边框 const bankShine = new Sprite(Texture.from("assets/UIShop/UI1_11.png")); bankShine.width *= this.app.rat; bankShine.height *= this.app.rat; bankShine.anchor.set(0.5, 0.5); bankShine.visible = false; // 2. 创建按钮背景 const bankBgSprite = new Sprite(Texture.from("assets/UIShop/UI1_22.png")); bankBgSprite.width *= this.app.rat; bankBgSprite.height *= this.app.rat; bankBgSprite.anchor.set(0.5, 0.5); bankBgSprite.interactive = true; bankBgSprite.buttonMode = true; // 3. 创建银行Logo图标 const bankLogo = new Sprite(Texture.from(iconPath)); bankLogo.width = bankBgSprite.width * 1; bankLogo.height = bankBgSprite.height * 1; bankLogo.anchor.set(0.5, 0.5); // 将Logo居中放在背景上 bankLogo.position.set(0, 0); bankBgSprite.addChild(bankLogo); // 计算位置 - 每排个,多排布局 const row = Math.floor(i / maxPerRow); const col = i % maxPerRow; const xPos = startX + col * spacingX; const yPos = startY + row * rowHeight; bankShine.position.set(xPos, yPos); bankBgSprite.position.set(xPos, yPos); // 添加点击事件 bankBgSprite.on("pointerdown", () => { // 隐藏所有发光边框 this.bankShineSprites.forEach(shine => { shine.visible = false; }); // 显示当前选中的发光边框 bankShine.visible = true; this.selectedBankIndex = i; this.selectedBankData = bankData; this.updateBankConfirmButton(); console.log("选中银行:", { bankId: bankData.bankId,id: bankData.id,bankCode: bankData.bankCode,payoutLimit: bankData.payoutLimit }); }); // 存储引用(用于后续控制显示/隐藏) this.bankSprites.push(bankBgSprite); this.bankShineSprites.push(bankShine); // 添加到滚动容器 this.bankScrollContainer.addChild(bankBgSprite, bankShine); } // === 添加滚动功能 === if (totalRows > 1) {// 只有当有多排时才启用滚动 let isDragging = false; let lastY = 0; let currentScrollY = 0; // 鼠标/触摸按下事件 this.bankScrollContainer.interactive = true; this.bankScrollContainer.on('mousedown', (event) => { isDragging = true; lastY = event.data.global.y; }); this.bankScrollContainer.on('touchstart', (event) => { isDragging = true; lastY = event.data.global.y; }); // 鼠标/触摸移动事件 this.bankScrollContainer.on('mousemove', (event) => { if (isDragging) { const deltaY = event.data.global.y - lastY; lastY = event.data.global.y; // 更新滚动位置,限制在可滚动范围内 currentScrollY += deltaY; currentScrollY = Math.max(-scrollableHeight, Math.min(0, currentScrollY)); this.bankScrollContainer.y = currentScrollY; } }); this.bankScrollContainer.on('touchmove', (event) => { if (isDragging) { const deltaY = event.data.global.y - lastY; lastY = event.data.global.y; // 更新滚动位置,限制在可滚动范围内 currentScrollY += deltaY; currentScrollY = Math.max(-scrollableHeight, Math.min(0, currentScrollY)); this.bankScrollContainer.y = currentScrollY; } }); // 鼠标/触摸释放事件 this.bankScrollContainer.on('mouseup', () => { isDragging = false; }); this.bankScrollContainer.on('touchend', () => { isDragging = false; }); this.bankScrollContainer.on('mouseupoutside', () => { isDragging = false; }); this.bankScrollContainer.on('touchendoutside', () => { isDragging = false; }); // 鼠标滚轮事件 this.bankScrollContainer.on('wheel', (event) => { const scrollAmount = event.deltaY > 0 ? 15 : -15; // 更新滚动位置,限制在可滚动范围内 currentScrollY += scrollAmount; currentScrollY = Math.max(-scrollableHeight, Math.min(0, currentScrollY)); this.bankScrollContainer.y = currentScrollY; }); } } // 创建银行确认按钮 createBankConfirmButton() { // 银行确认按钮:亮色(可点)与灰色(不可点)切换 this.bankConfirmYes = new Sprite(Texture.from("assets/UIShop/UI1_09.png")); // 可点击亮色图 this.bankConfirmYes.width *= this.app.rat; this.bankConfirmYes.height *= this.app.rat; this.bankConfirmYes.anchor.set(0.5, 0.5); this.bankConfirmYes.position.set(275, 240); this.bankConfirmYes.visible = false; // 初始隐藏 this.bankConfirmYes.eventMode = "none"; // 初始禁用交互 this.bankConfirmYes.cursor = "default"; this.bankConfirmNo = new Sprite(Texture.from("assets/UIShop/UI1_09_2.png")); // 不可点击灰色图 this.bankConfirmNo.width *= this.app.rat; this.bankConfirmNo.height *= this.app.rat; this.bankConfirmNo.anchor.set(0.5, 0.5); this.bankConfirmNo.position.set(275, 240); this.bankConfirmNo.visible = true; // 初始显示 this.bankConfirmNo.eventMode = "none"; this.bankConfirmNo.cursor = "default"; // 创建按钮文字 - 为两个按钮分别创建文本 this.confirmTextYes = new Text("确认", { fontFamily: "Arial", fontSize: 40, fill: 0xffffff, align: "center", stroke: "#FFA500", }); this.confirmTextYes.anchor.set(0.5, 0.5); this.confirmTextYes.position.set(0, -5); this.confirmTextNo = new Text("确认", { fontFamily: "Arial", fontSize: 40, fill: 0xffffff, align: "center", stroke: "#FFA500", }); this.confirmTextNo.anchor.set(0.5, 0.5); this.confirmTextNo.position.set(0, -5); // 添加到按钮 this.bankConfirmYes.addChild(this.confirmTextYes); this.bankConfirmNo.addChild(this.confirmTextNo); // 添加到容器 this.bankPopupContainer.addChild(this.bankConfirmYes, this.bankConfirmNo); // 绑定点击事件:只有亮色按钮才可点击 this.bankConfirmYes.on("pointerdown", async () => { if (this.selectedBankIndex !== -1 && this.selectedBankData) { console.log("确认支付,银行信息:", this.selectedBankData); // 调用支付接口 await this.SendPaymentRequest(); } }); } // 创建支付成功确认按钮 createPaymentConfirmButton() { // 创建确认按钮 this.paymentConfirmButton = new Sprite(Texture.from("assets/UIShop/UI1_09.png")); this.paymentConfirmButton.width = 200; this.paymentConfirmButton.height= 80; this.paymentConfirmButton.anchor.set(0.5, 0.5); this.paymentConfirmButton.position.set(5, 150); this.paymentConfirmButton.eventMode = "static"; this.paymentConfirmButton.cursor = "pointer"; // 创建按钮文字 this.confirmText = new Text("这是Confirm", { fontFamily: "Arial", fontSize: 30, fill: 0xffffff, align: "center", stroke: "#FFA500", }); this.confirmText.anchor.set(0.5, 0.5); this.confirmText.position.set(0, 0); this.paymentConfirmButton.addChild(this.confirmText); // 绑定点击事件 this.paymentConfirmButton.on("pointerdown", () => { this.hidePaymentSuccess(); }); // 添加到支付成功容器 this.paymentSuccessContainer.addChild(this.paymentConfirmButton); } // 更新银行确认按钮状态 updateBankConfirmButton() { if (this.selectedBankIndex !== -1) { // 有选中银行,显示亮色按钮 this.bankConfirmYes.visible = true; this.bankConfirmNo.visible = false; this.bankConfirmYes.eventMode = "static"; this.bankConfirmYes.cursor = "pointer"; } else { // 未选中银行,显示灰色按钮 this.bankConfirmYes.visible = false; this.bankConfirmNo.visible = true; this.bankConfirmYes.eventMode = "none"; this.bankConfirmYes.cursor = "default"; } } // 显示支付成功动画 showPaymentSuccess(amount) { // 隐藏银行弹窗遮罩 this.bankPopupContainer.visible = false; this.overlay.visible = false; // 恢复主界面交互 this.enableMainContainerInteractivity(true); // 设置支付金额文本 显示数字 this.paymentAmountText.text = amount.toString(); // 显示支付成功容器 this.paymentSuccessContainer.visible = true; // 动画效果:透明到不透明,再放大 this.paymentAmountText.alpha = 0; this.paymentAmountText.scale.set(0.5); // 动画序列 const animate = () => { // 渐显 if (this.paymentAmountText.alpha < 1) { this.paymentAmountText.alpha += 0.05; requestAnimationFrame(animate); return; } // 放大 if (this.paymentAmountText.scale.x < 1.2) { this.paymentAmountText.scale.x += 0.02; this.paymentAmountText.scale.y += 0.02; requestAnimationFrame(animate); return; } // 缩小回正常 if (this.paymentAmountText.scale.x > 1) { this.paymentAmountText.scale.x -= 0.01; this.paymentAmountText.scale.y -= 0.01; requestAnimationFrame(animate); } }; // 开始动画 animate(); } // 隐藏支付成功展示 hidePaymentSuccess() { this.paymentSuccessContainer.visible = false; } // 启用或禁用主界面的交互性 enableMainContainerInteractivity(enable) { // 设置主容器内所有可交互元素的交互性 this.closeButton.eventMode = enable ? "static" : "none"; this.closeButton.cursor = enable ? "pointer" : "default"; // 设置金额框的交互性 for (const amount of this.amountSprites) { amount.eventMode = enable ? "static" : "none"; amount.cursor = enable ? "pointer" : "default"; } // 设置充值按钮的交互性 if (this.rechargeYes) { this.rechargeYes.eventMode = enable && this.selectedIndex !== -1 ? "static" : "none"; this.rechargeYes.cursor = enable && this.selectedIndex !== -1 ? "pointer" : "default"; } // 设置历史记录按钮的交互性 if (this.depositHistory) { this.depositHistory.eventMode = enable ? "static" : "none"; this.depositHistory.cursor = enable ? "pointer" : "default"; } // 设置清除输入按钮的交互性 const clearInput = this.mainContainer.children.find(child => child.texture && child.texture.textureCacheIds && child.texture.textureCacheIds.includes("assets/UIShop/UI1_17.png") ); if (clearInput) { clearInput.eventMode = enable ? "static" : "none"; clearInput.cursor = enable ? "pointer" : "default"; } } // 清除选中状态 clearSelection() { this.inputDisplayText.visible = false; this.inputAmountText.visible = true; // 恢复提示文字 this.selectedIndex = -1; this.selectedAmountValue = 0; this.selectedStarCount = 0; this.rpText.text = `${this._rpLabel} 0`; // 恢复 rpText 为默认状态 for (const shine of this.shineSprites) { // 隐藏所有发光边框 shine.visible = false; } // 清除选择时切回灰色按钮 if (this.rechargeYes && this.rechargeNo) { this.rechargeYes.visible = false; this.rechargeNo.visible = true; this.rechargeYes.eventMode = "none"; this.rechargeYes.cursor = "default"; } } async show() { console.log(this.constructor.name + " 1"); if (!this.isInist) { await this.init(); } else { this.resetState(); } console.log(this.constructor.name + " 2"); this.container.visible = true; UIManager.hideLoading(); // 开启动画 this.animateOpen(); } // 动画从中间向两边展开 animateOpen() { this.mainContainer.scale.set(0, 1); const duration = 500; const startTime = performance.now(); const tick = (currentTime) => { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); const easeProgress = 1 - Math.pow(1 - progress, 2); // 缓动效果 this.mainContainer.scale.x = easeProgress; if (progress < 1) { requestAnimationFrame(tick); } }; requestAnimationFrame(tick); } resetState() { if (this.shopBg3Container) this.shopBg3Container.visible = true; if (this.shopBgButton) this.shopBgButton.visible = true; if (this.overlay) this.overlay.visible = true; if (this.mainContainer) { this.mainContainer.alpha = 1; this.mainContainer.scale.set(1); } if (this.closeButton) { this.closeButton.eventMode = "none"; this.closeButton.cursor = "default"; } // 重置选择状态 this.clearSelection(); this.enableMainContainerInteractivity(false); } hide() { this.container.visible = false; } localize() { this.uiname="uiShop"; this.buttonText.text = super.getLocalWords("Confirmation"); this.introduceText.text = super.getLocalWords("introduce"); this.depositText.text = super.getLocalWords("deposit"); this.depositText2.text = super.getLocalWords("deposit"); this.topUpAmountText.text = super.getLocalWords("topUpAmount"); this.inputAmountText.text = super.getLocalWords("inputAmount"); this.freeAmountText.text = super.getLocalWords("freeAmount"); this._rpLabel = super.getLocalWords("rp") || "Rp"; this.rpText.text = `${this._rpLabel} 0`; // 默认状态 this.selectAmountText.text = super.getLocalWords("selectAmount"); this.inputAmountText.visible = true; this.depositHistoryText.text = super.getLocalWords("depositHistory"); this.tipText.text = super.getLocalWords("tip"); this.confirmText.text = super.getLocalWords("confirm"); this.confirmTextYes.text =super.getLocalWords("confirmTextYes"); this.confirmTextNo.text =super.getLocalWords("confirmTextNo"); this.bankIntroduceText.text = super.getLocalWords("bankIntroduceText"); // 历史记录本地化 this.historyText.text = super.getLocalWords("depositHistory"); if (this.historyTextObjects && this.historyTextObjects.length > 0) { this.historyTextObjects.forEach(recordTexts => { if (recordTexts.amountLabel && recordTexts.amountLabel.localKey) { recordTexts.amountLabel.text = super.getLocalWords(recordTexts.amountLabel.localKey); } if (recordTexts.orderLabel && recordTexts.orderLabel.localKey) { recordTexts.orderLabel.text = super.getLocalWords(recordTexts.orderLabel.localKey); } if (recordTexts.timeLabel && recordTexts.timeLabel.localKey) { recordTexts.timeLabel.text = super.getLocalWords(recordTexts.timeLabel.localKey); } if (recordTexts.statusLabel && recordTexts.statusLabel.localKey) { recordTexts.statusLabel.text = super.getLocalWords(recordTexts.statusLabel.localKey); } if (recordTexts.statusFinished && recordTexts.statusFinished.localKey) { recordTexts.statusFinished.text = super.getLocalWords(recordTexts.statusFinished.localKey); } if (recordTexts.statusProcessing && recordTexts.statusProcessing.localKey) { recordTexts.statusProcessing.text = super.getLocalWords(recordTexts.statusProcessing.localKey); } if (recordTexts.statusWaiting && recordTexts.statusWaiting.localKey) { recordTexts.statusWaiting.text = super.getLocalWords(recordTexts.statusWaiting.localKey); } }); } // 无数据提示 if (this.noDataText) { this.noDataText.text = super.getLocalWords("oDataText"); } } formatNumberWithUnit(value) { if (value == null || isNaN(value)) return "0"; let formatted; if (value >= 1_000_000_000_000) { formatted = (value / 1_000_000_000_000).toFixed(2).replace(/\.00$/, ''); return `${formatted}T`; } else if (value >= 1_000_000_000) { formatted = (value / 1_000_000_000).toFixed(2).replace(/\.00$/, ''); return `${formatted}B`; } else if (value >= 1_000_000) { formatted = (value / 1_000_000).toFixed(2).replace(/\.00$/, ''); return `${formatted}M`; } else if (value >= 1_000) { formatted = (value / 1_000).toFixed(2).replace(/\.00$/, ''); return `${formatted}K`; } else { return String(value); } } // 接口请求:获取商店商品列表 async SendShopData() { const requestData = { token: gameData.gameToken, playerIndex: gameData.playerDto.playerIndex, }; console.log("发送请求参数:", requestData); const response = await networkMgr.postMessage("getShopGoods", requestData); if (!response) { throw new Error("接口返回空数据"); } this.shopGoodsList = response.shopGoodsList || response["shopGoodsList"] || []; console.log("获取商店数据:", this.shopGoodsList); if (this.isInist) { this.localize(); } } // 获取支付银行接口 async SendShopBankData(isInit = false) { try { // 如果是初始化调用,使用第一个商品的ID;否则使用当前选中的ID const goodsId = isInit && this.shopGoodsList.length > 0 ? this.shopGoodsList[0].id : this.id; const requestData = { token: gameData.gameToken, playerIndex: gameData.playerDto.playerIndex, goodsId: goodsId, }; console.log("发送请求参数:", requestData); const response = await networkMgr.postMessage("getPaymentBank", requestData); if (!response) { throw new Error("接口返回空数据"); } this.bankList = response.bankList || response["bankList"] || []; console.log("获取支付银行数据:", this.bankList); this.createBankSelectionArea(); // 如果不是初始化调用,显示弹窗 if (!isInit) { this.bankPopupContainer.visible = true; this.overlay.visible = true; if (this.closeButton) { this.closeButton.eventMode = "none"; this.closeButton.cursor = "default"; } // 重置银行选择状态 this.selectedBankIndex = -1; this.selectedBankData = null; for (const shine of this.bankShineSprites) { shine.visible = false; } // 更新确认按钮状态 this.updateBankConfirmButton(); this.enableMainContainerInteractivity(false); } } catch (error) { console.error("获取银行数据失败:", error); this.bankList = []; } } // 发送支付请求 async SendPaymentRequest() { if (!this.selectedBankData || !this.id) { console.error("未选择银行或商品"); return; } try { const requestData = { token: gameData.gameToken, playerIndex: gameData.playerDto.playerIndex, goodsId: this.id, bankId: this.selectedBankData.bankId, bankCode: this.selectedBankData.bankCode }; console.log("发送支付请求参数:", requestData); const response = await networkMgr.postMessage("payment", requestData); if (!response) { throw new Error("支付接口返回空数据"); } console.log("支付接口返回:", response); if (response.result === 0) { console.log("支付成功,金额:", response.amount); // 显示支付成功动画 this.showPaymentSuccess(response.amount); } else { // 支付失败 console.error("支付失败:", response.msg); } } catch (error) { console.error("支付请求失败:", error); } } // 历史记录接口 async SendHistoryData(isInit = false) { try { const requestData = { token: gameData.gameToken, playerIndex: gameData.playerDto.playerIndex, }; console.log("发送历史请求参数:", requestData); const response = await networkMgr.postMessage("getPaymentRecord", requestData); if (!response) { throw new Error("接口返回空数据"); } this.recordList = response.recordList || response["recordList"] || []; console.log("获取历史记录数据:", this.recordList); // 渲染历史记录列表 this.renderHistoryList(); // 如果不是初始化调用,显示弹窗 if (!isInit) { this.historyListContainer.visible = true; this.overlay.visible = true; for (const shine of this.bankShineSprites) { shine.visible = false; } this.enableMainContainerInteractivity(false); } this.localize(); } catch (error) { console.error("获取历史记录失败:", error); this.recordList = []; // 渲染列表 this.renderHistoryList(); if (!isInit) { this.historyListContainer.visible = true; this.overlay.visible = true; this.enableMainContainerInteractivity(false); } } } // 历史记录列表 renderHistoryList() { // 清空之前的记录文本对象 if (this.scrollContent) { this.scrollContent.removeChildren(); } this.historyTextObjects = []; // 配置 const listWidth = 500; // 列表总宽度 const listHeight = 250; // 显示区域高度 const itemHeight = 95; // 每条记录的高度 const contentStartX = 0; // 左边距 const contentStartY = 0; // 上边距 // 重新创建滚动内容容器 this.scrollContent = new Container(); this.scrollContent.position.set(contentStartX, contentStartY); this.historyListContainer.addChild(this.scrollContent); // 如果没有数据,显示提示文字 if (!this.recordList || this.recordList.length === 0) { this.noDataText = new Text("暂无充值记录", { fontSize: 18, fill: 0x999999 }); this.noDataText.anchor.set(0.5); this.noDataText.position.set(listWidth / 2, 100); this.scrollContent.addChild(this.noDataText); return; } // 有数据时遍历生成每一条记录 this.recordList.forEach((record, index) => { const y = index * itemHeight; // 为每条记录创建一个对象来存储所有文本元素 const recordTexts = {}; // 圆角背景框 const bg = new Graphics(); bg.lineStyle(2, 0x7d503a, 0.6); // 边框颜色 bg.beginFill(0x7d503a, 0.3); // 背景色 bg.drawRoundedRect( 10, // x y + 5, // y listWidth - 15, // 宽:左右各留边距 itemHeight - 10, // 高:上下留空 18 // 圆角半径 ); bg.endFill(); this.scrollContent.addChild(bg); // 时间格式化 const date = new Date(record.createDate); const formattedTime = date.toLocaleString('en-US', { month: 'numeric', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit', hour12: true }); // 充值金额标签 recordTexts.amountLabel = new Text("Top up amount", { fontSize: 13, fill: 0xb88e7e }); recordTexts.amountLabel.position.set(20, y + 12); recordTexts.amountLabel.localKey = "amountText"; // 添加本地化标识 // 充值金额值 recordTexts.amountValue = new Text(`${record.amount.toLocaleString()}`, { fontSize: 13, fill: 0xe1ae68 }); recordTexts.amountValue.position.set(180, y + 12); // 订单标签 recordTexts.orderLabel = new Text("Recharge Order", { fontSize: 13, fill: 0xb88e7e }); recordTexts.orderLabel.position.set(20, y + 32); recordTexts.orderLabel.localKey = "rechargeOrderText"; // 订单号 recordTexts.orderId = new Text(record.id, { fontSize: 13, fill: 0xb77760 }); recordTexts.orderId.position.set(180, y + 32); // 时间标签 recordTexts.timeLabel = new Text("Top up time", { fontSize: 13, fill: 0xb88e7e }); recordTexts.timeLabel.position.set(20, y + 52); recordTexts.timeLabel.localKey = "rechargeTimeText"; // 时间值 recordTexts.timeValue = new Text(formattedTime, { fontSize: 13, fill: 0xb77760 }); recordTexts.timeValue.position.set(180, y + 52); // 状态标签 recordTexts.statusLabel = new Text("Order status", { fontSize: 13, fill: 0xb88e7e }); recordTexts.statusLabel.position.set(20, y + 72); recordTexts.statusLabel.localKey = "statusText"; // 状态文本 recordTexts.statusFinished = new Text("已成功", { fontSize: 12, fill: 0x00ff00 }); recordTexts.statusFinished.position.set(180, y + 72); recordTexts.statusFinished.visible = record.status === "1"; recordTexts.statusFinished.localKey = "status_1"; recordTexts.statusProcessing = new Text("在等待处理", { fontSize: 12, fill: 0xffff00 }); recordTexts.statusProcessing.position.set(180, y + 72); recordTexts.statusProcessing.visible = record.status === "0"; recordTexts.statusProcessing.localKey = "status_0"; recordTexts.statusWaiting = new Text("失败", { fontSize: 12, fill: 0xaa0000 }); recordTexts.statusWaiting.position.set(180, y + 72); recordTexts.statusWaiting.visible = !(record.status === "1" || record.status === "0"); recordTexts.statusWaiting.localKey = "status_2"; // 添加到滚动容器 this.scrollContent.addChild( recordTexts.amountLabel, recordTexts.amountValue, recordTexts.orderLabel, recordTexts.orderId, recordTexts.timeLabel, recordTexts.timeValue, recordTexts.statusLabel, recordTexts.statusFinished, recordTexts.statusProcessing, recordTexts.statusWaiting ); // 存储到数组 this.historyTextObjects.push(recordTexts); // 复制图标(点击复制订单号) const copyIcon = new Sprite(Texture.from("assets/UIShop/复制.png")); copyIcon.width = 55 * this.app.rat; copyIcon.height = 55 * this.app.rat; copyIcon.anchor.set(0.5); copyIcon.position.set(listWidth - 40, y + itemHeight / 2); // 靠右居中 copyIcon.eventMode = "static"; copyIcon.cursor = "pointer"; copyIcon.on("pointerdown", (e) => { e.stopPropagation(); navigator.clipboard.writeText(record.id).then(() => { console.log(`已复制订单号: ${record.id}`); copyIcon.tint = 0x00ff00; setTimeout(() => { copyIcon.tint = 0xffffff; }, 300); }).catch(err => { alert("复制失败,请手动选择文本复制"); console.warn("Clipboard error:", err); }); }); this.scrollContent.addChild(copyIcon); }); // 限制可见范围 const clipMask = new Graphics(); clipMask.beginFill(0x000000, 0.01); clipMask.drawRect(0, 0, listWidth, listHeight); clipMask.endFill(); clipMask.position.set(0, 0); this.historyListContainer.addChild(clipMask); this.scrollContent.mask = clipMask; // 添加拖拽滚动功能 this.setupHistoryScroll(); } // 设置历史记录滚动功能 setupHistoryScroll() { const listHeight = 250; // 显示区域高度 const itemHeight = 95; // 每条记录的高度 let isDragging = false; let dragStartY = 0; let scrollStartY = 0; this.app.view.removeEventListener("pointermove", this.handleHistoryPointerMove); this.app.view.removeEventListener("pointerup", this.handleHistoryPointerUp); this.app.view.removeEventListener("pointerupoutside", this.handleHistoryPointerUp); // 创建新的方法引用 this.handleHistoryPointerMove = (e) => { if (!isDragging) return; const dy = e.clientY - dragStartY; let newY = scrollStartY + dy; const maxScrollUp = 0; const totalItemsHeight = this.recordList.length * itemHeight; const maxScrollDown = listHeight - totalItemsHeight; if (newY > maxScrollUp) newY = maxScrollUp; else if (newY < maxScrollDown) newY = maxScrollDown; this.scrollContent.y = newY; }; this.handleHistoryPointerUp = () => { isDragging = false; this.scrollContent.cursor = "grab"; }; // 设置滚动容器的交互 this.scrollContent.eventMode = "static"; this.scrollContent.cursor = "grab"; this.scrollContent.on("pointerdown", (event) => { isDragging = true; dragStartY = event.global.y; scrollStartY = this.scrollContent.y; this.scrollContent.cursor = "grabbing"; event.stopPropagation(); }); // 绑定全局事件 this.app.view.addEventListener("pointermove", this.handleHistoryPointerMove); this.app.view.addEventListener("pointerup", this.handleHistoryPointerUp); this.app.view.addEventListener("pointerupoutside", this.handleHistoryPointerUp); } } export const UIShop = new uiShop(); 需求:我这个文件用了很多这种定死的位置position.set(XXX, XXX);但是我希望这个可以全部不要 ,根据我主界面的商店底1_1.png图片来变化, 因为运行的浏览器尺寸啥的这个会发生变化,所以定死的话可能尺寸不一样就打开就很奇怪了,懂我意思吗,可以帮我看看然后改一下吗,中文解释, 改了全文写下来,注释也不要落,谢谢
10-10
import uiBase from "./uiBase.js"; import { Container, Graphics, Text, Assets, Sprite, Texture } from "pixi.js"; import gameData from "../Frame/gameDataMgr.js"; import networkMgr from "../Frame/networkMgr.js"; import { dataPersistenceMgr } from "../Frame/dataPersistenceMgr.js"; import { UIManager } from "../Frame/uiManager.js"; class uiShop extends uiBase { mainContainer; // 主界面容器 overlay; // 背景遮罩 shopBg3Container; // 弹窗容器 shopBgButton; // 按钮 buttonText; // 按钮文字 closeButton; // 叉叉按钮 introduceText; // 介绍背景 isInist = false; isPivotInitialized = false; CONTAINER_WIDTH = 650; CONTAINER_HEIGHT = 400; shopGoodsList = []; _rpLabel = "Rp"; // 记录当前选中的索引金额值 selectedIndex = -1; // 当前选中的金额框索引 selectedAmountValue = 0; // 当前选中的金额数值 selectedStarCount = 0; // 当前赠送的星数 id = 0; // 当前选中的id inputDisplayText; // 显示选中金额的文本 rechargeYes; // 可点击亮色图 rechargeNo; // 不可点击灰色图 depositHistory; // 历史记录按钮 depositHistoryText; // 历史记录文本 // =====银行选择弹窗容器 ===== bankPopupContainer; // 充值支付银行弹窗容器 bankList = []; // 银行列表数据 bankSprites = []; // 银行图标精灵数组 bankShineSprites = []; // 银行图标发光边框数组 selectedBankIndex = -1; // 当前选中的银行索引 selectedBankData = null; // 当前选中的银行数据 bankConfirmYes; // 银行确认按钮亮色 bankConfirmNo; // 银行确认按钮灰色 // =====支付成功展示 ===== paymentSuccessContainer; // 支付成功展示容器 paymentAmountText; // 支付金额文本 paymentConfirmButton; // 支付成功确认按钮 scrollContainer;//创建滚动容器 // =====历史记录弹窗容器 ===== historyListContainer; recordList= []; async init() { super.init(); // 创建主界面容器 this.mainContainer = new Container(); this.mainContainer.position.set(330, 210); this.container.addChild(this.mainContainer); // 预加载资源 await Assets.load([ "assets/UIShop/+.png", "assets/UIShop/复制.png", "assets/UIShop/商店底1_1.png", "assets/UIShop/商店底2.png", "assets/UIShop/商店底3.png", "assets/UIShop/赠送底.png", "assets/UIShop/赠送金额底.png", "assets/UIShop/UI1_02.png", "assets/UIShop/UI1_03.png", "assets/UIShop/UI1_05.png", "assets/UIShop/UI1_07.png", "assets/UIShop/UI1_09_2.png", "assets/UIShop/UI1_09.png", "assets/UIShop/UI1_11.png", "assets/UIShop/UI1_14.png", "assets/UIShop/UI1_17.png", "assets/UIShop/UI1_22.png", "assets/UIShop/商店底4.png", "assets/UIShop/UI8_6.png", "assets/UIShop/WithdrawalBankIcon_DANA.png", "assets/UIShop/WithdrawalBankIcon_OVO.png", "assets/UIShop/WithdrawalBankIcon_QRIS.png", "assets/UIShop/WithdrawalBankIcon_BNI.png", "assets/UIShop/WithdrawalBankIcon_BRI.png", "assets/UIShop/WithdrawalBankIcon_ALLIANCE.png", "assets/UIShop/WithdrawalBankIcon_BCA.png", "assets/UIShop/WithdrawalBankIcon_BIMB.png", "assets/UIShop/商店底5.png", "assets/UIShop/复制.png", ]); // 获取商店数据银行数据 await Promise.all([ this.SendShopData(), this.SendShopBankData(true), // 传入true表示是初始化调用 this.SendHistoryData(true) ]); // === 主界面内容 === const shopBg1 = new Sprite(Texture.from("assets/UIShop/商店底2.png")); shopBg1.width = 650; shopBg1.height = 55; shopBg1.position.set(-330, -184); const shopBg2 = new Sprite(Texture.from("assets/UIShop/商店底1_1.png")); shopBg2.width = 650; shopBg2.height = 390; shopBg2.position.set(-330, -210); this.closeButton = new Sprite(Texture.from("assets/UIShop/UI1_03.png"));//主界面叉叉按钮 this.closeButton.width = 50; this.closeButton.height = 50; this.closeButton.anchor.set(1, 0); this.closeButton.position.set(335 - 20, -190 + 20); // 右上偏移 this.closeButton.eventMode = "static"; this.closeButton.cursor = "pointer"; this.closeButton.on("pointerdown", () => this.hide()); // 主界面装饰文本 this.depositText = Object.assign( new Text("存款deposit", {fontFamily: "Arial",fontSize: 35,fill: 0xffff84,align: "center", stroke: "#FFA500", strokeThickness: 2, fontWeight: "bold", }), { anchor: { x: 0.5, y: 0 }, position: { x: -25, y: -170 } } ); this.topUpAmountText = Object.assign( new Text("haha", {fontFamily: "Arial",fontSize: 14,fill: 0xcf9f6c,align: "center",fontWeight: "bold", }), { anchor: { x: 0.5, y: 0 }, position: { x: -110, y: -100 } } ); this.inputAmountText = Object.assign( new Text("haha", {fontFamily: "Arial",fontSize: 14.5,fill: 0xd37744,align: "center", }), { anchor: { x: 0.5, y: 0 }, position: { x: -90, y: -70 } } ); const inputBox = new Sprite(Texture.from("assets/UIShop/UI1_02.png")); // 输入框图 inputBox.width = 275; inputBox.height *= this.app.rat; inputBox.anchor.set(0.5, 0.5); inputBox.position.set(-80, -60); // 显示选中金额的文本 this.inputDisplayText = new Text("", { fontSize: 18, fill: 0xb18b5b, fontFamily: "Arial", fontWeight: "bold", }); this.inputDisplayText.anchor.set(0.5); this.inputDisplayText.position.set(-80, -60); this.inputDisplayText.visible = false; // 初始不显示 const clearInput = new Sprite(Texture.from("assets/UIShop/UI1_17.png")); // 输入框里的叉叉 clearInput.width *= this.app.rat; clearInput.height *= this.app.rat; clearInput.anchor.set(0.5, 0.5); clearInput.position.set(40, -60); clearInput.eventMode = "static"; clearInput.cursor = "pointer"; // 绑定叉叉点击事件:清除输入框中的金额 clearInput.on("pointerdown", () => { if (this.inputDisplayText.visible) { this.clearSelection(); // 清除状态 } }); const addPicture = new Sprite(Texture.from("assets/UIShop/+.png")); // 加号图 addPicture.width *= this.app.rat; addPicture.height *= this.app.rat; addPicture.anchor.set(0.5, 0.5); addPicture.position.set(80, -60); const givePicture = new Sprite(Texture.from("assets/UIShop/赠送金额底.png")); // 赠送金额底 givePicture.width *= this.app.rat; givePicture.height *= this.app.rat; givePicture.anchor.set(0.5, 0.5); givePicture.position.set(180, -60); const givePicture2 = new Sprite(Texture.from("assets/UIShop/赠送底.png")); // 赠送底 givePicture2.width *= this.app.rat; givePicture2.height *= this.app.rat; givePicture2.anchor.set(0.5, 0.5); givePicture2.position.set(180, -60); this.freeAmountText = Object.assign( new Text("haha", { fontFamily: "Arial", fontSize: 14, fill: 0xcf9f6c, align: "center", fontWeight: "bold", }), { anchor: { x: 0.5, y: 0.5 }, position: { x: 180, y: -80 } } ); this.rpText = Object.assign( new Text("haha", { fontFamily: "Arial", fontSize: 25, fill: 0xffffff, align: "center", fontWeight: "bold", }), { anchor: { x: 0.5, y: 0.5 }, position: { x: 185, y: -50 } } ); this.selectAmountText = Object.assign( new Text("haha", { fontFamily: "Arial", fontSize: 18, fill: 0xcf9f6c, align: "center", fontWeight: "bold", }), { anchor: { x: 0.5, y: 0.5 }, position: { x: -10, y: -20 } } ); //添加到舞台 画面上 this.mainContainer.addChild(shopBg2,shopBg1,this.closeButton,inputBox,clearInput,addPicture,givePicture,givePicture2); this.mainContainer.addChild(this.depositText, this.topUpAmountText,this.inputAmountText,this.freeAmountText,this.rpText,this.selectAmountText,this.inputDisplayText ); console.log("看一下有没有赋值进去~~~", this.shopGoodsList); // 创建金额框图8个、小红角星8张、发光边框8个 this.amountSprites = []; // 存储所有金额图的数组 this.starSprites = []; // 存储所有五角星的数组 this.shineSprites = []; // 存储所有发光边框的数组 this.selectedIndex = -1; // 当前选中的索引,-1表示没有选中 // === 创建滚动容器 === this.scrollContainer = new Container(); this.scrollContainer.x = 0; this.scrollContainer.y = 0; this.mainContainer.addChild(this.scrollContainer); // === 设置滚动参数 === const visibleRows = 2; // 显示2排 const colsPerRow = 3; // 每排3个 const totalRows = Math.ceil(this.shopGoodsList.length / colsPerRow); // 总排数 const scrollableHeight = (totalRows - visibleRows) * 60; // 可滚动的高度 // === 设置遮罩,限制显示区域 === const mask = new Graphics(); mask.beginFill(0xFFFFFF); mask.drawRect(-280, 0, 560, 120); mask.endFill(); this.scrollContainer.mask = mask; this.mainContainer.addChild(mask); // 调整金额框的间距参数 const startX = -120; // 起始X坐标 const startY = 30; // 起始Y坐标 const spacingX = 100; // X轴间距(左右间隔) const spacingY = 60; // Y轴间距(上下间隔) for (let i = 0; i < this.shopGoodsList.length; i++) { // 1. 创建发光边框图 const shine = new Sprite(Texture.from("assets/UIShop/UI1_11.png")); shine.width *= this.app.rat; shine.height *= this.app.rat; shine.anchor.set(0.5, 0.5); shine.visible = false; // 初始隐藏,只有选中时才显示 // 2. 创建金额框图 const amount = new Sprite(Texture.from("assets/UIShop/UI1_22.png")); amount.width *= this.app.rat; amount.height *= this.app.rat; amount.anchor.set(0.5, 0.5); amount.interactive = true; // 启用点击交互 amount.buttonMode = true; // 鼠标移到上面变成手型 // 3. 创建小红角星图 const star = new Sprite(Texture.from("assets/UIShop/UI1_14.png")); star.width *= this.app.rat; star.height *= this.app.rat; star.anchor.set(0.5, 0.5); // 4. 创建文本显示 - 金额 const amountText = new Text("", { fontSize: 15, fill: 0xff7e36, // 橘色 fontFamily: "Arial", }); amountText.anchor.set(0.5, 0.5); amountText.interactive = true; // 启用点击交互 amountText.buttonMode = true; // 鼠标移到上面变成手型 amountText.cursor = "pointer"; // 设置鼠标指针为手型 // 5. 创建文本显示 - 星星 const starText = new Text("", { fontSize: 11, fill: 0xffffff, // 白色 fontWeight: "normal", fontFamily: "Arial, sans-serif", }); starText.anchor.set(0.5, 0.5); starText.interactive = true; // 启用点击交互 starText.buttonMode = true; // 鼠标移到上面变成手型 starText.cursor = "pointer"; // 设置鼠标指针为手型 // 计算位置(每排3个,多排) const row = Math.floor(i / colsPerRow); const col = i % colsPerRow; const xPos = startX + col * spacingX; const yPos = startY + row * spacingY; // 设置所有元素的位置 shine.position.set(xPos, yPos); // 发光边框在底层 amount.position.set(xPos, yPos); // 金额框在中间 star.position.set(xPos + 18, yPos - 22); // 五角星在金额框右上方 amountText.position.set(xPos, yPos + 2); // 金额文本在金额框中心偏下 starText.position.set(xPos + 18, yPos - 25); // 星星数量文本在五角星中心 // shopGoodsList获取数据 let item, extraItem, count, starCount, id; if (this.shopGoodsList && this.shopGoodsList[i]) { item = this.shopGoodsList[i].items[0]; extraItem = this.shopGoodsList[i].extraItems[0]; count = item ? item.count : 0; starCount = extraItem ? extraItem.count : 0; id = this.shopGoodsList[i].id; } else { count = 0; starCount = 0; id = 0; } // 设置金额文本 const label = this._rpLabel || "Rp"; amountText.text = count > 0 ? `${label} ${count}` : "0"; // 只有当starCount大于0时才显示星星图片文本 if (starCount > 0) { starText.text = `+${starCount}`; star.visible = true; starText.visible = true; star.interactive = true; star.buttonMode = true; } else { starText.text = ""; star.visible = false; starText.visible = false; star.interactive = false; star.buttonMode = false; } // 创建点击处理函数 const handleItemClick = () => { if (this.bankPopupContainer && this.bankPopupContainer.visible) { // 如果支付弹窗已显示 return; } // 先隐藏所有发光边框 for (let j = 0; j < this.shineSprites.length; j++) { this.shineSprites[j].visible = false; } // 显示当前点击的发光边框 this.shineSprites[i].visible = true; this.selectedIndex = i; this.selectedAmountValue = count; this.selectedStarCount = starCount; this.id = id; // 输入框显示为 "Rp 数值" this.inputDisplayText.text = `${label} ${count}`; this.inputDisplayText.visible = true; this.inputAmountText.visible = false; // 隐藏提示文字 // rpText 显示为纯数字 this.rpText.text = `${this._rpLabel} ${this.formatNumberWithUnit(starCount)}`; console.log("选中了第", i + 1, "个金额图,金额:", count, "赠送星:", starCount,"id:", id); // 选中后切换为亮色可点击按钮 if (this.rechargeYes && this.rechargeNo) { this.rechargeYes.visible = true; this.rechargeNo.visible = false; this.rechargeYes.eventMode = "static"; this.rechargeYes.cursor = "pointer"; } }; // 给金额图添加点击事件 amount.on("pointerdown", handleItemClick); // 给金额文本添加点击事件 amountText.on("pointerdown", handleItemClick); // 当starCount大于0时,才给星星图片文本添加点击事件 if (starCount > 0) { star.on("pointerdown", handleItemClick); starText.on("pointerdown", handleItemClick); } // 将创建的元素存储到数组 this.amountSprites.push(amount); this.starSprites.push(star); this.shineSprites.push(shine); // 按层级顺序添加到滚动容器(从下到上) this.scrollContainer.addChild(amount); // 最底层:金额图 this.scrollContainer.addChild(shine); // 中间层:发光边框 // 只有当starCount大于0时,才添加星星图片文本到容器 if (starCount > 0) { this.scrollContainer.addChild(star); // 最上层:五角星 this.scrollContainer.addChild(starText); // 星星数量文本 } this.scrollContainer.addChild(amountText); // 金额文本 } // === 添加滚动功能 === if (scrollableHeight > 0) { // 只有需要滚动时才添加滚动功能 let isDragging = false; let lastY = 0; let currentScrollY = 0; // 鼠标/触摸按下事件 this.scrollContainer.interactive = true; this.scrollContainer.on('mousedown', (event) => { isDragging = true; lastY = event.data.global.y; }); this.scrollContainer.on('touchstart', (event) => { isDragging = true; lastY = event.data.global.y; }); // 鼠标/触摸移动事件 this.scrollContainer.on('mousemove', (event) => { if (isDragging) { const deltaY = event.data.global.y - lastY; lastY = event.data.global.y; // 更新滚动位置,限制在可滚动范围内 currentScrollY += deltaY; currentScrollY = Math.max(-scrollableHeight, Math.min(0, currentScrollY)); this.scrollContainer.y = currentScrollY; } }); this.scrollContainer.on('touchmove', (event) => { if (isDragging) { const deltaY = event.data.global.y - lastY; lastY = event.data.global.y; // 更新滚动位置,限制在可滚动范围内 currentScrollY += deltaY; currentScrollY = Math.max(-scrollableHeight, Math.min(0, currentScrollY)); this.scrollContainer.y = currentScrollY; } }); // 鼠标/触摸释放事件 this.scrollContainer.on('mouseup', () => { isDragging = false; }); this.scrollContainer.on('touchend', () => { isDragging = false; }); this.scrollContainer.on('mouseupoutside', () => { isDragging = false; }); this.scrollContainer.on('touchendoutside', () => { isDragging = false; }); // 鼠标滚轮事件 this.scrollContainer.on('wheel', (event) => { const scrollAmount = event.deltaY > 0 ? 30 : -30; // 滚轮滚动量 // 更新滚动位置,限制在可滚动范围内 currentScrollY += scrollAmount; currentScrollY = Math.max(-scrollableHeight, Math.min(0, currentScrollY)); this.scrollContainer.y = currentScrollY; }); } // === 充值按钮:亮色(可点)与灰色(不可点)切换 === const rechargeYes = new Sprite(Texture.from("assets/UIShop/UI1_09.png")); // 可点击亮色图 rechargeYes.width *= this.app.rat; rechargeYes.height *= this.app.rat; rechargeYes.anchor.set(0.5, 0.5); rechargeYes.position.set(-20, 140); rechargeYes.visible = false; // 初始隐藏 rechargeYes.eventMode = "none"; // 初始禁用交互 rechargeYes.cursor = "default"; const rechargeNo = new Sprite(Texture.from("assets/UIShop/UI1_09_2.png")); // 不可点击灰色图 rechargeNo.width *= this.app.rat; rechargeNo.height *= this.app.rat; rechargeNo.anchor.set(0.5, 0.5); rechargeNo.position.set(-20, 140); rechargeNo.visible = true; // 初始显示 rechargeNo.eventMode = "none"; rechargeNo.cursor = "default"; // 创建按钮文字 this.depositText2 = Object.assign( new Text("嘟嘟嘟", { fontFamily: "Arial", fontSize: 20, fill: 0xffffff, align: "center", stroke: "#FFA500", }), { anchor: { x: 0.5, y: 0.5 }, position: { x: -20, y: 135 } } ); // 添加到容器 this.mainContainer.addChild(rechargeYes, rechargeNo, this.depositText2); this.rechargeYes = rechargeYes; this.rechargeNo = rechargeNo; // 绑定点击事件:只有亮色按钮才可点击 rechargeYes.on("pointerdown", async () => { // 如果支付弹窗已显示,则忽略点击 if (this.bankPopupContainer && this.bankPopupContainer.visible) { return; } if (this.selectedIndex !== -1) { console.log("充值发起支付请求,金额:", this.selectedAmountValue, "赠送星数:", this.selectedStarCount,"id:", this.id); // 获取支付银行接口 await this.SendShopBankData(false); // 传入false表示不是初始化调用 } }); // =======点击充值银行弹窗开始========= this.bankPopupContainer = new Container(); this.bankPopupContainer.visible = false; // 初始隐藏 this.bankPopupContainer.zIndex = 100; // 确保置顶 this.bankPopupContainer.position.set(-270, -110); // 居中对齐主界面 // 创建弹窗背景 const bank = new Sprite(Texture.from("assets/UIShop/商店底4.png")); bank.width = 550; bank.height = 320; bank.position.set(0, -40); this.bankPopupContainer.addChild(bank); this.tipText = new Text("请选择支付方式", {fontFamily: "Arial",fontSize: 25,fill: 0xfff1a5,align: "center" }); this.tipText.anchor.set(0.5,0.5); this.tipText.position.set(275, -10); this.bankPopupContainer.addChild(this.tipText); // 创建银行介绍文本 this.bankIntroduceText = new Text("银行介绍", {fontFamily: "Arial",fontSize: 13,fill: 0xd18b5c,align: "center", }); this.bankIntroduceText.anchor.set(0.5, 0.5); this.bankIntroduceText.position.set(260, 190); this.bankPopupContainer.addChild(this.bankIntroduceText); // 创建银行选择区域 this.createBankSelectionArea(); // 创建银行确认按钮 this.createBankConfirmButton(); // 关闭充值银行弹窗按钮 const closeBankBtn = new Sprite(Texture.from("assets/UIShop/UI8_6.png")); closeBankBtn.width *= this.app.rat; closeBankBtn.height *= this.app.rat; closeBankBtn.position.set(510, -22); closeBankBtn.eventMode = "static"; closeBankBtn.cursor = "pointer"; closeBankBtn.on("pointerdown", () => { this.bankPopupContainer.visible = false; if (this.overlay) this.overlay.visible = false;//恢复主界面亮度 if (this.closeButton) {//主界面叉叉按钮可点击 this.closeButton.eventMode = "static"; this.closeButton.cursor = "pointer"; } // 恢复主界面的交互性 this.enableMainContainerInteractivity(true); }); this.bankPopupContainer.addChild(closeBankBtn); this.container.addChild(this.bankPopupContainer); // =======充值弹窗结束========= // =======支付成功展示容器开始========= this.paymentSuccessContainer = new Container(); this.paymentSuccessContainer.visible = false; // 初始隐藏 this.paymentSuccessContainer.zIndex = 200; // 最高层级 this.paymentSuccessContainer.eventMode = "static"; // 启用交互 // 创建全屏透明背景 const successOverlay = new Graphics(); successOverlay.beginFill(0x000000, 0.01); // 透明 successOverlay.drawRect(-375, -667, 750, 1334); successOverlay.endFill(); this.paymentSuccessContainer.addChild(successOverlay); // 创建支付金额文本 this.paymentAmountText = new Text("", { fontFamily: "Arial", fontSize: 60, fill: 0xffff00, // 黄色 align: "center", fontWeight: "bold", stroke: "#000000", strokeThickness: 4 }); this.paymentAmountText.anchor.set(0.5); this.paymentAmountText.position.set(0, 0); this.paymentSuccessContainer.addChild(this.paymentAmountText); // 创建确认按钮 this.createPaymentConfirmButton(); // 点击任意位置隐藏支付成功展示 this.paymentSuccessContainer.on("pointerdown", (event) => { // 检查点击的是否是确认按钮,如果不是则隐藏 if (!event.target || !event.target.parent || event.target.parent !== this.paymentConfirmButton) { this.hidePaymentSuccess(); } }); // 将支付成功容器添加到根容器 this.container.addChild(this.paymentSuccessContainer); // =======支付成功展示容器结束========= this.depositHistory = new Sprite(Texture.from("assets/UIShop/UI1_05.png")); //历史记录图按钮 this.depositHistory.width *= this.app.rat; this.depositHistory.height *= this.app.rat; this.depositHistory.anchor.set(0.5, 0.5); this.depositHistory.position.set(220, 140); this.depositHistory.interactive = true; this.depositHistory.buttonMode = true; this.mainContainer.addChild(this.depositHistory); this.depositHistory.on('pointerdown', async () => { // 添加点击事件监听器 // 如果支付弹窗已显示,忽略点击 if (this.bankPopupContainer && this.bankPopupContainer.visible) { return; } console.log('depositHistory 被点击了'); // 在这里添加点击后的 await this.SendHistoryData(); }); const threeStripes = new Sprite(Texture.from("assets/UIShop/UI1_07.png")); //三条杠图(历史) threeStripes.width *= this.app.rat; threeStripes.height *= this.app.rat; threeStripes.anchor.set(0.5, 0.5); threeStripes.position.set(180, 140); this.depositHistoryText = Object.assign(//提示历史文字(历史) new Text("haha", { fontFamily: "Arial", fontSize: 12, fill: 0xd87910, align: "center" }), { anchor: { x: 0.5, y: 0.5 }, position: { x: 235, y: 139 } } ); this.mainContainer.addChild(threeStripes, this.depositHistoryText); // === 历史记录弹窗开始=== console.log("拿到历史数据:", this.recordList); this.historyListContainer = new Container(); this.historyListContainer.visible = false; // 默认隐藏 this.historyListContainer.zIndex = 100; this.historyListContainer.position.set(-270, -110); // 居中对齐主界面 // 背景图 const recordBg = new Sprite(Texture.from("assets/UIShop/商店底5.png")); recordBg.width *= this.app.rat; recordBg.height *= this.app.rat; recordBg.anchor.set(0.5, 0.5); recordBg.position.set(260, 110); this.historyListContainer.addChild(recordBg); // 配置 const listWidth = 500; // 列表总宽度 const listHeight = 250; // 显示区域高度 一开始能看到几条 const itemHeight = 95; // 每条记录的高度 const contentStartX = 0; // 左边距 const contentStartY = 0; // 上边距 // 滚动内容容器 this.scrollContent = new Container(); this.scrollContent.position.set(contentStartX, contentStartY); this.historyListContainer.addChild(this.scrollContent); // 如果没有数据,显示提示文字 if (!this.recordList || this.recordList.length === 0) { this.noDataText = new Text("暂无充值记录", { fontSize: 18, fill: 0x999999 }); this.noDataText.anchor.set(0.5); this.noDataText.position.set(listWidth / 2, 100); this.scrollContent.addChild(this.noDataText); } else { // 有数据时遍历生成每一条记录 this.recordList.forEach((record, index) => { const y = index * itemHeight; // 圆角背景框 const bg = new Graphics(); bg.lineStyle(2, 0x7d503a, 0.6); // 边框颜色 bg.beginFill(0x7d503a, 0.3); // 背景色 bg.drawRoundedRect( 10, // x y + 5, // y listWidth - 15, // 宽:左右各留边距 itemHeight - 10, // 高:上下留空 18 // 圆角半径 ); bg.endFill(); this.scrollContent.addChild(bg); // 时间格式化 const date = new Date(record.createDate); const formattedTime = date.toLocaleString('en-US', { month: 'numeric', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit', hour12: true }); this.amountLabel = new Text("Top up amount", {fontSize: 13,fill: 0xb88e7e // 第一行:Top up amount }); this.amountLabel.position.set(20, y+12); this.historyListContainer.addChild(this.amountLabel); this.amountValue = new Text(`${record.amount.toLocaleString()}`, { fontSize: 13,fill: 0xe1ae68 }); this.amountValue.position.set(180, y + 12); this.orderLabel = new Text("Recharge Order", {// 第二行:Recharge Order fontSize: 13,fill: 0xb88e7e }); this.orderLabel.position.set(20, y + 32); this.orderId = new Text(record.id, { fontSize: 13,fill: 0xb77760 }); this.orderId.position.set(180, y + 32); this.timeLabel = new Text("Top up time", {// 第三行:Top up time fontSize: 13,fill: 0xb88e7e }); this.timeLabel.position.set(20, y + 52); this.timeValue = new Text(formattedTime, { fontSize: 13,fill: 0xb77760 }); this.timeValue.position.set(180, y + 52); this.statusLabel = new Text("Order status", { // 第四行:Order status fontSize: 13,fill: 0xb88e7e }); this.statusLabel.position.set(20, y + 72); // 创建三个文本对象 this.statusFinished = new Text("已成功", { fontSize: 12,fill: 0x00ff00 // 绿色 }); this.statusFinished.position.set(180, y + 72); this.statusFinished.visible = record.status === "1"; this.statusProcessing = new Text("在等待处理", { fontSize: 12, fill: 0xffff00 // 黄色 }); this.statusProcessing.position.set(180, y + 72); this.statusProcessing.visible = record.status === "0"; this.statusWaiting = new Text("失败", { fontSize: 12, fill: 0xaa0000 //红色 }); this.statusWaiting.position.set(180, y + 72); this.statusWaiting.visible = !(record.status === "1" || record.status === "0"); // 将所有文本添加进滚动容器 this.scrollContent.addChild( this.amountLabel, this.amountValue, this.orderLabel, this.orderId, this.timeLabel, this.timeValue, this.statusLabel, this.statusFinished, this.statusProcessing, this.statusWaiting, // 三个状态文本都加进去 ); // 复制图标(点击复制订单号) const copyIcon = new Sprite(Texture.from("assets/UIShop/复制.png")); copyIcon.width = 55 * this.app.rat; copyIcon.height = 55 * this.app.rat; copyIcon.anchor.set(0.5); copyIcon.position.set(listWidth - 40, y + itemHeight / 2); // 靠右居中 copyIcon.eventMode = "static"; copyIcon.cursor = "pointer"; copyIcon.on("pointerdown", (e) => { e.stopPropagation(); navigator.clipboard.writeText(record.id).then(() => { console.log(`已复制订单号: ${record.id}`); copyIcon.tint = 0x00ff00; setTimeout(() => { copyIcon.tint = 0xffffff; }, 300); }).catch(err => { alert("复制失败,请手动选择文本复制"); console.warn("Clipboard error:", err); }); }); this.scrollContent.addChild(copyIcon); }); // 裁剪区域 限制可见范围 const clipMask = new Graphics(); clipMask.beginFill(0x000000, 0.01); clipMask.drawRect(0, 0, listWidth, listHeight); clipMask.endFill(); clipMask.position.set(0, 0); this.historyListContainer.addChild(clipMask); this.scrollContent.mask = clipMask; // 拖拽滚动逻辑 let isDragging = false; let dragStartY = 0; let scrollStartY = 0; this.scrollContent.eventMode = "static"; this.scrollContent.cursor = "grab"; this.scrollContent.on("pointerdown", (event) => { isDragging = true; dragStartY = event.global.y; scrollStartY = this.scrollContent.y; this.scrollContent.cursor = "grabbing"; event.stopPropagation(); }); const handlePointerMove = (e) => { if (!isDragging) return; const dy = e.clientY - dragStartY; let newY = scrollStartY + dy; const maxScrollUp = 0; const totalItemsHeight = this.recordList.length * itemHeight; const maxScrollDown = listHeight - totalItemsHeight; if (newY > maxScrollUp) newY = maxScrollUp; else if (newY < maxScrollDown) newY = maxScrollDown; this.scrollContent.y = newY; }; const handlePointerUp = () => { isDragging = false; this.scrollContent.cursor = "grab"; }; this.app.view.addEventListener("pointermove", handlePointerMove); this.app.view.addEventListener("pointerup", handlePointerUp); this.app.view.addEventListener("pointerupoutside", handlePointerUp); } // 关闭按钮 const closeHistory = new Sprite(Texture.from("assets/UIShop/UI1_03.png")); closeHistory.width *= this.app.rat; closeHistory.height *= this.app.rat; closeHistory.position.set(500, -30); closeHistory.eventMode = "static"; closeHistory.cursor = "pointer"; closeHistory.on("pointerdown", () => { this.historyListContainer.visible = false; if (this.overlay) this.overlay.visible = false; this.enableMainContainerInteractivity(true); // 恢复主界面操作 }); this.historyListContainer.addChild(closeHistory); // 添加到主容器显示 this.container.addChild(this.historyListContainer); // === 历史记录弹窗结束 === // === 弹窗与阴影遮罩 === this.overlay = new Graphics(); this.overlay.beginFill(0x000000, 0.6); this.overlay.drawRect(-375, -667, 750, 1334); this.overlay.endFill(); this.overlay.visible = true; this.container.addChild(this.overlay); const shopBg3 = new Sprite(Texture.from("assets/UIShop/商店底3.png")); shopBg3.width = 450; shopBg3.height = 300; shopBg3.position.set(0, 0); this.shopBg3Container = new Container(); this.shopBg3Container.position.set(-240, -140); this.shopBg3Container.addChild(shopBg3); this.container.addChild(this.shopBg3Container); this.introduceText = new Text("介绍背景", { fontFamily: "Arial", fontSize: 13, fill: 0xffffff, align: "center", stroke: "#000000", strokeThickness: 0, lineHeight: 25, }); this.introduceText.anchor.set(0.5); this.introduceText.position.set(shopBg3.width / 2 + 48, shopBg3.height / 2 - 10); this.shopBg3Container.addChild(this.introduceText); this.shopBgButton = new Sprite(Texture.from("assets/UIShop/UI1_09.png")); this.shopBgButton.width = 210; this.shopBgButton.height = 60; this.shopBgButton.position.set(-80, 100); this.shopBgButton.eventMode = "static"; this.shopBgButton.cursor = "pointer"; this.buttonText = new Text("哈咯", { fontSize: 28, fill: 0xffffff, align: "center", stroke: "#000000", strokeThickness: 2, }); this.buttonText.anchor.set(0.5); this.buttonText.position.set(this.shopBgButton.width / 2, this.shopBgButton.height / 2); this.shopBgButton.addChild(this.buttonText); this.shopBgButton.on("pointerdown", () => { // 点击按钮后隐藏弹窗 if (this.shopBg3Container) this.shopBg3Container.visible = false; if (this.shopBgButton) this.shopBgButton.visible = false; if (this.overlay) this.overlay.visible = false; if (this.closeButton) { this.closeButton.eventMode = "static"; this.closeButton.cursor = "pointer"; } this.enableMainContainerInteractivity(true); }); this.container.addChild(this.shopBgButton); // 只设置一次 pivot:围绕内容中心缩放 if (!this.isPivotInitialized) { this.mainContainer.pivot.set(this.CONTAINER_WIDTH / 2, this.CONTAINER_HEIGHT / 2); this.isPivotInitialized = true; } // 初始化状态 this.resetState(); this.localize(); this.isInist = true; } // 创建银行选择区域 createBankSelectionArea() { // 清除现有的银行选项 this.bankSprites.forEach(sprite => sprite.destroy()); this.bankShineSprites.forEach(sprite => sprite.destroy()); this.bankSprites = []; this.bankShineSprites = []; // 银行选项布局参数 - 每排5个 const startX = 100; const startY = 100; const spacingX = 90; // 左右间隔 const rowHeight = 70; // 行高 const maxPerRow = 4; // 每排显示的数量 // 定义 bankCode 到纹理路径的映射表 const bankIconMap = { "DANA": "assets/UIShop/WithdrawalBankIcon_DANA.png", "OVO": "assets/UIShop/WithdrawalBankIcon_OVO.png", "QRIS": "assets/UIShop/WithdrawalBankIcon_QRIS.png", "BNI": "assets/UIShop/WithdrawalBankIcon_BNI.png", "BRI": "assets/UIShop/WithdrawalBankIcon_BRI.png" }; // === 创建银行选择的滚动容器 === this.bankScrollContainer = new Container(); this.bankScrollContainer.x = 0; this.bankScrollContainer.y = 0; this.bankPopupContainer.addChild(this.bankScrollContainer); // === 设置滚动参数 === const visibleRows = 1; // 只显示1排 const totalRows = Math.ceil(this.bankList.length / maxPerRow); // 总排数 const scrollableHeight = (totalRows - visibleRows) * rowHeight; // 可滚动的高度 // === 设置遮罩,限制显示区域 === const bankMask = new Graphics(); bankMask.beginFill(0xFFFFFF); // 遮罩高度只显示一排,宽度足够显示5个图标 bankMask.drawRect(50, 50, 450, rowHeight); bankMask.endFill(); this.bankScrollContainer.mask = bankMask; this.bankPopupContainer.addChild(bankMask); for (let i = 0; i < this.bankList.length; i++) { const bankData = this.bankList[i]; // 获取当前银行的 bankCode const bankCode = bankData.bankCode; const iconPath = bankIconMap[bankCode]; if (!iconPath) { continue; } // 1. 创建发光边框 const bankShine = new Sprite(Texture.from("assets/UIShop/UI1_11.png")); bankShine.width *= this.app.rat; bankShine.height *= this.app.rat; bankShine.anchor.set(0.5, 0.5); bankShine.visible = false; // 2. 创建按钮背景 const bankBgSprite = new Sprite(Texture.from("assets/UIShop/UI1_22.png")); bankBgSprite.width *= this.app.rat; bankBgSprite.height *= this.app.rat; bankBgSprite.anchor.set(0.5, 0.5); bankBgSprite.interactive = true; bankBgSprite.buttonMode = true; // 3. 创建银行Logo图标 const bankLogo = new Sprite(Texture.from(iconPath)); bankLogo.width = bankBgSprite.width * 1; bankLogo.height = bankBgSprite.height * 1; bankLogo.anchor.set(0.5, 0.5); // 将Logo居中放在背景上 bankLogo.position.set(0, 0); bankBgSprite.addChild(bankLogo); // 计算位置 - 每排个,多排布局 const row = Math.floor(i / maxPerRow); const col = i % maxPerRow; const xPos = startX + col * spacingX; const yPos = startY + row * rowHeight; bankShine.position.set(xPos, yPos); bankBgSprite.position.set(xPos, yPos); // 添加点击事件 bankBgSprite.on("pointerdown", () => { // 隐藏所有发光边框 this.bankShineSprites.forEach(shine => { shine.visible = false; }); // 显示当前选中的发光边框 bankShine.visible = true; this.selectedBankIndex = i; this.selectedBankData = bankData; this.updateBankConfirmButton(); console.log("选中银行:", { bankId: bankData.bankId,id: bankData.id,bankCode: bankData.bankCode,payoutLimit: bankData.payoutLimit }); }); // 存储引用(用于后续控制显示/隐藏) this.bankSprites.push(bankBgSprite); this.bankShineSprites.push(bankShine); // 添加到滚动容器 this.bankScrollContainer.addChild(bankBgSprite, bankShine); } // === 添加滚动功能 === if (totalRows > 1) {// 只有当有多排时才启用滚动 let isDragging = false; let lastY = 0; let currentScrollY = 0; // 鼠标/触摸按下事件 this.bankScrollContainer.interactive = true; this.bankScrollContainer.on('mousedown', (event) => { isDragging = true; lastY = event.data.global.y; }); this.bankScrollContainer.on('touchstart', (event) => { isDragging = true; lastY = event.data.global.y; }); // 鼠标/触摸移动事件 this.bankScrollContainer.on('mousemove', (event) => { if (isDragging) { const deltaY = event.data.global.y - lastY; lastY = event.data.global.y; // 更新滚动位置,限制在可滚动范围内 currentScrollY += deltaY; currentScrollY = Math.max(-scrollableHeight, Math.min(0, currentScrollY)); this.bankScrollContainer.y = currentScrollY; } }); this.bankScrollContainer.on('touchmove', (event) => { if (isDragging) { const deltaY = event.data.global.y - lastY; lastY = event.data.global.y; // 更新滚动位置,限制在可滚动范围内 currentScrollY += deltaY; currentScrollY = Math.max(-scrollableHeight, Math.min(0, currentScrollY)); this.bankScrollContainer.y = currentScrollY; } }); // 鼠标/触摸释放事件 this.bankScrollContainer.on('mouseup', () => { isDragging = false; }); this.bankScrollContainer.on('touchend', () => { isDragging = false; }); this.bankScrollContainer.on('mouseupoutside', () => { isDragging = false; }); this.bankScrollContainer.on('touchendoutside', () => { isDragging = false; }); // 鼠标滚轮事件 this.bankScrollContainer.on('wheel', (event) => { const scrollAmount = event.deltaY > 0 ? 15 : -15; // 更新滚动位置,限制在可滚动范围内 currentScrollY += scrollAmount; currentScrollY = Math.max(-scrollableHeight, Math.min(0, currentScrollY)); this.bankScrollContainer.y = currentScrollY; }); } } // 创建银行确认按钮 createBankConfirmButton() { // 银行确认按钮:亮色(可点)与灰色(不可点)切换 this.bankConfirmYes = new Sprite(Texture.from("assets/UIShop/UI1_09.png")); // 可点击亮色图 this.bankConfirmYes.width *= this.app.rat; this.bankConfirmYes.height *= this.app.rat; this.bankConfirmYes.anchor.set(0.5, 0.5); this.bankConfirmYes.position.set(275, 240); this.bankConfirmYes.visible = false; // 初始隐藏 this.bankConfirmYes.eventMode = "none"; // 初始禁用交互 this.bankConfirmYes.cursor = "default"; this.bankConfirmNo = new Sprite(Texture.from("assets/UIShop/UI1_09_2.png")); // 不可点击灰色图 this.bankConfirmNo.width *= this.app.rat; this.bankConfirmNo.height *= this.app.rat; this.bankConfirmNo.anchor.set(0.5, 0.5); this.bankConfirmNo.position.set(275, 240); this.bankConfirmNo.visible = true; // 初始显示 this.bankConfirmNo.eventMode = "none"; this.bankConfirmNo.cursor = "default"; // 创建按钮文字 - 为两个按钮分别创建文本 this.confirmTextYes = new Text("确认", { fontFamily: "Arial", fontSize: 40, fill: 0xffffff, align: "center", stroke: "#FFA500", }); this.confirmTextYes.anchor.set(0.5, 0.5); this.confirmTextYes.position.set(0, -5); this.confirmTextNo = new Text("确认", { fontFamily: "Arial", fontSize: 40, fill: 0xffffff, align: "center", stroke: "#FFA500", }); this.confirmTextNo.anchor.set(0.5, 0.5); this.confirmTextNo.position.set(0, -5); // 添加到按钮 this.bankConfirmYes.addChild(this.confirmTextYes); this.bankConfirmNo.addChild(this.confirmTextNo); // 添加到容器 this.bankPopupContainer.addChild(this.bankConfirmYes, this.bankConfirmNo); // 绑定点击事件:只有亮色按钮才可点击 this.bankConfirmYes.on("pointerdown", async () => { if (this.selectedBankIndex !== -1 && this.selectedBankData) { console.log("确认支付,银行信息:", this.selectedBankData); // 调用支付接口 await this.SendPaymentRequest(); } }); } // 创建支付成功确认按钮 createPaymentConfirmButton() { // 创建确认按钮 this.paymentConfirmButton = new Sprite(Texture.from("assets/UIShop/UI1_09.png")); this.paymentConfirmButton.width = 200; this.paymentConfirmButton.height= 80; this.paymentConfirmButton.anchor.set(0.5, 0.5); this.paymentConfirmButton.position.set(5, 150); this.paymentConfirmButton.eventMode = "static"; this.paymentConfirmButton.cursor = "pointer"; // 创建按钮文字 this.confirmText = new Text("这是Confirm", { fontFamily: "Arial", fontSize: 30, fill: 0xffffff, align: "center", stroke: "#FFA500", }); this.confirmText.anchor.set(0.5, 0.5); this.confirmText.position.set(0, 0); this.paymentConfirmButton.addChild(this.confirmText); // 绑定点击事件 this.paymentConfirmButton.on("pointerdown", () => { this.hidePaymentSuccess(); }); // 添加到支付成功容器 this.paymentSuccessContainer.addChild(this.paymentConfirmButton); } // 更新银行确认按钮状态 updateBankConfirmButton() { if (this.selectedBankIndex !== -1) { // 有选中银行,显示亮色按钮 this.bankConfirmYes.visible = true; this.bankConfirmNo.visible = false; this.bankConfirmYes.eventMode = "static"; this.bankConfirmYes.cursor = "pointer"; } else { // 未选中银行,显示灰色按钮 this.bankConfirmYes.visible = false; this.bankConfirmNo.visible = true; this.bankConfirmYes.eventMode = "none"; this.bankConfirmYes.cursor = "default"; } } // 显示支付成功动画 showPaymentSuccess(amount) { // 隐藏银行弹窗遮罩 this.bankPopupContainer.visible = false; this.overlay.visible = false; // 恢复主界面交互 this.enableMainContainerInteractivity(true); // 设置支付金额文本 显示数字 this.paymentAmountText.text = amount.toString(); // 显示支付成功容器 this.paymentSuccessContainer.visible = true; // 动画效果:透明到不透明,再放大 this.paymentAmountText.alpha = 0; this.paymentAmountText.scale.set(0.5); // 动画序列 const animate = () => { // 渐显 if (this.paymentAmountText.alpha < 1) { this.paymentAmountText.alpha += 0.05; requestAnimationFrame(animate); return; } // 放大 if (this.paymentAmountText.scale.x < 1.2) { this.paymentAmountText.scale.x += 0.02; this.paymentAmountText.scale.y += 0.02; requestAnimationFrame(animate); return; } // 缩小回正常 if (this.paymentAmountText.scale.x > 1) { this.paymentAmountText.scale.x -= 0.01; this.paymentAmountText.scale.y -= 0.01; requestAnimationFrame(animate); } }; // 开始动画 animate(); } // 隐藏支付成功展示 hidePaymentSuccess() { this.paymentSuccessContainer.visible = false; } // 启用或禁用主界面的交互性 enableMainContainerInteractivity(enable) { // 设置主容器内所有可交互元素的交互性 this.closeButton.eventMode = enable ? "static" : "none"; this.closeButton.cursor = enable ? "pointer" : "default"; // 设置金额框的交互性 for (const amount of this.amountSprites) { amount.eventMode = enable ? "static" : "none"; amount.cursor = enable ? "pointer" : "default"; } // 设置充值按钮的交互性 if (this.rechargeYes) { this.rechargeYes.eventMode = enable && this.selectedIndex !== -1 ? "static" : "none"; this.rechargeYes.cursor = enable && this.selectedIndex !== -1 ? "pointer" : "default"; } // 设置历史记录按钮的交互性 if (this.depositHistory) { this.depositHistory.eventMode = enable ? "static" : "none"; this.depositHistory.cursor = enable ? "pointer" : "default"; } // 设置清除输入按钮的交互性 const clearInput = this.mainContainer.children.find(child => child.texture && child.texture.textureCacheIds && child.texture.textureCacheIds.includes("assets/UIShop/UI1_17.png") ); if (clearInput) { clearInput.eventMode = enable ? "static" : "none"; clearInput.cursor = enable ? "pointer" : "default"; } } // 清除选中状态 clearSelection() { this.inputDisplayText.visible = false; this.inputAmountText.visible = true; // 恢复提示文字 this.selectedIndex = -1; this.selectedAmountValue = 0; this.selectedStarCount = 0; this.rpText.text = `${this._rpLabel} 0`; // 恢复 rpText 为默认状态 for (const shine of this.shineSprites) { // 隐藏所有发光边框 shine.visible = false; } // 清除选择时切回灰色按钮 if (this.rechargeYes && this.rechargeNo) { this.rechargeYes.visible = false; this.rechargeNo.visible = true; this.rechargeYes.eventMode = "none"; this.rechargeYes.cursor = "default"; } } async show() { console.log(this.constructor.name + " 1"); if (!this.isInist) { await this.init(); } else { this.resetState(); } console.log(this.constructor.name + " 2"); this.container.visible = true; UIManager.hideLoading(); // 开启动画 this.animateOpen(); } // 动画从中间向两边展开 animateOpen() { this.mainContainer.scale.set(0, 1); const duration = 500; const startTime = performance.now(); const tick = (currentTime) => { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); const easeProgress = 1 - Math.pow(1 - progress, 2); // 缓动效果 this.mainContainer.scale.x = easeProgress; if (progress < 1) { requestAnimationFrame(tick); } }; requestAnimationFrame(tick); } resetState() { if (this.shopBg3Container) this.shopBg3Container.visible = true; if (this.shopBgButton) this.shopBgButton.visible = true; if (this.overlay) this.overlay.visible = true; if (this.mainContainer) { this.mainContainer.alpha = 1; this.mainContainer.scale.set(1); } if (this.closeButton) { this.closeButton.eventMode = "none"; this.closeButton.cursor = "default"; } // 重置选择状态 this.clearSelection(); this.enableMainContainerInteractivity(false); } hide() { this.container.visible = false; } localize() { this.uiname="uiShop"; this.buttonText.text = super.getLocalWords("Confirmation"); this.introduceText.text = super.getLocalWords("introduce"); this.depositText.text = super.getLocalWords("deposit"); this.depositText2.text = super.getLocalWords("deposit"); this.topUpAmountText.text = super.getLocalWords("topUpAmount"); this.inputAmountText.text = super.getLocalWords("inputAmount"); this.freeAmountText.text = super.getLocalWords("freeAmount"); this._rpLabel = super.getLocalWords("rp") || "Rp"; this.rpText.text = `${this._rpLabel} 0`; // 默认状态 this.selectAmountText.text = super.getLocalWords("selectAmount"); this.inputAmountText.visible = true; this.depositHistoryText.text = super.getLocalWords("depositHistory"); this.tipText.text = super.getLocalWords("tip"); this.confirmText.text = super.getLocalWords("confirm"); this.confirmTextYes.text =super.getLocalWords("confirmTextYes"); this.confirmTextNo.text =super.getLocalWords("confirmTextNo"); this.bankIntroduceText.text = super.getLocalWords("bankIntroduceText"); //this.noDataText.text = super.getLocalWords("oDataText");//历史记录 this.amountLabel.text = super.getLocalWords("amountText"); this.orderLabel.text = super.getLocalWords("rechargeOrderText"); this.timeLabel.text = super.getLocalWords("rechargeTimeText"); this.statusLabel.text = super.getLocalWords("statusText"); this.statusFinished.text = super.getLocalWords("status_1"); this.statusProcessing.text = super.getLocalWords("status_0"); this.statusWaiting.text = super.getLocalWords("status_2"); } formatNumberWithUnit(value) { if (value == null || isNaN(value)) return "0"; let formatted; if (value >= 1_000_000_000_000) { formatted = (value / 1_000_000_000_000).toFixed(2).replace(/\.00$/, ''); return `${formatted}T`; } else if (value >= 1_000_000_000) { formatted = (value / 1_000_000_000).toFixed(2).replace(/\.00$/, ''); return `${formatted}B`; } else if (value >= 1_000_000) { formatted = (value / 1_000_000).toFixed(2).replace(/\.00$/, ''); return `${formatted}M`; } else if (value >= 1_000) { formatted = (value / 1_000).toFixed(2).replace(/\.00$/, ''); return `${formatted}K`; } else { return String(value); } } // 接口请求:获取商店商品列表 async SendShopData() { const requestData = { token: gameData.gameToken, playerIndex: gameData.playerDto.playerIndex, }; console.log("发送请求参数:", requestData); const response = await networkMgr.postMessage("getShopGoods", requestData); if (!response) { throw new Error("接口返回空数据"); } this.shopGoodsList = response.shopGoodsList || response["shopGoodsList"] || []; console.log("获取商店数据:", this.shopGoodsList); if (this.isInist) { this.localize(); } } // 获取支付银行接口 async SendShopBankData(isInit = false) { try { // 如果是初始化调用,使用第一个商品的ID const goodsId = isInit && this.shopGoodsList.length > 0 ? this.shopGoodsList[0].id : this.id; const requestData = { token: gameData.gameToken, playerIndex: gameData.playerDto.playerIndex, goodsId: goodsId, }; console.log("发送请求参数:", requestData); const response = await networkMgr.postMessage("getPaymentBank", requestData); if (!response) { throw new Error("接口返回空数据"); } this.bankList = response.bankList || response["bankList"] || []; console.log("获取支付银行数据:", this.bankList); // 如果不是初始化调用,显示弹窗 if (!isInit) { // 重新创建银行选择区域 this.createBankSelectionArea(); this.bankPopupContainer.visible = true;//获取到了数据就显示弹窗 this.overlay.visible = true;//背景变黑暗 if (this.closeButton) {//主界面叉叉按钮 不可点击 this.closeButton.eventMode = "none"; this.closeButton.cursor = "default"; } // 重置银行选择状态 this.selectedBankIndex = -1; this.selectedBankData = null; for (const shine of this.bankShineSprites) { shine.visible = false; } // 更新确认按钮状态 this.updateBankConfirmButton(); this.enableMainContainerInteractivity(false);// 禁止主界面的交互 } } catch (error) { console.error("获取银行数据失败:", error); this.bankList = []; } } // 发送支付请求 async SendPaymentRequest() { if (!this.selectedBankData || !this.id) { console.error("未选择银行或商品"); return; } try { const requestData = { token: gameData.gameToken, playerIndex: gameData.playerDto.playerIndex, goodsId: this.id, bankId: this.selectedBankData.bankId, bankCode: this.selectedBankData.bankCode }; console.log("发送支付请求参数:", requestData); const response = await networkMgr.postMessage("payment", requestData); if (!response) { throw new Error("支付接口返回空数据"); } console.log("支付接口返回:", response); if (response.result === 0) { console.log("支付成功,金额:", response.amount); // 显示支付成功动画 this.showPaymentSuccess(response.amount); } else { // 支付失败 console.error("支付失败:", response.msg); } } catch (error) { console.error("支付请求失败:", error); } } // 获取商店商品列表 async SendShopData() { const requestData = { token: gameData.gameToken, playerIndex: gameData.playerDto.playerIndex, }; console.log("发送请求参数:", requestData); const response = await networkMgr.postMessage("getShopGoods", requestData); if (!response) { throw new Error("接口返回空数据"); } this.shopGoodsList = response.shopGoodsList || response["shopGoodsList"] || []; console.log("获取商店数据:", this.shopGoodsList); if (this.isInist) { this.localize(); } } // 历史记录接口 async SendHistoryData(isInit = false) { const requestData = { token: gameData.gameToken, playerIndex: gameData.playerDto.playerIndex, }; console.log("发送历史请求参数:", requestData); const response = await networkMgr.postMessage("getPaymentRecord", requestData); if (!response) { throw new Error("接口返回空数据"); } this.recordList = response.recordList || response["recordList"] || []; console.log("获取历史记录数据:", this.recordList); // 如果不是初始化调用,显示弹窗 if (!isInit) { this.historyListContainer.visible = true;//获取到了数据就显示历史弹窗 this.overlay.visible = true;//背景变黑暗 for (const shine of this.bankShineSprites) { shine.visible = false; } this.enableMainContainerInteractivity(false);// 禁止主界面的交互 } } } export const UIShop = new uiShop(); 需求:为什么我历史记录弹窗里面的东西我不能本地化 是什么原因 本地化没效果 this.amountLabel, this.amountValue, this.orderLabel, this.orderId, this.timeLabel, this.timeValue, this.statusLabel, this.statusFinished, this.statusProcessing, this.statusWaiting, // 三个状态文本都加进去
09-27
public class HomeFragment extends BaseFragment { RecyclerView rv,sr_rv, qy_rv, xz_rv; ImageView iv_ss, iv_gd, sr_start, qy_start, xz_start,iv_bf; TextView tv_time,tv_song_name; private MediaPlayer mediaPlayer; private int currentPlayingPosition = -1; // 初始值设为 -1,表示没有播放 private boolean isScrollInProgress = false; // 防止重复滚动 private ObjectAnimator rotationAnimator; private SongTopAdapter songTopAdapter; private ImageView currentRotationView; private boolean isPlayingState = false; // 记录当前是否处于“正在播放”的逻辑状态 private boolean wasPlayingBeforeScroll = false; // 是否在滑动前是播放状态 @Override protected int setLayout() { return R.layout.fragment_home; } @Override protected void initView() { iv_ss = fvbi(R.id.iv_ss); iv_gd = fvbi(R.id.iv_gd); iv_bf = fvbi(R.id.iv_bf); rv = fvbi(R.id.rv); sr_rv = fvbi(R.id.sr_rv); qy_rv = fvbi(R.id.qy_rv); xz_rv = fvbi(R.id.xz_rv); sr_start = fvbi(R.id.sr_start); qy_start = fvbi(R.id.qy_start); xz_start = fvbi(R.id.xz_start); tv_time = fvbi(R.id.tv_time); tv_song_name = fvbi(R.id.tv_song_name); tv_song_name.setEllipsize(TextUtils.TruncateAt.MARQUEE); tv_song_name.setSingleLine(true); tv_song_name.setSelected(true); tv_song_name.setFocusable(true); tv_song_name.setFocusableInTouchMode(true); } @Override protected void initClick() { //跳转搜索页面 iv_ss.setOnClickListener(new OnMultiClickListener() { @Override public void onMultiClick(View v) { toClass(SearchActivity.class); } }); // 设置点击事件:切换播放/暂停 iv_bf.setOnClickListener(v -> { if (mediaPlayer == null) return; if (mediaPlayer.isPlaying()) { // 当前正在播放 → 暂停 mediaPlayer.pause(); isPlayingState = false; iv_bf.setImageResource(R.mipmap.img_lan_bf); // 显示“播放”图标 stopRotationAnimation(); // 停止旋转动画 ToastUtil.showShortToast("已暂停"); } else { // 当前暂停 → 继续播放 mediaPlayer.start(); isPlayingState = true; iv_bf.setImageResource(R.mipmap.img_lan_zt); // 显示“暂停”图标 // 启动旋转:需要获取当前播放项的封面 // startRotationForCurrentItem(currentPlayingPosition); ToastUtil.showShortToast("继续播放"); } }); } @Override protected void initData() { Calendar calendar = Calendar.getInstance(); int hour = calendar.get(Calendar.HOUR_OF_DAY); // 获取 24 小时制的小时 String greeting; if (hour >= 5 && hour < 11) { greeting = "HI,早上好"; } else if (hour == 11) { greeting = "HI,中午好"; } else if (hour == 12) { greeting = "HI,下午好"; } else if (hour >= 13 && hour < 18) { greeting = "HI,下午好"; } else { // hour >= 18 或 hour < 5(凌晨) greeting = "HI,晚上好"; } tv_time.setText(greeting); musicAppInfo(); } private void musicAppInfo() { HashMap<String, String> map = new HashMap<>(); showDialog(); HttpBuiler.getInfo(Url.music, map).build().execute( new GenericsCallback<MusicBean>(new JsonGenericsSerializator()) { @Override public void onError(Call call, Exception e, int id) { dissmiss(); ToastUtil.showShortToast(e.getMessage()); } @Override public void onResponse(MusicBean response, int id) { dissmiss(); if (response.getCode().equals("0")) { if (null != response.getData() && response.getData().size() > 0) { // 1. 创建 Adapter songTopAdapter = new SongTopAdapter(); // 2. 设置布局管理器适配器 rv.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false)); rv.setAdapter(songTopAdapter); // 3. 【关键】先设置播放状态监听器(必须在 setup 前!) songTopAdapter.setOnPlayStateChangedListener((position, coverView) -> { Log.d("Rotation", "收到播放通知: position=" + position); stopRotationAnimation(); // 停止旧动画 startRotationAnimation(coverView); // 启动新动画 }); // 4. 【重要】现在才能调用 setupSnappingAndPlayback // 因为这里面会触发滚动检测 → 可能立即调用 playMusicAtPosition → 需要监听器已准备好! setupSnappingAndPlayback(rv, songTopAdapter); // 5. 最后设置数据(避免提前触发 convert 导致空指针) songTopAdapter.setList(response.getData()); } else { ToastUtil.showShortToast("暂无歌曲数据"); } } else { ToastUtil.showShortToast(response.getMessage()); } } }); } // ================== 滚动监听 + 自动播放 ================== private void setupSnappingAndPlayback(RecyclerView rv, SongTopAdapter adapter) { LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager(); if (layoutManager == null) return; int fixedTargetX = dp2px(50f); // 模糊图右边界 // 使用 SnapHelper(保留吸附动画) FixedSideSnapHelper snapHelper = new FixedSideSnapHelper(fixedTargetX); snapHelper.attachToRecyclerView(rv); // 新增:记录已触发播放的位置,防止重复 final AtomicInteger lastPlayedPosition = new AtomicInteger(RecyclerView.NO_POSITION); rv.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); int pos = findBestObscuredItem(recyclerView, fixedTargetX); if (pos != RecyclerView.NO_POSITION && pos != lastPlayedPosition.get()) { playMusicAtPosition(pos, adapter.getData()); lastPlayedPosition.set(pos); } dispatchVisualFeedback(recyclerView, adapter); } @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_DRAGGING || newState == RecyclerView.SCROLL_STATE_SETTLING) { // 用户开始滑动或惯性滑动 → 暂停音乐动画 if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); wasPlayingBeforeScroll = true; // 记录原来是播放状态 stopRotationAnimation(); iv_bf.setImageResource(R.mipmap.img_lan_bf); // 更新按钮图标 Log.d("ScrollPause", "滑动中暂停音乐"); } else { wasPlayingBeforeScroll = false; } isScrollInProgress = true; } if (newState == RecyclerView.SCROLL_STATE_IDLE) { // 滑动结束 → 如果原来是播放状态,则恢复 isScrollInProgress = false; if (wasPlayingBeforeScroll && mediaPlayer != null) { mediaPlayer.start(); iv_bf.setImageResource(R.mipmap.img_lan_zt); startRotationForCurrentItem(currentPlayingPosition); Log.d("ScrollResume", "滑动结束,恢复播放"); } } } }); setupOneWayScroll(rv, rv.getContext()); } private void dispatchVisualFeedback(RecyclerView rv, SongTopAdapter adapter) { LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager(); if (layoutManager == null) return; int targetX = dp2px(100f); // 模糊图右侧位置 for (int i = 0; i < rv.getChildCount(); i++) { View child = rv.getChildAt(i); if (child == null) continue; int left = layoutManager.getDecoratedLeft(child); int right = layoutManager.getDecoratedRight(child); int centerX = (left + right) / 2; float distance = Math.abs(centerX - targetX); // 只处理附近的 item if (distance > 600) continue; float alpha = 1.0f - Math.min(1.0f, distance / 300f); float scale = 0.8f + 0.2f * (1.0f - Math.min(1.0f, distance / 200f)); // child.setAlpha(alpha); // child.setScaleX(scale); // child.setScaleY(scale); } } /** * 查找当前是否有 item 被模糊图遮挡其左半部分约 50% * 假设模糊图固定显示在 [0, fuzzyRightEdge] 区域内(例如宽度 100dp) */ private int findBestObscuredItem(RecyclerView rv, int fuzzyRightEdge) { LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager(); if (layoutManager == null) return RecyclerView.NO_POSITION; int childCount = layoutManager.getChildCount(); int bestPos = RecyclerView.NO_POSITION; float minDiffToHalf = Float.MAX_VALUE; for (int i = 0; i < childCount; i++) { View child = layoutManager.getChildAt(i); if (child == null) continue; int left = layoutManager.getDecoratedLeft(child); int right = layoutManager.getDecoratedRight(child); int width = right - left; // 必须有交集:item 必须与模糊图区域 [0, fuzzyRightEdge] 相交 if (right <= 0 || left >= fuzzyRightEdge) continue; // 无交集 // 计算重叠区域:模糊图 [0, fuzzyRightEdge] 与 item [left, right] int overlapStart = Math.max(left, 0); int overlapEnd = Math.min(right, fuzzyRightEdge); int overlapWidth = Math.max(0, overlapEnd - overlapStart); // 计算被遮挡的比例(仅左半部分被遮才算) float coveredRatio = (float) overlapWidth / width; // 我们希望刚好遮住一半(左半边) float diff = Math.abs(coveredRatio - 0.5f); // 筛选合理范围内的遮挡(避免噪声) if (coveredRatio >= 0.3f && coveredRatio <= 0.7f && diff < minDiffToHalf) { minDiffToHalf = diff; bestPos = layoutManager.getPosition(child); } } return bestPos; } private void setupOneWayScroll(RecyclerView rv, Context context) { final boolean[] hasScrolledLeft = {false}; // 标记是否已左滑 final float[] startX = {0f}; rv.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() { @Override public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: startX[0] = e.getX(); break; case MotionEvent.ACTION_MOVE: float currentX = e.getX(); float dx = currentX - startX[0]; // 手指移动距离 if (dx < -20 && !hasScrolledLeft[0]) { // 用户向左滑动(内容向右移出视野),视为“已前进” hasScrolledLeft[0] = true; } // 如果已经左滑过,则禁止任何右滑(手指向右拖) if (hasScrolledLeft[0] && dx > 15) { // 检测到右滑意图 → 拦截事件,不交给 RecyclerView Toast.makeText(context, "暂只支持左滑操作", Toast.LENGTH_SHORT).show(); return true; // 拦截触摸事件,防止 RecyclerView 滚动 } break; } return false; // 正常处理其他情况 } }); } private void playMusicAtPosition(int position, List<MusicBean.DataBean> dataList) { if (position == RecyclerView.NO_POSITION || position >= dataList.size()) return; MusicBean.DataBean bean = dataList.get(position); currentPlayingPosition = position; try { if (mediaPlayer == null) { mediaPlayer = new MediaPlayer(); } else { if (mediaPlayer.isPlaying()) mediaPlayer.stop(); mediaPlayer.reset(); mediaPlayer.setOnPreparedListener(null); mediaPlayer.setOnCompletionListener(null); } mediaPlayer.setDataSource(bean.getMusic()); mediaPlayer.setAudioAttributes(new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build()); mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(mp -> { mp.start(); isPlayingState = true; iv_bf.setImageResource(R.mipmap.img_lan_zt); // 播放时显示暂停图标 }); mediaPlayer.setOnCompletionListener(mp -> { Log.d("AutoPlay", "歌曲播放完成: " + bean.getTitle()); playNextSong(dataList); }); // 通知 Adapter 刷新界面(触发 convert) songTopAdapter.updatePlayingPosition(position); // 尝试获取当前可见的 itemView 上的 ImageView 并启动旋转 // startRotationForCurrentItem(position); Log.d("AutoPlay", "正在播放: " + bean.getTitle()); ToastUtil.showShortToast("播放: " + bean.getTitle()); tv_song_name.setText(bean.getTitle()+"-"+bean.getSinger()); // 在 playMusicAtPosition 方法末尾 iv_bf.setImageResource(R.mipmap.img_lan_zt); // 新歌开始播,设为暂停图标 } catch (Exception e) { e.printStackTrace(); ToastUtil.showShortToast("无法播放"); playNextSong(dataList); } } private void playNextSong(List<MusicBean.DataBean> dataList) { int nextPos = currentPlayingPosition + 1; if (nextPos < dataList.size()) { playMusicAtPosition(nextPos, dataList); scrollToTriggerPosition(nextPos, rv); // 自动滚动到触发位置 } else { ToastUtil.showShortToast("已播放完全部歌曲"); } } private void scrollToTriggerPosition(int targetPos, RecyclerView recyclerView) { LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); if (layoutManager == null) return; LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) { private static final float TARGET_OFFSET = 80f; // 调整此值 @Override public PointF computeScrollVectorForPosition(int targetPosition) { return layoutManager.computeScrollVectorForPosition(targetPosition); } @Override public int calculateDxToMakeVisible(View view, int snapPreference) { // 使用 layoutManager 实例的方法 int left = layoutManager.getDecoratedLeft(view); int right = layoutManager.getDecoratedRight(view); int center = (left + right) / 2; int targetX = dp2px(50f); // 模糊图右边界的 X 坐标 return targetX - center; // 偏移量 } @Override protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { return 150f / displayMetrics.densityDpi; } }; scroller.setTargetPosition(targetPos); layoutManager.startSmoothScroll(scroller); } private int dp2px(float dp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, rv.getResources().getDisplayMetrics() ); } /** * 启动封面旋转动画 */ private void startRotationAnimation(ImageView imageView) { if (imageView == null || imageView.getDrawable() == null) return; // 先清除旧动画 if (rotationAnimator != null && rotationAnimator.isRunning()) { rotationAnimator.cancel(); } // 方法一:使用 ObjectAnimator(保留,但需设置 LayerType) rotationAnimator = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f); rotationAnimator.setDuration(10000); // 10秒一圈 rotationAnimator.setRepeatCount(ObjectAnimator.INFINITE); rotationAnimator.setInterpolator(new LinearInterpolator()); // 关键一步:启用硬件层加速(否则某些设备上动画卡顿或不显示) imageView.setLayerType(View.LAYER_TYPE_HARDWARE, null); rotationAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { imageView.setLayerType(View.LAYER_TYPE_NONE, null); } }); rotationAnimator.start(); } /** * 停止旋转动画 */ private void stopRotationAnimation() { if (rotationAnimator != null) { rotationAnimator.cancel(); rotationAnimator = null; } if (currentRotationView != null) { currentRotationView.setLayerType(View.LAYER_TYPE_NONE, null); currentRotationView = null; } } /** * 根据 position 手动启动该 itemView 的旋转动画(用于恢复播放) */ private void startRotationForCurrentItem(int position) { if (position == RecyclerView.NO_POSITION || songTopAdapter == null) return; RecyclerView.ViewHolder viewHolder = rv.findViewHolderForAdapterPosition(position); if (viewHolder != null) { ImageView coverView = viewHolder.itemView.findViewById(R.id.iv_song_logo); // 替换为你的封面ID if (coverView != null) { stopRotationAnimation(); // 安全起见先停 startRotationAnimation(coverView); } } else { // 如果 item 不可见,通过 adapter 回调间接启动(convert 会触发) Log.d("Rotation", "目标 itemView 不可见,等待 convert 触发"); } } @Override public void onDestroy() { if (mediaPlayer != null) { if (mediaPlayer.isPlaying()) mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; } super.onDestroy(); } } public class SongTopAdapter extends RecyclerView.Adapter<SongTopAdapter.DeliveryViewHolder> { private List<MusicBean.DataBean> dataList = new ArrayList<>(); private int currentPlayingPosition = -1; // 播放状态监听器 public interface OnPlayStateChangedListener { void onPlayingAtPosition(int position, ImageView coverView); } private OnPlayStateChangedListener playStateListener; public void setOnPlayStateChangedListener(OnPlayStateChangedListener listener) { this.playStateListener = listener; } // 设置数据(替代 setList) public void setList(List<MusicBean.DataBean> list) { this.dataList.clear(); if (list != null) { this.dataList.addAll(list); } notifyDataSetChanged(); } public List<MusicBean.DataBean> getData() { return dataList; } public int getCurrentPlayingPosition() { return currentPlayingPosition; } public void updatePlayingPosition(int pos) { int oldPos = currentPlayingPosition; currentPlayingPosition = pos; // 通知旧位置新位置刷新 if (oldPos >= 0 && oldPos < dataList.size()) { notifyItemChanged(oldPos); } if (pos >= 0 && pos < dataList.size()) { notifyItemChanged(pos); } } // ViewHolder 类 public static class DeliveryViewHolder extends RecyclerView.ViewHolder { ImageView ivCover; public DeliveryViewHolder(@NonNull View itemView) { super(itemView); ivCover = itemView.findViewById(R.id.iv_song_logo); } } @NonNull @Override public DeliveryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.song_top_adapter, parent, false); return new DeliveryViewHolder(view); } @Override public void onBindViewHolder(@NonNull DeliveryViewHolder holder, int position) { MusicBean.DataBean dataBean = dataList.get(position); // 使用 Glide 加载封面 Glide.with(holder.itemView.getContext()) .load(dataBean.getCover()) .transform(new CircleCrop()) .error(getErrorDrawable(holder.itemView.getContext())) .into(holder.ivCover); boolean isPlaying = (position == currentPlayingPosition); if (isPlaying && playStateListener != null) { holder.ivCover.post(() -> playStateListener.onPlayingAtPosition(position, holder.ivCover)); } } @Override public int getItemCount() { return dataList.size(); } // 静态错误图缓存(防止重复创建) private static BitmapDrawable sErrorDrawable; private BitmapDrawable getErrorDrawable(Context context) { if (sErrorDrawable == null) { Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.img_home_sing); Bitmap circularBitmap = ImageUtils.getCircularBitmap(bitmap); sErrorDrawable = new BitmapDrawable(context.getResources(), circularBitmap); } return sErrorDrawable; } } 保持现有逻辑,其中有两张歌曲图片是因为添加的错误图片没有旋转动画,怎么修改让他也有旋转动画 .error(getErrorDrawable(holder.itemView.getContext()))
最新发布
12-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值