我对Banner的定义:
- 多个图片
- 可以左右拖拽
- 下方的toggle会与上方的图片同时变化
- 可以自动轮播
类似于明日方舟主界面左下角的这个效果
好了,下面放代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;
namespace MetaFramework.UI.Widget
{
public class BannerWidget : MonoBehaviour, IBeginDragHandler, IEndDragHandler
{
public class BannerToggleView : MonoBehaviour
{
public void Init(int index, Action<int> action)
{
var toggle = GetComponent<Toggle>();
if (toggle == null) return;
toggle.onValueChanged.AddListener((tog) =>
{
if (tog)
{
action?.Invoke(index);
}
});
}
}
private ToggleGroup toggleGroup;
private Toggle[] toggleArray;
private ScrollRect rect;
private float startDragPosX;
private float rectContentTargetPosition;//0-1
private float speed = 4;//滑动速度
private float minDragOffset = 0.06f;
private float autoDragTime = 5f;
private List<float> posList = new List<float>();
private bool isDrag = false;
private float startTime = 0;
private int curIndex = 0;
private bool isInit = false;
void Start()
{
toggleGroup = GetComponentInChildren<ToggleGroup>();
toggleArray = toggleGroup.GetComponentsInChildren<Toggle>();
rect = GetComponent<ScrollRect>();
var imgChilds = rect.content.transform.childCount;
if (imgChilds != toggleArray.Length)
{
Debug.LogError("DragToggleScrollView init failed! toggle count not equal image count");
return;
}
var targetImgSize = rect.GetComponent<RectTransform>().sizeDelta;
for (int i = 0; i < rect.content.transform.childCount; i++)
{
var imgChild = rect.content.transform.GetChild(i);
imgChild.GetComponent<RectTransform>().sizeDelta = targetImgSize;
var layoutElement = imgChild.GetComponent<LayoutElement>();
if (layoutElement == null) layoutElement = imgChild.gameObject.AddComponent<LayoutElement>();
layoutElement.flexibleWidth = targetImgSize.x;
layoutElement.flexibleHeight = targetImgSize.y;
}
var rectWidth = GetComponent<RectTransform>().rect.width;
var totalWidth = rectWidth * rect.content.transform.childCount;
rect.content.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, totalWidth);
var horizontalLength = rect.content.rect.width - rectWidth;
for (int i = 0; i < rect.content.transform.childCount; i++)
{
posList.Add(rectWidth * i / horizontalLength);
}
for (int i = 0; i < toggleArray.Length; i++)
{
var dtv = toggleArray[i].gameObject.AddComponent<BannerToggleView>();
dtv.Init(i, MoveToPage);
}
isInit = true;
MoveToPage(0);
StartCoroutine(AutoDrag());
}
private IEnumerator AutoDrag()
{
while (true)
{
yield return new WaitForSeconds(autoDragTime);
if (curIndex >= toggleArray.Length - 1)
{
MoveToPage(0);
}
else
{
MoveNext();
}
}
yield return null;
}
void Update()
{
if (!isDrag)
{
startTime += Time.deltaTime;
float t = startTime * speed;
rect.horizontalNormalizedPosition = Mathf.Lerp(rect.horizontalNormalizedPosition, rectContentTargetPosition, t);
}
}
public void OnBeginDrag(PointerEventData eventData)
{
if (!isInit) return;
isDrag = true;
StopAllCoroutines();
startDragPosX = rect.horizontalNormalizedPosition;
}
public void OnEndDrag(PointerEventData eventData)
{
if (!isInit) return;
float endDragPosX = rect.horizontalNormalizedPosition;
float offset = endDragPosX - startDragPosX;
offset = Mathf.Abs(offset);
if (offset < minDragOffset)
{
MoveToPage(curIndex);
return;
}
if (startDragPosX <= endDragPosX)
{
MoveNext();
}
else
{
MovePre();
}
StartCoroutine(AutoDrag());
}
public void MoveNext()
{
MoveToPage(curIndex + 1);
}
public void MovePre()
{
MoveToPage(curIndex - 1);
}
public void MoveToPage(int targetIndex)
{
if (targetIndex < 0 || targetIndex >= toggleArray.Length)
{
return;
}
curIndex = targetIndex;
isDrag = false;
startTime = 0;
rectContentTargetPosition = posList[curIndex];
toggleArray[curIndex].isOn = true;
}
}
}
打个广告
我自己写了一个UI框架,有以下特性:
- 自动生成代码
- 使用MVC结构
- 脱离了Monobehaviour,方便热更
- 实现了UI栈和普通UI
- 虚拟列表
- 公用UI
- 红点系统
非常好用,喜欢的同学点个Star,谢谢
https://github.com/Meta404Dev/MetaJUI