Inflating Custom Views - Layout File Inconsistencies - android

No, no one answered that question, and the problem still remains... This question here is about another symptom to the same problem (please see comments below):
In Monodroid atleast, when inflating a custom view from a layout, sometimes it needs to be wrapped in a ViewGroup (ie, LinearLayout) in order to not get an exception, and other times does not.
I was wondering if anyone is familiar with this situation, and if it happens in "raw" Android as well (ie, no Monodroid) ?
I always first try without, as in
TextView1.axml
<?xml version="1.0" encoding="utf-8"?>
<Monodroid.Activity1.TextView1
android:id="#+id/text_view1"
android:layout_width="300dp"
android:layout_height="50dp"/>
but if I get an inflation exception, then I'll have to wrap it up
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ll_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Monodroid.Activity1.TextView1
android:id="#+id/text_view1"
android:layout_width="300dp"
android:layout_height="50dp"/>
</LinearLayout>
where
public class TextView1 : TextView
{
public TextView1 (Context context) : base(context) { }
public TextView1 (Context context, IAttributeSet attributes) : base(context, attributes) { }
public TextView1 (Context context, IAttributeSet attributes, int defStyle) : base(context, attributes, defStyle) { }
}
Thank you.
This layout file inflates with no containing viewgroup:
<?xml version="1.0" encoding="utf-8"?>
<fieldinspection.droid.views.custom.FieldInput
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/RecordDataFieldInput"
style="#style/FieldInput"
android:layout_marginRight="0dip"/>
and this one (inner class PagedFragmentFieldInput) does not (it needs to be within a LinearLayout or else inflation exception):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ll_record_data_field_input2_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<FieldInspection.Droid.Views.ComplaintView.PagedFragmentRecordDataFieldBox.PagedFragmentFieldInput
android:id="#+id/RecordDataFieldInput"
style="#style/FieldInput"
android:layout_marginRight="0dip"/>
</LinearLayout>
Its read as PagedFragment-RecordDataFieldBox, its a RecordDataFieldBox thats within a Fragment thats within a ViewPager.

I took your first sample and tried it out here. I get no error wrapping it or not.
TextViewInherit.cs:
using Android.Content;
using Android.Util;
using Android.Widget;
namespace InflationShiz
{
public class TextViewInherit : TextView
{
public TextViewInherit(Context context, IAttributeSet attrs) :
this(context, attrs, 0)
{
}
public TextViewInherit(Context context, IAttributeSet attrs, int defStyle) :
base(context, attrs, defStyle)
{
}
}
}
One.axml:
<?xml version="1.0" encoding="utf-8"?>
<inflationshiz.TextViewInherit
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
Two.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<inflationshiz.TextViewInherit
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
Both work when I inflate in my Activity like so:
var one = LayoutInflater.Inflate(Resource.Layout.One, null);
var two = LayoutInflater.Inflate(Resource.Layout.Two, null);
I find it hard to reproduce your issue; Your code is scattered over 3 different SO questions and even more scattered because you have created answers to your own question where you try to elaborate on your initial questions.

Related

Custom view hierarchy child not added

