There are so many default view widgets in Android, although we tried hard to keep up, some attributes or views are still not supported by Android-Binding. Of course, we open to any suggestion to include them, please report it in the issue tracker or post in the discussion group.

Wait, you want to DIY? We have two approaches:

  1. Extend that View that you need attributes, and treat it like Custom View (Easier)
  2. Create your own Binding Provider (Better reusability, and please submit it to me!)
Following I will discuss with adding a attribute to WebView, named “data”, and the usage is simple:
binding:data=”STRING”
This reflects the method WebView.loadData();

The Attribute class

We need that attribute, no matter which path we are using, and following is the code:

       public class DataViewAttribute extends ViewAttribute<WebView, String>{
		public DataViewAttribute(WebView view,
				String attributeName) {
			super(String.class, view, attributeName);
		}

		private String mValue = "";

		@Override
		protected void doSetAttributeValue(Object newValue) {
                        if (newValue==null) return;
			mValue = newValue.toString();
			getView().loadData(mValue, "text/html", null);
		}

		@Override
		public String get() {
			return mValue;
		}
	}

All Attributes in widget must extends from View Attribute<Th, T>. Where, Th is the target widget’s class, and T is the type of that attribute. In the above case, data is supposed to be a string of HTML markups.

We have two methods to override, doSetAttributeValue() and get(). get() is simply a getter, which actually doesn’t really matters to WebView’s data and we can just return null meaning the attribute is “set” only. But we just return something anyway in our example above. doSetAttributeValue() is a bit tricky as it actually allows Object instead of String as input. This is because some of our view attributes can accept more than one kind of input, for example, the visibility view attribute can be either Integer or Boolean. Because of this, type checking is necessary to perform here. Since we need a String, simply calling Object.toString() needs much less effort to do type checking.

Now, we need to let the framework aware of this new Attribute. We have two different ways to do, as mentioned above.

The easier path: Custom View

Custom views in Android needs to extends from View, and for Android Binding, you need to implement IBindableView as well. Although we are not making a new widget, we create a new custom view, that is capable to response with the new DataViewAttribute. Here is the full code:

public class BindableWebView extends WebView implements IBindableView<BindableWebView> {

	public BindableWebView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public BindableWebView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public BindableWebView(Context context) {
		super(context);
	}

	@Override
	public ViewAttribute<? extends View, ?> createViewAttribute(
			String attributeId) {
		if ("data".equals(attributeId)){
                    return new DataViewAttribute(this, "data");
		}
		return null;
	}
}

The only method need to implement is createViewAttribute. And there, we just need to return our new Attribute if the id is “data”, and returning null if not; return null will let the framework to handle the other attributes, unless you want to override the default attributes. Note that the custom view is not necessarily to hold the reference of that attribute, and each attribute is guaranteed to create at most once.

Create your own Binding Provider

All attributes of default widgets, is injected by binding providers. In this case, we need to create the following BindingProvider for WebViews:

public class WebViewBindingProvider extends BindingProvider {
	@Override
	public <Tv extends View> ViewAttribute<Tv, ?> createAttributeForView(
	        View view, String attributeId) {
		if (!(view instanceof WebView)) return null;
		if ("data".equals(attributeId)){
                    return new DataViewAttribute(this, "data");
		}
		return null;
	}
}

We need to first check if the view asking for attribute is WebView or not, and then we create attribute according to what attributeId it is requesting. Again, returning null will pass the handle to other providers.

Now, we need to register the above provider to the binding framework, remember in Application.onCreate(), we need to call Binder.init()? This is where we should register the provider:

        @Override
	public void onCreate() {
		super.onCreate();
                AttributeBinder.getInstance().registerProvider(new WebViewBindingProvider());
		Binder.init(this);
	}

That’s it! If you register your custom provider before init(), then the provider will be called before default providers are called, so in case you need to override default attributes, you should do this.

If you use Binding Provider method, you can add attributes to Android’s default widgets without extending from it. And it leads a cleaner xml layout file; moreover, it is easily to ship as a reusable library. Comparing to custom view method, which is easier to create (most of the time I make those Attributes as Inline static class) instead of making a separate class file.

Advertisements