Button onClick attribute is none if activity written in Kotlin - kotlin

Follow this tutorial: Android - Start Another Activity if I made MainActivity.java button OnClick attribute has the sendMessage() method.
But if I made MainActivity.kt button OnClick attribute has nothing to show, just a none.
Is this an Android Studio 3 bug or I missed something for Kotlin?
Java mainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/** Called when the user taps the Send button */
public void sendMessage(View view) {
// Do something in response to button
}
}
Kotlin mainActivity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
/** Called when the user taps the Send button */
fun sendMessage(view: View) {
// Do something in response to button
}
}
XML layout (Java and Kotlin project are the same)
<?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="ir.bigbang.vahid.myapplication.MainActivity">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
tools:layout_editor_absoluteX="148dp"
tools:layout_editor_absoluteY="81dp" />
</android.support.constraint.ConstraintLayout>

It seems like the designer does not support Kotlin yet. Here are some solution:
XML (Not Recommended)
Add the following line to your Button tag. This is exactly what the designer will do.
android:onClick="sendMessage"
Old Fashion
No need to add anything.
val button = findViewById<Button>(R.id.Button)
button.setOnClickListener {
}
kotlin-android-extensions (Recommended)
Add apply plugin: "kotlin-android-extensions" to your build.gradle
// button is the Button id
button.setOnClickListener {
}

Your code will like this:
button.setOnClickListener(){
Toast.makeText(this#MainActivity, "Its toast!", Toast.LENGTH_SHORT).show();
}
Here import will:
import kotlinx.android.synthetic.main. activity_main.*
Here "button" is the id of that Button in .xml file. Here the advantage is no need to create Button object in your java class.

Once defined the sendMessage class as :
/** Called when the user taps the Send button */
fun sendMessage(view: View) {
setContentView(R.layout.activity_second)
// Do something in response to button
}
And also defined a second activity as:
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
}
}
I added the SendMessage to the OnClick function:
And then it worked.

You can easily define this inside the XML itself. But using the android:onClick attribute is still a little expensive.
Instead you could consider using the Kotlin Android Extensions and synthetic properties:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
// Do something in response to button
}
}

Button OnClick implementation it's can be done by some ways in Android
some of the possible ways are below in sample:
1>Using OnClickListener as a interface
Here we implement our main activity with OnClicklistener
and override the function onClick
override fun onClick(v: View?) {
when (v?.id){
(R.id.btn1) -> {
toastmsg("Button1");
}
R.id.btn2 -> {
toastmsg("Button2");
}
}
}
2>And create a function and pass the OnClickListener with
variable sample:
findViewById<Button>(R.id.btn3).setOnClickListener(btnClick);
var btnClick =
OnClickListener {
Toast.makeText(this, "BtnClick", Toast.LENGTH_SHORT).show() ;
}
3>Create OnClickListener in Oncreate()
btn1=findViewById(R.id.btn1);
btn1?.setOnClickListener {
toastmsg("test button1");
}
full sample Code of the example it contains all the possible implementation of the Button OnClickListener :
class MainActivity : AppCompatActivity() , OnClickListener{
lateinit var tv1:TextView;
lateinit var tv2:TextView;
lateinit var tv3:TextView;
var btn1: Button? =null;
var btn2: Button? =null;
var btn3: Button? =null;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn1=findViewById(R.id.btn1);
btn1?.setOnClickListener {
toastmsg("test button1");
}
findViewById<Button>(R.id.btn2).setOnClickListener(this);
findViewById<Button>(R.id.btn3).setOnClickListener(btnClick);
}
var btnClick =
OnClickListener {
Toast.makeText(this, "BtnClick", Toast.LENGTH_SHORT).show() ;
}
override fun onClick(v: View?) {
when (v?.id){
(R.id.btn1) -> {
toastmsg("Button1");
}
R.id.btn2 -> {
toastmsg("Button2");
}
}
}
private fun toastmsg(msg: String){
Toast.makeText(this, "DaggerTest" + msg, Toast.LENGTH_SHORT).show();
}
}

Here's the solution I came up with in the MainActivity.kt file.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
sendMessage()
}
}
/** Called when the user taps the Send button */
private fun sendMessage() {
val editText = findViewById<EditText>(R.id.editText)
val message = editText.text.toString()
val intent = Intent(this, DisplayMessageActivity::class.java).apply
{
putExtra(EXTRA_MESSAGE, message)
}
startActivity(intent)
}

Related

Kotlin - setOnclickListener button is not working

