Custom LayoutInflater
Posted by on July 1, 2015
Using custom UI libraries has one downside – in xml you have to write the full package name of each View. Android widgets can be used without full name. It’s also possible with custom Views – all we have to do is to replace the default LayoutInflater.
Some time ago my friend showed me a nice library called Calligraphy. The most interesting thing about it is that it adds its attributes to existing Android Views without any custom widgets. After a while with the code I found out how they achieved that and how I can use that techinique to quickly replace Android widgets with Carbon ones.
The trick Android does is very simple. If a class doesn’t have any prefix, add ‘android.widget.’ and then try to load it. We can easily create such loader for our package name:
public class CarbonLayoutInflater extends LayoutInflater {
protected CarbonLayoutInflater(LayoutInflater original, Context newContext) {
super(original, newContext);
}
@Override
public LayoutInflater cloneInContext(Context newContext) {
return new CarbonLayoutInflater(this, newContext);
}
@Override
protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
if (Character.isUpperCase(name.charAt(0)))
return createView(name, "carbon.widget.", attrs);
return super.onCreateView(name, attrs);
}
}
This class is very simple and does exactly what I wrote in the previous paraghaph. How to use it? We need a ContextWrapper class which would return our Inflater as an inflater service:
public class CarbonContextWrapper extends ContextWrapper {
private CarbonLayoutInflater mInflater;
public CarbonContextWrapper(Context base) {
super(base);
}
@Override
public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = new CarbonLayoutInflater(LayoutInflater.from(getBaseContext()), this);
}
return mInflater;
}
return super.getSystemService(name);
}
}
Now all we have to do is to attach our wrapper. Like this:
public class ContextWrapperActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contextwrapper);
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(new CarbonContextWrapper(newBase));
}
}
Since now, each packageless component will be resolved as a carbon.widget.*. Check out our layout with CarbonContextWrapper attached: 
What Calligraphy does is much more complex, but I don’t really need that. Their technique gave me an idea to try to mess with ContextWrapper and LayoutInflater. Carbon has a good number of widgets which can replace Android ones. With custom LayoutInflater you can quickly move to Carbon without replacing all Views in your layouts.

Sorry for posting anonymously above, please remove the comment.
Somehow this does not work, crashing with : `Binary XML file line #29: Error inflating class ViewStub`. (of course there is no ViewStub in the layout xml). Any ideas ?
LikeLike
ok, due to some investigation and looking at “hidden” implementation of onCreatView in com.android.internal.policy.impl.PhoneLayoutInflater my conclusion is that although a great idea but the approach in this post won’t work, unless some extra hacks are involved.
LikeLike
You’re right – this is just an idea. Works with as simple case as shown above π Each Android version behaves a little bit different, my sample doesn’t support View classes without match, etc.
Would you like to present your findings? It may be very useful for me and others π
LikeLike