package com.example.firstapp.ui.invitation
|
|
import android.app.Dialog
|
import android.content.ClipData
|
import android.content.ClipboardManager
|
import android.content.Context
|
import android.content.Intent
|
import android.graphics.Bitmap
|
import android.graphics.Color
|
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.ColorDrawable
|
import android.net.Uri
|
import android.os.Bundle
|
import android.os.Handler
|
import android.os.Looper
|
import android.text.Html
|
import android.util.TypedValue
|
import android.view.View
|
import android.view.ViewGroup
|
import android.widget.Button
|
import android.widget.ImageView
|
import android.widget.TextView
|
import android.widget.Toast
|
import androidx.appcompat.app.AppCompatActivity
|
import androidx.core.content.ContextCompat
|
import androidx.core.content.FileProvider
|
import androidx.lifecycle.lifecycleScope
|
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearSmoothScroller
|
import androidx.recyclerview.widget.RecyclerView
|
import com.example.firstapp.App.Companion.context
|
import com.example.firstapp.R
|
import com.example.firstapp.adapter.InvitationAdapter
|
import com.example.firstapp.adapter.InvitationRecordAdapter
|
import com.example.firstapp.database.request.SmsLoginRequest
|
import com.example.firstapp.database.service.RetrofitClient
|
import com.example.firstapp.entity.InvitationRecord
|
import com.example.firstapp.utils.Log
|
import com.example.firstapp.utils.PreferencesManager
|
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.withContext
|
import java.io.File
|
import java.io.FileOutputStream
|
import kotlin.math.abs
|
|
class InvitationActivity : AppCompatActivity() {
|
|
private lateinit var recyclerSuccessView: RecyclerView
|
private lateinit var recyclerRecordView: RecyclerView
|
private lateinit var adapter: InvitationAdapter
|
private lateinit var recordadapter: InvitationRecordAdapter
|
private var data = mutableListOf<InvitationRecord>()
|
private var recorddata = mutableListOf<InvitationRecord>()
|
private val handler = Handler(Looper.getMainLooper())
|
private val scrollInterval = 3000L
|
private var currentScrollPosition = 0
|
private var currentRecordScrollPosition = 0
|
private var itemHeight = 0 // 动态存储item高度
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
super.onCreate(savedInstanceState)
|
setContentView(R.layout.activity_invitation_main)
|
|
// 初始化视图
|
initViews()
|
|
//初始化Adapter
|
initSuccessAdapter()
|
|
initRecorddapter()
|
|
//加载数据
|
getInvitereward()
|
//loadData()
|
|
//loadRecordData()
|
|
//启动轮播
|
startAutoScroll()
|
|
//分享
|
val btnInvite = findViewById<Button>(R.id.btnInvite)
|
btnInvite.setOnClickListener {
|
showImagePreviewDialog()
|
}
|
|
//邀请码
|
val invitationCodeText = findViewById<TextView>(R.id.invitationCodeText)
|
//invitationCodeText.text = "A1B2"
|
invitationCodeText.text = formatInvitationCode(PreferencesManager.getInviteCode());
|
findViewById<Button>(R.id.copyButton).setOnClickListener {
|
val clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
val clip = ClipData.newPlainText(
|
"邀请码",
|
invitationCodeText.text.toString().replace(" ", "") // 复制时去掉空格
|
)
|
clipboard.setPrimaryClip(clip)
|
Toast.makeText(this, "已复制邀请码", Toast.LENGTH_SHORT).show()
|
}
|
}
|
|
private fun initViews() {
|
findViewById<TextView>(R.id.tv_notic).apply {
|
text = Html.fromHtml(getString(R.string.invite_reward_text), Html.FROM_HTML_MODE_LEGACY)
|
}
|
recyclerSuccessView = findViewById(R.id.invitationsuccessRecyclerView)
|
addSuccessListener()
|
|
recyclerRecordView = findViewById(R.id.invitationrecordRecyclerView)
|
addRecordListener()
|
}
|
|
private fun addSuccessListener() {
|
recyclerSuccessView.viewTreeObserver.addOnGlobalLayoutListener {
|
if (recyclerSuccessView.childCount > 0) {
|
// 计算预期高度(60dp转px)
|
val expectedHeight = dpToPx(60f)
|
if (itemHeight != expectedHeight) {
|
// 修正所有item的高度
|
for (i in 0 until recyclerSuccessView.childCount) {
|
recyclerSuccessView.getChildAt(i).layoutParams.height = expectedHeight
|
}
|
// 请求重新布局
|
recyclerSuccessView.requestLayout()
|
itemHeight = expectedHeight
|
}
|
}
|
}
|
recyclerSuccessView.layoutManager = object : LinearLayoutManager(this@InvitationActivity) {
|
override fun onMeasure(
|
recycler: RecyclerView.Recycler, state: RecyclerView.State,
|
widthSpec: Int, heightSpec: Int
|
) {
|
if (itemHeight > 0) {
|
// 使用实际测量的高度
|
setMeasuredDimension(
|
View.resolveSize(widthSpec, width),
|
itemHeight
|
)
|
} else {
|
// 默认高度60dp
|
val defaultHeight = TypedValue.applyDimension(
|
TypedValue.COMPLEX_UNIT_DIP, 60f,
|
resources.displayMetrics
|
).toInt()
|
setMeasuredDimension(
|
View.resolveSize(widthSpec, width),
|
defaultHeight
|
)
|
}
|
}
|
}
|
}
|
|
private fun addRecordListener() {
|
recyclerRecordView.viewTreeObserver.addOnGlobalLayoutListener {
|
if (recyclerSuccessView.childCount > 0) {
|
// 计算预期高度(60dp转px)
|
val expectedHeight = dpToPx(60f)
|
if (itemHeight != expectedHeight) {
|
// 修正所有item的高度
|
for (i in 0 until recyclerSuccessView.childCount) {
|
recyclerSuccessView.getChildAt(i).layoutParams.height = expectedHeight
|
}
|
// 请求重新布局
|
recyclerSuccessView.requestLayout()
|
itemHeight = expectedHeight
|
}
|
}
|
}
|
recyclerRecordView.layoutManager = object : LinearLayoutManager(this@InvitationActivity) {
|
override fun onMeasure(
|
recycler: RecyclerView.Recycler, state: RecyclerView.State,
|
widthSpec: Int, heightSpec: Int
|
) {
|
if (itemHeight > 0) {
|
// 使用实际测量的高度
|
setMeasuredDimension(
|
View.resolveSize(widthSpec, width),
|
itemHeight
|
)
|
} else {
|
// 默认高度60dp
|
val defaultHeight = TypedValue.applyDimension(
|
TypedValue.COMPLEX_UNIT_DIP, 60f,
|
resources.displayMetrics
|
).toInt()
|
setMeasuredDimension(
|
View.resolveSize(widthSpec, width),
|
defaultHeight
|
)
|
}
|
}
|
}
|
}
|
|
private fun initSuccessAdapter() {
|
adapter = InvitationAdapter(this, data).apply {
|
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
// 数据插入时检查高度
|
recyclerSuccessView.post {
|
if (recyclerSuccessView.childCount > 0) {
|
itemHeight = recyclerSuccessView.getChildAt(0).height
|
}
|
}
|
}
|
})
|
}
|
recyclerSuccessView.adapter = adapter
|
}
|
|
private fun initRecorddapter() {
|
recordadapter = InvitationRecordAdapter(this, recorddata).apply {
|
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
// 数据插入时检查高度
|
recyclerRecordView.post {
|
if (recyclerRecordView.childCount > 0) {
|
itemHeight = recyclerRecordView.getChildAt(0).height
|
}
|
}
|
}
|
})
|
}
|
recyclerRecordView.adapter = recordadapter
|
}
|
|
private fun getInvitereward() {
|
lifecycleScope.launch {
|
try {
|
val response = RetrofitClient.apiService.getInvitereward()
|
if (response.code == "0") {
|
response.data?.let { records ->
|
// 正确更新现有列表(保持原有引用)
|
data.apply {
|
clear()
|
addAll(records.successInvite ?: emptyList())
|
}
|
|
recorddata.apply {
|
clear()
|
addAll(records.myInvite ?: emptyList()) // 注意这里使用myInvite
|
}
|
|
// 在UI线程更新适配器
|
withContext(Dispatchers.Main) {
|
adapter.notifyDataSetChanged()
|
recordadapter.notifyDataSetChanged()
|
}
|
} ?: run {
|
Log.w("API", "Response data is null")
|
}
|
} else {
|
Log.w("API", "Server error: ${response.msg}")
|
}
|
} catch (e: Exception) {
|
Log.e("getinviterewardError", "message: ${e.message}", e)
|
withContext(Dispatchers.Main) {
|
Toast.makeText(context, "请求失败: ${e.message}", Toast.LENGTH_SHORT).show()
|
}
|
}
|
}
|
}
|
|
// private fun loadData() {
|
// data.clear()
|
// data.addAll(
|
// listOf(
|
// InvitationRecord("H****e", "获得了1天会员", "已注册"),
|
// InvitationRecord("U****r", "获得了2天会员", "已注册"),
|
// InvitationRecord("A****e", "获得了免广告特权", "已注册"),
|
// InvitationRecord("B****e", "获得了3天会员", "已注册"),
|
// InvitationRecord("C****o", "获得了4天会员", "已注册")
|
// )
|
// )
|
// adapter.notifyDataSetChanged()
|
// }
|
|
// private fun loadRecordData() {
|
// recorddata.clear()
|
// recorddata.addAll(
|
// listOf(
|
// InvitationRecord("M****e", "", "未注册"),
|
// InvitationRecord("Q****r", "", "已注册"),
|
// InvitationRecord("W****e", "", "未注册"),
|
// InvitationRecord("E****e", "", "未注册"),
|
// InvitationRecord("R****o", "", "已注册")
|
// )
|
// )
|
// recordadapter.notifyDataSetChanged()
|
// }
|
|
private fun showImagePreviewDialog() {
|
val dialog = Dialog(this, android.R.style.Theme_Translucent_NoTitleBar).apply {
|
// 设置全屏+透明背景
|
window?.apply {
|
setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
}
|
|
setContentView(R.layout.dialog_image_preview)
|
setCancelable(true) // 点击空白处可关闭
|
|
val imageView = findViewById<ImageView>(R.id.ivPreview)
|
|
// 长按分享(不关闭对话框)
|
imageView.setOnLongClickListener {
|
shareImageToWechat()
|
true
|
}
|
|
// 点击图片关闭(可选)
|
imageView.setOnClickListener {
|
dismiss()
|
}
|
}
|
|
dialog.show()
|
}
|
|
|
// 分享资源图片到微信
|
private fun shareImageToWechat() {
|
try {
|
// 1. 将 drawable 图片保存到缓存目录
|
val drawable = ContextCompat.getDrawable(this, R.drawable.invite)!!
|
val bitmap = (drawable as BitmapDrawable).bitmap
|
|
// 2. 创建临时图片文件
|
val cacheDir = File(cacheDir, "shared_images").apply { mkdirs() }
|
val imageFile = File(cacheDir, "invite.jpg") // 指定文件名
|
FileOutputStream(imageFile).use { out ->
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)
|
}
|
|
// 3. 使用 FileProvider 获取 URI
|
val imageUri = FileProvider.getUriForFile(
|
this,
|
"${packageName}.fileprovider",
|
imageFile
|
)
|
|
// 4. 创建分享 Intent
|
val intent = Intent().apply {
|
action = Intent.ACTION_SEND
|
type = "image/jpeg"
|
putExtra(Intent.EXTRA_STREAM, imageUri)
|
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
|
setPackage("com.tencent.mm") // 指定微信包名
|
}
|
|
// 5. 启动分享选择器
|
val chooserIntent = Intent.createChooser(intent, "分享邀请图片")
|
if (intent.resolveActivity(packageManager) != null) {
|
startActivity(chooserIntent)
|
} else {
|
Toast.makeText(this, "未找到可分享的应用", Toast.LENGTH_SHORT).show()
|
}
|
} catch (e: Exception) {
|
Toast.makeText(this, "分享失败: ${e.message}", Toast.LENGTH_SHORT).show()
|
}
|
}
|
|
private fun formatInvitationCode(code: String): String {
|
return if (code.length > 1) {
|
code.chunked(1).joinToString(" ")
|
} else {
|
code
|
}
|
}
|
|
private val scrollRunnable = object : Runnable {
|
override fun run() {
|
if (data.isEmpty() || itemHeight <= 0) {
|
handler.postDelayed(this, scrollInterval)
|
return
|
}
|
|
// 使用取模运算确保位置在有效范围内
|
currentScrollPosition++
|
//currentRecordScrollPosition++
|
|
// 创建自定义平滑滚动器
|
val smoothScroller = object : LinearSmoothScroller(this@InvitationActivity) {
|
override fun getVerticalSnapPreference(): Int = SNAP_TO_START
|
|
override fun calculateDyToMakeVisible(view: View, snapPreference: Int): Int {
|
// 计算需要滚动的距离,确保完整显示下一个item
|
val top = view.top
|
val height = view.height
|
return when {
|
snapPreference == SNAP_TO_START -> -top
|
snapPreference == SNAP_TO_END -> -(top - (recyclerSuccessView.height - height))
|
else -> -(top - (recyclerSuccessView.height / 2 - height / 2))
|
}
|
}
|
|
override fun calculateTimeForScrolling(dx: Int): Int {
|
// 根据滚动距离动态计算时间,保持匀速
|
return maxOf((500f * abs(dx) / itemHeight).toInt(), 200)
|
}
|
}.apply {
|
targetPosition = currentScrollPosition
|
}
|
|
// val smoothRecordScroller = object : LinearSmoothScroller(this@InvitationActivity) {
|
// override fun getVerticalSnapPreference(): Int = SNAP_TO_START
|
//
|
// override fun calculateDyToMakeVisible(view: View, snapPreference: Int): Int {
|
// // 计算需要滚动的距离,确保完整显示下一个item
|
// val top = view.top
|
// val height = view.height
|
// return when {
|
// snapPreference == SNAP_TO_START -> -top
|
// snapPreference == SNAP_TO_END -> -(top - (recyclerRecordView.height - height))
|
// else -> -(top - (recyclerRecordView.height / 2 - height / 2))
|
// }
|
// }
|
//
|
// override fun calculateTimeForScrolling(dx: Int): Int {
|
// // 根据滚动距离动态计算时间,保持匀速
|
// return maxOf((500f * abs(dx) / itemHeight).toInt(), 200)
|
// }
|
// }.apply {
|
// targetPosition = currentRecordScrollPosition
|
// }
|
|
// 启动平滑滚动
|
recyclerSuccessView.layoutManager?.startSmoothScroll(smoothScroller)
|
|
//recyclerRecordView.layoutManager?.startSmoothScroll(smoothRecordScroller)
|
|
// 更智能的边界检测
|
(recyclerSuccessView.layoutManager as? LinearLayoutManager)?.let { lm ->
|
val lastVisible = lm.findLastVisibleItemPosition()
|
val totalItems = adapter.itemCount
|
|
// 当接近"虚拟列表"末尾时,跳转到中间位置
|
if (lastVisible >= totalItems - 3) {
|
val jumpPosition = (totalItems / 2) * (Int.MAX_VALUE / totalItems)
|
currentScrollPosition = jumpPosition
|
recyclerSuccessView.scrollToPosition(jumpPosition)
|
}
|
}
|
|
// (recyclerRecordView.layoutManager as? LinearLayoutManager)?.let { lm ->
|
// val lastVisible = lm.findLastVisibleItemPosition()
|
// val totalItems = recordadapter.itemCount
|
//
|
// // 当接近"虚拟列表"末尾时,跳转到中间位置
|
// if (lastVisible >= totalItems - 3) {
|
// val jumpPosition = (totalItems / 2) * (Int.MAX_VALUE / totalItems)
|
// currentRecordScrollPosition = jumpPosition
|
// recyclerRecordView.scrollToPosition(jumpPosition)
|
// }
|
// }
|
|
handler.postDelayed(this, scrollInterval)
|
}
|
}
|
|
private fun dpToPx(dp: Float): Int {
|
return (dp * resources.displayMetrics.density).toInt()
|
}
|
|
private fun startAutoScroll() {
|
handler.removeCallbacks(scrollRunnable) // 先移除之前的回调
|
handler.postDelayed(scrollRunnable, scrollInterval)
|
}
|
|
override fun onPause() {
|
handler.removeCallbacks(scrollRunnable)
|
super.onPause()
|
}
|
|
override fun onResume() {
|
super.onResume()
|
startAutoScroll()
|
}
|
}
|