So the Problem is, I don't know why, but the setOnClickListener is not reacting (Toasting) when I klick it.
Testet it several times and just doesn't react.
private lateinit var signInButton: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
initVariables()
addListeners()
}
private fun initVariables() {
signInButton = findViewById(R.id.sign_in_button)
}
private fun addListeners() {
signInButton.setOnClickListener {
println("Hello")
signIn()
}
}
private fun signIn() {
Toast.makeText(this, "Button pressed", Toast.LENGTH_SHORT).show()
val signInIntent = googleSignInClient.signInIntent
startActivityForResult(signInIntent, RC_SIGN_IN)
}

how to navigate to fragment inside recycler view?

I have an activity that is controlled with a navigation component, it has few fragments, inside one of these fragments there is a recyclerView that has some items, when I click on an Item I want it to navigate me to another fragment that has additional information about the item, I don't know how to use navigation component inside a recycelerView, when I type findNavController it has some parameters that am not sure what to put in or if its even the right function, I also tried to do it by code like this:
val fm = (context as AppCompatActivity).supportFragmentManager
fm.beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.addToBackStack(null)
.commit()
by the way this is the code that asks for other parameters:
// it asks for a (fragment) or (activity, Int)
findNavController().navigate(R.id.action_settingsFragment_to_groupUnits)
the problem is when I navigate out of this fragment or use the drawer navigation (nav component for the other fragments), this fragment that I navigated to stays displayed in the screen, I see both fragments at the same time, I assume its a fragment backStack issue but I don't know how to solve it, thanks for the help and your time in advance
You do not need to navigate from RecyclerView item click to AdditionalDetails fragment directly.
You can do this same thing by help of interface.
Steps:
Create an interface with a method declaration.
Extend Interface from the fragment where you are using your RecyclerView and Implement interface method.
Pass this interface with the adapter.
Using the interface from adapter you just pass object when click on item.
Finally from your fragment you just navigate to AdditionalDetails fragment with argument.
Lets see sample code from my current project:
Interface
interface ChatListClickListener {
fun onChatListItemClick(view:View, user: User)
}
Adapter Class
class UserAdapter(val Users: List<User>, val chatListClickListener: ChatListClickListener) : RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
inner class UserViewHolder(
val recyclerviewUsersBinding: RecyclerviewChatlistBinding
) : RecyclerView.ViewHolder(recyclerviewUsersBinding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val vh = UserViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.recyclerview_chatlist,
parent,
false
)
)
return vh
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.recyclerviewUsersBinding.user = Users[position]
holder.recyclerviewUsersBinding.root.setOnClickListener{
chatListClickListener.onChatListItemClick(it,Users[position])
}
}
override fun getItemCount(): Int {
return Users.size
}
}
My fragment
class FragmentChatList : Fragment(), ChatListClickListener {
lateinit var binding: FragmentChatListBinding
lateinit var viewModel: ChatListViewModel
lateinit var listener: ChatListClickListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val args: FragmentChatListArgs by navArgs()
binding = FragmentChatListBinding.inflate(layoutInflater, container, false)
val factory = ChatListFactory(args.user)
viewModel = ViewModelProvider(this, factory).get(ChatListViewModel::class.java)
binding.viewModel = viewModel
listener = this
lifecycleScope.launch {
viewModel.addUserWhenUserConnect()
}
viewModel.userList.observe(viewLifecycleOwner, Observer { data ->
binding.rvChatList.apply {
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
adapter = UserAdapter(data, listener)
}
})
return binding.root
}
override fun onChatListItemClick(view: View, user: User) {
Toast.makeText(requireContext(), user.name + "", Toast.LENGTH_SHORT).show()
// here you navigate to your fragment....
}
}
I guess this will be helpful.

Kotlin OnItemClickListener for a RecyclerView inside a Fragment

