Android resource linking failed in binding adapter data binding - android

I want to set text using binding adapter.Here is the MyBindingAdapter.kt
#BindingAdapter("android:setTitle")
fun setTitle(textView : TextView,text: String){
textView.text = text
}
in activity_table.xml I used setTitle like this
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_column="1"
android:layout_weight="1"
android:gravity="center"
android:setTitle="Hello word"/>
and I getting below error in compile time

You need to change from #BindingAdapter("android:setTitle") to #BindingAdapter("setTitle") in your MyBindingAdapter.kt.
Also you need to change in xml file from android:setTitle="Hello word" to setTitle="Hello word"
It should work now!

Android studio will not consider #BindingAdapter("android:setTitle") while binding it, you need to write only #BindingAdapter("setTitle") to execute it perfectly, otherwise it will give that binding error while compile time.

First make sure you have to enclose you layout within the <layout> tag
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
..... </layout>
and #BindingAdapter("android:setTitle") change as #BindingAdapter("title")
it should work.

Related

Android Studio - Kotlin: Import Button

I know this is probably a quite simple question to answer but I want to import my button from activity_main.xml with the id "button1":
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
In the MainActivity.kt so I can add a setOnClickListener to it.
I saw something like this:
MainActivity.kt:
val button1 = findViewById(R.id.button1) as Button
That gives me 2 errors:
unresolved reference: findViewById;
unresolved reference: Button;
This is the action I want to add:
button1.setOnClickListener {
Toast.makeText(applicationContext, "Hello World", Toast.LENGTH_LONG).show()
}
So I could use the setOnClickListener on "button1" but it wont work.
Whats the standard way of importing a button with kotlin so I can use it properly?
So apparently, I needed to add
apply plugin: 'kotlin-android-extensions'
to the build.gradle (Module:app).
After that I was able to import all views via
import kotlinx.android.synthetic.main.activity_main.*
on the MainActivity.kt so I could use the setOnClickListener to the Id I gave the button in the activity_main.xml
I think the findViewById() is just another way to do this, still good. Thanks
You Could Do This:
val button1 = findViewById<Button>(R.id.button1)
In your code, make sure that the Button and findViewById both were referenced from Activity class.
There are different ways for view binding. The recommended one is to use either data binding or view binding feature.
Kotlin synthetics, part of Android Kotlin Extensions Gradle plugin, is Kotlin only method of view binding which doesn't expose nullabilty. That means all the views are declared as platform types. Now beginning from Kotlin 1.4.20, they will not be supported. Check migration to view binding here: https://developer.android.com/topic/libraries/view-binding/migration
findVewbyId is another way for the same purpose, but this comes with some expenses like traversing the view hierarchy to get the view, runtime exceptions, boilerplate code.
Other way is to use library like ButterKnife

View Binding is working for one layout, but not working for another

New to android development, I'm trying to apply View Binding as they are the recommended view referencing method by Google as of now. I have two layouts content_main.xml and content_note_list.xml. I got the first 'content_main_xml' (MainActivity) to work with View Binding. However I'm having trouble implementing view binding for second 'content_note_list.xml'.
Below is the code that keeps crashing the app within the simulator
here's the image of the code block
It works when I don't use the view binding ie.
setContentView(R.layout.activity_note_list)
but as soon as I try to use the view bindings, it opens up the app and crashes momentarily
setContentView(binding.root)
I don't know what I'm doing wrong, I have followed the description provided by the official android dev site, multiple videos.
Any help would be necessary.
EDIT:
content_note_list.xml activity_note_list.xml
Apologize for attaching image files, the code becomes all messed up when I try to append it.
It seems to be the name. Remembering from android documentation. If your layout is result_profile.xml
<LinearLayout ... >
<TextView android:id="#+id/name" />
<ImageView android:cropToPadding="true" />
<Button android:id="#+id/button"
android:background="#drawable/rounded_button" />
</LinearLayout>
ViewBinding will generate ResultProfileBinding.
If view binding is enabled for a module, a binding class is generated for each XML layout file that the module contains. Each binding class contains references to the root view and all views that have an ID. The name of the binding class is generated by converting the name of the XML file to Pascal case and adding the word "Binding" to the end.
So, with that in mind. if your layout is activity_note_list it will generate ActivityNoteListBinding class or something like that. In your code, you are setting ContentNoteListBinding class. Try to replace ContentNoteListBinding with ActivityNoteListBinding. Also, if it does not solve the problem. Try to add the code from console log. it has more details about the error.
Update
If you want to handle View or ViewGroup which are include in your activity/fragment over <include> tag, you can access these views almost directly. You need to add an Id in this tag <include>. And then you will have access to these components: For example:
This is a activity_note_list.xml
........
<include
id="+#id/ly_content_list_note"
layout="#layout/content_list_note"/>
........
And content_note_list.xml
<listView
id="+id/listNotes"
.......
/>
Now in your Activity class, you can access in this way:
binding.lyContentListNote.listNotes
As you can see, first access directly to the id of the container, which is ly_content_list_note and then all the view components inside that, in this case listNotes.
adding to #rguzman answer, to avoid running into issues like this where the type of the binding is accidentally set wrong, you can use DataBindingUtil library.
For Activity
val binding = DataBindingUtil.setContentView(this, R.layout.your_file)
For Fragments
binding = DataBindingUtil.inflate(inflater, R.layout.your_file, container, false)
//other logic
return binding.root
link to official docs for more info

