Kotlin synthetic extension and several include same layout - android

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 = ""

Related

TextView issues after migrate from Kotlin synthetics to Jetpack view binding

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>

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

What is a possible use case for duplicate +id layout-wide?

We don't have a compilation problem, we just want to expand our knowledge. Reading through Google Code Documentation, we have read that +id doesn't have to be unique layout-wide.
Below a compiling example:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.bq.testviewids.MainActivity">
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hola Mundo" />
</LinearLayout>
private void initViews() {
txtText = (TextView) findViewById(R.id.text);
txtText.setId(View.generateViewId());
txtText = (TextView) findViewById(R.id.text);
}
private void changeTextView() {
txtText.setText("");
}
If in initViews() we don't have the last two lines:
txtText.setId(View.generateViewId());
txtText = (TextView) findViewById(R.id.text);
When we do a change to txtText (in changeTextView()), the first TextView gets modified.
If we do have the last two lines the second TextView gets modified.
We didn't know that this was possible, and we thought that if you had two items layout-wide with the same +id, it wouldn't compile. Now, we understand that the identifier doesn't have to be unique in this view's hierarchy, but, we don't understand the usefulness of this behavior.
Does someone know a use case in which a non-unique identifier is useful?
This doubt comes to us when we see in Android for MSM (CAF) in Settings project this code with a duplicate id (#+id/fields):
<LinearLayout android:id="#+id/fields"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/wifi_section"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/wifi_item">
...
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/wifi_item">
...
</LinearLayout>
<LinearLayout android:id="#+id/fields"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/wifi_section" >
...
</LinearLayout>
The use case that comes to mind are the duplicate id's that you would have if you have a layout that is included two or more times in another layout. It would save you the headache of trying to get unique id's in each duplicated layout.
It would also allow you to work within each included layout using the same code without having to track different id's depending upon which sub-layout you are working on.

program is working fine but eclipse is showing warning for textview

i would like to know why eclipse is showing warning "[I18N] Hardcoded string "TextView", should use #string resource" in the xml code below .Actually i am trying to get the text written by user in an edit Text in an activity to this current activity.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="0.01"
android:text="TextView" />
</LinearLayout>
The reason that you are receiving a warning, is due to the fact that you are trying to hardcode a string which is not good convention in Android programming due to possible redundancy:
<TextView
...
android:text="TextView" />
You should rather create a reference to a string in the .../res/values/strings.xml file like so:
<TextView
...
android:text="#string/TextView" />
.. and define it in your strings.xml file:
<string name="TextView">TextView</string>
Hope this helps.
As it says, you are using a "hard-coded" string which is less efficient than using the String resource. Simply remove
android:text="TextView"
if you don't want the warning to show. If you want it there then disregard the warning or add it to the String resource file. The Text property isn't needed. If you are expecting user input then you should change it to an EditText anyway unless you have a reason for using TextView
<EditText
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="0.01" />
Then if you want it to display something such as "Enter input here" in the View then you can add android:hint"Text to display". But this will give you the same warning if you don't add it to the strings.xml and use android:hint="#string/nameInStringsFile".
But these warnings are just that. Suggesting possibly more efficient methods or ways of implementing whatever you are doing.
Change the XML to the following to remove the warning
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="0.01" />
</LinearLayout>
The reason you are seeing the warning is because you had set the text to "TextView" inside the XML layout file. It is Android best practice to put all Strings inside the strings.xml file in your res/values folder which creates string resources. One you have a string in a resource file you can reference it from your layout file using the syntax "#string/string_name".

import xml into another xml

I have lots of control repeated in my xml (a Button for instance). Is there any possibility to write the Button once in a xml and then import it in all the layout I need it?
You can use
<include layout="#layout/commonlayout" android:id="#+id/id" />
commonlayout.xml should be defined in res/layout where you can add the repeated parts.
As Labeeb P rightly said, it works.
Just want to add that you can also override parameters too:
<include
layout="#layout/commonlayout"
android:id="#+id/id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="2sp"
android:layout_marginRight="2sp"
/>
In addition to those great answers, you can also avoid code duplication by using the <merge> tag, like so:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/add"/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/delete"/>
</merge>
The <merge> part gets stripped when you include it into other xml. This might help including more than a single Button at a time. See the official documentation.
You can use the default include XML tag to include an external layout:
<include layout="#layout/somelayout" />
This layout should have an outside ViewGroup that encapsulates the content or a merge tag to avoid having to use an unnecessary layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#string/hello_world" />
</LinearLayout>
<!-- OR -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#string/hello_world" />
</merge>
Also, if you need a better way to include pieces of layout that acts like a container (a custom ViewGroup), you can use this custom ViewGroup. Note that this does not import an XML into another XML file, it inflates the content from the external layout and replaces into the view. It's similar to ViewStub, a "ViewGroupStub" like.
This lib acts as if the ViewStub could be used as following (note that this example does not work! ViewStub isn't a ViewGroup subclass!):
<ViewStub layout="#layout/somecontainerlayout"
inflate_inside="#+id/somecontainerid">
<TextView android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#string/hello_world" />
</ViewStub>

Categories

Resources