29

How do I pass the current AttributeSet to a custom View class? If I use a constructor that only has Context in the arguments, I lose all themes and the ability to use "style" tags in the xml for that custom View.

What I've done is create an activity that contains my custom view already in the xml file, and then programmatically create a new one and add it to the layout. What I find is the one that is made in the xml has the proper styling, and the one I create programmatically doesn't.

The difference between the two as far as I can tell is that the system uses the CustomLayout1(Context context, AttributeSet attrs) constructor. The problem is I can't figure out how to get the AttributeSet for the application to pass to this custom view when I create it programmatically.

Here's the Activity:

import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;

public class ThemeOne extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        LinearLayout layout = (LinearLayout) findViewById(R.id.mainlayout);

        layout.addView(new CustomLayout1(getApplicationContext()));
    }
}

Here's the main xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical" 
 android:id="@+id/mainlayout"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent">
 <com.clearsync.test.theme1.CustomLayout1 android:id="@+id/maincustom"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content" />
</LinearLayout>

The custom view class:

import com.clearsync.test.theme1.R;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;

public class CustomLayout1 extends LinearLayout {
 private Context context = null;

 public CustomLayout1(Context context) {
  super(context);
  this.context = context;
  create();
 }

 public CustomLayout1(Context context, AttributeSet attrs) {
  super(context, attrs);
  this.context = context;
  create();
 }

 private void create(){
  LayoutInflater layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  layoutInflater.inflate(R.layout.inflateme, this, true);
 }
}

and finally, the custom view 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">
 <TextView android:layout_width="wrap_content"
  android:layout_height="wrap_content" 
  android:text="Oh, Hewroh..."
  style="?textview_style1" />
</LinearLayout>
atraudes
  • 2,368
  • 2
  • 21
  • 31

3 Answers3

27

Instead of building it with layout.addView(new CustomLayout1(getApplicationContext())); inflate it with the LayoutInflater in your Activity.

LayoutInflater inflater = LayoutInflater.from(this);
inflater.inflate(R.layout.yourcustomviewxml, layout);
Ian G. Clifton
  • 9,349
  • 2
  • 33
  • 34
  • 1
    Simple. Elegant. Thank you. I should have thought of that myself :-P That is a terribly annoying behavior I would love to see fixed. I've implemented that solution in my main code, and of course uncovered a slew of other related issues. It seems like a bug that theme and style would just disappear so mysteriously simply because of the way you call a custom view. *sigh* – atraudes Sep 14 '10 at 23:00
  • [Here is a fuller example of using a Layout Inflater to copy a custom view.](http://stackoverflow.com/a/41500409/3681880) – Suragch Jan 06 '17 at 06:55
4

Your code creates LinearLayout inside of linear layout for your custom view. Correct way of doing this is changing your custom view xml from:

<?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:layout_width="wrap_content"
  android:layout_height="wrap_content" 
  android:text="Oh, Hewroh..."
  style="?textview_style1" />
</LinearLayout>

to

<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:layout_width="wrap_content"
      android:layout_height="wrap_content" 
      android:text="Oh, Hewroh..."
      style="?textview_style1" 
/>
</merge>
Nimer
  • 61
  • 2
  • Adding reference to http://developer.android.com/training/improving-layouts/reusing-layouts.html and http://stackoverflow.com/questions/2732682/simple-example-of-merge-and-include-usage-in-android-xml-layouts – EpicPandaForce May 06 '15 at 14:05
2

What are you trying to accomplish here? Looks like you have an endless recursive loop here using your create method, as inflate() will call the constructor that takes attributes. Anyways to answer your question you get the attributes in the constructor with the attributes!

That is the constructor that is called when loading from XML, otherwise it calls one of the other constructors that you supply.

One other helpful thing, you can get a reference to the inflater much easier from the static View method. View.inflate :D

Nathan Schwermann
  • 31,285
  • 16
  • 80
  • 91
  • This was a test class I made to solve a problem I'm having in another project. I'm creating custom views and adding them into a gallery view. The problem I'm having is I can't apply the theme I'm using to anything inside a custom class. This seems perverted and wrong :-P My question is how do I get the attribute sets in the activity to pass to my custom class? I'm currently doing this: new CustomLayout1(getApplicationContext()) and I would like to do something like this instead: new CustomLayout1(getApplicationContext(), AttributeSet) in hopes that it would actually apply my theme – atraudes Sep 14 '10 at 00:48
  • you get them in the constructor, just make an AttributeSet member mAttset = attrs – Nathan Schwermann Sep 14 '10 at 19:52