set android:textAppearance with DataBinding

I am trying to set android:textAppearance with use of DataBinding , but it is not allowing me to use ?android:attr/textAppearanceLarge with ternary operator.
android:textAppearance="#{position==1 ? ?android:attr/textAppearanceLarge : ?android:attr/textAppearanceMedium}"
It is showing me compile time error <expr> expected, got '?'.
Does there any other way to use this with DataBinding?
You cannot use it directly, however here's trick I use in such cases. Create own styles using textAppearanceLarge and textAppearanceMedium as parents and then set these styles instead:
First create Foo style:
<style name="Foo" parent="TextAppearance.AppCompat.Large">
... [whatever you need to set or override ] ...
</style>
and do the same for for FooMedium. Then edit your layout file as shown below. Note you must import project's R class in <data> block first:
<data>
<import type="<your-package-id>.R"
</data>
Finally apply the appearance as you formerly wanted:
android:textAppearance="#{ position==1 ? R.style.Foo : R.style.FooMedium }"
You can use android.R.attr package instead of the ?android:attr
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.R.attr"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hello world"
android:textAppearance='#{age==1 ? android.R.attr.textAppearanceLarge : android.R.attr.textAppearanceMedium}'
tools:textAppearance="?android:textAppearanceLarge"
/>
</LinearLayout>
</layout>
Looks like DataBinding treats android:textAppearance in some special way (at least on Android Studio 3.2.1).
For example the following expression according to this should be fine, but it is not.
The following expression is being accepted by the compiler but it is doing nothing:
android:textAppearance="#{R.style.MyStyleTest}"
I have tried several options and only vanilla way worked for me:
android:textAppearance="#style/MyStyleTest"
As a Solution
I would suggest to use #BindingAdapter and perform all the logic there.
For example if you would like to work with references to attributes (which looks like: ?attr/...) use the following approach:
layout.xml
<TextView
...
bind:textAppearanceAttr="#{position==1 ? android.R.attr.textAppearanceLarge: android.R.attr.textAppearanceMedium}"
/>
sources.kt
#BindingAdapter(value = ["textAppearanceAttr"])
fun textAppearanceAttr(textView: TextView, #AttrRes attrRef: Int?) {
attrRef?.also {
val attrs = textView.context.obtainStyledAttributes(intArrayOf(attrRef))
val styleRes = attrs.getResourceId(0, -1)
attrs.recycle()
if (styleRes != -1) {
TextViewCompat.setTextAppearance(textView, styleRes)
}
}
}
or in case of style resource ids (#style/...):
layout.xml
bind:textAppearanceStyle="#{position==1 ? R.style.MyStyleTest1: R.style.MyStyleTest2}"
sources.kt
#BindingAdapter(value = ["textAppearanceStyle"])
fun textAppearanceStyle(textView: TextView, #StyleRes style: Int?) {
style?.also { TextViewCompat.setTextAppearance(textView, it) }
}
You should use textStyle instead of textAppearance,
Try to use code give below:
android:textStyle="#{position==1 ? #style/TextAppearance.Material.Large :#style/TextAppearance.Material.Medium}"
For more reference use below image

Android dataBinding - #BindingAdapter custom app namespace being ignored

I have created a custom bindingAdapter in android and when i pass in the color i want the color to change, this is actually for a test im working on just to make sure it works. Here is the code:
here is my view Model for the data binding:
public class User {
public ObservableInt visible;
public User(int visible) {
this.visible=new ObservableInt(visible);
}
#BindingAdapter({"app:bindColor"}) //notice the bindColor custom attribute
public static void setTextColor(TextView view,String color) {
if("green".equals(color))
view.setTextColor(Color.parseColor("#63f421"));
}
}
Now in my xml file which is binded to this model im expected to pass in a color so that the setTextColor method can use it:
<?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 class="MainActivityBinder">
<variable name="user" type="com.example.android.floatingactionbuttonbasic.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tv_one"
android:text="my first textview"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tv_two"
android:text="my second textview"
android:visibility="#{user.visible}"
app:bindColor="#{'green'}" //see im passing in the green string here
android:textColor="#android:color/holo_green_dark"/>
</LinearLayout>
</layout>
I am getting the error at runtime time of:
Error:(27, 65) error: package com.example.android.floatingactionbuttonbasic.databinding does not exist
Warning:Application namespace for attribute app:bindColor will be ignored.
Error:(24, 33) Identifiers must have user defined types from the XML file. een is missing it
Error:Execution failed for task ':Application:compileDebugJavaWithJavac'.
> java.lang.RuntimeException: Found data binding errors.
if i take out the bindingAdapter stuff it works perfectly with the other databinding stuff. Its just this custom binding thats not working. My project is titled floatingactionbuttonbasic btw.
I was able to get this to work. The issue was how i was passing in the string. It should have the `` around it.
app:bindColor="#{`green`}"
you can also do this :
app:bindColor='#{"green"}'
But what seems to not be allowed is this notation:
app:bindColor="#{'green'}"
i wrote a blog about it to help others if interested.

Possibility to add parameters in button xml?

I currently have an activity with some buttons.
In my xml, buttons are defined like this:
<ImageButton (...) android:onClick="GoToPageX"/>
and I have in my activity:
public void GotoPageX() {
startActivity(new Intent(this, PageX.class));
finish();
}
The problem is that I have hundreds of buttons and do not want to write
<ImageButton (...) android:onClick="GoToPage1"/>
<ImageButton (...) android:onClick="GoToPage2"/>
<ImageButton (...) android:onClick="GoToPage3"/>
...
<ImageButton (...) android:onClick="GoToPage100"/>
and all the scripts.
I am now using
public void GotoPage( int i) {
startActivity(new Intent(getBaseContext(), activities.get(i)));
finish();
}
and would like to give the parameter i from the xml, is that possible?
Thank a lot for any help.
It is not directly possible. However, maybe you could use android:tag to get your parameter.
<ImageButton (...) android:onClick="goToPage" android:tag="25"/>
public void goToPage(View v) {
String pageNumber = v.getTag().toString();
/* ... */
}
You could also do this by enabling data binding and using a lambda expression for the onClick value. This way is especially useful if you plan to use multiple inputs of different types. Here's an example of a simple MainActivity.xml in which this strategy is used.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="main" type="com.example.android.myapp.MainActivity" />
</data>
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageButton (...) android:onClick='#{() -> main.GotoPage(1,"one")}'/>
<ImageButton (...) android:onClick='#{() -> main.GotoPage(2,"two")}'/>
<ImageButton (...) android:onClick='#{() -> main.GotoPage(3,"three")}'/>
...
<ImageButton (...) android:onClick='#{() -> main.GotoPage(100,"one hundred")}'/>
</LinearLayout>
</layout>
and in MainActivity.java
public void GotoPage(int i, String otherVariable) {
/** code using i and otherVariable **/
}
UPDATE: For those who don't know how to set up data binding, I will explain it here so you don't have to google around for it. First, enable dataBinding in the build.gradle file:
android {
...
dataBinding {
enabled = true
}
...
}
Also, make sure jcenter() is in your repositories.
Then, go to the XML of the layout where onClick will be used and wrap its layout in a layout tag with a data section like this:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="main" type="com.example.android.yourapp.MainActivity" />
</data>
<YourLayout>
...
</YourLayout>
</layout>
For the variable tag's type parameter, you need to put the class that will contain the function which onClick points to. In this example, I will use the main activity class, which is named MainActivity in my test project.
After you have your layout wrapped in a layout tag like in the example above, clean the project in Android Studio. You may also need to invalidate cache/restart or close and reopen Android Studio.
Next, if the the layout with onClick you are trying to set up data binding for is the same layout set by setContentView in your main activity class, open the file that contains your main activity class. If the layout with onClick you are trying to set up data binding for is inflated programmatically in a different file, open the file in which the layout is inflated instead.
Add these imports to that file:
import com.example.android.yourapp.databinding.YourLayoutBinding;
import android.databinding.DataBindingUtil;
That first class you are importing is generated when you clean the project (and possibly have to invalidate cache/restart) and is automatically named after the XML file you added the layout wrapper to. If the layout file is named your_layout.xml, the import class will be named YourLayoutBinding. The exact import path will depend on your app name and structure, but it will always be within a databinding parent class.
The next step depends on whether the layout you are adding data binding to is set with setContentView or is inflated with inflate. Both versions of the following step make use of the method setMain. The setMain method is automatically generated and named using the value of the name parameter in the layout wrapper we added. Since we put name="main", the method is called setMain.
If the layout you are adding data binding to is the same layout set by setContentView find the line in your main activity class that looks like setContentView(R.layout.your_layout); and change it to use DataBindingUtil.setContentView instead of setContentView, adding this as its first argument. Use binding.setMain to point the layout's main variable to the current activity.
YourLayoutBinding binding = DataBindingUtil.setContentView(this, R.layout.your_layout);
binding.setMain(this);
If the layout you are adding data binding to is not set by setContentView but rather inflated go to where it is inflated in your code. It should look something like this:
return inflater.inflate(R.layout.your_layout, container, false);
Modify it to use DataBindingUtil.inflate, adding the previous inflater as its first argument. Use binding.setMain to point the layout's main variable to the main activity, and use binding.getRoot() to get the view. It should end up like this:
YourLayoutBinding binding = DataBindingUtil.inflate(inflater, R.layout.your_layout, container, false);
binding.setMain((MainActivity) getActivity());
return binding.getRoot();
Now the data binding is ready to use. Add a function for onClick to point to within your main activity class.
public void exampleFunction(int number, String text) {
System.out.println("Number: " + number + ", Text: " + text);
}
You can call it from the layout you added data binding to using a lambda expression. This example function doesn't require a View, so it can be used like this:
<Button android:id="#+id/buttonID"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="26sp"
android:text="Test"
android:onClick='#{() -> main.exampleFunction(123, "test")}'/>
Make sure to use single quotes around the value for onClick if you plan on using a String input.
If you do need to pass the button's view to your function, simply add a View parameter to your function's required arguments and use a lambda expression like this instead:
<Button android:id="#+id/buttonID"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="26sp"
android:text="Test"
android:onClick='#{(view) -> main.exampleFunction(view, 123, "test")}'/>
If you will create some layout element in xml you can use there
<ImageButton
android:id="#+id/some_id_value" />
where some_id_value is kind of unique string which will be translate into id which is kept in R.java (better for you- don't change anything there) than in code you can get that id by using
R.id.some_id_value
read a little bit there that's really basics.
You can set Tags for a view. Tags are basically a way for views to have memories.
xml:
<ImageButton
...Other Parameters...
android:id="#+id/Button2"
android:tag="2"
android:onClick="GoToPageX"/>
<ImageButton
...Other Parameters...
android:id="#+id/Button3"
android:tag="3"
android:onClick="GoToPageX"/>
The line android:tag="2" set a tag value of 2(string data type) to Button2
Java file:
General Case:
Inside GoToPageX(View v) function,
use v.getTag() to get the tag value of corresponding view(From which ever view the method was called).
Your case:
Add the method as follows
public void GoToPageX(View v){
int i = Integer.parseInt(v.getTag()); //parseInt converts string to integer
startActivity(new Intent(getBaseContext(), activities.get(i)));
finish();
}

Categories

Resources