TextView issues after migrate from Kotlin synthetics to Jetpack view binding - android

I recently migrate binding method from Kotlin synthetics to Jetpack view binding. I have AutofitTextView (by grantland) in my fragment and I set some text to the textview. After I started the activity and attached the fragment, the app crash. The error said
java.lang.ClassCastExceptioni: androidx.appcompt.widget.AppCompatTextView cannot be cast to me.grantland.widget.AutofitTextView
So, I decided to change from AutoFitTextView to AppCompatTextView but I face another issue. I cannot build the app because there is an error
Unresolve reference: setText
I tried various settext methods but none of them works. It seems that the TextView is seen as a View so it does not have a setText method.
========
Config details
Android Studio 4.1.2
buildToolsVersion 28.0.3
jvmTarget 1.8
com.android.databinding:compiler 3.1.4
androidx.appcommpat:appcompat:1.2.0
==========
fragment_main.xml
<FrameLayout
.... >
<LinearLayout
.... >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<me.grantland.widget.AutofitTextView
android:id="#+id/myTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="This is textview" />
....
</LinearLayout>
</LinearLayout>
</FrameLayout>
Set text
FragmentMainBinding.myTextView.text = "The new text"

I found the cause!! It's my mistake. I have 2 layouts with the same name, one for portrait and another one for landscape. I migrate the views in portrait layout but not in landscape layout so when binding class is generated, it generate the two different TextViews as View and View does not have "text" attribute. That's what the error said.

Try this sample.
val binder = FragmentMainBinding.bind(View.inflate(requireContext(), R.layout.fragment_main, null))
binder.myTextView.text = "The new text"

So if that is your entire xml for fragment_main.xml, you need to wrap all of it with <layout> tag. That's the only way the compiler knows to build the binding classes.
<layout
...>
<FrameLayout
.... >
<LinearLayout
.... >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<me.grantland.widget.AutofitTextView
android:id="#+id/myTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="This is textview" />
....
</LinearLayout>
</LinearLayout>
</FrameLayout>
</layout>

Related

Two-way binding Attribute is missing the Android namespace prefix <Data> <Variable>

Trying to implement two way binding
As mentioned:
Data Binding Library
Two-way Android Data Binding - How to use two-way Data Binding to manage a layout
2-way Data Binding on Android!
However on
<variable type="com.example.gideonsassoon.avariel.datamodels.Player" name="name"/>
I am getting the following message.:
"Attribute is missing the Android namespace prefix less... (Ctrl+F1)
Most Android views have attributes in the Android namespace. When
referencing these attributes you must include the namespace prefix, or
your attribute will be interpreted by aapt as just a custom attribute.
Similarly, in manifest files, nearly all attributes should be in the
android: namespace."
If I try to build it puts all the R. in my file in red and state they don't exist etc.
I have edited my build gradle file to have
dataBinding.enabled = true
Full code up to the point relevant below
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:windowSoftInputMode="adjustPan"
tools:context="com.example.gideonsassoon.avariel.ui.MainFragmentActivity">
<data>
<variable type="com.example.gideonsassoon.avariel.datamodels.Player" name="name"/>
</data>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<android.support.v4.view.PagerTabStrip
android:id="#+id/viewpagerStrip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"/>
</android.support.v4.view.ViewPager>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="7dp"
android:layout_marginTop="34dp">
<TextView
android:id="#+id/tv_character_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/name_colon"
android:textAppearance="#style/TextAppearance.AppCompat.Title"
android:textIsSelectable="false" />
<EditText
android:id="#+id/et_character_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#={player.name}"
android:layout_alignBaseline="#+id/tv_character_name"
android:layout_marginStart="15dp"
android:layout_toEndOf="#+id/tv_character_name"
android:ems="12"
android:hint="#string/character_name"
android:inputType="textPersonName" />
Ahh it appears to be that I have it as relativeLayout. It can't be "binding" so it can only be layout. Not sure how that's gonna affect my files as a whole but I guess we'll just have to see how it plays out. Here's my source. However if anyone wants to answer to that effect it would be most appreciated.
Using data binding in Android - Tutorial

Android - Include layout from another module

I have a module called TestGUILib and I have specified testview.xml with a simple FrameLayout.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Testing View!" />
</FrameLayout>
I tried to use this layout in my main app activity_main. Here is my code:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.sam.testapp1.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<include layout="#layout/testview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
</android.support.constraint.ConstraintLayout>
Everything works fine. It compiles ok and runs ok. But the preview doesn't work. It shows the blank screen (ie. when I clicked on "Design", it turns blank) with render errors:
Could not find layout resource matching value 0x7F04002F (resolved name: testview) in current configuration.
Also, there is a line under testview (ie. include layout="#layout/testview"). When I rolled my mouse over, it said
Typo in the word 'testview'
Spellchecker inspection helps locate typos and misspelling in your code, comments and literals, and fix them in one click.
Any idea? Thanks
go to "File > Invalidate Caches/Restart...", click "Invalidate and Restart". it should work.
Lets assume you have moduleOne and moduleTwo and if you want to use moduleTwo resources in moduleOne add following (sample) code in moduleOne gradle
dependencies {
compile project(':moduleTwo')
}
This will include moduleOne into you moduleTwo in order to use its resouces including layouts.
Possible you have forgotten to enable
buildFeatures {
viewBinding true
}
in the second module. In that case viewbinding will not find view and show it red like an unknown element

