Cannot find onClick method in activity from xml definition - android

I am trying to link a function to the onClick property of a button in AndroidStudio but for some reason the system cannot identify the method that I coded.
The funny thing is that it works correctly when I code it in Java. In Kotlin it does not. I updated my Kotlin and checked its configuration but I cannot find the problem. My friend tried the same thing I did in a Linux computer and it worked for him. I have a Mac OS, I don't know if there is some extra configuration to take care of. Can anyone help me?
This is my simple activity:
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.View
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun sendMessage(view: View) {
}
}
This is the xml:
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:onClick="sendMessage"
android:text="#string/send_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/editText"
app:layout_constraintTop_toTopOf="parent" />
In build.gradle I have:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
Error message:
Corresponding method handler 'public void sendMessage(android.view.View)' not found
The onClick attribute value should be the name of a method in this View's context to invoke when the view is clicked.
This name must correspond to a public method that takes exactly one parameter of type View. Must be a string value, using '\;' to escape characters such as '\n' or '\uxxxx' for a unicode character

Try to add
tools:context="com.somepackage.MainActivity"
to your top view in xml

It works now. I had to restart AndroidStudio. I still cannot add the function using the attributes window in the design tab but it works if I edit the XML directly.

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

Getting an "Unresolved reference" error for an editText id in main activity

I have just started Android Development with a basic app which takes the username and display in Toast but came across an error in the main activity. It says "Unresolved references: editName". I tried making the app again, manually importing editText widget and changing the id name but nothing works.
It would be great if someone can tell me what's going wrong.
ScreenShot of the code with error underlined with red..... Module build.... Project build
I'll show you the example in Java.
You must create a variable in your activity class
EditText editName;
then you must override the onCreate() method of the Activity class:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
editName = (EditText) findViewById(R.id.editName);
}
overriding the onCreate() you can take a reference to your EditText.
If you're following a tutorial, they're probably expecting you to use Kotlin Synthetic Binding which means you need this in your build.gradle:
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
That will automagically create variables for each View in the layout that has an ID assigned - so if you have an EditText with an ID of editName, then a val editName: EditText will be created in the background when you inflate the layout. Yes it's magic, yes it trips everyone up and accounts for a lot of the problems people run into on here!
Savatore's answer is the normal, standard, non-magical way of doing it. If you want the convenience, look into the View Binding library, which is the official way of automatically binding views to variables (Kotlin synthetics are deprecated)

EditTextPreference not showing numeric keyboard after migrating project to androidx

After migrating my project to AndroidX using the Migrate to AndroidX... functionality provided by Android Studio and having made changes to my dependencies accordingly to have everything running like it is supposed to, I have encountered a minor problem which I haven't been able to resolve.
To set a device number in my application I used an EditTextPreference like the following defined in my pref_screen.xml which is set in a PreferenceFragmentCompat class with
setPreferencesFromResource(R.xml.pref_screen, string):
<EditTextPreference
android:icon="#drawable/ic_perm_device_information_black_24dp"
android:inputType="number"
android:key="change_device_id"
android:maxLines="1"
android:selectAllOnFocus="true"
android:singleLine="true"
android:summary="#string/settings_device_id"
android:title="#string/pref_title_change_device_id" />
It used to show a numeric keyboard to change the value but after migrating to AndroidX it keeps showing a normal keyboard as shown in the image below. I tried changing the inputType and defining the decimals in xml but to no avail. Has something changed to set the inputType for the keyboard after migrating to AndroidX or am I missing something obvious?
android:inputType="numberDecimal"
android:digits="0123456789"
From an answer here:
https://stackoverflow.com/a/55461028/7059947
Cast your Preference to EditTextPreference and use setInputType On Bind.
This saved my day :)
EditTextPreference edpPrefernce = (EditTextPreference) pPreference;
edpPrefernce.setOnBindEditTextListener(new EditTextPreference.OnBindEditTextListener() {
#Override
public void onBindEditText(#NonNull EditText editText) {
editText.setInputType(InputType.TYPE_CLASS_NUMBER);
}
});
Google has not fixed this yet. You can use takisoft's fix for this problem:
https://bintray.com/takisoft/android/com.takisoft.preferencex%3Apreferencex/1.0.0
Add to build.gradle (project):
buildscript {
...
repositories {
maven {
url "https://dl.bintray.com/takisoft/android"
}
}
....
}
Add to build.gradle (app module):
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
Change the imports in your classes and the components in your XML resources:
androidx.preference.EditTextPreference -> com.takisoft.preferencex.EditTextPreference
androidx.preference.PreferenceCategory -> com.takisoft.preferencex.PreferenceCategory
androidx.preference.PreferenceFragmentCompat -> com.takisoft.preferencex.PreferenceFragmentCompat
In your PreferenceFragmentCompat subclass, change the onCreatePreferences(...) declaration to onCreatePreferencesFix(...).
... and voila! The old parameters, like numeric and singleLine will be back and work!
[Solved]
Add to build.gradle (app module):
implementation 'androidx.preference:preference:1.1.0-rc01'

android:visibility with condition delays before display

I'm trying to use android databinding. But I got some problem when I use android:visibility with condition. Like android:visibility="size == 0 ? View.GONE : View.VISIBLE".
When it returns false, it will show this view first, then for a while it turns to gone.
Can I fix this? Or is this the databing's bug?
I ever tried code like this
android:visibility="#{size==0?View.GONE:View.VISIBLE}",
but it can not be compiled.I don't know why, maybe it is a bug.
So I using code following instead, firstly, write a Helper.
public class Helper {
#BindAdapter("bind:attr")
public static void setAttr(View view, Object obj) {
if (someCondition(obj)){
view.doSomething();
}
}
}
secondly, using custom attr in xml file
<View
...other attrs...
app:attr="#{obj}" />
note:
the 'attr' in the annotation must be the same with 'attr' used in xml file.
the View in the static method must be the same with the View in xml file.
My Gradle version and dataBinder version
dependencies {
classpath 'com.android.tools.build:gradle:1.5.0'
classpath "com.android.databinding:dataBinder:1.+"
}
And, remove other operations unrelated on this view.
Or offer your activity code.

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