Using custom views in XML without using a fully qualified class name

I have my own style for buttons defined as themes, but I also use my own class to handle buttons (due to my own fonts). Is it possible to call my button with a beautiful name, for example

<MyButton> 

instead

 <com.wehavelongdomainname.android.ui.MyButton> 
+7
android android-layout
source share
4 answers

So the answer is, surprisingly, yes. I found out about this recently, and in fact you can do this to increase the efficiency of your user inflation. IntelliJ still warns you that it is not valid (although it will compile and run successfully). I'm not sure if Eclipse is warning you or not.

In any case, you will need to define your own subclass of LayoutInflater.Factory :

 public class CustomViewFactory implements LayoutInflater.Factory { private static CustomViewFactory mInstance; public static CustomViewFactory getInstance () { if (mInstance == null) { mInstance = new CustomViewFactory(); } return mInstance; } private CustomViewFactory () {} @Override public View onCreateView (String name, Context context, AttributeSet attrs) { //Check if it one of our custom classes, if so, return one using //the Context/AttributeSet constructor if (MyCustomView.class.getSimpleName().equals(name)) { return new MyCustomView(context, attrs); } //Not one of ours; let the system handle it return null; } } 

Then, in any activity or context in which you are inflating a layout that contains these custom views, you need to assign a factory << 22> for that context:

 public class CustomViewActivity extends Activity { public void onCreate (Bundle savedInstanceState) { //Get the LayoutInflater for this Activity context //and set the Factory to be our custom view factory LayoutInflater.from(this).setFactory(CustomViewFactory.getInstance()); super.onCreate(savedInstanceState); setContentView(R.layout.layout_with_custom_view); } } 

Then you can use the simple class name in your XML:

 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <MyCustomView android:id="@+id/my_view" android:layout_width="match_parent" android:layout_height="60dp" android:layout_gravity="center_vertical" /> </FrameLayout> 
+12
source share

Defining your own subclass of LayoutInflater.Factory seems like a lot of work to me. Just override Activity onCreateView () with some common code:

 @Override public View onCreateView(String name, Context context, AttributeSet attrs) { View view; // No need wasting microseconds getting the inflater every time. // This method gets called a great many times. // Better still define these instance variables in onCreate() if (mInflator == null){ mInflator = LayoutInflater.from(context); mPrefix = ((Activity) context).getComponentName().getClassName(); // Take off the package name including the last period // and look for custom views in the same directory. mPrefix = mPrefix.substring(0, mPrefix.lastIndexOf(".")+1); } // Don't bother if 'a path' is already specified. if (name.indexOf('.') > -1) return null; try{ view = mInflator.createView(name, mPrefix, attrs); } catch (ClassNotFoundException e) { view = null; } catch (InflateException e) { view = null; } // Returning null is no big deal. The super class will continue the inflation. return view; } 

Please note that user views must be in the same package (i.e. in the same directory) as this action, but then this is just a common piece of code that you can tickle in any activity (or even better, inherit from user parent activity class). You are not worried about looking for a specific class, as indicated in the solution offered by kcoppock:

if (MyCustomView.class.getSimpleName().equals(name)) {....

Of course, you are not creating a completely new class.

The real magic is in the class of the main library LayoutInflator.java. See Call, mPrivateFactory.onCreateView (), below ?:

  if (view == null && mPrivateFactory != null) { view = mPrivateFactory.onCreateView(parent, name, mContext, attrs); } if (view == null) { if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); } else { view = createView(name, null, attrs); } } 

You see, if the so-called mPrivateFactory returns null (mPrivateFactory, by the way, is your activity class), LayoutInflator simply continues with it another alternative approach and continues inflation:

 if (view == null) { if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); } else { view = createView(name, null, attrs); } } 

Itโ€™s a good idea to โ€œgo throughโ€ the library classes using the IDE debugger and really see how Android works. :)

Please note that the code if (-1 == name.indexOf('.')) { Is for you guys who still insist on pasting the full path with your custom views, <com.wehavelongdomainname.android.ui.MyButton> If there is a "dot" in the name, then creatview () is called with a prefix (second parameter) as null: view = createView(name, null, attrs);

Why I use this approach because I discovered that there were times when the package name moves (i.e. changed) during the initial development. However, unlike changes to the package name that are performed in the Java code itself, the compiler does not detect the changes and discrepancies that are now present in any XML files. Using this approach, now it is not necessary.

Greetings.

+1
source share

You can also do this:

 <view class="com.wehavelongdomainname.android.ui.MyButton" ... /> 

Wed http://developer.android.com/guide/topics/ui/custom-components.html#modifying

0
source share

Not. You need to give the "full path" to your class, otherwise the framework will not be able to inflate your layout.

-one
source share

All Articles