【问题标题】:Why can't we inflate a view into customview while that customview is initializing?为什么我们不能在自定义视图初始化时将视图膨胀到自定义视图中?
【发布时间】:2016-08-04 07:35:35
【问题描述】:

我创建了一个名为 PinView 从 RelativeLayout 扩展的自定义视图。 在构造函数中,我调用了我的 init() 方法,它会从布局中扩展视图。

inflate(getContext(), R.layout.layout_pin_view, this);

在这个膨胀流程之后,我需要通过请求 addView() 在这个膨胀视图中添加我的其他视图 (Imageview)。并抛出:

catch (NoSuchMethodException e) {
            InflateException ie = new InflateException(attrs.getPositionDescription()
                    + ": Error inflating class "
                    + (prefix != null ? (prefix + name) : name));
            ie.initCause(e);
            throw ie; 

我可以通过以下方式成功充气:

mLlPins.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                mLlPins.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                ....
                ....
                addView(...)



            }
        }); 

你知道为什么我们不能创建一个同步膨胀的动态视图吗?

我的代码部分:

private void init() {
    View view = inflate(getContext(), R.layout.layout_pin_view, this);
    mEtPin = (PinEditText) view.findViewById(R.id.etPin);
    mLlPins = (LinearLayout) view.findViewById(R.id.llPins);
    mEtPin.setVisibility(View.VISIBLE);
    mEtPin.setFilters(new InputFilter[]{new InputFilter.LengthFilter(mMaxLength)});
    mEtPin.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return false;
        }
    });
    mEtPin.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            if (mImageViews != null) {
                for (int i = 0; i < mImageViews.length; i++)
                    mImageViews[i].setEnabled(i < s.length());
                if (s.length() == mMaxLength) {
                    InputMethodManager imm = (InputMethodManager) mEtPin.getContext().getSystemService(
                            Context.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(mEtPin.getWindowToken(), 0);
                }
            }
        }
    });
    mLlPins.setClickable(false);
    mEtPin.setText(mEtPin.getText());

    mEtPin.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            if (mImageViews == null) return;
            for (int i = 0; i < mMaxLength; i++) {
                if (mImageViews[i] != null) {
                    if (i < s.length()) {
                        mImageViews[i].setImageAlpha(255);
                        continue;
                    }
                }
                mImageViews[i].setImageAlpha(0);
            }

        }
    });

    setMaxLength(4);
}
private void setMaxLength(int maxLength) {
    LayoutInflater inflater = LayoutInflater.from(mLlPins.getContext());
    mMaxLength = maxLength;
    mEtPin.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
    mImageViews = new ImageView[maxLength];
    mLlPins.removeAllViews();
    int totalContentLenght = mLlPins.getMeasuredWidth() - dpToPx((maxLength - 1) * mMargin);
    int itemWidthHeight = totalContentLenght / maxLength;
    int itemMargin = dpToPx(mMargin / 2);
    int itemPadding = (int) (itemWidthHeight * srcRatio);
    for (int i = 0; i < maxLength; i++) {
        mImageViews[i] = (ImageView) inflater.inflate(mPinImage, null);
        mImageViews[i].setEnabled(false);
        mImageViews[i].setPadding(itemPadding, itemPadding, itemPadding, itemPadding);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(itemWidthHeight, itemWidthHeight);
        if (i == 0) {
            params.setMargins(0, 0, itemMargin, 0);
        } else if (i < maxLength - 1) {
            params.setMargins(itemMargin, 0, itemMargin, 0);
        } else {
            params.setMargins(itemMargin, 0, 0, 0);

        }
        mImageViews[i].setImageAlpha(0);
        mLlPins.addView(mImageViews[i], params);

    }

}

【问题讨论】:

  • 请发布您的构造函数和init() 方法,以及来自捕获的Exception 的堆栈跟踪。
  • 啊!这是我的错。我在尝试获取日志时发现了问题。在Activity中,oncreate之后,这个Activity被一些控件关闭了。如果我调试这个过程,它会在我写的时候抛出错误。我需要结束这个问题。而且我还需要使用 globallayoutlistener 来获取视图的大小:)

标签: android android-view android-custom-view layout-inflater


【解决方案1】:

在我的理解中,你不能膨胀视图,因为父视图(比如容器)没有被绘制,这意味着它的所有布局参数都没有解决,当你添加一个视图时,你的视图不能计算它自己的视图参数到容器尚未启动。 如果你想在容器的 onCreate 方法中使用initial child view方法,你可以在你的容器create方法中尝试如下:

containerVariable.post(new Runnable(){
    @Override
    public void run(){
      // initiate your child view and add child view here 
    }
});

【讨论】:

  • 我可以用它代替 GlobalLayoutListener 吗?哪种方法更合适?
  • 在我的理解中,GlobalLayoutListener 是标准 Android 库中提供的一种方法,它是更可取的,但是,由于 GlobalLayoutListener 需要处理不同的 Android 版本问题,我建议使用 post 方法,而 post 方法是某种流程处理方式。
猜你喜欢
  • 2012-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多