home.


Tagged: android-custom-views


Android: Passing objects to custom views with databinding

Update: There’s now a much, much simplier version of this.

This is a bit voodoo, but that said… Let’s say you include something like this as a static method anywhere in your codebase.

@BindingAdapter("app:thing")
public static void setThing(View v, Object s) {
  Log.d("A log, innit", "Called setThing");
}

Note that the method name setThing is derived from app:thing. And the first parameter is a View and the second is an Object.

And then, in a databinding layout file, have something like

<layout xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:app="http://schemas.android.com/apk/res-auto">
  <data>
    <variable
        name="thing"
        type="String"/>
  </data>
  <com.example.blar.myapplication.CustomView
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      app:thing="@{thing}"
      />
  ...

Then the above static method will be called when we try to set the app:thing attribute.

If in your static method you have something like this:

CustomViewDatabindingSettable st = (CustomViewDatabindingSettable) v;
st.passedDataBindingObject(s);

Then, providing your custom view implements CustomViewDatabindingSettable, you can call passedDataBindingObject passing through the databound variable.

For example, your custom view could be:

public class ListView extends FrameLayout implements CustomViewDatabindingSettable {
  ...
  @Override
  public void passedDataBindingObject(Object o) {
    Log.d("HIYA", "We're in passedDataBindingObject: " + o);
    // Now do something with the data bound object.
  }
  ...
}

Databinding and ‘instant run’ seems to mess up the autogeneration sometimes, in Android Studio 2.0 beta anyhow.

I had to uninstall the project from the device occassionally to remove dead code.

android android-databinding android-custom-views

Android: Simpler passing objects to custom views with databinding

If you have a custom view, you can pass an object to it via databinding. Let’s say you have this basic custom view.

public class CustomV extends Button {

  public static class APojo {
    private String oj;

    public String getOj() {
     return oj;
    }

    public void setOj(String oj) {
     this.oj = oj;
     }
  }

  private APojo thing;

  public CustomV(Context context) {
    super(context);
    init(null, 0);
  }

  public CustomV(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(attrs, 0);
  }

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

  private void init(AttributeSet attrs, int defStyle) {
    setText("Custom innit");
  }

  public APojo getThing() {
    return thing;
  }

  public void setThing(APojo thing) {
    this.thing = thing;
  }
}

It’s a basic custom view class, with a basic pojo class at at the top, a class variable and a getter and setter for that.

Now initialise this custom view:

<com.example.CustomV
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  app:thing="@{somevar.someinstance}"
/>

The somevar is just a databinding variable instance you have. someinstance is an instance of the APojo class. And the app: namespace is the standard xml:app="http://schemas.android.com/apk/res-auto".

Now when you initalise that view, the setThing method in your custom view will be called.

android android-databinding android-custom-views

Android: Custom views and attributes

First create a class that extends a View, like FrameLayout here. It inflates a normal layout. You can skip that if you extends a TextView or something.

 public class CustomView extends FrameLayout {

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

  public CustomView(Context context, AttributeSet attrs) {
    super(context, attrs);
    LayoutInflater layoutInflator = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View layout = layoutInflator.inflate(R.layout.generic_error_overlay, this);
  }

  @Override
  public void onRestoreInstanceState(Parcelable state) {
    if (state instanceof Bundle) {
      Bundle bundle = (Bundle) state;
      // Restore things from bundle here
      super.onRestoreInstanceState(bundle.getParcelable("instanceState"));
      return;
    }
    super.onRestoreInstanceState(state);
  }

  @Override
  protected Parcelable onSaveInstanceState() {
    Bundle bundle = new Bundle();
    bundle.putParcelable("instanceState", super.onSaveInstanceState());
    // Add things to bundle here
    return bundle;
  }

 }

The onSaveInstanceState / onRestoreInstanceState methods allow you to save the View’s state.

To use this in a layout you can do something like:

<your.package.where.the.view.lives.CustomView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content" />

If you want to pass custom attributes, first define the XML namespace in the root element in your layout file:

xmlns:yournamespace=“http://schemas.android.com/apk/res-auto
Then you can use a custom attribute in your XML custom view:

<your.package.where.the.view.lives.CustomView
 android:layout_width="wrap_content"
 yournamespace:your_attribute="Hello"
 android:layout_height="wrap_content" />

You then need to define this attribute in attrs.xml. We’ll make this one a string.

<resources>
...
 <declare-styleable name="YourAttribute">
     <attr name="your_attribute" format="string"></attr>
 </declare-styleable>
...
</resources>

Then in the constructor for your custom view, you can grab this:

...
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.YourAttribute, 0, 0);
try {
 String string = a.getString(R.styleable.YourAttribute_your_attribute);
} finally {
 a.recycle();
}   
...

Note we’re recycling the TypedArray as it’s a shared object. We also refer the the generated styleable attributes in gen that are generated when you added values on attrs.xml.

android android-custom-views

Page 1 of 1