I have a hierarchy of custom views that looks like this:
Activity(RelativeLayout) -> ParentLayout(FrameLayout) -> ChildLayout(LinearLayout)
The activity and parent layout are added and displayed just fine, but the child is not. I have looked at the hierarchy viewer in the device monitor to confirm it is not being added to the view hierarchy.
Really all I'm trying to do here is create a view hierarchy so I can play around with handling touch events at various places in the view.
Here is everything:
main_activity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity">
<net.openeye.touchevents.ParentLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#558833" />
</RelativeLayout>
parent_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<net.openeye.touchevents.ParentLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<net.openeye.touchevents.ChildLayout
android:id="#+id/child_view"
android:layout_width="300dp"
android:layout_height="300dp" />
</net.openeye.touchevents.ParentLayout>
ParentLayout.java:
public class ParentLayout extends FrameLayout implements View.OnTouchListener {
public ParentLayout(Context context) {
super(context);
}
public ParentLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ParentLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
child_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<net.openeye.touchevents.ChildLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="#0066dd"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hi"/>
</net.openeye.touchevents.ChildLayout>
ChildLayout.java:
public class ChildLayout extends LinearLayout {
public ChildLayout(Context context) {
super(context);
}
public ChildLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChildLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
What am I missing? I have another project that is basically set up the same way, except the child views are dynamically inflated and added, instead of being directly added in the xml layout files. This seems like it should work and I don't understand why it doesn't.
So it looks like when you have a custom view class, you don't want to have the view of the layout file be the same type as the custom class. i.e., if I have ParentLayout.java, I don't want parent_layout.xml's root to be <net.openeye.TouchEvents.ParentLayout>. It seems that when you want both a custom layout file and custom view class, you need to have the view class inflate the layout. If the layout has an element (the root, on this case) that is the same as the class, it will cause infinite recursion as the view inflates the layout, which instantiates the class, which inflates the layout... and so on.
I got this to work finally by making the following changes:
parent_layout.xml:
Change the root element from net.openeye.TouchEvents.ParentLayout to the class it extends, FrameLayout. It now looks like this:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- ... -->
</FrameLayout>
child_layout.xml:
Change the root element from net.openeye.TouchEvents.ChildLayout to the class it extends, LinearLayout. It now looks like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dp"
android:layout_height="300dp"
android:orientation="vertical">
<!-- ... -->
</LinearLayout>
ParentLayout.java: Inflate it's layout during instantiation. It now looks like this:
public ParentLayout(Context context) {
super(context);
init(context);
}
public ParentLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ParentLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
inflate(context, R.layout.parent_layout, this);
}
ChildLayout.java: Same thing as ParentLayout.java, but inflate child_layout.
After getting this working and thinking about why this is happening, it makes sense that this is how it works.

Custom xml attributes without attrs.xml file

Recently I've faced with adding custom xml parameters to my views in xml layout. I know that I should use attrs.xml file for this purpose, but... I have found, that I can use custom parameters without any attrs.xml file at all. Can somebody explain this ? Is this a bug or is this a valid case ?
here is my custom view:
public class TestView extends View {
public TestView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public TestView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
final String scheme = "http://red.com/ui/scheme";
if (attrs != null) {
Log.d("TestView", "custom param value: " + attrs.getAttributeValue(scheme, "cutom"));
}
}
}
and here is the main layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:red="http://red.com/ui/scheme"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello" />
<com.red.ui.TestView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ffAABBCC"
red:cutom="customvalue"
/>
</LinearLayout>
It is a simple scratch project, created by Android wizard.
The custom attribute that you added is not available in R.java
I think the main motto of making custom attributes is to use it at multiple places.
But through this code we cann't accomplish the same scenario.
Here is the sample code - attrs.xml file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyLayout">
<attr name="text" format="string" />
</declare-styleable>
</resources>
I am changing main.xml to add the the text attribute
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:red="http://red.com/ui/scheme"
xmlns:myapp="http://schemas.android.com/apk/res/com.psl"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello"
myapp:text="Text String" />
<com.psl.TestView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ffAABBCC"
myapp:text="My Special Text String"
red:cutom="customvalue" />
</LinearLayout>
TestView.java -
public class TestView extends View {
public TestView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public TestView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
final String scheme = "http://red.com/ui/scheme";
if (attrs != null) {
Log.d("TestView", "custom param value: " + attrs.getAttributeValue(scheme, "cutom"));
}
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.MyLayout);
CharSequence s = a.getString(R.styleable.MyLayout_text);
Log.d("MyTestView", "attrs param value: " + s.toString());
}
}
If you noticed after making the attr in attrs.xml. It is available everywhere.
But the attr defined in xml itself through custom namespace is available only through the namespace that you have to define everywhere.
May be its a bug because the attribute is getting added to some custom namespace and not in the application itself.
This is not a "bug" of course. This is how you use a custom view in your xml.
refer to this : http://developer.android.com/guide/topics/ui/custom-components.html

Problem with creating Compound Control in Android

I am learning to create a compound control in android.
For starters i tried an edit text with an attached button to clear it.
The problem is even though i can see the compound control in the graphical view of the
main.xml, there is an error message : "Custom view ClearableEditText is not using the 2- or 3-argument View constructors; XML attributes will not work"
This is not visible in project explorer under errors only in the xml graphical view
i am able to compile and run but get a force close.
XML : COMPOUND CONTROL clearable_edit_text.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<EditText android:id="#+id/editText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button android:id="#+id/clearButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CLEAR"
/>
</LinearLayout>
CLASS
public class ClearableEditText extends LinearLayout
{
EditText et;
Button btn;
public ClearableEditText(Context context)
{
super(context);
LayoutInflater li=(LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
li.inflate(R.layout.clearable_edit_text,this,true);
et=(EditText)findViewById(R.id.editText);
btn=(Button)findViewById(R.id.clearButton);
hookupButton();
}
private void hookupButton()
{
btn.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View v)
{
et.setText("");
}
});
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.commsware.android.merge.ClearableEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<com.commsware.android.merge.ClearableEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
Your class extends LinearLayout but you never add any views to it. You need to call addView(...) and pass your inflated view as the parameter.
Also, to define your view in XML you need to override the 2 and 3 argument constructors for a LinearLayout. Add this to your code:
public ClearableEditText(Context context, AttributeSet attrs) {
super( context, attrs );
}
public ClearableEditText(Context context, AttributeSet attrs, int defStyle) {
super( context, attrs, defStyle );
}
To get all 3 constructors to use the same initialization code, move your code from the single argument constructor to the 3 argument constructor, then in the other 2 constructors call this(context, null, 0) and this(context, attrs, 0) respectively.

