How can I use navigationRailView together with the Navigation library? (Have a standard switching of fragments, display of all icons) - android-studio

Rail menu with nav_graph added. All the icons are switched, but the fragments do not change.
P.S I know about the option with Selected, but I want everything to work cleanly and with the Navigation library
My code is now:
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navHostFragment = supportFragmentManager.findFragmentById(
R.id.nav_host_container
) as NavHostFragment
navController = navHostFragment.navController
// Setup navigationRailView
val navigationRailView = findViewById<NavigationRailView>(R.id.navigation_rail)
navigationRailView.setupWithNavController(navController)
}
}
Android Studio offers:
// private fun NavigationRailView.setupWithNavController(navController: NavController) {}

Related

E/RecyclerView: No adapter attached; skipping layout / Kotlin

I have been reading the different answers here on stackoverflow and on this blog post and tried to implement their solutions but I am still getting the error. The code is from a yt tutorial. I hope someone can help me. Thanks
Error E/RecyclerView: No adapter attached; skipping layout
My Adapter
This is my Main Activity
class MainActivity : AppCompatActivity(), IDrinkLoadListener {
lateinit var drinkLoadListener: IDrinkLoadListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
loadDrinkFromFirebase()
}
private fun loadDrinkFromFirebase() {
val drinkModels : MutableList<DrinkModel> = ArrayList()
FirebaseDatabase.getInstance()
.getReference("Drink")
.addListenerForSingleValueEvent(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
if(snapshot.exists()) {
for (drinkSnapshot in snapshot.children) {
val drinkModel = drinkSnapshot.getValue(DrinkModel::class.java)
drinkModel!!.key = drinkSnapshot.key
drinkModels.add(drinkModel)
}
drinkLoadListener.onDrinkLoadSuccess(drinkModels)
} else
drinkLoadListener.onDrinkLoadFailed("Drink items not exist")
}
override fun onCancelled(error: DatabaseError) {
drinkLoadListener.onDrinkLoadFailed(error.message)
}
})
}
private fun init() {
drinkLoadListener = this
val gridLayoutManager = GridLayoutManager(this, 2)
recycler_drink.layoutManager = gridLayoutManager
recycler_drink.addItemDecoration(SpaceItemDecoration())
}
override fun onDrinkLoadSuccess(drinkModelList: List<DrinkModel>?) {
val adapter = MyDrinkAdapter(this,drinkModelList!!)
recycler_drink.adapter = adapter
}
override fun onDrinkLoadFailed(message: String?) {
Snackbar.make(mainLayout,message!!,Snackbar.LENGTH_LONG).show()
}
}
class SpaceItemDecoration : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
if(parent.getChildLayoutPosition(view) % 2 != 0)
{
outRect.top= 25
outRect.bottom= -25
}else outRect.top = 0
}
}
Thanks in advance!!!
That happens because the adapter is attached to the recycler after an asynchronous operation on onDrinkLoadSuccess
You can attach the adapter to the recycler during onCreate and then the onDrinkLoadSuccess update it with the data.
The ListAdapter class has a submit method for it. If you are using plain recycler is usually something like this:
Recycler ... {
private val dataList = mutableListOf()
fun update(list: List<YourType>) {
dataList.clear()
dataList.addAll(list)
notifyDataDetChanged() //important updates the UI
}
}
There are more specific updates methods like notifyItemRangeChanged, etc.
Also there are helpers for finding difference in the data sets DiffUtil.ItemCallback

kotlin app crashes when going one to another activity

