Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inflate XML-Layout-File correctly inside Custom ViewGroup?

I want to inflate a XML-Layout-File in a custom ViewGroup Class, my Problem is that it produces just a empty screen. Doing the same in the Activity Class works fine. Here is my simple 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> 

Here is the working Version, inflating the shownumberlayout.xml in the Activity 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);     } } 

This shows a White Background with the black Text “Test” centered.

Now the Version inflating the xml in the Custom ViewGroup-Class:

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 public class ShowNumber extends Activity { /** Called when the activity is first created. */

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

}

Im doing it basically like in this Answer explained. This just produces a Empty Black Screen. What I am doing wrong?

UPDATE 1

@Konstantin I applied your changes but still just a blank screen, i also made a log-ouput for getting the number of children. It remains always 1, even i add one more Textview to the XML-Layout-File. Before the Changes it remains always 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 This is the Logcat, after the Changes from Konstantin:

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 

UPDATE 2

The Content is finally showing correctly up. The missing thing was to override the Method onLayout (thanks to Franco) in the RelativeLayout-Sublcass:

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);                 }             } ... } 

Note: Later you should also override the Method onMeasurement(), but currently the content is also showing correctly without overriding it.

Now the solution for the Method initView from Franco do not align the TextView in the Center, but puts it in the top left corner. The solution from Konstantin puts it correctly in the Center of the View:

public class ViewNumber extends RelativeLayout { ... private void initView(Context context){         View.inflate(context, R.layout.shownumberlayout,this);      } ... } 
like image 315
Harold Avatar asked Dec 15 '10 10:12

Harold


People also ask

What does it mean to inflate a layout?

"Inflating" a view means taking the layout XML and parsing it to create the view and viewgroup objects from the elements and their attributes specified within, and then adding the hierarchy of those views and viewgroups to the parent ViewGroup.


2 Answers

I don't know what's going on in these answers.. I think calling onLayout manually is a bit crazy.

The LayoutInflater.inflate function is rather straight forward. If you're using the one that accepts the 3rd boolean argument (attachToRoot) and it's true, it will add the views from your XML to your parent view (the 2nd argument). If you're using the one that accepts only 2 arguments, it will pass true to attachToRoot by default if you provide a parent that isn't null.

In any case, most of the mess you're experiencing is because the views from your XML are added to your custom view. Since your XML has a RelativeLayout root and your custom view is also a RelativeLayout - you're getting a relative layout inside a relative layout.

This is probably not what you want.

The answer given by Konstantin makes sense, but you're wasting a view because you're placing a RelativeLayout inside a FrameLayout. Since Android can't hold too many nested views, it's a good idea to optimize and not add this unnecessary FrameLayout.

I suggest keeping your custom view as RelativeLayout and changing your XML root to <merge>. Then use the inflate form which adds the XML views to your parent - like View.inflate(context,int,this)

The purpose of the <merge> tag is to skip the root node in the XML.. look it up.

like image 57
talkol Avatar answered Sep 19 '22 11:09

talkol


Your layout is inflated nowhere.. make your ViewNumber extend FrameLayout and inflate into it:

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: Also you do not need to override onLayout method, that looks very incorrect.. at least call super.onLayout() on the very start:

@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {     super.onLayout(changed, left, top, right, bottom); } 
like image 26
Konstantin Burov Avatar answered Sep 20 '22 11:09

Konstantin Burov