Wheel View in action from my exposure helper

Last year I bought a Pen-FT from ebay, which became my most favorite film camera. Only thing that bothers me is the malfunction light meter and there seems no way to fix. There are many apps in Android that can help calculating a-s settings, but I don’t find they are handy enough. So, I started the above stuff (note: the above is still work-in-progress so it looks ugly)

In designing the interface, I found that I need to use a vertical wheel, after some research, I end up finding the above one from here. This one looks good and so I decided to include in my project; but I need to make it compatible with Android-Binding.

It’s not difficult to make a view compatible with Android-Binding, especially when you are not going to ship that widget as an independent library: you just need to implement those attributes that you needed. In the Wheel View, I need a few attributes only:

  1. adapter: by default, WheelView takes WheelViewAdapter as adapter, and it is NOT inherited from android.widget.adapter. We’ll investigate this later;
  2. selectedPosition: Integer, two-way bindable; and if I change the value, it got cool animation animating the movement.

Implementation

We start with the following signature, just like any custom views in Android Binding:

public class BindableWheel extends WheelView implements IBindableView{
@Override
    public ViewAttribute<? extends View, ?> createViewAttribute(String arg0) {
		if (arg0.equals("adapter")){
			return adapterAttr;
		}else if (arg0.equals("selectedPosition"))
			return new SelectedPositionAttr(this);
	    return null;
    }
}

Here I used two styles in handling the attributes, the adapterAttr is created as anonymous class, while selectedPositionAttr is a normal inner class, following is the SelectedPositionAttr class:

public class SelectedPositionAttr extends ViewAttribute<BindableWheel, Integer>
		implements OnWheelChangedListener{

		public SelectedPositionAttr(BindableWheel view) {
	        super(Integer.class, view, "selectedPosition");
	        view.addChangingListener(this);
        }

		@Override
        public void onChanged(WheelView wheel, int oldValue, int newValue) {
			this.notifyChanged();
        }

		@Override
        protected void doSetAttributeValue(Object arg0) {
			if (arg0 instanceof Integer){
				getView().setCurrentItem((Integer)arg0, true);
			}
        }

		@Override
        public Integer get() {
			return getView().getCurrentItem();
        }
	}

The SelectedPositionAttr just listen to the WheelView for any change event. If so, it broadcast with “notifyChanged()”. For the AdapterAttr, I created in anonymous class:

protected ViewAttribute<BindableWheel, WheelViewAdapter> adapterAttr =
			new ViewAttribute<BindableWheel, WheelViewAdapter>(
					WheelViewAdapter.class, BindableWheel.this, "adapter"){
				@Override
                protected void doSetAttributeValue(Object arg0) {
					if (arg0 instanceof WheelViewAdapter){
						getView().setViewAdapter((WheelViewAdapter)arg0);
						return;
					}else if (arg0 instanceof Adapter){
						getView().setViewAdapter(new AdapterToWheelAdapter((Adapter)arg0));
					}
                }

				@Override
                public WheelViewAdapter get() {
					return getView().getViewAdapter();
                }

				@Override
                protected BindingType AcceptThisTypeAs(Class<?> type) {
					return BindingType.OneWay;
                }
	};

One thing to notice is in Android-Binding, we have a handy converter called “ADAPTER” and “STITCH”, which the usage is like:

ADAPTER({source=SOURCE, template=TEMPLATE})

STITCH(ADAPTER, ADAPTER…)

they are both handy and good for better code-view separation. Problem is, they both return “android.widget.Adapter”. In order to take advantage to them, I created a simple “adapter” from “android.widget.Adapter” to “WheelViewAdapter”:

public class AdapterToWheelAdapter extends AbstractWheelAdapter{
		private final Adapter mAdapter;
		public AdapterToWheelAdapter(Adapter adapter){
			mAdapter = adapter;
		}

		@Override
        public int getItemsCount() {
	        return mAdapter.getCount();
        }

		@Override
        public View getItem(int index, View convertView, ViewGroup parent) {
			return mAdapter.getView(index, convertView, parent);
        }

		@Override
        public void registerDataSetObserver(DataSetObserver observer) {
			mAdapter.registerDataSetObserver(observer);
        }

		@Override
        public void unregisterDataSetObserver(DataSetObserver observer) {
	        mAdapter.unregisterDataSetObserver(observer);
        }
	}

This is a very handy tricks to convert adapters to other adapters that is not inherited from android.widget.Adapter, for example in the ActionBar’s List Navigation, I also used such trick to convert the adapters. With the above implementations, I can now “Bind” the WheelView using following xml:

<com.gueei.exposureHelper.widgets.BindableWheel
                 android:layout_width="fill_parent"
                 android:layout_height="200dp"
                 android:background="@drawable/condition_background"
                 binding:adapter="ADAPTER({source=ShutterSpeeds, template=@layout/dial_item})"
                 binding:selectedPosition="ShutterIndex" />

Full source code will be available to download once I finished the project!

Advertisements