Kotlin synthetic extension and several include same layout

How to access to view using kotlin synthetic extension if I have a layout like below:
file:two_days_view.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
android:id="#+id/day1"
layout="#layout/day_row"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include
android:id="#+id/day2"
layout="#layout/day_row"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
file: day_row.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/dayName"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
How to access to dayName? I looked for some like this:
day1.dayName.text = "xxx"
day2.dayName.text = "sss"
I see in Studio that I have access to dayName but to which one of dayName TextView is reference to?
Normal if I have only one included layout it works fine. But now I have multiple times included same layout.
of course I can always do:
day1.findViewById(R.id.dayName).text = "xxx"
but I'm looking for nice solution. :)
As a general rule of thumb, you should not construct layouts that end up having multiple views with the same id - for this very reason.
But, to solve your problem:
Instead of importing
kotlinx.android.synthetic.main.layout.day_row.*
you can import
kotlinx.android.synthetic.main.layout.day_row.view.* (Notice the additional .view at the end).
This will import the views not as properties on the Activity/Fragment level, but instead as extension properties for View. That way, you can do it the way you want, assuming that day1 and day2 contain the views you want:
day1.dayName.text = "xxx"
day2.dayName.text = "sss"
In this case, use:
(day1 as TextView).text = ""
(day2 as TextView).text = ""

Preview layout with merge root tag in Intellij IDEA/Android Studio

Let's imagine we are developing compound component based on LinearLayout. So, we create class like this:
public class SomeView extends LinearLayout {
public SomeView(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(LinearLayout.VERTICAL);
View.inflate(context, R.layout.somelayout, this);
}
}
If we'll use LinearLayout as a root of somelayout.xml, we'll have extra view level, so we use merge tag:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some text"
android:textSize="20sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some other text"/>
</merge>
But in Preview tab in IDE merge always acts as FrameLayout, and we'll see something like that:
(It is Android Studio, Intellij IDEA is just the same, about Eclipse I don't know)
Preview speed up developing layouts a lot, it's sad lose such a great help even for some layouts. May be there is a way to specify, how Preview should interpret merge tag in particular layout?
There is a new parentTag tools attribute (added in Android Studio 2.2) that you can use to specify the layout type for a merge tag, which will make the layout render correctly in the layout editor preview.
So using your example:
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:parentTag="LinearLayout"
tools:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some text"
android:textSize="20sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Some other text"/>
</merge>
Note: Both android:layout_width and android:layout_height must be specified in order for the layout to display properly in the editor.
Edit: Outdated answer. See answer by starkej2.
Android Studio 0.5.8 added support for tools:showIn. By using it it is possible to preview < merge > layouts.
http://tools.android.com/recent/androidstudio058released
layout/layout_merge.xml with tools:showIn:
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="#layout/simple_relativelayout">
......
</merge>
layout/simple_relativelayout.xml with include:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/layout_merge"/>
</RelativeLayout>
Is also possible use custom class as parent instead of merge like
<com.mycompany.SomeView xmlns:android="http://schemas.android.com/apk/res/android">
...
</com.mycompany.SomeView>
And then directly inflate this layout and cast result view to SomeView.
Android studio will directly check parent class of SomeView and handle preview like LinerLayout.
You can use onFinishInflate() method in the SomeView to bind views by findViewById().
Benefit of this solution is that you can put all layout definitions or style definition directly to the layout file, you can't use method like setOrientation() in code.

Accessing Android views on nested layouts

I have trouble accessing Views from a layout that is included in another layout.
Please take a look at this picture:
http://dl.dropbox.com/u/3473245/layout_includes.png
How do I access the 4 text views programmatically?
Its probably something really simple that I'm missing.
Thank you very much!
You can do as follows:
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<include android:id="#+id/item_base_lang" layout="#layout/dictionary_list_item" />
<include android:id="#+id/item_learn_lang" layout="#layout/dictionary_list_item" />
</LinearLayout>
dictionary_list_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/dictionary_list_item_text_header"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/dictionary_list_item_text_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
To set the text programmatically:
((TextView)findViewById(R.id.item_base_lang).findViewById(R.id.dictionary_list_item_text_header)).setText("item_base_lang_header");
((TextView)findViewById(R.id.item_base_lang).findViewById(R.id.dictionary_list_item_text_content)).setText("item_base_lang_content");
((TextView)findViewById(R.id.item_learn_lang).findViewById(R.id.dictionary_list_item_text_header)).setText("item_learn_lang_header");
((TextView)findViewById(R.id.item_learn_lang).findViewById(R.id.dictionary_list_item_text_content)).setText("item_learn_lang_content");
This Android wiki page shows how to use reusable UI components with XML layouts, but it doesn't show how to access nested reusable components from code.
Although it is fairly straightforward, it might be not so clear for those who are pretty new to Android Views.
The following two lines should help you get the languageHeader of both includes. You can do the same for languageText
findViewByid(R.id.activityBaseLangView).findViewById(R.id.languageHeader)
findViewByid(R.id.activityLearnLangView).findViewById(R.id.languageHeader)

Categories

Resources