使用addView方法时,保持已有动态控件位置不被改变

项目效果图,需求点击添加图片选择图片后动态添加到自定义画板控件中。



在做项目时,有个需要做类似画板的功能,需要动态选择图片,将图片存储到自定义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;
   }
哈哈,有什么疑问欢迎大家提出来吖,大家共同学习,一起进步哈



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值