使用data binding和Picasso加载图片

英文原文:Loading images with data binding and Picasso 

你在尝试使用Android data binding library吗?是否不知道如何加载图片到ImageViews?这里是如何显示图片的两个例子。

最近我创建了一个 MVVM 的app,它是作为Archi的一部分发布。Archi演示了安卓上的不同架构模式。这个app需要从GitHub API 下载profile图片并通过data binding把它们显示在一个ImageView上。本文我将大致描绘下实现它的两种方法。

下面的例子中使用的是Square的 Picasso 库来加载图片,但是你可以根据相同的方法使用其它图片加载库。

方法 1

使用 @BindingAdapter

这个方法使用BindingAdapter注解来创建一个自定义的xml属性。当这个属性以正确的类型设置到布局文件中时,ata binding 框架会触发被注解的方法。我把这个自定义的属性命名为imageUrl而参数类型为String。记住注解方法必须是静态的。

ProfileViewModel.java 

public class ProfileViewModel {
    public String getImageUrl() {
        // The URL will usually come from a model (i.e Profile)
        return "http://cdn.meme.am/instances/60677654.jpg";
    }
    @BindingAdapter({"bind:imageUrl"})
    public static void loadImage(ImageView view, String imageUrl) {
        Picasso.with(view.getContext())
                .load(imageUrl)
                .placeholder(R.drawable.placeholder)
                .into(view);
    }
}

view model准备好之后,你可以创建你的布局文件并添加新的imageUrl属性到ImageView中。自定义属性需要使用app命名空间而不是android。

profile_activity.xml 

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" >
    <data>
        <variable
            name="viewModel"
            type="uk.ivanc.imagedatabinding.ProfileViewModel" />
    </data>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:layout_width="200dp"
            android:layout_height="200dp"
            app:imageUrl="@{viewModel.imageUrl}" />
    </RelativeLayout>
</layout>

最后一步像普通的data binding布局那样处理。

ProfileActivity.java 

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ProfileActivityBinding binding =
            DataBindingUtil.setContentView(this, R.layout.profile_activity);
    binding.setViewModel(new ProfileViewModel(this));
}

方法2

ObservableField 与自定义 Picasso Target

第二种方法需要更多的代码不过跟第一种一样有效。这里的思路是使用一个类型为Drawablet的ObservableField与一个自定义Picasso Target。ObservableField可以包含任意类型的对象,在值发生改变的时候自动刷新布局,这里是一个Drawable对象。

这里的关键在于创建了一个接受了ObservableField与更新其值的句柄的自定义Picasso target。

ProfileViewModel.java

public class ProfileViewModel {
    // The URL will usually come from a model (i.e Profile)
    static final String IMAGE_URL = "http://cdn.meme.am/instances/60677654.jpg";
    public ObservableField<Drawable> profileImage;
    private BindableFieldTarget bindableFieldTarget;
    public ProfileViewModel(Context context) {
        profileImage = new ObservableField<>();
        // Picasso keeps a weak reference to the target so it needs to be stored in a field
        bindableFieldTarget = new BindableFieldTarget(profileImage, context.getResources());
        Picasso.with(context)
                .load(IMAGE_URL)
                .placeholder(R.drawable.placeholder)
                .into(bindableFieldTarget);
    }
    public class BindableFieldTarget implements Target {
        private ObservableField<Drawable> observableField;
        private Resources resources;
        public BindableFieldTarget(ObservableField<Drawable> observableField, Resources resources) {
            this.observableField = observableField;
            this.resources = resources;
        }
        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
            observableField.set(new BitmapDrawable(resources, bitmap));
        }
        @Override
        public void onBitmapFailed(Drawable errorDrawable) {
            observableField.set(errorDrawable);
        }
        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {
            observableField.set(placeHolderDrawable);
        }
    }
}

BindableFieldTarged类需要只被写一次并且能在不同的项目中复用。

在布局文件中我们需要设置ImageView的src属性,让它指向ObservableField。

profile_activity.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="uk.ivanc.imagedatabinding.ProfileViewModel" />
    </data>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:src="@{viewModel.profileImage}"/>
    </RelativeLayout>
</layout>

最后,记得按照前面的做法再进行一次绑定。参照第一个例子中的ProfileActivity.java。

两种方法都可以很好的工作但是我推荐使用第一种。它需要更少的代码而且可读性也更强。

如果你喜欢本文,点击推荐按钮!