Method of Setting Width height Ratio with Android Control

  • 2021-08-21 21:19:08
  • OfStack

0. A problem that has been bothering me for a long time

The width and height of the Android control is kept in proportion, which is a requirement that I have encountered continuously since I came into contact with Android. In the past, you either set the width and height directly in the code, or you customized the control. There is also an open source custom ViewGroup on the Internet, which can make it easier to set the ratio of width to height for its sub-View. However, these implementations are still troublesome and not intuitive enough. Until DataBinding, we can easily add custom properties to the control, and we can easily set the aspect ratio of the control in the layout file.

1. How to achieve

The following method is bound for all View through BinderAdapter, which is called when the widthHeightRatio property is set. This is a bit of an AOP, and we have dealt with all View.


public class DataBindingAdapters {
  //  According to View The height and aspect ratio of, set the height 
  @BindingAdapter("widthHeightRatio")
  public static void setWidthHeightRatio(final View view, final float ratio) {
    view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {
        int height = view.getHeight();
        if (height > 0) {
          view.getLayoutParams().width = (int) (height * ratio);
          view.invalidate();
          view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
      }
    });
  }
}

After we get the height of the control, we calculate the width according to the scale, and then set it to the control. OnGlobalLayoutListener is registered here because the height of the control may not be calculated yet. After getting the height, remove listening and avoid redundant calls.


<ImageView
  android:layout_width="120dp"
  android:layout_height="match_parent"
  app:widthHeightRatio="@{1}"/>

Then we can set the aspect ratio directly in the layout file. This layout file must use DataBinding, that is, the outermost layer should use layout tag. The property value must be added with @ {}, otherwise it will be treated as a normal property, our method will not be called, and an error will be reported at compile time because the property cannot be found. Of course, this property can only calculate width based on height. If you want to calculate height based on width, you can add another property in the same way.

2. Brief analysis of the principle

In fact, there are no attributes we added in the compiled layout file (the compiled layout file can be seen under build/intermediates/data-binding-layout-out). To really set this property, we call our bound method directly in Java code. In the Binding class automatically generated by DataBinding, you can find calls like the following.


DataBindingAdapters.setWidthHeightRatio(this.mboundView0, cardWidthHeightRatio);

This is just a simple explanation. As for when this line of code will trigger, please look forward to my subsequent articles.

3. Other Wonders of BinderAdapter

ImageView automatically loads network pictures


@BindingAdapter({"android:src", "error"})
public static void setImageUrl(ImageView view, String url, @DrawableRes int errorImage) {
  if (!TextUtils.isEmpty(url)) {
    //  Packaged image loading tool 
    ImageManager.from(view.getContext()).displayImage(view, url, errorImage);
  } else {
    view.setImageResource(errorImage);
  }
}

Set the url of the picture to be loaded directly in the layout file, and the default picture to be displayed if the load fails.


Related articles: