使用Android DataBinding BindingAdapter和Dagger 2

使用Android 數(shù)據(jù)綁定庫實現(xiàn)綁定適配器的典型方法是使用@BindingAdapter注釋創(chuàng)建靜態(tài)方法,例如:

@BindingAdapter("android:imageUrl")
public static void loadImage(ImageView view, String url) {
    Picasso.get().load(url).into(view);
}

這已在網(wǎng)上廣泛使用和討論,并適用于很多個例(如果你熟悉DataBinding的話)。

但是,如果你想你的適配器具有很好耦合度,或者您不想在全局范圍內(nèi)定義它,該怎么辦?

BindingAdapter并不一定要是靜態(tài)的......

創(chuàng)建非靜態(tài)綁定適配器

不使用Dagger2也能實現(xiàn)

官方文檔完全沒有DataBinding的這種功能,但是在Stack Overflow上有一些例子。
我舉個例子說明一下。
我創(chuàng)建了三個綁定適配器的接口類:

public interface ImageViewBindingInterface {
    @BindingAdapter({"bind:imageUrl", "bind:error"})
    public  void loadImage(ImageView view, String url, Drawable error);
}
public interface TextViewBindingInterface {
    @BindingAdapter({"bind:font"})
      void setFont(TextView textView, String fontName);
}
public interface ViewBindingInterface {
    @BindingAdapter("android:paddingLeft")
    public  void setPaddingLeft(View view, int padding);
    @BindingAdapter("android:onViewAttachedToWindow")
    public  void setListener(View view, ViewBindingAdapter.OnViewAttachedToWindow attached);
}

build一下,我們發(fā)現(xiàn)DataBindingComponent類是會自動生成的(在build / generated / source / apt / dev下)。
此接口包含為每個單獨的適配器定義的getter:

public interface DataBindingComponent {
    ViewBindingInterface getViewBindingInterface();
    ViewBindingInterface getTextViewBindingInterface();
    ImageViewBindingInterface getImageViewBindingInterface();
}

然后我們創(chuàng)建一個基類去實現(xiàn)它

public class BaseImageViewBinding implements ImageViewBindingInterface{
    @Override
    public void loadImage(ImageView view, String url, Drawable error) {
          Picasso.with(view.getContext()).load(url).error(error).into(view);
    }
}

public class BaseTextViewBinding implements TextViewBindingInterface {
    @Override
    public void setFont(TextView textView, String fontName) {
        textView.setTypeface(Typeface.createFromAsset(textView.getContext().getAssets(), "fonts/" + fontName));
    }
}

public class BaseViewBinding implements ViewBindingInterface {
    @Override
    public void setPaddingLeft(View view, int padding) {
        view.setPadding(padding,
                view.getPaddingTop(),
                view.getPaddingRight(),
                view.getPaddingBottom());
    }
    @Override
    public void setListener(View view, ViewBindingAdapter.OnViewAttachedToWindow attached) {

    }
}

最后再設置你自己的DatabindingComponent:

public class MyOwnDefaultDataBindingComponent implements android.databinding.DataBindingComponent {
    @Override
    public ViewBindingInterface getViewBindingInterface() {
        return new BaseViewBinding();
    }
    @Override
    public TextViewBindingInterface getTextViewBindingInterface() {
        return new BaseTextViewBinding();
    }
    @Override
    public ImageViewBindingInterface getImageViewBindingInterface() {
        return new BaseImageViewBinding();
    }
}

最后在Application中設置默認的DataBindingComponent就可以了

public class MyApplication extends Application {
    public void onCreate() {
        super.onCreate();
        DataBindingUtil.setDefaultComponent(new MyOwnDefaultDataBindingComponent());
    }
}

優(yōu)化了一下,讓自己的代碼看起來更舒服,更低耦合。是不是也很簡單呢?

使用Dagger2注入

讓我們創(chuàng)建一個類似上面例子的簡單適配器:

public class ImageBindingAdapter {

    private final Picasso picasso;

    public ImageBindingAdapter(Picasso picasso) {
        this.picasso = picasso;
    }

    @BindingAdapter("android:imageUrl")
    public void loadImage(ImageView view, String url) {
        picasso.load(url).fit().into(view);
    }
}

Picasso現(xiàn)在不是每次都創(chuàng)建一個新的Picasso實例,而是作為一個依賴者傳遞給構造函數(shù)。特別是對于Picasso來說,如果你想使用構建器來創(chuàng)建一個自定義實例并且避免使用Picasso的多個實例,這可能會很有用。
以這種方式創(chuàng)建BindingAdapter時,DataBinding跟上面一樣也會生成DataBindingComponent接口

public interface DataBindingComponent {
    ImageBindingAdapter getImageBindingAdapter();
}

縱觀DataBindingUtil 文檔 ,我們可以看到有很多的利用方法DataBindingComponent,比如setDefaultComponentinflatesetContentView(以后會更多)。

使用適配器的一種方法是創(chuàng)建一個具體的實現(xiàn)DataBindingComponent,這可以手動完成,也可以使用Dagger 2來利用圖形來提供任何外部依賴。

使用Dagger構建DataBindingComponent

應該可以創(chuàng)建組件或子組件,在此示例中,我選擇使用組件,因為不必將整個圖形暴露給綁定適配器。

組件需要一個范圍,所以我@DataBinding創(chuàng)建了一個Scope作用域。

這是提供以下內(nèi)容的Module ImageBindingAdapter

@Module
public class BindingModule {

    @Provides @DataBinding 
    ImageBindingAdapter provideImageBindingAdapter(Picasso picasso) {
        return new ImageBindingAdapter(picasso);
    }
}

這是Component:

@DataBinding
@Component(dependencies = AppComponent.class, modules = BindingModule.class)
public interface BindingComponent extends DataBindingComponent {}

為了滿足Picasso的依賴性,BindingModule也必須由AppComponent(如果使用子組件則不需要此步驟)所引用:

@Singleton
@Component(modules = {AppModule.class, BindingModule.class})
public interface AppComponent {
    private final Application application;

    public AppModule(Application application) {
        this.application = application;
    }

    Picasso picasso();
}

Build一下,Dagger會自動實現(xiàn)了所有的方法DataBindingComponent。以下是自動生成的內(nèi)容片段:

public final class DaggerBindingComponent implements BindingComponent {

    private Provider<ImageBindingAdapter> provideImageBindingAdapterProvider;

    @Override
    public ImageBindingAdapter getImageBindingAdapter() {
        return provideImageBindingAdapterProvider.get();
    }

    ....
}

使用Component

使用新組件的最簡單方法是在Application類中創(chuàng)建它,特別是如果在Dagger 2.10+中使用Android注入,因為您不再需要將主要組件在·Acitivity·中inject。它現(xiàn)在是一個標準的Dagger組件,所以它只需要以正常的方式構建然后傳遞給它DataBindingUtil.setDefaultComponent(para)。

這是一個示例Application

public class MyApplication extends Application {

    @Override public void onCreate() {
        super.onCreate();

        AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule(this))
                .build();
        appComponent.inject(this);

        BindingComponent bindingComponent = DaggerBindingComponent.builder()
                .appComponent(appComponent)
                .build();

        DataBindingUtil.setDefaultComponent(bindingComponent);
    }
}

這當然在全局范圍內(nèi)有效啦!!這個組件也可以與特定布局一起使用?。。。?/p>

Activity中:

DataBindingUtil.setContentView(this, R.layout.activity_layout, bindingComponent);

Fragment中:

DataBindingUtil.inflate(inflater, R.layout.fragment_layout, container, false, bindingComponent);

參考文章

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容