1:修改群了会抽奖的动画

2:动画未完成
This commit is contained in:
2025-09-05 00:46:27 +08:00
parent c8adbbc590
commit cb3b7aa183
8 changed files with 828 additions and 30 deletions

View File

@@ -63,7 +63,7 @@ public class AppUpdateDialog extends BaseDialog<DialogAppUpdateBinding> implemen
};
mProgressDialog.setMax(100);//设置最大值
mProgressDialog.setTitle("安装包下载");//设置标题
mProgressDialog.setIcon(R.mipmap.ic_launcher);//设置标题小图标
mProgressDialog.setIcon(R.mipmap.ic_launcher_app);//设置标题小图标
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//设置样式为横向显示进度的样式
mProgressDialog.incrementProgressBy(0);//设置初始值为0其实可以不用设置默认就是0
mProgressDialog.setIndeterminate(false);//是否精确显示对话框flase为是反之为否

View File

@@ -10,7 +10,7 @@ import lombok.Data;
public class XlhDrawBean {
private int gift_id;
private String gift_name;
private String gift_base_image;
private String base_image;
private String gift_price;
private int count ;
}

View File

@@ -15,7 +15,7 @@ public class GiftItemAdapter extends BaseQuickAdapter<XlhDrawBean, BaseViewHolde
protected void convert(BaseViewHolder helper, XlhDrawBean item) {
helper.setText(R.id.tv_gift_name, item.getGift_name()+"x"+item.getCount());
ImageUtils.loadHeadCC(item.getGift_base_image(),helper.getView(R.id.gift_img));
ImageUtils.loadHeadCC(item.getBase_image(),helper.getView(R.id.gift_img));
}
}

View File

@@ -1,20 +1,143 @@
package com.xscm.moduleutil.dialog.giftLottery;
import android.view.View;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.xscm.moduleutil.R;
import com.xscm.moduleutil.bean.GiftBean;
import com.xscm.moduleutil.utils.ImageUtils;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
@Getter
public class GiftXlhChouAdapter extends BaseQuickAdapter<GiftBean, BaseViewHolder> {
private List<GiftBean> giftLists = new ArrayList<>();
private int selectedPosition = -1;
private static final int LOOP_COUNT = 6; // 循环倍数
public GiftXlhChouAdapter() {
super(R.layout.item_xlh);
}
// 设置数据时创建循环数据
@Override
public void setNewData(List<GiftBean> data) {
this.giftLists = data != null ? data : new ArrayList<>();
super.setNewData(createLoopData());
}
/**
* 创建循环数据
* @return 循环数据列表
*/
private List<GiftBean> createLoopData() {
List<GiftBean> loopData = new ArrayList<>();
if (giftLists.isEmpty()) {
return loopData;
}
// 创建足够多的循环数据以实现循环效果
for (int i = 0; i < LOOP_COUNT; i++) {
loopData.addAll(giftLists);
}
return loopData;
}
@Override
public int getItemCount() {
// 如果原始数据为空返回0
if (giftLists.isEmpty()) {
return 0;
}
// 返回循环数据的数量
return super.getItemCount();
}
/**
* 获取实际位置(将循环位置映射到原始数据位置)
* @param position 循环列表中的位置
* @return 原始数据中的实际位置
*/
private int getActualPosition(int position) {
if (giftLists.isEmpty()) {
return 0;
}
return position % giftLists.size();
}
@Override
protected void convert(BaseViewHolder helper, GiftBean item) {
helper.setText(R.id.tv_gift_name, item.getGift_name());
helper.setText(R.id.tv_gift_pic, item.getGift_price());
ImageUtils.loadHeadCC(item.getBase_image(),helper.getView(R.id.iv_gift_image));
// 获取实际位置
int actualPosition = getActualPosition(helper.getAdapterPosition());
GiftBean actualItem = giftLists.get(actualPosition);
helper.setText(R.id.tv_gift_name, actualItem.getGift_name());
helper.setText(R.id.tv_gift_pic, actualItem.getGift_price());
ImageUtils.loadHeadCC(actualItem.getBase_image(), helper.getView(R.id.iv_gift_image));
// 处理选中状态
View selectedIcon = helper.getView(R.id.selected_icon);
// 处理选中状态
if (selectedIcon != null) {
// 检查当前item是否为选中位置
if (actualPosition == selectedPosition) {
selectedIcon.setVisibility(View.VISIBLE);
} else {
selectedIcon.setVisibility(View.GONE);
}
}
}
/**
* 设置选中位置并更新UI
* @param position 选中的位置
*/
public void setSelectedPosition(int position) {
int previousPosition = selectedPosition;
selectedPosition = position;
if (previousPosition >= 0) {
notifyItemsByActualPosition(previousPosition, false);
}
if (selectedPosition >= 0) {
notifyItemsByActualPosition(selectedPosition, true);
}
}
/**
* 根据实际位置通知所有匹配的item更新
* @param actualPosition 原始数据中的位置
* @param isSelected 是否选中
*/
private void notifyItemsByActualPosition(int actualPosition, boolean isSelected) {
if (giftLists.isEmpty() || actualPosition >= giftLists.size()) {
return;
}
// 通知所有匹配该实际位置的item更新
for (int i = 0; i < getItemCount(); i++) {
if (getActualPosition(i) == actualPosition) {
notifyItemChanged(i);
}
}
}
/**
* 清除选中状态
*/
public void clearSelection() {
int previousPosition = selectedPosition;
selectedPosition = -1;
if (previousPosition >= 0) {
notifyItemsByActualPosition(previousPosition, false);
}
}
/**
* 获取原始数据大小
* @return 原始数据大小
*/
public int getOriginalDataSize() {
return giftLists.size();
}
}

