前言
在自定义view时如何让她动起来呢?本人在14年面世的时候就被问到了listview下拉刷新时,如何下拉如何上移,还记得本人当初的答案是使用属性动画,被人好好的鄙视了一番,说多了,好了拔剑吧
- offsetLeftAndRight(offsetX) or offsetTopAndBottom(offsetY)
- layout方法
- 动态设置margin
- 动态设置pading(专门试了一下可以使用)
- scrollby和scrollto
offsetLeftAndRight(offsetX) or offsetTopAndBottom(offsetY)
上源码
public void offsetLeftAndRight(int offset) {
if (offset != 0) {
final boolean matrixIsIdentity = hasIdentityMatrix();
if (matrixIsIdentity) {
if (isHardwareAccelerated()) {
invalidateViewProperty(false, false);
} else {
final ViewParent p = mParent;
if (p != null && mAttachInfo != null) {
final Rect r = mAttachInfo.mTmpInvalRect;
int minLeft;
int maxRight;
if (offset < 0) {
minLeft = mLeft + offset;
maxRight = mRight;
} else {
minLeft = mLeft;
maxRight = mRight + offset;
}
r.set(0, 0, maxRight - minLeft, mBottom - mTop);
p.invalidateChild(this, r);
}
}
} else {
invalidateViewProperty(false, false);
}
mLeft += offset;
mRight += offset;
mRenderNode.offsetLeftAndRight(offset);
if (isHardwareAccelerated()) {
invalidateViewProperty(false, false);
invalidateParentIfNeededAndWasQuickRejected();
} else {
if (!matrixIsIdentity) {
invalidateViewProperty(false, true);
}
invalidateParentIfNeeded();
}
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
判断offset是否为0,也就是说是否存在滑动距离,不为0的情况下,根据是否在矩阵中做过标记来操作。如果做过标记,没有开启硬件加速则开始计算坐标。先获取到父view,如果父view不为空,在offset<0时,计算出左侧的最小边距,在offset>0时,计算出右侧的最大值,其实分析了这么多主要的实现代码就那一句 mRenderNode.offsetLeftAndRight(offset),由native实现的左右滑动,以上分析的部分主要计算view显示的区域。
最后总结一下,offsetLeftAndRight(int offset)就是通过offset值改变了View的getLeft()和getRight()实现了View的水平移动。
offsetTopAndBottom(int offset)方法实现原理与offsetLeftAndRight(int offset)相同,offsetTopAndBottom(int offset)通过offset值改变View的getTop()、getBottom()值,同样给出核心代码mRenderNode.offsetTopAndBottom(offset),这个方法也是有native实现
在实现自定义view的时候,可以直接使用这两个方法,简单,方便
layout
layout(int l, int t, int r, int b)
第一个参数 view左侧到父布局的距离
第二个参数 view顶部到父布局之间的距离
第三个参数 view右侧到父布局之间的距离
第四个参数 view底端到父布局之间的距离
scrollTo 和 scrollBy
scrollBy方法简单粗暴,调用scrollTo 方法,在当前的位置继续偏移(x , y)
这里把它归类到通过改变父布局实现view移动是有原因,如果在view中使用这个方法改变的是内容,不是改变view本身,如果在ViewGroup使用这个方法,改变的是子view的位置,相对来说这个实用的概率比较大.
LayoutParams
LayoutParams保存布局参数,通过改变局部参数里面的值改变view的位置,如果布局中有多个view,那么多个view的位置整体移动
@Override public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
int offsetX = x - lastX;
int offsetY = y - lastY;
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) getLayoutParams();
params.leftMargin = getLeft() + offsetX;
params.topMargin = getTop() + offsetY;
setLayoutParams(params);
break;
}
return true;
}