项目效果图,需求点击添加图片选择图片后动态添加到自定义画板控件中。
在做项目时,有个需要做类似画板的功能,需要动态选择图片,将图片存储到自定义view控件中,在添加到父控件中。这时我们发现当我们调用Viewgroup的addview方法后Viewgroup下的子View会进行重绘,并且如果我们在addView()方法之前对子View进行view.layout(l,t,r,b)操作时会发现并没有什么卵用
具体我们可以查看源码:
public void addView(View child) { addView(child, -1); }
public void addView(View child, int index) { if (child == null) { throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); } LayoutParams params = child.getLayoutParams();//可以发现addView会获取View的LayoutParams if (params == null) { params = generateDefaultLayoutParams();//如果没有传入LayoutParams会产生个默认的LayoutParams if (params == null) { throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); } } addView(child, index, params); }接着往下看调用addView(child,index,params):
public void addView(View child, int index, LayoutParams params) { if (DBG) { System.out.println(this + " addView"); } if (child == null) { throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); } // addViewInner() will call child.requestLayout() when setting the new LayoutParams // therefore, we call requestLayout() on ourselves before, so that the child's request // will be blocked at our level requestLayout(); invalidate(true); addViewInner(child, index, params, false); }
private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) { if (mTransition != null) { // Don't prevent other add transitions from completing, but cancel remove // transitions to let them complete the process before we add to the container mTransition.cancel(LayoutTransition.DISAPPEARING); } if (child.getParent() != null) { throw new IllegalStateException("The specified child already has a parent. " + "You must call removeView() on the child's parent first."); } if (mTransition != null) { mTransition.addChild(this, child); } if (!checkLayoutParams(params)) { params = generateLayoutParams(params); } if (preventRequestLayout) { child.mLayoutParams = params; } else { child.setLayoutParams(params); } if (index < 0) { index = mChildrenCount; } addInArray(child, index);//添加子view到内部集合中 // tell our children if (preventRequestLayout) { child.assignParent(this); } else { child.mParent = this; } if (child.hasFocus()) { requestChildFocus(child, child.findFocus()); } AttachInfo ai = mAttachInfo; if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) { boolean lastKeepOn = ai.mKeepScreenOn; ai.mKeepScreenOn = false; child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK)); if (ai.mKeepScreenOn) { needGlobalAttributesUpdate(true); } ai.mKeepScreenOn = lastKeepOn; } if (child.isLayoutDirectionInherited()) { child.resetRtlProperties(); } dispatchViewAdded(child); if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; } if (child.hasTransientState()) { childHasTransientStateChanged(child, true); } if (child.getVisibility() != View.GONE) { notifySubtreeAccessibilityStateChangedIfNeeded(); } if (mTransientIndices != null) { final int transientCount = mTransientIndices.size(); for (int i = 0; i < transientCount; ++i) { final int oldIndex = mTransientIndices.get(i); if (index <= oldIndex) { mTransientIndices.set(i, oldIndex + 1); } } } }这是可以知道每当我们使用addView添加子View时都会获取view的LayoutParams进行判断如果不存在就会产生一个默认的LayoutParams,而我们在addView之前调用view.layout()方法无效也是可以说明addView绘制位置布局时是拿子View的LayoutParams进行设置位置参数,我们之前如果设置了子View的layout也会在addView方法执行时被覆盖
so~~~我们想到要保持但我们add多个View时,保持动态控件位置不变可以使用new一个LayoutParams设置给子View,具体看以下代码(由于项目内容比较杂,就取其他代码啦~):
private ImageView addIcon(int x, int y) { LayoutParams params = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, x, y); ImageView icon = new ImageView(this); icon.setImageResource(R.drawable.ic); icon.setLayoutParams(params); mLayout.addView(icon); return icon; }当我们在添加子View时,可以动态给其子View设置一个LayoutParams设置其宽高和对相对父控件的位置关系,当手指抬起是记录到其LayoutParams中
@Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); int mX = (int)event.getX(); int mY = (int)event.getY(); switch (action) { case MotionEvent.ACTION_DOWN: //logd("ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: //logd("ACTION_MOVE"); break; case MotionEvent.ACTION_UP: /** * 不能使用myImage.layout()方法,来改变位置。 * layout()虽然可以改变控件的位置, 但不会将位置信息保存到layoutparam中。 * 而调用addView往布局添加新的控件时,是根据layoutparam来重新布局控件位置的。 * 这里需要用另一种方法:先获取控件的layoutparam,改变其中相关的值后,再设置回去。 */ // myImage.layout(mX, mY, mX + myImage.getWidth(), mY + myImage.getHeight()); LayoutParams params = (LayoutParams) myImage.getLayoutParams(); params.x = mX; params.y = mY; myImage.setLayoutParams(params); break; default: break; } return true; }哈哈,有什么疑问欢迎大家提出来吖,大家共同学习,一起进步哈