View File

@@ -13,6 +13,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearSnapHelper;
import androidx.recyclerview.widget.RecyclerView;
import com.blankj.utilcode.util.TimeUtils;
import com.xscm.moduleutil.R;
@@ -26,6 +28,8 @@ import com.xscm.moduleutil.bean.blindboxwheel.XlhDrawBean;
import com.xscm.moduleutil.databinding.FragmentTourClubDialogBinding;
import com.xscm.moduleutil.dialog.WebViewDialog;
import com.xscm.moduleutil.utils.ImageUtils;
import com.xscm.moduleutil.widget.CenterScrollHelper;
import com.xscm.moduleutil.widget.EqualSpaceItemDecoration;
import com.xscm.moduleutil.widget.InfintLinearLayoutManager;
import com.xscm.moduleutil.widget.pagerecyclerview.PagerGridSnapHelper;
@@ -47,6 +51,10 @@ public class TourClubDialogFragment extends BaseMvpDialogFragment<GiftLotteryPre
private XlhRecordDialog xlhRecordDialog;
private GiftXlhChouAdapter giftXlhChouAdapter;
private CenterScrollHelper scrollHelper;
private static final int ITEM_COUNT = 24;
private static final int ROTATION_COUNT = 3;
@Override
protected GiftLotteryPresenter bindPresenter() {
@@ -121,14 +129,31 @@ public class TourClubDialogFragment extends BaseMvpDialogFragment<GiftLotteryPre
mBinding.tvJl.setOnClickListener(this::onClick);
giftXlhChouAdapter=new GiftXlhChouAdapter();
GridLayoutManager layoutManager = new GridLayoutManager(getActivity(), 3);
mBinding.recycleView.setLayoutManager(new InfintLinearLayoutManager(mBinding.recycleView));
// 使用 LinearLayoutManager 横向滚动
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
mBinding.recycleView.setLayoutManager(layoutManager);
mBinding.recycleView.setOnFlingListener(null);
// 设置滚动辅助工具
PagerGridSnapHelper pageSnapHelper = new PagerGridSnapHelper();
pageSnapHelper.attachToRecyclerView(mBinding.recycleView);
LinearSnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(mBinding.recycleView);
// 添加 ItemDecoration 实现等宽分布
int spacing = getResources().getDimensionPixelSize(R.dimen.dp_15); // 根据需要调整间距
mBinding.recycleView.addItemDecoration(new EqualSpaceItemDecoration(3, spacing));
mBinding.recycleView.setAdapter(giftXlhChouAdapter);
// 初始化滚动帮助类
scrollHelper = new CenterScrollHelper(mBinding.recycleView);
// 初始化时滚动到中间位置,确保有足够的前后数据
mBinding.recycleView.post(() -> {
if (giftXlhChouAdapter.getOriginalDataSize() > 0) {
int middlePosition = giftXlhChouAdapter.getItemCount() / 2;
mBinding.recycleView.scrollToPosition(middlePosition);
}
});
}
@@ -190,6 +215,8 @@ public class TourClubDialogFragment extends BaseMvpDialogFragment<GiftLotteryPre
getRule_url=blindBoxBean.getRule_url();
upTitle(blindBoxBean.getBox_price());
giftLists = blindBoxBean.getGift_list();
// 初始滚动到中间位置,避免边界问题
// mBinding.recycleView.scrollToPosition(Integer.MAX_VALUE / 2 - (Integer.MAX_VALUE / 2) % giftLists.size());
// 获取结束时间并启动倒计时
if (blindBoxBean.getXlh_end_time() != null && !blindBoxBean.getXlh_end_time().isEmpty()) {
@@ -228,7 +255,7 @@ public class TourClubDialogFragment extends BaseMvpDialogFragment<GiftLotteryPre
private void upTitle(int boxPrice){
mBinding.tvOne.setText(boxPrice+"币一次");
mBinding.tvTen.setText((boxPrice*10)+"币十次");
mBinding.tvHundred.setText((boxPrice*100)+"");
mBinding.tvHundred.setText((boxPrice*100)+"");
}
// TODO: 2025/8/29 接收im推送过来的消息
@@ -357,27 +384,367 @@ public class TourClubDialogFragment extends BaseMvpDialogFragment<GiftLotteryPre
@Override
public void xlhChouSuccess(List<XlhDrawBean> data) {
if (data != null){
// 创建并显示对话框
XlhObtainDialog dialog = new XlhObtainDialog(getActivity());
dialog.setOnGiftItemClickListener(new XlhObtainDialog.OnGiftItemClickListener() {
// 抽奖完成后执行动画滚动
mBinding.recycleView.postDelayed(() -> {
int winningPosition = findHighestValueWinningPosition(data);
if (winningPosition != -1) {
@Override
public void onPlayAgainClick() {
// 处理再玩一次点击事件
MvpPre.xlhChou(roomId,num);
startLotteryAnimation(winningPosition);
/**
// 计算在循环列表中的目标位置(滚动几圈后停在目标位置)
int loopCount = 4; // 滚动3圈
int originalSize = giftXlhChouAdapter.getOriginalDataSize();
// 计算目标在循环列表中的位置(确保在中间区域)
int middleBaseIndex = (loopCount * originalSize) + (originalSize / 2);
int targetLoopIndex = middleBaseIndex + (winningPosition % originalSize);
// 使用scrollWithCircles方法执行带动画的滚动带完成回调
scrollHelper.scrollWithCircles(
targetLoopIndex, // 在循环列表中的位置
loopCount, // 滚动圈数
500, // 每个item滚动时间200ms控制速度
originalSize, // 原始数据大小
() -> { // 滚动完成回调
// 滚动完成后更新选中状态(使用原始位置)
if (giftXlhChouAdapter != null) {
giftXlhChouAdapter.setSelectedPosition(winningPosition);
}
// 滚动完成后延迟一小段时间再居中确保UI更新完成
mBinding.recycleView.postDelayed(() -> {
// 手动将选中项居中
centerSelectedItem(winningPosition, originalSize);
// 显示结果对话框
getActivity().runOnUiThread(() -> {
showResultDialog(data);
});
}, 100);
// 在滚动完成后再显示对话框
// getActivity().runOnUiThread(() -> {
// showResultDialog(data);
// });
}
);
*/
} else {
// 如果没有找到中奖位置,直接显示对话框
showResultDialog(data);
}
@Override
public void onCloseClick() {
// 处理关闭点击事件
dialog.dismiss();
}
});
dialog.show();
dialog.setGiftList(data);
}, 300);
}
MvpPre.wallet();
}
private void startLotteryAnimation(int targetIndex) {
// 确保适配器有数据
if (giftXlhChouAdapter == null || giftXlhChouAdapter.getOriginalDataSize() <= 0) {
showResultDialogWithoutAnimation(targetIndex);
return;
}
LinearLayoutManager layoutManager = (LinearLayoutManager) mBinding.recycleView.getLayoutManager();
if (layoutManager == null) {
showResultDialogWithoutAnimation(targetIndex);
return;
}
// 获取当前第一个可见的item位置
int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();
if (firstVisiblePosition == RecyclerView.NO_POSITION) {
firstVisiblePosition = 0;
}
// 获取当前在原始数据中的实际位置
int currentActualPosition = firstVisiblePosition % giftXlhChouAdapter.getOriginalDataSize();
// 计算需要滚动的总item数
// 滚动指定圈数 + 从当前位置到目标位置的距离
int itemsToScroll = ROTATION_COUNT * giftXlhChouAdapter.getOriginalDataSize()
+ (targetIndex - currentActualPosition + giftXlhChouAdapter.getOriginalDataSize())
% giftXlhChouAdapter.getOriginalDataSize();
// 计算目标位置
int targetPosition = firstVisiblePosition + itemsToScroll;
// 确保目标位置在合理范围内
int maxPosition = giftXlhChouAdapter.getItemCount() - 1;
if (targetPosition > maxPosition) {
targetPosition = maxPosition;
}
// 确保目标位置有效
if (targetPosition >= 0) {
try {
SmoothScroller smoothScroller = new SmoothScroller(getActivity(), targetPosition);
layoutManager.startSmoothScroll(smoothScroller);
} catch (IllegalArgumentException e) {
// 如果仍然出现异常,使用备选方案
showResultDialogWithoutAnimation(targetIndex);
}
} else {
showResultDialogWithoutAnimation(targetIndex);
}
}
private void showResultDialogWithoutAnimation(int targetIndex) {
// 直接设置选中状态
if (giftXlhChouAdapter != null) {
giftXlhChouAdapter.setSelectedPosition(targetIndex);
}
// 延迟显示结果对话框
mBinding.recycleView.postDelayed(() -> {
// 创建假的XlhDrawBean数据用于显示结果
List<XlhDrawBean> fakeData = new ArrayList<>();
// 这里根据需要构造假数据
showResultDialog(fakeData);
}, 300);
}
private class SmoothScroller extends androidx.recyclerview.widget.LinearSmoothScroller {
private int targetPosition;
public SmoothScroller(android.content.Context context, int targetPosition) {
super(context);
this.targetPosition = targetPosition;
}
@Override
protected int calculateTimeForScrolling(int dx) {
// 自定义滚动时间,实现速度变化
float distance = Math.abs(dx);
float speed = 1f;
if (distance < 100) {
speed = 0.3f; // 慢
} else if (distance < 300) {
speed = 1f; // 快
} else {
speed = 0.3f; // 慢
}
return (int) (distance / speed);
}
@Override
public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
// 将目标项移动到屏幕中心
return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
}
@Override
public int getTargetPosition() {
// 添加更严格的边界检查
if (giftXlhChouAdapter != null && targetPosition >= 0 && targetPosition < giftXlhChouAdapter.getItemCount()) {
return targetPosition;
}
// 如果超出范围,返回一个安全的位置
if (giftXlhChouAdapter != null && giftXlhChouAdapter.getItemCount() > 0) {
return Math.max(0, Math.min(targetPosition, giftXlhChouAdapter.getItemCount() - 1));
}
return 0; // 返回默认位置
}
}
/**
* 将指定的原始位置item居中显示
* @param originalPosition 原始数据中的位置
* @param originalSize 原始数据大小
*/
private void centerSelectedItem(int originalPosition, int originalSize) {
LinearLayoutManager layoutManager = (LinearLayoutManager) mBinding.recycleView.getLayoutManager();
if (layoutManager == null) return;
// 计算出需要滚动到的目标位置
int targetPosition = originalPosition + 4 * originalSize;
// 使用smoothScrollToPosition滚动到目标位置
mBinding.recycleView.smoothScrollToPosition(targetPosition);
// 等待滚动完成
mBinding.recycleView.postDelayed(() -> {
// 计算出选中项的偏移量,确保它在中间
View view = layoutManager.findViewByPosition(targetPosition);
if (view == null) return;
// 计算出RecyclerView的宽度和View的宽度
int recyclerViewWidth = mBinding.recycleView.getWidth();
int viewWidth = view.getWidth();
// 计算偏移量确保view居中
int offsetToCenter = (recyclerViewWidth / 2) - (viewWidth / 2) - view.getLeft();
// 调整滚动的位置
mBinding.recycleView.scrollBy(offsetToCenter, 0);
}, 500); // 500毫秒的延迟根据实际情况调整
}
/**
* 直接滚动到指定位置并居中
* @param targetPosition 目标位置
*/
private void directScrollToCenter(int targetPosition) {
if (mBinding.recycleView.getLayoutManager() == null) return;
mBinding.recycleView.post(() -> {
int screenWidth = mBinding.recycleView.getWidth();
if (screenWidth <= 0) return;
// 计算item宽度
int itemWidth = screenWidth / 3;
// 计算目标位置的左边缘
int targetLeft = targetPosition * itemWidth;
// 计算使item居中需要滚动到的位置
int targetScrollX = targetLeft - (screenWidth - itemWidth) / 2;
// 直接滚动到目标位置
mBinding.recycleView.scrollBy(
targetScrollX - mBinding.recycleView.computeHorizontalScrollOffset(),
0
);
});
}
/**
* 滚动到指定位置并使该位置的item居中显示
* @param selectedPosition 选中的原始位置
*/
private void scrollToCenterWithSelected(int selectedPosition) {
if (mBinding.recycleView.getLayoutManager() == null) return;
int screenWidth = mBinding.recycleView.getWidth();
if (screenWidth <= 0) return;
// 计算item宽度
int itemWidth = screenWidth / 3;
// 计算要滚动到的中心位置使selectedPosition的item居中
int middlePositionInLoop = (giftXlhChouAdapter.getItemCount() / 2 / giftXlhChouAdapter.getOriginalDataSize())
* giftXlhChouAdapter.getOriginalDataSize() + selectedPosition;
// 滚动到目标位置
mBinding.recycleView.smoothScrollToPosition(middlePositionInLoop);
// 额外确保居中
mBinding.recycleView.postDelayed(() -> {
if (scrollHelper != null) {
scrollHelper.scrollToCenter(middlePositionInLoop);
}
}, 300);
}
/**
* 显示抽奖结果对话框
* @param data 中奖数据
*/
private void showResultDialog(List<XlhDrawBean> data) {
// 创建并显示对话框
XlhObtainDialog dialog = new XlhObtainDialog(getActivity());
dialog.setOnGiftItemClickListener(new XlhObtainDialog.OnGiftItemClickListener() {
@Override
public void onPlayAgainClick() {
// 处理再玩一次点击事件
MvpPre.xlhChou(roomId, num);
}
@Override
public void onCloseClick() {
// 处理关闭点击事件
dialog.dismiss();
}
});
dialog.show();
dialog.setGiftList(data);
}
/**
* 根据中奖结果找到最高价值奖品在giftLists中的位置
* @param data 中奖结果列表
* @return 最高价值奖品在giftLists中的下标如果没有找到则返回-1
*/
private int findHighestValueWinningPosition(List<XlhDrawBean> data) {
if (data == null || data.isEmpty() || giftLists == null || giftLists.isEmpty()) {
return -1;
}
int targetPosition = -1;
int highestPrice = -1;
// 遍历giftLists查找中奖项目
for (int i = 0; i < giftLists.size(); i++) {
GiftBean giftBean = giftLists.get(i);
// 在中奖数据中查找匹配的礼品
for (XlhDrawBean xlhDrawBean : data) {
// 比较gift_id是否匹配
if (giftBean.getGift_id().equals(String.valueOf(xlhDrawBean.getGift_id()))) {
// 解析价格处理可能的NumberFormatException
try {
int currentPrice = Integer.parseInt(giftBean.getGift_price());
// 如果当前价格更高,则更新目标位置和最高价格
if (currentPrice > highestPrice) {
highestPrice = currentPrice;
targetPosition = i;
}
} catch (NumberFormatException e) {
// 如果价格解析失败使用默认值0进行比较
if (highestPrice <= 0) {
highestPrice = 0;
targetPosition = i;
}
}
break; // 找到匹配项后跳出内层循环
}
}
}
return targetPosition;
}
/**
* 精确将指定位置的item滚动到中心
* @param position 目标位置
*/
private void preciseScrollToCenter(int position) {
if (mBinding.recycleView.getLayoutManager() == null) return;
int screenWidth = mBinding.recycleView.getWidth();
if (screenWidth <= 0) return;
// 计算item宽度
int itemWidth = screenWidth / 3;
// 计算屏幕中心位置
int screenCenter = screenWidth / 2;
// 计算目标item的中心位置在当前可见区域中
int targetItemCenter = (mBinding.recycleView.computeHorizontalScrollOffset() / itemWidth) * itemWidth
+ position * itemWidth + itemWidth / 2;
// 计算需要滚动的距离使目标item居中
int scrollDistance = targetItemCenter - screenCenter;
// 获取当前滚动位置
int currentScrollX = mBinding.recycleView.computeHorizontalScrollOffset();
// 执行滚动使选中项居中
mBinding.recycleView.smoothScrollBy(scrollDistance - currentScrollX, 0);
}
}

