大伙在使用UIScrollView时,经常和UICenterOn脚本配合使用,但是随着需求的不断增加,会出现这个需求:第一个列表在拖动后不让它在最中间显示,还是保留在UIScrollView裁剪区域的其实部分,比如,一些非常多的列表,要求一条条显示,同时又不能出现半个,大伙第一个就能想到UICenterOn,但是拖着列表过程中,第一个列表就被固定在了中间,这样显示很难看。怎样解决这个问题呢?我在看UIScrollView代码的过成中看到了这个方法:
public bool RestrictWithinBounds (bool instant, bool horizontal, bool vertical)
这个方法的作用是让列表的显示限制在裁剪区域
那怎么判断这个列表超出了这个区域了呢(即列表的起始,末尾部分往中间显示)
我写了一个方法来判断,至于是什么原理,大伙通过看代码来研究了:
///
/// 可以填充区域 但没有填充区域
///
private bool IsOutOfBoundsPanel()
{
if (mPanel == null) mPanel = GetComponent
Vector4 clip = mPanel.finalClipRegion;
Bounds b = GetSpecialBounds(transform);
float hx = (clip.z == 0f) ? Screen.width : clip.z * 0.5f;
float hy = (clip.w == 0f) ? Screen.height : clip.w * 0.5f;
if (canMoveHorizontally)
{
if ((b.min.x > clip.x - hx) && (b.max.x > clip.x + hx)) return true;
if ((b.max.x < clip.x + hx) && (b.min.x < clip.x - hx)) return true;
}
if (canMoveVertically)
{
if ((b.min.y > clip.y - hy) && (b.max.y > clip.y + hy)) return true;
if ((b.max.y < clip.y + hy) && (b.min.y < clip.y - hy)) return true;
return true;
}
return false;
}
注意里面的GetSpecialBounds方法就是我在上一栏中写的方法,大伙也可以用UIScrollView 的bounds属性
最后在改动UIScrollView 的lateupdate方法(幸好lateupdate方法可以完全重写,否则要改动UIScrollView的脚本了):
我把改动的部分给粘上
// Restrict the contents to be within the scroll view's bounds
if (restrictWithinPanel && mPanel.clipping != UIDrawCall.Clipping.None)
{
if (NGUITools.GetActive(centerOnChild))
{
if (centerOnChild.nextPageThreshold != 0f)
{
mMomentum = Vector3.zero;
mScroll = 0f;
}
else
{
if (restrictWithinPanelIgnoreCenterOnChild && IsOutOfBoundsPanel())
{
RestrictWithinBounds(false, canMoveHorizontally, canMoveVertically);
}
else
{
centerOnChild.Recenter();
}
}
}
else
{
RestrictWithinBounds(false, canMoveHorizontally, canMoveVertically);
}
}
最后附上这个脚本的源码,供大家参考
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class UIScrollViewForSpecialBounds : UIScrollView
{
///
/// 限制区域填充的时候是否忽略UICenterOnChild脚本的作用
///
public bool restrictWithinPanelIgnoreCenterOnChild=false;
///
/// 针对改名字获取相应的bounds
///
public string TargetWidgetBoundsName;
public override Bounds bounds
{
get
{
mCalculatedBounds = true;
mTrans = transform;
return GetSpecialBounds(transform);
}
}
void LateUpdate()
{
if (!Application.isPlaying) return;
float delta = RealTime.deltaTime;
// Fade the scroll bars if needed
if (showScrollBars != ShowCondition.Always && (verticalScrollBar || horizontalScrollBar))
{
bool vertical = false;
bool horizontal = false;
if (showScrollBars != ShowCondition.WhenDragging || mDragID != -10 || mMomentum.magnitude > 0.01f)
{
vertical = shouldMoveVertically;
horizontal = shouldMoveHorizontally;
}
if (verticalScrollBar)
{
float alpha = verticalScrollBar.alpha;
alpha += vertical ? delta * 6f : -delta * 3f;
alpha = Mathf.Clamp01(alpha);
if (verticalScrollBar.alpha != alpha) verticalScrollBar.alpha = alpha;
}
if (horizontalScrollBar)
{
float alpha = horizontalScrollBar.alpha;
alpha += horizontal ? delta * 6f : -delta * 3f;
alpha = Mathf.Clamp01(alpha);
if (horizontalScrollBar.alpha != alpha) horizontalScrollBar.alpha = alpha;
}
}
if (!mShouldMove) return;
// Apply momentum
if (!mPressed)
{
if (mMomentum.magnitude > 0.0001f || mScroll != 0f)
{
if (movement == Movement.Horizontal)
{
mMomentum -= mTrans.TransformDirection(new Vector3(mScroll * 0.05f, 0f, 0f));
}
else if (movement == Movement.Vertical)
{
mMomentum -= mTrans.TransformDirection(new Vector3(0f, mScroll * 0.05f, 0f));
}
else if (movement == Movement.Unrestricted)
{
mMomentum -= mTrans.TransformDirection(new Vector3(mScroll * 0.05f, mScroll * 0.05f, 0f));
}
else
{
mMomentum -= mTrans.TransformDirection(new Vector3(
mScroll * customMovement.x * 0.05f,
mScroll * customMovement.y * 0.05f, 0f));
}
mScroll = NGUIMath.SpringLerp(mScroll, 0f, 20f, delta);
// Move the scroll view
Vector3 offset = NGUIMath.SpringDampen(ref mMomentum, 9f, delta);
MoveAbsolute(offset);
// Restrict the contents to be within the scroll view's bounds
if (restrictWithinPanel && mPanel.clipping != UIDrawCall.Clipping.None)
{
if (NGUITools.GetActive(centerOnChild))
{
if (centerOnChild.nextPageThreshold != 0f)
{
mMomentum = Vector3.zero;
mScroll = 0f;
}
else
{
if (restrictWithinPanelIgnoreCenterOnChild && IsOutOfBoundsPanel())
{
RestrictWithinBounds(false, canMoveHorizontally, canMoveVertically);
}
else
{
centerOnChild.Recenter();
}
}
}
else
{
RestrictWithinBounds(false, canMoveHorizontally, canMoveVertically);
}
}
if (onMomentumMove != null)
onMomentumMove();
}
else
{
mScroll = 0f;
mMomentum = Vector3.zero;
SpringPanel sp = GetComponent
if (sp != null && sp.enabled) return;
mShouldMove = false;
if (onStoppedMoving != null)
onStoppedMoving();
}
}
else
{
// Dampen the momentum
mScroll = 0f;
NGUIMath.SpringDampen(ref mMomentum, 9f, delta);
}
}
///
/// 可以填充区域 但没有填充区域
///
private bool IsOutOfBoundsPanel()
{
if (mPanel == null) mPanel = GetComponent
Vector4 clip = mPanel.finalClipRegion;
Bounds b = GetSpecialBounds(transform);
float hx = (clip.z == 0f) ? Screen.width : clip.z * 0.5f;
float hy = (clip.w == 0f) ? Screen.height : clip.w * 0.5f;
if (canMoveHorizontally)
{
if ((b.min.x > clip.x - hx) && (b.max.x > clip.x + hx)) return true;
if ((b.max.x < clip.x + hx) && (b.min.x < clip.x - hx)) return true;
}
if (canMoveVertically)
{
if ((b.min.y > clip.y - hy) && (b.max.y > clip.y + hy)) return true;
if ((b.max.y < clip.y + hy) && (b.min.y < clip.y - hy)) return true;
return true;
}
return false;
}
///
/// 获取特定的Bounds 按照gameobject的名字进行过滤
///
private Bounds GetSpecialBounds(Transform scrollView)
{
Matrix4x4 toLocal = scrollView.worldToLocalMatrix;
bool isSet = false;
Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
if (!(scrollView == null || (!NGUITools.GetActive(scrollView.gameObject))))
{
Vector3[] corners = null;// p.worldCorners;
List
GetSpecialBoundsWidget(transform, TargetWidgetBoundsName, ref widigetList);
for (int i = 0; i < widigetList.Count; i++)
{
UIWidget w = widigetList[i];
corners = w.worldCorners;
for (int j = 0; j < 4; ++j)
{
Vector3 v = toLocal.MultiplyPoint3x4(corners[j]);
if (v.x > max.x) max.x = v.x;
if (v.y > max.y) max.y = v.y;
if (v.z > max.z) max.z = v.z;
if (v.x < min.x) min.x = v.x;
if (v.y < min.y) min.y = v.y;
if (v.z < min.z) min.z = v.z;
isSet = true;
}
}
}
if (isSet)
{
Bounds b = new Bounds(min, Vector3.zero);
b.Encapsulate(max);
return b;
}
return new Bounds(Vector3.zero,Vector3.zero);
}
private void GetSpecialBoundsWidget(Transform parent,string speciBoundsName,ref List
{
if (parent == null)
return;
foreach(Transform child in parent)
{
GetSpecialBoundsWidget(child,speciBoundsName,ref list);
}
if(parent.name==speciBoundsName)
{
list.Add(parent.GetComponent
}
}
}
最后希望对大伙有所帮助。