效果演示
关键代码
创建一个
DynamicLayout
继承ViewGroup
实现
onMeasure
方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//测量自己
int maxWidth = 0;
int maxHeight = 0;
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
}
}
setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), resolveSize(maxHeight, heightMeasureSpec));
//测量childView
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
final int width = Math.max(0, getMeasuredWidth() - lp.leftMargin - lp.rightMargin);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, lp.leftMargin + lp.rightMargin, lp.width);
}
final int childHeightMeasureSpec;
if (lp.height == LayoutParams.MATCH_PARENT) {
final int height = Math.max(0, getMeasuredHeight() - lp.topMargin - lp.bottomMargin);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, lp.topMargin + lp.bottomMargin, lp.height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
- 实现
onLayout
方法
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft = lp.leftMargin;
int childTop = lp.topMargin;
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
- 拦截TouchEvent事件
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
- 实现
onTouchEvent
方法
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: " + event.getAction());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//处理触摸按下事件
handleTouchDown(event);
break;
case MotionEvent.ACTION_MOVE:
//处理触摸移动事件
handleTouchMove(event);
break;
}
return true;
}
private void handleTouchMove(MotionEvent event) {
if (mSelectedChild == null) {
return;
}
float x = event.getX();
float y = event.getY();
MarginLayoutParams lp = (MarginLayoutParams) mSelectedChild.getLayoutParams();
//计算view新的x轴位置并且防止超出左右边界
int childX = (int) (x - mTouchOffsetX);
if (childX < 0) {
lp.leftMargin = 0;
} else {
int maxX = getMeasuredWidth() - mSelectedChild.getMeasuredWidth();
lp.leftMargin = childX > maxX ? maxX : childX;
}
//计算view新的y轴位置并且防止超出上下边界
int childY = (int) (y - mTouchOffsetY);
if (childY < 0) {
lp.topMargin = 0;
} else {
int maxY = getMeasuredHeight() - mSelectedChild.getMeasuredHeight();
lp.topMargin = childY > maxY ? maxY : childY;
}
//重新布局 刷新位置
requestLayout();
}
private void handleTouchDown(MotionEvent event) {
float x = event.getX();
float y = event.getY();
setViewTranslationZ(0);
mSelectedChild = null;
int count = getChildCount();
for (int i = count - 1; i >= 0; i--) {
View child = getChildAt(i);
//获取View上下左右的坐标
Rect rect = getChildRect(child);
//通过判断按下位置是否在View之内进行选中View
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
//将选中View存为成员变量供Move事件使用
mSelectedChild = child;
setViewTranslationZ(mTranslationZ);
//计算出按下位置相对于选中View的坐标并存为成员变量供Move事件使用
mTouchOffsetX = x - rect.left;
mTouchOffsetY = y - rect.top;
break;
}
}
}
//获取View的上下左右坐标
private Rect getChildRect(View child) {
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
return new Rect(lp.leftMargin, lp.topMargin, lp.leftMargin + child.getMeasuredWidth(), lp.topMargin + child.getMeasuredHeight());
}
Github
- DynamicLayoutSample