View File

@@ -0,0 +1,255 @@
package com.xscm.moduleutil.widget;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;
/**
* @Author lxj$
* @Time $ 2025-9-4 21:04:34$
* @Description 自定义滚动辅助类$
*/
public class CenterScrollHelper {
private RecyclerView recyclerView;
public CenterScrollHelper(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
}
/**
* 循环滚动指定圈数后停在目标位置
* @param targetPosition 目标位置
* @param circles 滚动圈数
* @param durationPerItem 每个item滚动的持续时间控制速度
* @param adapterSize 适配器总大小
*/
public void scrollWithCircles(int targetPosition, int circles, int durationPerItem, int adapterSize) {
scrollWithCircles(targetPosition, circles, durationPerItem, adapterSize, null);
}
/**
* 循环滚动指定圈数后停在目标位置(带回调)
* @param targetPosition 目标位置
* @param circles 滚动圈数
* @param durationPerItem 每个item滚动的持续时间控制速度
* @param adapterSize 适配器总大小
* @param onComplete 滚动完成回调
*/
public void scrollWithCircles(int targetPosition, int circles, int durationPerItem,
int adapterSize, Runnable onComplete) {
if (recyclerView.getLayoutManager() == null) {
if (onComplete != null) onComplete.run();
return;
}
// 计算总滚动位置(多圈+目标位置)
int totalItems = circles * adapterSize + targetPosition;
// 使用LinearSmoothScroller进行平滑滚动
LinearSmoothScroller smoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
@Override
protected int calculateTimeForScrolling(int dx) {
// 使用缓动函数计算时间,实现从慢到快再到慢的效果
// return calculateEasingTime(dx, durationPerItem);
// 简单线性时间计算,确保滚动速度一致
int screenWidth = recyclerView.getWidth();
if (screenWidth <= 0) {
return durationPerItem * 3;
}
int itemWidth = screenWidth / 3;
int items = Math.max(1, dx / itemWidth);
return durationPerItem * items;
}
@Override
protected void onStop() {
super.onStop();
// 滚动停止后确保目标位置居中
// scrollToCenter(targetPosition);
// 执行完成回调
if (onComplete != null) {
recyclerView.post(onComplete);
}
}
};
smoothScroller.setTargetPosition(totalItems);
recyclerView.getLayoutManager().startSmoothScroll(smoothScroller);
}
/**
* 使用缓动函数计算滚动时间,实现加速减速效果
* @param dx 滚动距离
* @param baseDurationPerItem 基础时间
* @return 计算后的时间
*/
private int calculateEasingTime(int dx, int baseDurationPerItem) {
if (dx <= 0) return 0;
// 获取屏幕宽度作为参考
int screenWidth = recyclerView.getWidth();
if (screenWidth <= 0) {
return baseDurationPerItem * 3; // 默认值
}
// 计算item宽度假设每屏3个item
int itemWidth = screenWidth / 3;
// 计算滚动的item数量
int items = Math.max(1, dx / itemWidth);
// 使用easeInOutQuad缓动函数实现先慢中快后慢的效果
double progress = Math.min(1.0, (double) items / 100); // 假设100个item为完整过程
// easeInOutQuad缓动函数
double easeProgress;
if (progress < 0.5) {
easeProgress = 2 * progress * progress; // 先慢后快
} else {
easeProgress = 1 - Math.pow(-2 * progress + 2, 2) / 2; // 后慢
}
// 计算时间:开始慢(500ms),后来快(50ms)
int minDuration = 50; // 最快速度
int maxDuration = 500; // 最慢速度
int calculatedTime = (int) (maxDuration - (maxDuration - minDuration) * easeProgress);
return Math.max(minDuration, calculatedTime);
}
/**
* 将指定位置的item精确居中显示
* @param position 需要居中的位置(在循环列表中的实际位置)
* @param originalSize 原始数据大小
*/
public void scrollToCenter(int position, int originalSize) {
if (recyclerView.getLayoutManager() == null) return;
int screenWidth = recyclerView.getWidth();
if (screenWidth <= 0) return;
// 计算item宽度假设每个item等宽
int itemWidth = screenWidth / 3; // 每屏显示3个item
// 计算使item居中需要滚动的总距离
int targetScrollX = position * itemWidth - (screenWidth - itemWidth) / 2;
// 获取当前滚动位置
int currentScrollX = recyclerView.computeHorizontalScrollOffset();
// 计算需要滚动的距离
int scrollDistance = targetScrollX - currentScrollX;
// 执行滚动
recyclerView.smoothScrollBy(scrollDistance, 0);
}
/**
* 将指定位置的item精确居中显示简化版
* @param position 需要居中的位置
*/
public void scrollToCenter(int position) {
if (recyclerView.getLayoutManager() == null) return;
int screenWidth = recyclerView.getWidth();
if (screenWidth <= 0) return;
// 计算item宽度
int itemWidth = screenWidth / 3;
// 计算目标位置的左边缘
int targetLeft = position * itemWidth;
// 计算使item居中需要滚动到的位置
int targetScrollX = targetLeft - (screenWidth - itemWidth) / 2;
// 获取当前滚动位置
int currentScrollX = recyclerView.computeHorizontalScrollOffset();
// 计算需要滚动的距离
int scrollDistance = targetScrollX - currentScrollX;
// 如果距离很小,就不需要滚动了
if (Math.abs(scrollDistance) > 5) {
// 执行滚动
recyclerView.smoothScrollBy(scrollDistance, 0);
}
}
/**
* 将指定位置的item居中显示支持循环
*/
public void centerItem(int targetPosition, int adapterSize) {
if (recyclerView.getLayoutManager() == null) return;
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int screenWidth = recyclerView.getWidth();
if (screenWidth <= 0) return;
int itemWidth = screenWidth / 3; // 每个item占屏幕1/3
// 计算需要滚动的距离使item居中
// 这里需要根据实际的item宽度和屏幕宽度来计算居中位置
int targetScrollX = targetPosition * itemWidth - (screenWidth - itemWidth) / 2;
int currentScrollX = recyclerView.computeHorizontalScrollOffset();
int scrollDistance = targetScrollX - currentScrollX;
// 使用smoothScrollBy确保平滑滚动到居中位置
recyclerView.smoothScrollBy(scrollDistance, 0);
}
/**
* 使用更复杂的缓动函数计算滚动时间
* @param dx 滚动距离
* @param baseDurationPerItem 基础时间
* @return 计算后的时间
*/
private int calculateAdvancedEasingTime(int dx, int baseDurationPerItem) {
if (dx <= 0) return 0;
int screenWidth = recyclerView.getWidth();
if (screenWidth <= 0) {
return baseDurationPerItem * 3;
}
int itemWidth = screenWidth / 3;
int totalItems = dx / itemWidth;
// 分段处理:开始慢,中间快,结束慢
int totalTime = 0;
for (int i = 0; i < totalItems; i++) {
// 计算当前位置的进度 (0-1)
double progress = (double) i / Math.max(1, totalItems);
// 使用贝塞尔缓动函数:慢-快-慢
double easedProgress = bezierEasing(progress, 0.25, 0.1, 0.25, 1.0);
// 根据进度调整时间
int minTime = 30; // 最快时间
int maxTime = baseDurationPerItem * 2; // 最慢时间
int itemTime = (int) (maxTime - (maxTime - minTime) * easedProgress);
totalTime += itemTime;
}
return Math.max(100, totalTime); // 至少100ms
}
/**
* 贝塞尔缓动函数
* @param t 时间进度 (0-1)
* @param x1 控制点1 x
* @param y1 控制点1 y
* @param x2 控制点2 x
* @param y2 控制点2 y
* @return 缓动后的值
*/
private double bezierEasing(double t, double x1, double y1, double x2, double y2) {
// 简化的贝塞尔曲线计算
// 这里使用一个近似算法
double a = 1 - t;
return 3 * a * a * t * y1 + 3 * a * t * t * y2 + t * t * t;
}
}

View File

@@ -0,0 +1,51 @@
package com.xscm.moduleutil.widget;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
/**
* @Author lxj$
* @Time $ 2025-9-4 20:32:52$
* @Description 自定义的让在item平分屏幕宽度$
*/
public class EqualSpaceItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacing;
public EqualSpaceItemDecoration(int spanCount, int spacing) {
this.spanCount = spanCount;
this.spacing = spacing;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
// 设置间距
outRect.left = spacing / 2;
outRect.right = spacing / 2;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
// 确保每个item宽度为屏幕的1/3
int childCount = parent.getChildCount();
int screenWidth = parent.getWidth();
int itemWidth = screenWidth / spanCount;
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
ViewGroup.LayoutParams params = child.getLayoutParams();
params.width = itemWidth;
child.setLayoutParams(params);
}
}
}

View File

@@ -279,6 +279,8 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/dp_15"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintTop_toBottomOf="@+id/cl_gift"
app:layout_constraintBottom_toTopOf="@+id/exchange_layout"/>