如何在Custom ViewGroup中正确地扩展XML-Layout-File?

时间:2010-12-15 10:10:05

标签: android android-layout

我想在自定义ViewGroup类中扩展XML-Layout-File,我的问题是它只生成一个空屏幕。在Activity Class中执行相同操作可以正常工作。 这是我简单的XML-Layout-File:

shownumberlayout.xml:

    <RelativeLayout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent"
        android:background="#FFFFFF" 
        android:id="@+id/layoutForNumber">

        <TextView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:id="@+id/tvNumber"
            android:layout_centerHorizontal="true" 
            android:textColor="#000000" 
            android:text="Test" 
            android:layout_centerVertical="true" 
            android:textSize="30dip">
        </TextView>

    </RelativeLayout>

以下是工作版本,在活动shownumberlayout.xml中夸大了ShowNumber

ShowNumber.class

public class ShowNumber extends Activity {
    /** Called when the activity is first created. */

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        ViewGroup vg = (ViewGroup) inflater.inflate(R.layout.shownumberlayout, null);
        setContentView(vg);
    }
}

这显示白色背景,黑色文本“测试”居中。

现在版本在自定义ViewGroup - 类:

中膨胀xml
ViewGroup.class
public class ViewNumber extends ViewGroup {

    private LayoutInflater inflater;

