I have an activity that shows two fragments at the same time on tablet and one at a time on handset. Because I am making the app for both tablets and android, I have to separate the functionality of the navigation between fragments into a separate function "displaySecondFragmentOnHandset()".
On the smaller handset when a move from the first fragment to the second and the then try to go back to the first, the screen is blank.
MyActivity:
class CentralActivity : AppCompatActivity() {
val manager = supportFragmentManager
var firstFrag : FirstFrag? = null
override fun onCreate(savedInstanceState: Bundle?) {
...
var ft: FragmentTransaction = manager.beginTransaction();
firstFrag = FirstFrag.newInstance()
ft.add(R.id.real_container, firstFrag!!, "firstFrag")
if (screenLayoutSize >= Configuration.SCREENLAYOUT_SIZE_LARGE) {
var secondFrag = SecondFrag.newInstance()
ft.add(R.id.real_container, secondFrag, "secondFrag")
}
ft.commit()
}
fun displaySecondFragmentOnHandset (){
var secondFrag = SecondFrag.newInstance()
var ft: FragmentTransaction = manager.beginTransaction();
ft!!.replace(R.id.real_container, secondFrag).addToBackStack("secondFrag").commit()
}
Then in my FirstFragemnt, if i'm on a smaller handset I do:
class FirstFragemnt : Fragment() {
private var viewModel: SharedViewModel? = null
fun goToSecondFragment(){
if (screenLayoutSize!! < Configuration.SCREENLAYOUT_SIZE_LARGE) {
viewModel!!.setMsgInCommunicator(collection)
var centralActivity: CentralActivity = activity as CentralActivity
centralActivity.displaySecondFragmentOnHandset()
}
}
The problem is when I press the back button to go to the first fragment, The screen is blank.
in CentralActivity replace
ft!!.replace(R.id.real_container, secondFrag).addToBackStack("secondFrag").commit()
with
ft!!.add(R.id.real_container, secondFrag).addToBackStack("secondFrag").commit()
The solution was to put the fun goToSecondFragment() inside of override fun onViewCreated
Related
I have a Fragment_A on Activity_1 and whenever a Button is clicked in the Fragment_A I have to start Activity_2 witch have a Fragment_B
So i need to send a user_Id from the Fragment_A in Activity 1 to the Fragment_B in activity_2.
Is there any way to send the data directly from Frament_A in Activity_1 to Fragment_B in Activity_2?
I am a student , so i am a beginner and i did found this question here but i didn't understand what to do to solve this
I did find some solution about interface but i didn't know how to do it
i keep getting class_id null
Language : Kotlin
Class : ClassesAdapter
class ClassesAdapter(val c:Fragment,val l:android.content.Context? ,val classeslist: ArrayList<Classes>) : RecyclerView.Adapter <ClassesAdapter.ClassesViewHolder> (){
lateinit var auth: FirebaseAuth
lateinit var DataBase : DatabaseReference
lateinit var DataBase2 : DatabaseReference
lateinit var clipboardManager: ClipboardManager
private lateinit var mListener :onItemClickListener
interface onItemClickListener {
fun onItemClick(position :Int)
}
/*fun setOnItemClickListener(listener : onItemClickListener){
// mListener=listener
}*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ClassesViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.class_items2,parent,false)
//return ClassesViewHolder(itemView,mListener)
return ClassesViewHolder(itemView,)
}
override fun onBindViewHolder(holder: ClassesViewHolder, position: Int) {
val currentitem = classeslist[position]
holder.classname.text = currentitem.name
holder.classrole.text = currentitem.role
holder.itemView.setOnClickListener {
val intent = Intent(l, DetailedClassActivity::class.java)
intent.putExtra("classId", currentitem.class_id)
l!!.startActivity(intent)
}
Class : Acttivity2 who has the fragment iwant to send it the class_id`
package com.example.myclass1
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.viewpager2.widget.ViewPager2
import com.example.myclass1.databinding.ActivityDetailedClassBinding
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import com.google.firebase.auth.FirebaseAuth
class DetailedClassActivity : DrawerBaseActivity() {
private lateinit var binding: ActivityDetailedClassBinding
//lateinit var toggle: ActionBarDrawerToggle
override lateinit var auth: FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding= ActivityDetailedClassBinding.inflate(layoutInflater)
setContentView(binding.root)
// add it to change the toolbar title's to the activity name
val actionBar = supportActionBar
if (actionBar != null) {
val dynamicTitle: String = HomeActivity::class.java.simpleName
//Setting a dynamic title at runtime. Here, it displays the current activity name
actionBar.setTitle("Home")
}
// navigate between documents ,notification and chatroom : tabLayout
val tablayout2 : TabLayout =findViewById(R.id.tab_layout2)
val viewpager21 : ViewPager2 =findViewById(R.id.viewPager21)
val adapter =PagerAdapter2(supportFragmentManager,lifecycle)
viewpager21.adapter = adapter
TabLayoutMediator(tablayout2, viewpager21) { tab, position ->
when(position){
0->{tab.text="Documents"}
1->{tab.text="Note"}
2->{tab.text="ChatRoom"}
}
}.attach()
val intent=getIntent()
var classId= intent.getStringExtra("classId")
fun getMyData(): String? {
return classId
}
}
class : fragment B
enter code here
btnAddcourse.setOnClickListener {
var nameofthecourse = Coursename.text.toString().trim()
var course_id :String?=DataBase3.push().key
var classId = arguments?.getString("classId")
val dateofthecourse: String = formatTimeStamp()
if (TextUtils.isEmpty(nameofthecourse)) {
// No name added
Coursename.error = "No name added !! Please enter the name of the class"
}
else{
courseList.add(Course(course_id,nameofthecourse,dateofthecourse,classId))
coursesAdapter.notifyDataSetChanged()
Toast.makeText(activity,"Adding Course Success", Toast.LENGTH_SHORT).show()
//Toast.makeText(activity,"class_id is null ", Toast.LENGTH_SHORT).show()
if (classId != null) {
addCourseToDataBase(course_id!!,nameofthecourse,dateofthecourse,classId)
}else{
Toast.makeText(activity,"class_id is null ", Toast.LENGTH_SHORT).show()
}
}
}
fragmentA
button.setOnClickListener {
val intent = Intent(requireContext(), Activity2::class.java)
intent.putExtra("user_Id", user_Id)
startActivity(intent)
}
activity2
val intent = getIntent()
val message = intent.getStringExtra("user_Id")
val bundle = Bundle()
bundle.putString("user_Id", message) //the part i updated
val fragmet= fragment_B()
fragmet.setArguments(bundle)
fragmentB
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_B, container, false)
val bundle = this.arguments //the part i updated
val myString = bundle!!.getString("user_Id", "defaultValue") //the part i updated
}
1-Read About Singleton Design Pattern
for make user id global in all app
read it to understand
2-Shared Preference :save user id to can using for all app
Shared Preferences
3-you can try Navigation Args : better choose for performance
and can use it for movement through you app
Navigation
Android: I have made a simple Todo list app using Kotlin. And the theme (Light/Dark) of this app is set to system default theme MODE_NIGHT_FOLLOW_SYSTEM. Now the problem is that when I changed theme to Dark mode(because my emulator's theme was set to Light theme previously), all added Todo items were gone and same when I changed to Light mode again. Then I noticed the size of my ArrayList became 0 (empty).
Here is screenshots to understand it better:
Light Mode
https://i.stack.imgur.com/eWbzO.png
after changing system theme to Dark
Dark Mode
https://i.stack.imgur.com/B9iqg.png
tasks are added to list: ArrayList when saveButton: Textview is clicked
class MainActivity : AppCompatActivity() {
var modified = false
private lateinit var listView: ListView
private lateinit var input: EditText
private lateinit var saveButton: TextView
private lateinit var cancelButton: TextView
private val list = ArrayList<String>()
private lateinit var listAdapter: CustomAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
listView = findViewById(R.id.listView)
input = findViewById(R.id.input)
saveButton = findViewById(R.id.saveBtn)
cancelButton = findViewById(R.id.cancelBtn)
listAdapter = CustomAdapter(this, list)
listView.adapter = listAdapter
val warningToast = Toast.makeText(this,
"The text may be empty or contains only spaces",
Toast.LENGTH_LONG)
saveButton.setOnClickListener {
val text = input.text.toString()
// if text is empty or only contains blank
if (text.isEmpty() || text.isBlank()) {
warningToast.show()
return#setOnClickListener
}
if (!modified) {
// output list size 0 after changing theme
Log.d("MY_DEBUG", "size fo list is : ${list.size}")
list.add(input.text.toString())
} else {
// ...
}
input.text.clear()
listAdapter.notifyDataSetChanged()
}
// ...
}
}
I thought that this is because it triggers a uiMode configuration change when theme mode is changed. So, the activity is recreated automatically. I want to keep added items even after changing theme. Is there any way to prevent loss of added items. Thank you!
Changing theme recreates your activity and it reinitiates your list. It is not because of changing theme itself.
You have to save your data persistently, in a database or in a SharedPreferences instance with your own serialization method (like JSON).
i am translating an app in Kotlin and the user have the choice to choose between two different languages i created new Activity:
#Suppress("DEPRECATION")
class Languages_Activity : AppCompatActivity() {
lateinit var spinner: Spinner
lateinit var locale: Locale
var back_btn: LinearLayout? = null
private var currentLanguage = "en"
private var currentLang: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_languages_)
title = "KotlinApp"
currentLanguage = intent.getStringExtra(currentLang).toString()
spinner = findViewById(R.id.spinner)
val list = ArrayList<String>()
list.add("Select Language")
list.add("English")
list.add("Malay")
val adapter = ArrayAdapter(this, R.layout.support_simple_spinner_dropdown_item, list)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = adapter
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
when (position) {
0 -> {
}
1 -> setLocale("en")
2 -> setLocale("my")
}
}
override fun onNothingSelected(parent: AdapterView<*>) {}
}
back_btn = findViewById(R.id.back_btn_language)
back_btn?.setOnClickListener {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}
private fun setLocale(localeName: String) {
if (localeName != currentLanguage) {
locale = Locale(localeName)
val res = resources
val dm = res.displayMetrics
val conf = res.configuration
conf.locale = locale
res.updateConfiguration(conf, dm)
val refresh = Intent(
this,
Languages_Activity::class.java
)
refresh.putExtra(currentLang, localeName)
startActivity(refresh)
} else {
Toast.makeText(
this#Languages_Activity, "Language, , already, , selected)!", Toast.LENGTH_SHORT).show();
}
}
override fun onBackPressed() {
val intent = Intent(Intent.ACTION_MAIN)
intent.addCategory(Intent.CATEGORY_HOME)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
startActivity(intent)
finish()
exitProcess(0)
}
}
and when i choose a language the app will display the values of the chosen language but when i close the app and run it again it the choice will reset
how can i keep the item selected even after closing the app and run it again?
you can use shared preferences
for this usecase.
shared preferences are helpful when you want to store key-value pairs that can persist data across app close and open.
this data will get deleted only when you clear your app data in settings or uninstall the app
in your onCreate you can add this snippet
val sharedPref = activity?.getPreferences(Context.MODE_PRIVATE)
val selectedLanguageIndex = sharedPref?.getInt("LANGUAGE_SELECTED", 0)?:0
spinner.setSelection(selectedLanguageIndex)
in your onItemSelected
val sharedPref = requireActivity().getPreferences(Context.MODE_PRIVATE)
with (sharedPref.edit()) {
putInt("LANGUAGE_SELECTED", position)
apply()
}
kotlin has sharedpreference system.
it can help when u have to remember some options until change.
I am working on an app with 5 fragments, having one fragment named home fragment.
I want whenever I press the back button on any of the other fragments, it navigates to the home fragment only.
pls, suggest to me how can I do this.? the below code is provided for reference
val expensefragment = expenseFragment()
val notesfragment = notesFragment()
val forumfragment = forumFragment()
val printfragment = printFragment()
val homefragment = homefragment()
setCurrentFragment(homefragment)
val bottomNavigationview = findViewById<BottomNavigationView>(R.id.bottomNavigationView)
bottomNavigationview.setOnNavigationItemSelectedListener { item ->
when(item.itemId) {
(R.id.expensebar)-> setCurrentFragment(expensefragment)
(R.id.notesbar)-> setCurrentFragment(notesfragment)
(R.id.printbar)-> setCurrentFragment(printfragment)
(R.id.forumbar)-> setCurrentFragment(forumfragment)
(R.id.homebar) -> setCurrentFragment(homefragment)
}
true
}
}
private fun setCurrentFragment(fragment: Fragment){
supportFragmentManager.beginTransaction().apply {
replace(R.id.frameLayout,fragment)
commit()
}
}
After replace and before commit call the following function
.addToBackStack(backStateName);
if you don't want to give name of stack , pass null or empty value. This function will create backstack as Intent does.
i am new in android, i am working on an app that retrieve data from server using retrofit and kodein and MvvM in kotlin
i set a Navigation drawer in my app and the purpose is that when i click on the item of navigation drawer
new activity opens and in this activity i want show recyclerview
but when new activty opens recyclerview cant set listitem on recycler
i debug my code in my repositories and viewmodel class and i see that they received data
i debug my code in new activty and i see that viewmodel cant receive those data and set a invalid icon next to my code in viewmodel.observe
this is my repository class:
fun getdigitalproduct(): LiveData<List<DigitalProduct>>{
val dpData:MutableLiveData<List<DigitalProduct>> = MutableLiveData<List<DigitalProduct>>()
val apiClient = ApiClient()
val call:Call<List<DigitalProduct>> = apiClient.getClient().create(ApiService::class.java).getdigitalproduct()
call.enqueue(object : Callback<List<DigitalProduct>>{
override fun onResponse(
call: Call<List<DigitalProduct>>,
response: Response<List<DigitalProduct>>
) {
dpData.value = response.body()
}
override fun onFailure(call: Call<List<DigitalProduct>>, t: Throwable) {
dpData.value = null
}
})
return dpData
}
this is for ViewModel
var repoDigitalProduct: LiveData<List<DigitalProduct>> = repositorys.getdigitalproduct()
fun getdigitalproduct(): LiveData<List<DigitalProduct>>{
return repoDigitalProduct
}
this is for new activity:
private fun getDigitalProduct() {
viewModel.getdigitalproduct().observe(this, Observer {
digipro.addAll(it)
})
this is digipro:
var digipro: ArrayList<DigitalProduct> = ArrayList()
and this i use this code in oncreate method in new activity:
viewModel = ViewModelProviders.of(this, factory).get(AllViewModel::class.java)
getDigitalProduct()
setdigitalProductRecycler()
i use viewmodelprovider.of code in Mainactivity too
this is for setdigitalProductRecycler:
private fun setdigitalProductRecycler() {
val digiproRecycler = digital_product_recycler
digiproRecycler.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true)
digiproRecycler.adapter = DigitalProductAdapter(digipro)
}
my codes works in Mainactivity but when i try it in new activity.........
what should i do?
The best way to solve this issue is create your viewmodel in activity or you can use shared viewmodel. So your viewmodel will retain as your activity retain
https://stackoverflow.com/a/52611554/8868638
i put repositorie and viewmodel in companion object then i called viewmodel of MainActivity in that activity
this is for MainActivty:
companion object{
val repositorys = AllRepositorys()
var viewModel: AllViewModel = AllViewModel(repositorys)
}
this is for newActivity:
lateinit var viewModel: AllViewModel
and i put this in oncreate() Method in new activity:
viewModel = MainActivity.viewModel
UPDATE
Now I handle this issue with creating a viewmodel for every fragment or activity. Because, ViewModel is designed to store and manage UI-related data in a lifecycle of Activity or Fragment