Android custom view causing force close

I have a custom view in src > myproject.test > HomeView
In my main layout xml I have the following:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/home_root"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<myproject.test.HomeView
android:id="#+id/home_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
</myproject.test.HomeView>
</LinearLayout>
In the HomeActivity I have a call like this in the onCreate method.
setContentView(R.layout.main);
HomeView mHomeView = (HomeView) this.findViewById(R.id.home_view);
The app force closed when the setContentView method is called. It seems that my main xml is not correct.
Any ideas?
Do you mean its not getting to the
HomeView mHomeView = (HomeView) this.findViewById(R.id.home_view);
and crashing on the line before it?
Check if your constructor is
HomeView(final Context context, final AttributeSet attrs){
super(context, attrs);
and not
HomeView(final Context context){
super(context);
you need the AttributeSet
Check constructors that your HomeView implements:
public HomeView(Context context, AttributeSet atts) {
super(context, atts);
}
<LinearLayout xmlns:android =
http://schemas.android.com/apk/res/android"
android:id="#+id/home_root"
android:orientation="vertical"
None of my layouts have #+id. Maybe you should set the view to home_root. Check with you R.java for the name of the layout, or try
setContentView(R.layout.home_root);

custom view with layout

ok,
what i am trying to do is to embed a custom view in the default layout main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.lam.customview.CustomDisplayView
android:id="#+id/custom_display_view1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="#+id/prev"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="50"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="#string/prev" />
</LinearLayout>
</LinearLayout>
as you can see the class is called com.lam.customview.CustomDisplayView, with the id of custom_display_view1.
now in the com.lam.customview.CustomDisplayView class, i want to use another layout called custom_display_view.xml because i don't want to programmatically create controls/widgets.
custom_display_view.xml is just a button and an image, the content of which i want to change based on certain conditions:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="#+id/display_text_view1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello"
/>
<ImageView
android:id="#+id/display_image_view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</ImageView>
</LinearLayout>
i tried to do:
1)
public CustomDisplayView(Context context, AttributeSet attrs) {
super(context, attrs);
try
{
// register our interest in hearing about changes to our surface
SurfaceHolder holder = getHolder();
holder.addCallback(this);
View.inflate(context, R.layout.custom_display_view, null);
...
but got this error, "03-08 20:33:15.711: ERROR/onCreate(10879): Binary XML file line #8: Error inflating class java.lang.reflect.Constructor
".
2)
public CustomDisplayView(Context context, AttributeSet attrs) {
super(context, attrs);
try
{
// register our interest in hearing about changes to our surface
SurfaceHolder holder = getHolder();
holder.addCallback(this);
View.inflate(context, R.id.custom_display_view1, null);
...
but got this error, "03-08 20:28:47.401: ERROR/CustomDisplayView(10806): Resource ID #0x7f050002 type #0x12 is not valid
"
also, if i do it this way, as someone has suggested, it's not clear to me how the custom_display_view.xml is associated with the custom view class.
thanks.
I had the exact same issue, and the problem was that I was using the wrong ID... and it look's like you are too.
You should be referencing
R.layout.custom_display_view
-not-
R.id.custom_display_view
This blog post helped me understand what to do immensely http://trickyandroid.com/protip-inflating-layout-for-your-custom-view/. In case the blog post disappears, here are some parts of the code:
public class Card extends RelativeLayout {
public Card(Context context) {
super(context);
init();
}
public Card(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public Card(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
inflate(getContext(), R.layout.card, this);
}
}
with this layout:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/card_padding"
android:background="#color/card_background">
<ImageView
... />
<TextView
... />
</merge>
The control is included like:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin">
<com.trickyandroid.customview.app.view.Card
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/card_background"
android:padding="#dimen/card_padding"/>
</FrameLayout>
Where com.trickyandroid.customview.app.view is the namespace of class card. One thing that was new to me was the "merge" tag, which ends up being the same node as the Card tag in the containing doc.
You can inflate the cutom_display_view into your custom class by:
public CustomDisplayView(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(inflater != null){
inflater.inflate(R.layout.custom_display_view, this);
}
}
When you inflate your layout, you should pass in a reference to the view instance to identify it as the root. So instead of calling:
View.inflate(context, R.layout.custom_display_view, null);
Call:
View.inflate(context, R.layout.custom_display_view, this);
See: The docs
Line #8 is your custom view in the first layout. Are you trying to load the wrong layout, so it's trying to load itself recursively? (I just did the same thing)
also you have:
R.layout.custom_display_view
vs.
R.id.custom_display_view1
with the 1 on the end there... which one is it?
Try using
context.getLayoutInflater().inflate( R.id.custom_display_view1, null );

Categories

Resources