i have 3 activitiy when i start my app i can go 1 activity to second but i cant go third it crashes
this is code of my third( last activity )
class thirdActivity3 : AppCompatActivity() {
private lateinit var textView: TextView
private lateinit var textView2: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_third3)
textView = findViewById(R.id.textView)
textView2 = findViewById(R.id.textView2)
val extras = intent.extras
if(extras != null){
textView.text = extras.getString("NAME") //getString("NAME","")
textView2.text = extras.getInt("AGE").toString() //getInt("AGE",0).toString()
}
}
}
and this is my second activity
class secondActivity2 : AppCompatActivity() {
private lateinit var inputTextAge:EditText
private lateinit var finishButton: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second2)
inputTextAge=findViewById(R.id.inputTextAge)
finishButton=findViewById(R.id.finishButton)
val extras = intent.extras
var name = ""
if (extras != null){
name=extras.getString("PERSON_NAME","")
}
finishButton.setOnClickListener {
val age = finishButton.text.toString().toInt()
val intent = Intent(this,thirdActivity3::class.java)
intent.putExtra("NAME",name)
intent.putExtra("AGE",age)
startActivity(intent)
}
}
}
i am trying to make wizard for learning but my code have some problems. i have 3 activitys in kotling but when i go second activity to third it's stopping but its workd when i going first to second
2020-11-27 18:50:41.472 19343-19343/com.example.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.myapplication, PID: 19343
java.lang.NumberFormatException: For input string: "Finish"
at java.lang.Integer.parseInt(Integer.java:615)
at java.lang.Integer.parseInt(Integer.java:650)
at com.example.myapplication.secondActivity2$onCreate$1.onClick(secondActivity2.kt:33)
at android.view.View.performClick(View.java:7125)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:992)
at android.view.View.performClickInternal(View.java:7102)
at android.view.View.access$3500(View.java:801)
at android.view.View$PerformClick.run(View.java:27336)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
this is from logcat
Looking at the error message, it looks like your problem is this line:
val age = finishButton.text.toString().toInt()
The error is saying that you're trying to parse the string "Finish" as a number. It makes sense from this line that you're doing that. You have a button named "Finish". You get the text from the button and then call toInt() on that text, and you get this error.
What do you expect this line to do? Why do you expect querying a button's text to return an integer that represents age?

Android studio >> Buttons do not work properly

Second and third buttons work only if I click the first button first. If I click on the second or third button before trying first button then the buttons do not work.
Can you please help me to find out my mistakes? IDE: Android Studio, Language: Kotlin.
Codes are here:
package com.example.habiganjkotlin
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
info_button.setOnClickListener {
val intent = Intent(this, habiganjinfo::class.java)
// start your next activity
startActivity(intent)
news_button.setOnClickListener {
val intent = Intent(this, habiganjnews::class.java)
// start your next activity
startActivity(intent)
corona_button.setOnClickListener {
val intent = Intent(this, corona::class.java)
// start your next activity
startActivity(intent)
} } } } }
The problem of your code is that you have put button 2 and button 3 in button1.setOnClickListener. If these three buttons are independent and you can choose any of them, put them in three seperate setOnClickListener. I mean:
Button1.setOnClickListener{...}
Button2.setOnClickListener{...}
But you have written:
Button1.setOnClickListener{...
Button2.setOnClickListener{...}}
And this is wrong.

While loop blocks UI and wont exit via button press in Android Studio

A while loop is blocking the UI of my app from showing so I have no chance of pushing the cancel button to break out of it. Also if I am able to get the UI to show by calling the setup function from a TextToSpeech OnInitListener block, I still cant register the button press and exit out.
Why is this happening and how can I get normal, non blocking while loop behavior that I can exit out of with a button click from my cancel button in the UI?
This onCreate does not show the UI at all
class MainActivity : AppCompatActivity() {
lateinit var myTestClass: TestClass
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupTheTestClass()
}
This onCreate shows the UI as I call the setupTheTestClass() within the TextToSpeech OnInitListener but won't register the button press to let me exit the while loop
class MainActivity : AppCompatActivity() {
lateinit var myTestClass: TestClass
lateinit var mTTS:TextToSpeech
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mTTS = TextToSpeech(applicationContext, TextToSpeech.OnInitListener { status ->
setupTheTestClass()
})
}
fun setupTheTestClass()
{
myTestClass = TestClass(cancelBtn)
myTestClass.setupOnClickForButtons()
myTestClass.testWhileLoop() // blocks UI in first example
}
class TestClass(val cancelBtn: Button) {
var running: Boolean = true
fun setupOnClickForButtons()
{
cancelBtn.setOnClickListener {
running = false
println("no longer running")
}
}
fun testWhileLoop() {
while (running == true) {
println("running")
}
}
}
Putting the testWhileLoop() function in a coroutine seems to work
fun setupTheTestClass()
{
myTestClass = TestClass(cancelBtn)
myTestClass.setupOnClickForButtons()
// myTestClass.testWhileLoop()
GlobalScope.launch{
myTestClass.testWhileLoop()
}
}

Can't communication between DialogFragment and Activity using Observer pattern?