    public ViewNumber(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public ViewNumber(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    public ViewNumber(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        initView(context);
    }

    private void initView(Context context){
        inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.shownumberlayout, null);  
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // TODO Auto-generated method stub
    }

}

ShowNumber.class 公共类ShowNumber扩展Activity {     /** 在第一次创建活动时调用。 * /

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ViewGroup vg = new ViewNumber(this); 
    setContentView(vg);
}

}

我这样做基本上就像this答案解释的那样。 这只会产生一个空黑屏。我做错了什么?

更新1

@Konstantin 我应用了你的更改,但仍然只是一个空白的屏幕,我也做了一个日志输出,以获得孩子的数量。它始终为1,即使我再添加一个Textview到XML-Layout-File。在更改之前,它始终为0。

public class ViewNumber extends RelativeLayout {
...
private void initView(Context context){
        //inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //inflater.inflate(R.layout.shownumberlayout, null);
        View.inflate(context, R.layout.shownumberlayout,this); 
        Log.v("ViewNumber", "Number of Child: " + this.getChildCount());//output is 1,before it remains 0
    }
...
}

@Sankar 这是Logst,在康斯坦丁的变化之后:

12-16 09:24:23.606: DEBUG/AndroidRuntime(8951): >>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<
12-16 09:24:23.606: DEBUG/AndroidRuntime(8951): CheckJNI is OFF
12-16 09:24:23.606: DEBUG/dalvikvm(8951): creating instr width table
12-16 09:24:23.656: DEBUG/AndroidRuntime(8951): --- registering native functions ---
12-16 09:24:23.916: DEBUG/AndroidRuntime(8951): Shutting down VM
12-16 09:24:23.916: DEBUG/dalvikvm(8951): Debugger has detached; object registry had 1 entries
12-16 09:24:23.916: INFO/AndroidRuntime(8951): NOTE: attach of thread 'Binder Thread #3' failed
12-16 09:24:24.076: DEBUG/AndroidRuntime(8960): >>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<
12-16 09:24:24.076: DEBUG/AndroidRuntime(8960): CheckJNI is OFF
12-16 09:24:24.076: DEBUG/dalvikvm(8960): creating instr width table
12-16 09:24:24.126: DEBUG/AndroidRuntime(8960): --- registering native functions ---
12-16 09:24:24.376: INFO/ActivityManager(78): Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=org.customview.harold/.ShowNumber }
12-16 09:24:24.426: DEBUG/AndroidRuntime(8960): Shutting down VM
12-16 09:24:24.426: DEBUG/jdwp(8960): Got wake-up signal, bailing out of select
12-16 09:24:24.426: DEBUG/dalvikvm(8960): Debugger has detached; object registry had 1 entries
12-16 09:24:24.456: INFO/AndroidRuntime(8960): NOTE: attach of thread 'Binder Thread #3' failed
12-16 09:24:24.456: VERBOSE/ViewNumber(8923): Number of Child: 1
12-16 09:24:24.496: VERBOSE/RenderScript_jni(164): surfaceDestroyed
12-16 09:24:24.526: INFO/ActivityManager(78): Displayed activity org.customview.harold/.ShowNumber: 104 ms (total 104 ms)
12-16 09:24:24.576: DEBUG/dalvikvm(158): GC_FOR_MALLOC freed 10631 objects / 526248 bytes in 52ms
12-16 09:24:34.606: DEBUG/dalvikvm(164): GC_EXPLICIT freed 1776 objects / 106960 bytes in 91ms

更新2

内容最终正确显示。 缺少的是覆盖RelativeLayout-Sublcass中的方法onLayout(感谢 Franco ):

public class ViewNumber extends RelativeLayout {
...

    @Override
            protected void onLayout(boolean changed, int l, int t, int r, int b) {
                // TODO Auto-generated method stub
                for(int i = 0 ; i < getChildCount() ; i++){
                    getChildAt(i).layout(l, t, r, b);
                }
            }
...
}

注意:稍后您还应该覆盖方法onMeasurement(),但目前内容也正确显示而不会覆盖它。

现在,来自 Franco 的方法initView的解决方案不会在中心对齐TextView,而是将其放在左上角。 Konstantin 的解决方案将其正确放入视图中心:

public class ViewNumber extends RelativeLayout {
...
private void initView(Context context){
        View.inflate(context, R.layout.shownumberlayout,this); 
    }
...
}

6 个答案:

答案 0 :(得分:43)

我不知道这些答案中发生了什么。我认为手动调用onLayout有点疯狂。

LayoutInflater.inflate功能非常简单。如果您正在使用接受第3个布尔参数(attachToRoot)并且 true 的那个,它将把您的XML视图添加到父视图(第二个参数)。如果您使用的只接受2个参数,如果您提供的父级不是 null ,则默认情况下会将 true 传递给attachToRoot。

在任何情况下,您遇到的大部分混乱都是因为XML中的视图已添加到您的自定义视图中。由于您的XML具有RelativeLayout根,并且您的自定义视图也是RelativeLayout,因此您在相对布局中获得了相对布局。

这可能不是你想要的。

康斯坦丁给出的答案是有道理的,但是你浪费了一个观点,因为你在RelativeLayout内放置了FrameLayout。由于Android无法容纳太多嵌套视图,因此优化并不添加此不必要的FrameLayout是一个好主意。

我建议您将自定义视图保留为RelativeLayout,并将XML根目录更改为<merge>。然后使用inflate表单将XML视图添加到您的父级 - 如View.inflate(context,int,this)

<merge>标记的目的是跳过XML中的根节点。查找它。

答案 1 :(得分:34)

你的布局无处膨胀..让你的ViewNumber扩展FrameLayout并膨胀到它:

public class ViewNumber extends FrameLayout {

    public ViewNumber(Context context) {
        super(context);
        initView(context);
    }

    public ViewNumber(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public ViewNumber(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView(context);
    }

    private void initView(Context context){
        View.inflate(context, R.layout.shownumberlayout, this);  //correct way to inflate..
    }
}

UPD:你也不需要覆盖onLayout方法,看起来非常不正确..至少在一开始就调用super.onLayout():

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
}

答案 2 :(得分:29)

你试过这个吗?

private void initView(Context context){
        inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.addView(inflater.inflate(R.layout.shownumberlayout, null));  
    }
编辑:我快速回复...... ViewGroup有责任为他们的孩子准备布局,在布局方法中试试这个:

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            // TODO Auto-generated method stub
            for(int i = 0 ; i < getChildCount() ; i++){
                getChildAt(i).layout(l, t, r, b);
            }
        }

答案 3 :(得分:1)

试试这个:

 LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
 inflater.inflate( R.layout.login_view_layout, null );

答案 4 :(得分:1)

覆盖onLayout是必要的。当你创建ViewNumber(ViewGroup vg = new ViewNumber(this))的对象时,就像在下面膨胀xml一样:

<ViewNumber
           I have no child :(
</ViewNumber>

因此,如果您不覆盖onLayout,则ViewNumber的onLayout无法找到任何子项,那么它将为空白。

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
}

覆盖后,如果您的代码是:

,它将使用ViewNumber父级的onLayout方法
public class ViewNumber extends RelativeLayout {
...
private void initView(Context context){
        View.inflate(context, R.layout.shownumberlayout,this); 
    }
...
}

然后它就像在

下面膨胀xml一样
  <RelativeLayout
    <RelativeLayout 
            your xml file
                   />
  </RelativeLayout>

还有一个RelativeLayout。要解决这个问题,你可以写一下:

public class ViewNumber extends RelativeLayout {
...
private void initView(Context context){
        inflater.inflate( R.layout.login_view_layout, null );
              //change (..,this) to (..,null)
    }
...
}

但是,会发生意外情况。您的xml文件的android:layout_xxx=".."可能会发生变化(这就是您的文字放在左上角的原因)。要了解更多信息并获得更好的解决方案,您可以看到 talkol &#39;回答和 fgeorgiew 的评论:)

答案 5 :(得分:1)

我遇到相同的问题,并在科特林中回答我的版本:

您可以通过xml或代码添加自定义视图。区别在于构造函数。

YourView.kt

class YourView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {

    init {
        LayoutInflater.from(context).inflate(R.layout.your_view, this, true)
     orientation = HORIZONTAL
    }
}

如果要通过代码创建布局,则只需使用:

class YourView(context: Context) : LinearLayout(context)

your_view.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">


        <TextView
            android:id="@+id/searchView"/>

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Cancel" />


</merge>

<merge>在这里做什么?

如果您在布局中使用YourView,例如:

<LinearLayout> 
   <YourView>
   </YourView>
</LinearLayout>

那么实际的视图层次是:

<merge>

   <LinearLayout>
      <TextView> </TextView>
      <Button> </Button>
   </LinearLayout >

<merge>(您需要像ViewGroup一样使用<LinearLayout>

 <LinearLayout> 
       < LinearLayout >
          <TextView> </TextView>
          <Button> </Button>
       </LinearLayout >
    </LinearLayout>