Estou tentando definir o ID de recurso drawable para android: src de ImageView usando vinculação de dados
Aqui está meu objetivo:
public class Recipe implements Parcelable {
public final int imageResource; // resource ID (e.g. R.drawable.some_image)
public final String title;
// ...
public Recipe(int imageResource, String title /* ... */) {
this.imageResource = imageResource;
this.title = title;
}
// ...
}
Aqui está meu layout:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="recipe"
type="com.example.android.fivewaystocookeggs.Recipe" />
</data>
<!-- ... -->
<ImageView
android:id="@+id/recipe_image_view"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:src="@{recipe.imageResource}" />
<!-- ... -->
</layout>
E, finalmente, aula de atividade:
// ...
public class RecipeActivity extends AppCompatActivity {
public static final String RECIPE_PARCELABLE = "recipe_parcelable";
private Recipe mRecipe;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mRecipe = getIntent().getParcelableExtra(RECIPE_PARCELABLE);
ActivityRecipeBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_recipe);
binding.setRecipe(mRecipe);
}
// ...
}
Ele não exibe nenhuma imagem. O que estou fazendo errado?
Aliás, estava funcionando perfeitamente com a maneira padrão:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recipe);
final ImageView recipeImageView = (ImageView) findViewById(R.id.recipe_image_view);
recipeImageView.setImageResource(mRecipe.imageResource);
}
fonte
@BindingAdapter
. Mas, o valor deve serandroid:src
, nãoimageResource
:@BindingAdapter("android:src")
. Obrigado!<ImageView imageResource="@{recipe.imageResource}" />
e@BindingAdapter("imageResource")
. Eu só perdeuimageResource="@{recipe.imageResource}"
parte do seu código cortou :)app:imageResource
?Não há necessidade de costume
BindingAdapter
. Apenas useapp:imageResource="@{yourResId}"
e vai funcionar bem.
Verifique isso para saber como ele funciona.
fonte
ImageView
aula e seguindo osetImageResource
método, parece que eventualmente está resolvidoresolveUri
e não há senão zero validação. Então isso funcionaria paraInt
eu me pergunto o que poderia acontecer seInt?
. Quando as ligações são executadas, por exemplo, se alguma outra coisa chamarexecutePendingBindings
então não anulável são padronizados para zero, anuláveis para nulo.definir:
@BindingAdapter({"android:src"}) public static void setImageViewResource(ImageView imageView, int resource) { imageView.setImageResource(resource); }
usar:
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:scaleType="center" android:src="@{viewModel.imageRes, default=@drawable/guide_1}"/>
fonte
Nunca substitua os atributos padrão do SDK ao criar o seu próprio
@BindingAdapter
!Essa não é uma boa abordagem por muitos motivos como: ela vai impedir a obtenção de benefícios de novas correções na atualização do Android SDK nesse atributo. Além disso, pode confundir os desenvolvedores e, certamente, ser complicado para reutilização (porque não pode ser substituído)
você pode usar um namespace diferente, como:
custom:src="@{recipe.imageResource}"
ou
mybind:src="@{recipe.imageResource}"
------ iniciar a atualização 2.Jul.2018
O uso de namespace não é recomendado, portanto, é melhor confiar em um prefixo ou nome diferente como:
app:custom_src="@{recipe.imageResource}"
ou
app:customSrc="@{recipe.imageResource}"
------ finalizar a atualização 2.Jul.2018
No entanto, eu recomendaria uma solução diferente como:
android:src="@{ContextCompat.getDrawable(context, recipe.imageResource)}"
a visão de contexto está sempre disponível dentro da expressão de ligação
@{ ... }
fonte
Com base na resposta de Maher Abuthraa, isso é o que acabei usando no XML:
android:src="@{context.getDrawable(recipe.imageResource)}"
A
context
variável está disponível na expressão de ligação sem quaisquer importações. Além disso, nenhum costumeBindingAdapter
necessário. Apenas ressalva: o métodogetDrawable
está disponível apenas a partir da API 21.fonte
Para Kotlin, coloque isso em um arquivo utils de nível superior, nenhum contexto estático / complementar necessário:
@BindingAdapter("android:src") fun setImageViewResource(view: ImageView, resId : Int) { view.setImageResource(resId) }
fonte
Quanto mais você pode fazer com
DataBindingAdapter
Defina qualquer um destes tipos:
android:src="@{model.profileImage}" android:src="@{roundIcon ? @drawable/ic_launcher_round : @drawable/ic_launcher_round}" android:src="@{bitmap}" android:src="@{model.drawableId}" android:src="@{@drawable/ic_launcher}" android:src="@{file}" android:src="@{`https://placekitten.com/200/200`}"
Definir imagem de erro / imagem de espaço reservado
placeholderImage="@{@drawable/img_placeholder}" errorImage="@{@drawable/img_error}" <ImageView placeholderImage="@{@drawable/ic_launcher}" errorImage="@{@drawable/ic_launcher}" android:layout_width="100dp" android:layout_height="100dp" android:src="@{`https://placekitten.com/2000/2000`}" />
Testado todos os tipos
Isso se torna possível com um único adaptador de ligação. Basta copiar este projeto de método.
public class BindingAdapters { @BindingAdapter(value = {"android:src", "placeholderImage", "errorImage"}, requireAll = false) public static void loadImageWithGlide(ImageView imageView, Object obj, Object placeholder, Object errorImage) { RequestOptions options = new RequestOptions(); if (placeholder instanceof Drawable) options.placeholder((Drawable) placeholder); if (placeholder instanceof Integer) options.placeholder((Integer) placeholder); if (errorImage instanceof Drawable) options.error((Drawable) errorImage); if (errorImage instanceof Integer) options.error((Integer) errorImage); RequestManager manager = Glide.with(App.getInstance()). applyDefaultRequestOptions(options); RequestBuilder<Drawable> builder; if (obj instanceof String) { builder = manager.load((String) obj); } else if (obj instanceof Uri) builder = manager.load((Uri) obj); else if (obj instanceof Drawable) builder = manager.load((Drawable) obj); else if (obj instanceof Bitmap) builder = manager.load((Bitmap) obj); else if (obj instanceof Integer) builder = manager.load((Integer) obj); else if (obj instanceof File) builder = manager.load((File) obj); else if (obj instanceof Byte[]) builder = manager.load((Byte[]) obj); else builder = manager.load(obj); builder.into(imageView); } }
Motivo pelo qual usei o Glide para carregar todos os objetos
Se você me perguntar por que usei o Glide para carregar a id do drawable / recurso, em vez disso, poderia usar
imageView.setImageBitmap();
ouimageView.setImageResource();
. Então, a razão é queSe você usa o Piccaso, Fresso ou qualquer outra biblioteca de carregamento de imagens, pode fazer alterações no
loadImageWithGlide
método .fonte
public Drawable getImageRes() { return mContext.getResources().getDrawable(R.drawable.icon); } <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:scaleType="center" android:src="@{viewModel.imageRes}"/>
fonte
você pode fazer o seguinte
android:src="@{expand?@drawable/ic_collapse:@drawable/ic_expand}"
fonte
Não sou especialista em Android, mas passei horas tentando decifrar as soluções existentes. A coisa boa é que entendi toda a ideia de vinculação de dados usando
BindingAdapter
um pouco melhor. Por isso, estou pelo menos grato pelas respostas existentes (embora bastante incompletas). Aqui está uma análise completa da abordagem:Também vou usar o
BindingAdapter
neste exemplo. Preparando oxml
:<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="model" type="blahblah.SomeViewModel"/> </data> <!-- blah blah --> <ImageView android:id="@+id/ImageView" app:appIconDrawable="@{model.packageName}"/> <!-- blah blah --> </layout>
Portanto, aqui estou mantendo apenas as coisas importantes:
SomeViewModel
é o queViewModel
eu uso para vinculação de dados. Você também pode usar uma classe que estendeBaseObservable
e usa@Bindable
. No entanto,BindingAdapter
neste exemplo, não precisa estar em uma classeViewModel
ouBaseObservable
! Uma aula simples é suficiente! Isso será ilustrado mais tarde.app:appIconDrawable="@{model.packageName}"
. Sim ... isso estava realmente me causando dores de cabeça! Vamos decompô-lo:app:appIconDrawable
: Isso pode ser qualquer coisaapp:iCanBeAnything
:! Mesmo. Você também pode manter"android:src"
! No entanto, tome nota da sua escolha, iremos utilizá-la mais tarde!Vamos supor que usamos esta classe Observable simples:
public class SomeViewModel extends BaseObservable { private String packageName; // this is what @{model.packageName} // access via the getPackageName() !!! // Of course this needs to be set at some // point in your program, before it makes // sense to use it in the BindingAdapter. @Bindable public String getPackageName() { return packageName; } public void setPackageName(String packageName) { this.packageName = packageName; notifyPropertyChanged(BR.packageName); } // The "appIconDrawable" is what we defined above! // Remember, they have to align!! As we said, we can choose whatever "app:WHATEVER". // The BindingAdapter and the xml need to be aligned, that's it! :) // // The name of the function, i.e. setImageViewDrawable, can also be // whatever we want! Doesn't matter. @BindingAdapter({"appIconDrawable"}) public static void setImageViewDrawable(ImageView imageView, String packageName) { imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName)); } }
Conforme prometido, você também pode mover o
public static void setImageViewDrawable()
, para alguma outra classe, por exemplo, talvez você possa ter uma classe que tenha uma coleção deBindingAdapters
:public class BindingAdapterCollection { @BindingAdapter({"appIconDrawable"}) public static void setImageViewDrawable(ImageView imageView, String packageName) { imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName)); } }
Outra observação importante é que na minha
Observable
aula eu costumavaString packageName
passar informações extras para osetImageViewDrawable
. Você também pode escolher, por exemploint resourceId
, com os getters / setters correspondentes, para os quais o adaptador se torna:public class SomeViewModel extends BaseObservable { private String packageName; // this is what @{model.packageName} // access via the getPackageName() !!! private int resourceId; // if you use this, don't forget to update // your xml with: @{model.resourceId} @Bindable public String getPackageName() { return packageName; } public void setPackageName(String packageName) { this.packageName = packageName; notifyPropertyChanged(BR.packageName); } @Bindable public int getResourceId() { return packageName; } public void setResourceId(int resourceId) { this.resourceId = resourceId; notifyPropertyChanged(BR.resourceId); } // For this you use: app:appIconDrawable="@{model.packageName}" (passes String) @BindingAdapter({"appIconDrawable"}) public static void setImageViewDrawable(ImageView imageView, String packageName) { imageView.setImageDrawable(Tools.getAppIconDrawable(imageView.getContext(), packageName)); } // for this you use: app:appIconResourceId="@{model.resourceId}" (passes int) @BindingAdapter({"appIconResourceId"}) public static void setImageViewResourceId(ImageView imageView, int resource) { imageView.setImageResource(resource); } }
fonte
Este trabalho para mim. Eu teria adicionado à resposta @hqzxzwb como comentário, mas devido a limitações de reputação.
Eu tenho isso na minha visão Modelo
var passport = R.drawable.passport
Então no meu xml, eu tenho
android:src="@{context.getDrawable(model.passort)}"
E é isso
fonte
Usando Fresco (biblioteca de imagens do Facebook)
public class YourCustomBindingAdapters { //app:imageUrl="@{data.imgUri}" @BindingAdapter("bind:imageUrl") public static void loadImage(SimpleDraweeView imageView, String url) { if (url == null) { imageView.setImageURI(Uri.EMPTY); } else { if (url.length() == 0) imageView.setImageURI(Uri.EMPTY); else imageView.setImageURI(Uri.parse(url)); } } }
fonte
Em seu estado de exibição ou classe de modelo de exibição;
fun getSource(context: Context): Drawable? { return ContextCompat.getDrawable(context, R.drawable.your_source) }
Em seu XML;
<androidx.appcompat.widget.AppCompatImageButton . . . android:src="@{viewState.getSource(context)}"
fonte
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="model" type="YourViewModel"/> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" android:paddingStart="@dimen/dp16" android:paddingTop="@dimen/dp8" android:paddingEnd="@dimen/dp8" android:paddingBottom="@dimen/dp8"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@{model.selected ? @drawable/check_fill : @drawable/check_empty}"/> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
fonte
definir imagem como esta,
<ImageView android:layout_width="28dp" android:layout_height="28dp" android:src="@{model.isActive ? @drawable/white_activated_icon :@drawable/activated_icon}" tools:src="@mipmap/white_activated_icon" />
fonte