When you press the button to open a separate input window, there is a function to display the results toast.
class MainActivity : AppCompatActivity() {
val disposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
val f = TestPopup()
usingRxJava(f)
//usingLiveData(f)
}
}
private fun usingRxJava(f: TestPopup) {
val subject = SingleSubject.create<String>()
f.show(supportFragmentManager, "TAG")
button.post {
f.dialog.setOnDismissListener {
val str = f.arguments?.getString(TestPopup.TEST_KEY) ?: ""
subject.onSuccess(str)
}
}
subject.subscribe({
Toast.makeText(this, "Accept : $it", Toast.LENGTH_SHORT).show()
}, {
}).addTo(disposable)
}
private fun usingLiveData(f: TestPopup) {
val liveData = MutableLiveData<String>()
f.show(supportFragmentManager, "TAG")
button.post {
f.dialog.setOnDismissListener {
val str = f.arguments?.getString(TestPopup.TEST_KEY) ?: ""
liveData.postValue(str)
}
}
liveData.observe(this, Observer {
Toast.makeText(this, "Accept : $it", Toast.LENGTH_SHORT).show()
})
}
override fun onDestroy() {
disposable.dispose()
super.onDestroy()
}
}
DialogFragment
class TestPopup : DialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_test, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
button_test.setOnClickListener {
val arg = Bundle()
arg.putString(TEST_KEY, edit_test.text.toString())
arguments = arg
dismiss()
}
}
companion object {
const val TEST_KEY = "KEY"
}
}
(Sample Project Url : https://github.com/heukhyeon/DialogObserverPattern )
This sample code works in normal cases. However, the toast does not float after the following procedure.
Developer Option - Dont'keep activities enable
Open TestPopup, and enter your text. (Do not press OK button)
Press the home button to move the app to the background
The app is killed by the system.
Reactivate the app (Like clicking on an app in the apps list)
In this case, the text I entered remains on the screen, but nothing happens when I press the OK button.
Of course I know this happens because at the end of the activity the observe relationship between the activity and the Dialog is over.
Most of the code uses the implementation of the callback interface for that Dialog in the Activity to handle that case.
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
button_test.setOnClickListener {
val input = edit_test.text.toString()
(activity as MyListener).inputComplete(input)
dismiss()
}
}
class MainActivity : AppCompatActivity(), TestPopup.MyListener {
override fun inputComplete(input: String) {
Toast.makeText(this, "Accept : $input", Toast.LENGTH_SHORT).show()
}
}
But I think it's a way that doesn't match the Observer pattern, and I want to implement it using the Observer pattern as much as possible.
I'm thinking of getting a Fragment from the FragmentManager and subscribing again at onCreate, but I think there's a better way.
Can someone help me?
Your understanding of the problem is correct, except that the problem happens with any configuration changes, including screen rotation. You can reproduce issue without using the developer mode. Try this for example:
Open TestPopup, and enter your text. (Do not press OK button)
Rotate screen
See toast message not popping up.
Also note that your "observer pattern" implementation is not a proper observer pattern. Observer pattern has a subject and an observer. In your implementation, the activity is acting as both the subject and the observer. The dialog is not taking any part in this observer pattern, and using .setOnDismissListener is just another form of a listener pattern.
In order to implement observer pattern between the Fragment(the subject) and the Activity(the observer), the Activity needs to get the reference of the Fragment using the FragmentManager as you suggested. I suggest to use view model and establish observer pattern between view layer and view model layer instead.
RxJava example:
//MainViewModel.kt
class MainViewModel: ViewModel() {
val dialogText = PublishProcessor.create<String>()
fun postNewDialogText(text: String) {
dialogText.onNext(text)
}
}
// Activity
val disposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
viewModel.dialogText.subscribe {
Toast.makeText(this, "Accept : $it", Toast.LENGTH_SHORT).show()
}.addTo(disposable)
button.setOnClickListener {
TestPopup().show(supportFragmentManager, "TAG")
// usingRxJava(f)
// usingLiveData(f)
}
}
override fun onDestroy() {
disposable.dispose()
super.onDestroy()
}
// Dialog Fragment
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// Important!! use activity when getting the viewmodel.
val viewModel = ViewModelProviders.of(requireActivity()).get(MainViewModel::class.java)
button_test.setOnClickListener {
viewModel.postNewDialogText(edit_test.text.toString())
dismiss()
}
}

Resources