The problem is related to the fact that I don't manage to implement a OnItemClick listener for a RecyclerView that is located in a "mainFragment" by implementing the OnClickListener in the Adapter.
I would like my application (kotlin) to launch another fragment ("deletePage" in the code bellow) everytime an itemView (an ImageView) from the RecyclerView is clicked, this fragment would display the same photo in big.
My Adapter code is the following one:
class MyAdapter : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
private var photo = emptyList<Photo>()
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.row_layout, parent, false)
)
}
override fun getItemCount(): Int {
return photo.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.itemView.longitude.text = photo[position].latitude
holder.itemView.latitude.text = photo[position].longitude
holder.itemView.imageView.load(photo[position].photo)
}
fun setData(photo: List<Photo>) {
this.photo = photo
notifyDataSetChanged()
}
}
And my mainFragment code is the following one:
class MainFragment : Fragment(){
private lateinit var myView: MyViewModel
private val adapter by lazy { MyAdapter() }
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_main, container, false)
//Recyclerview
val adapter = MyAdapter()
val recyclerView = view.recycler_view
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())
myView = ViewModelProvider(this).get(MyViewModel::class.java)
myView.readPhoto.observe(viewLifecycleOwner, Observer {photo ->
adapter.setData(photo)
})
setHasOptionsMenu(true)
return view
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_database, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when {
item.itemId == R.id.deleteAll -> findNavController().navigate(R.id.deletePage)
item.itemId == R.id.refresh -> {
Toast.makeText(activity, "yep", Toast.LENGTH_SHORT).show()
instertDataToDatabase()
}
}
if (item.itemId == R.id.deleteAll) {
}
return super.onOptionsItemSelected(item)
}
}
The objective is that when you click on an item of the RecyclerView the "findNavController().navigate(R.id.deletePage)" fragment is displayed but everytime I try to implement a solution the application crashes when clicking at an item of the RecyclerView. Right now the navigation works by the click on a button in the Menu at the toolbar but is not the ideal solution.
Any sort of help or advice would be very much appreciated!
Firstly you need to give click listener of your itemView in onBindViewHolder
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.itemView.longitude.text = photo[position].latitude
holder.itemView.latitude.text = photo[position].longitude
holder.itemView.imageView.load(photo[position].photo)
holder.itemView.setOnClickListener { navigateToFragment() }
}
For the navigation somehow you should have a context instance.
Here is the possible solutions that came up to my mind:
Give navigator instance to adapter
class Navigator(fragment: Fragment) {
private val fragmentRef = WeakReference(fragment)
fun navigate(){
// make your navigations here with using fragmentRef.get()
}
}
In your fragment:
private val navigator = Navigator(this)
...
val adapter = MyAdapter(navigator)
In your adapter:
class MyAdapter(private val navigator: Navigator) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
fun navigateToFragment(){
navigator.navigate()
}
Make interface for callback
2.1. Implement that interface in fragment and give adapter the interface instance
interface Navigable{
fun navigate()
}
In your fragment:
class MainFragment : Fragment(), Navigable{
...
override fun navigate(){
// make your navigation
}
val adapter = MyAdapter(navigator)
In your adapter:
class MyAdapter(private val navigable: Navigable) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
fun navigateToFragment(){
navigable.navigate()
}
2.2. Make the interface, but do not pass reference, use findFragment in adapter
In your adapter: override onAttachedToRecyclerView to find fragment
private lateinit var navigable: Navigable
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
navigable = recyclerView.findFragment<Fragment>() as Navigable
}
fun navigateToFragment(){
navigable.navigate()
}

New to coding; Can't get button to link to new activity in Kotlin

It opens the first activity on the emulator, but fails on button click. I created the second activity (AddProductActivity), so maybe I did something wrong there. I've tried all the variables I could find online to change how the button operates.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.content_main)
val button = findViewById<Button>(R.id.goToAddProduct)
button.setOnClickListener {
val intent = Intent(this, AddProductActivity::class.java)
startActivity(intent)
}
}
}
class AddProductActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.add_product)
}
}
on content_main.xml:
<Button
android:id="#+id/goToAddProduct"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/add_to_inventory"/>
Make sure you have registered your new activity in the manifest. This is a common mistake by newcomers.
If that is not the case before you click on the button open your logcat view in android studio. Once you click on the button please investigate the crash issue from here.

How to use one event handler fro multiple buttons in Kotlin (I've already seen java with switch statements)

<Button
android:id="#+id/firstButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="firstClicked"
android:text="Color 1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.117"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
This is is the code for one button, the other 2 look the same. What I can't seem to achieve is in MainActivity.kt to have only one event handler for all three. I have found java code and can't use it as a guide correctly to transfor it to kotlin successfully, any hints?
You need to implement the OnClickListener interface in your MainActivity with a switch statement that could help you identify which button is clicked.
public class SecondActivity extends AppCompatActivity implements View.OnClickListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Button b1 = findViewById(R.id.b1);
Button b2 = findViewById(R.id.b2);
b1.setOnClickListener(this);
b2.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.b1:
Intent in = new Intent(SecondActivity.this, MainActivity.class);
SecondActivity.this.startActivity(in);
break;
case R.id.b2:
Toast.makeText(this, "Yay", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
}
For kotlin:
class MainActivity : AppCompatActivity(), View.OnClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn_start_second.setOnClickListener(this)
btn_start_toast.setOnClickListener(this)
}
override fun onClick(p0: View?) {
when (p0?.id) {
R.id.btn_start_toast -> {
Toast.makeText(this,"Toast", Toast.LENGTH_SHORT).show()
}
R.id.btn_start_second -> {
startSecondApp()
}
}
}
}