Beginner Android developer here. I'm currently using Navigation Component in my project, and I have a DialogFragment which has a Toolbar and menu as a destination. However, the menu title is cut off even though the dialog could be expanded a bit more.
Here's what it looks like right now:
However, as you can see there's still room for it to expand. I want it to look like this:
But I'm not sure how to achieve it without setting the layout to MATCH_PARENT programatically.
Here's the code for my DialogFragment:
package tang.song.edu.yugiohcollectiontracker.ui_inventory
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.DialogFragment
import androidx.navigation.fragment.findNavController
import tang.song.edu.yugiohcollectiontracker.R
import tang.song.edu.yugiohcollectiontracker.databinding.FragmentTransactionDialogBinding
import tang.song.edu.yugiohcollectiontracker.viewBinding
import javax.inject.Inject
class TransactionDialogFragment : DialogFragment(), Toolbar.OnMenuItemClickListener {
#Inject
lateinit var mViewModelFactory: TransactionDialogViewModelFactory
private lateinit var viewModel: TransactionDialogViewModel
private val binding by viewBinding(FragmentTransactionDialogBinding::inflate)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initToolbar()
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
TODO("Not yet implemented")
}
private fun initToolbar() {
binding.newTransactionToolbar.apply {
inflateMenu(R.menu.transaction_dialog_toolbar_menu)
setOnMenuItemClickListener(this#TransactionDialogFragment)
setNavigationOnClickListener {
findNavController().navigateUp()
}
}
}
}
and my layout, which is just a Constraint Layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/new_transaction_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:navigationIcon="#drawable/ic_close_black_24dp"
app:title="#string/lbl_new_transaction" />
...
</androidx.constraintlayout.widget.ConstraintLayout>
The default parameters of setLayout(int, int) are MATCH_PARENT
You may try to use FILL_PARENT instead to see if it is able to change the width
Ref: https://developer.android.com/reference/android/view/Window#setLayout(int,%20int)
Related
I've worked on the FragmentContainerView activity with some fragments, in that fragment, I want to use Compose alongside XML using ComposeViewin the first fragment, but when I try to navigate from the second fragment and back to the first fragment, the component from ComposeView was disappeared.
I've set up a fragment with FragmentContainerView using the replace strategy and I put the fragment inside the list like usual, and I have no idea what happens with this situation.
You can watch in this video
also this is my code
NavigationActivity.kt
import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.navigation.NavDeepLinkRequest
import androidx.navigation.Navigation
import androidx.navigation.findNavController
import id.derysudrajat.inlinebinding.viewBinding
import id.derysudrajat.library.R
import id.derysudrajat.library.databinding.ActivityNavigationBinding
class NavigationActivity : AppCompatActivity() {
private val binding by viewBinding(ActivityNavigationBinding::inflate)
private val listOfFragment = listOf(FragmentMainNav(), FragmentDetailNav())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.btnHome.setOnClickListener{
replace(HOME_NAV)
}
binding.btnDetail.setOnClickListener{
replace(DETAIL_NAV)
}
Navigation.createNavigateOnClickListener(R.id.fragmentDetailNav, null)
}
private fun replace(nav: String) {
supportFragmentManager
.beginTransaction()
.replace(
binding.fragmentContainerView.id,
if (nav == HOME_NAV) listOfFragment[0] else listOfFragment[1]
)
.commit()
}
companion object {
const val HOME_NAV = "home_nav"
const val DETAIL_NAV = "detail_nav"
}
}
FragmentMainNav.kt
import android.os.Bundle
import android.view.View
import androidx.compose.material3.Text
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.lifecycle.Lifecycle
import androidx.navigation.findNavController
import id.derysudrajat.inlinebinding.BindingFragment
import id.derysudrajat.inlinebinding.viewBinding
import id.derysudrajat.library.R
import id.derysudrajat.library.databinding.FragmentNavMainBinding
class FragmentMainNav : BindingFragment<FragmentNavMainBinding>() {
override val binding by viewBinding(FragmentNavMainBinding::inflate)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.textMain.text = "Fragment Main"
binding.composeView.apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
Text(text = "This is Compose")
}
}
}
}
fragment_nav_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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/text_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fragment Main"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.compose.ui.platform.ComposeView
android:id="#+id/compose_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/text_main" />
</androidx.constraintlayout.widget.ConstraintLayout>
FragmentDetailNav.kt
import android.os.Bundle
import android.view.View
import id.derysudrajat.inlinebinding.BindingFragment
import id.derysudrajat.inlinebinding.viewBinding
import id.derysudrajat.library.databinding.FragmentNavDetailBinding
import id.derysudrajat.library.databinding.FragmentNavMainBinding
class FragmentDetailNav : BindingFragment<FragmentNavDetailBinding>() {
override val binding by viewBinding(FragmentNavDetailBinding::inflate)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.textDetail.text = "Detail Navigation"
}
}
fragment_nav_detail.kt
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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">
<TextView
android:id="#+id/text_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Detail Navigation"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
I've tried using different ways like changing ViewCompositionStrategy and it still not working, I expected to show ComposeView still show when the fragment was replaced and back, btw the compose that I was tried is from compose BOM 2022.10.00 - 2023.01.00 but still disappeared when the fragment was replaced.
I think you could move the binding.composeView.apply {} block from onViewCreated to onCreateView as shown by provided sample in official documentation here. I've tested it using compose bom 2023.01.00 and plain ViewBinding and it works fine. So the HomeFragment would be like this:
class HomeFragment : Fragment() {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
val view = binding.root
binding.composeView.apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
Text(text = "This is Compose")
}
}
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
As additional note, i don't think the Navigation.createNavigateOnClickListener() is necessary and could just be removed.
**The flow: ** There are two xml files:
activity_main.xml
bottomsheet_fragment.xml
I want to click on "click here" button (xml id: btn_show) and show the bottom sheet fragment [This is happening correctly) .. corresponding files: activity_main.xml, MainActivity.kt
Now I want to click on the button in the bottom sheet(xml id: btn_button1), and show the text you pressed button 1 via toast(or anything else is fine too, I just want to show something when I click the button) [This part is not happenning correctly) .. corresponding files: bottomsheet_fragment.xml, BottomSheetFragment.kt
I am attaching codes for the files below:
bottomsheet_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/btn_button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:text="Sample button 1"
/>
</LinearLayout>
BottomSheetFragment.kt
package android.example.naruto
import android.example.naruto.databinding.ActivityMainBinding
import android.example.naruto.databinding.BottomsheetFragmentBinding
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
class BottomSheetFragment: BottomSheetDialogFragment() {
private lateinit var binding2: BottomsheetFragmentBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding2 = BottomsheetFragmentBinding.inflate(layoutInflater)
binding2.btnButton1.setOnClickListener{
Toast.makeText(context, "You pressed on button 1!", Toast.LENGTH_SHORT).show()
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".MainActivity">
<Button
android:id="#+id/btn_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click here"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
main_activity.kt
package android.example.naruto
import android.example.naruto.databinding.ActivityMainBinding
import android.example.naruto.databinding.BottomsheetFragmentBinding
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
private lateinit var binding1: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding1 = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding1.root)
val bottomSheetFragment= BottomSheetFragment()
binding1.btnShow.setOnClickListener {
bottomSheetFragment.show(supportFragmentManager, "BottomSheetDialog")
}
}
}
Thanks in advance for the help! Please let me know if you need any more details or clarifications.
I referred to these tutorials: https://www.youtube.com/watch?v=yqnVPiWAw0o&t=21s, https://www.youtube.com/watch?v=4GXflIdrlus
I am not able to see onCreateView method in BottomSheetFragment and probably that's why it is not working
You can change your code like this
From
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding2 = BottomsheetFragmentBinding.inflate(layoutInflater)
binding2.btnButton1.setOnClickListener{
Toast.makeText(context, "You pressed on button 1!", Toast.LENGTH_SHORT).show()
}
}
To
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding2 = BottomSheetFamilyTreeBinding.inflate(inflater, container, false)
return binding2.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding2.btnButton1.setOnClickListener{
Toast.makeText(context, "You pressed on button 1!", Toast.LENGTH_SHORT).show()
}
}
I am trying to develope an app which lets the user choose and read a csv file from his phone, so the app can set a variable with the chosen file content (string). This variable can then be used to set the text of a TextView.
I already was able to let the user of my app choose the desired file. I couldn't get the file content though. And I've already seen that onActivityResult and startActivityForResult are depreacated which means that a lot of tutorials are outdated. Plus I was not able to get a solution to my problem by the documentation here.
This picture shows you, that after the user chooses his csv file, the textView is not changing.
This is what I have come up with so far for my ContentFragment.kt file. As you can see I already have a line for setting the TextView text to the content of a csv file which is located in the asset folder. That works great (here it is commented out - only there fyi), but I would like to do the same for the user's csv file:
package com.example.myapplication
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.myapplication.databinding.FragmentContentBinding
import kotlinx.android.synthetic.main.fragment_content.*
import java.io.BufferedReader
import java.io.InputStreamReader
class ContentFragment : Fragment() {
var _binding: FragmentContentBinding? = null
val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentContentBinding.inflate(inflater, container, false)
val file = BufferedReader(InputStreamReader(resources.assets.open("Testfile.csv"))).use {
it.readText()
} // The variable is unused. This line of code only exists to demonstrate that the file_content.settext(file) in the TestButton click event is working.
binding.TestButton.setOnClickListener {
// file_content.setText(file) // <-- This line works
val intent = Intent()
.setType("*/*")
.setAction(Intent.ACTION_GET_CONTENT)
startActivity(Intent.createChooser(intent, "Select a file"))
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
override fun onActivityResult(
requestCode: Int, resultCode: Int, resultData: Intent?) {
resultData?.data?.also { uri ->
val file = BufferedReader(InputStreamReader(resources.assets.open(resultData.toString()))).use {
it.readText()
}
file_content.setText(file)
}
}
}
This is the xml of the fragment:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ContentFragment"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/TestButton"
android:text="Import File"
tools:ignore="MissingConstraints"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="In here the content of the file will appear."
android:id="#+id/file_content"
tools:ignore="MissingConstraints" />
</LinearLayout>
And here are more files, which are not so necessary:
MainActivity.kt:
package com.example.myapplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.myapplication.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".MainActivity"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="56dp"
app:layout_constraintBottom_toBottomOf="parent">
<fragment
android:id="#+id/fragment"
class="com.example.myapplication.ContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Replace:
val file = BufferedReader(InputStreamReader(resources.assets.open(resultData.toString()))).use {
it.readText()
}
with:
val file = BufferedReader(InputStreamReader(requireContext().contentResolver.openInputStream(uri))).use {
it.readText()
}
Note that your use of */* for the MIME type means that the user can choose non-text content. Consider using text/*.
I was able to solve my problem by editing ContentFragment.kt. Now it works to let the user choose a csv-file and show its content in a textView.
package com.example.myapplication
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import com.example.myapplication.databinding.FragmentContentBinding
import kotlinx.android.synthetic.main.fragment_content.*
import java.io.BufferedReader
import java.io.InputStreamReader
class ContentFragment : Fragment() {
var _binding: FragmentContentBinding? = null
val binding get() = _binding!!
private val request = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri != null) {
val inputStream = requireContext().contentResolver.openInputStream(uri)
val file = BufferedReader(InputStreamReader(inputStream)).use {
it.readText()
}
file_content.setText(file)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentContentBinding.inflate(inflater, container, false)
binding.TestButton.setOnClickListener {
val intent = Intent()
.setType("*/*")
.setAction(Intent.ACTION_GET_CONTENT)
request.launch("*/*")
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}
I want to learn it by myself but can't find it on the internet because I do not know what its name is .. screen? dialogue? menu? I just do not know and therefore do not find in an Internet search
In my understanding, you are talking about the bottom sheet
Here are the five simple steps to create a bottom sheet in android
Step 1
Add the material support design dependency in the app-level build.gradle file
implementation "com.android.support:design:27.0.2"
Step 2
Design the layout file that was to be shown as the bottom sheet.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content"
android:paddingBottom="8dp">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/txt_download"
style="#style/BottomSheetItem"
android:drawableStart="#drawable/ic_baseline_save_alt_24"
android:drawableLeft="#drawable/ic_baseline_save_alt_24"
android:text="Download"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/txt_copy"
style="#style/BottomSheetItem"
android:drawableStart="#drawable/copy_iocn"
android:drawableLeft="#drawable/copy_iocn"
android:text="Copy"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txt_download" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/txt_share"
style="#style/BottomSheetItem"
android:layout_marginTop="8dp"
android:drawableStart="#drawable/share_iocn"
android:drawableLeft="#drawable/share_iocn"
android:text="Share"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txt_copy" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/txt_whats_app"
style="#style/BottomSheetItem"
android:drawableStart="#drawable/ic_whatsapp_new"
android:drawableLeft="#drawable/ic_whatsapp_new"
android:text="Whats App"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txt_share" />
</androidx.constraintlayout.widget.ConstraintLayout>
You can add the item style in styles.xml
<style name="BottomSheetItem">
<item name="android:textSize">20sp</item>
<item name="android:drawablePadding">20dp</item>
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:padding">15dp</item>
<item name="fontFamily">#font/mono_bold</item>
</style>
Step 3
Now extend the class with BottomSheetDialogFragment and override the onCreateView method to provide the layout.
package com.sample
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.sample.R
import kotlinx.android.synthetic.main.bottom_sheet_options.*
import java.lang.ref.WeakReference
class OptionsBottomSheetFragment : BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.bottom_sheet_options, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpViews()
}
private fun setUpViews() {
// We can have cross button on the top right corner for providing elemnet to dismiss the bottom sheet
//iv_close.setOnClickListener { dismissAllowingStateLoss() }
txt_download.setOnClickListener {
dismissAllowingStateLoss()
Toast.makeText(application, "Download option clicked", Toast.LENGTH_LONG)
.show()
}
txt_share.setOnClickListener {
dismissAllowingStateLoss()
Toast.makeText(application, "Share option clicked", Toast.LENGTH_LONG)
.show()
}
}
companion object {
#JvmStatic
fun newInstance(bundle: Bundle): OptionsBottomSheetFragment {
val fragment = OptionsBottomSheetFragment()
fragment.arguments = bundle
return fragment
}
}
}
Note: We can set an interface and send the callback to parent activity to handle the actions accordingly.
With interface callback, it will be modified as follows
package com.sample
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.sample.R
import kotlinx.android.synthetic.main.bottom_sheet_layiut.*
class OptionsBottomSheetFragment : BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.bottom_sheet_options, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpViews()
}
private fun setUpViews() {
// We can have cross button on the top right corner for providing elemnet to dismiss the bottom sheet
//iv_close.setOnClickListener { dismissAllowingStateLoss() }
txt_download.setOnClickListener {
dismissAllowingStateLoss()
mListener?.onItemClick("Download")
}
txt_share.setOnClickListener {
dismissAllowingStateLoss()
mListener?.onItemClick("Share")
}
}
private var mListener: ItemClickListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is ItemClickListener) {
mListener = context as ItemClickListener
} else {
throw RuntimeException(
context.toString()
.toString() + " must implement ItemClickListener"
)
}
}
override fun onDetach() {
super.onDetach()
mListener = null
}
interface ItemClickListener {
fun onItemClick(item: String)
}
companion object {
#JvmStatic
fun newInstance(bundle: Bundle): OptionsBottomSheetFragment {
val fragment = OptionsBottomSheetFragment()
fragment.arguments = bundle
return fragment
}
}
}
Step 4
Design a layout with a button to show the dialog on its click.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_click_me"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
android:padding="#dimen/spacing_20"
android:background="#color/grey3"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="Click Me"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Step 5
The final step is to show the dialog in the activity
package com.sample
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.main.*
class SampleActivity :AppCompatActivity(),OptionsBottomSheetFragment.ItemClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.amin)
tv_click_me?.setOnClickListener {
supportFragmentManager.let {
OptionsBottomSheetFragment.newInstance(Bundle()).apply {
show(it, tag)
}
}
}
}
override fun onItemClick(param:String) {
when(param){
"share"->{
//Handle data
}
"Download"->{
//Handle data
}
else->{
//Handle data
}
}
}
}
I am very new to Kotlin, and i am studying it at school at the moment.
So the deal is that i have 3 tabs, that is supposed to contain stuff. Once i click "add" in one of the tabs, i want to open a new window to enter some information to save. I've been trying to use the fragment i'm on to create a new fragment, containing a very simple input field.
If anyone could tell me where i need to place my onClickListener and what to type in order to make it work, i'd be very thankful.
Any tips for refactoring my code in order to make it more readable for a newbie would be welcome also.
(DISCLAIMER: As it stands now, the app will crash due to "Unable to find explicit activity class ..."
I've fiddled a bit in the manifest to fix it, but to no avail. It feels wrong if the issue is there.)
Frag1.kt
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_frag1.view.*
class Frag1 : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val view: View = inflater.inflate(R.layout.fragment_frag1, container, false)
view.btnAddPerson.setOnClickListener {
requireActivity().startActivity(
Intent(requireActivity(), RegisterPersonFrag::class.java)
)
}
return view
}
}
fragment_frag1.xml
<androidx.constraintlayout.widget.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:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Frag1">
<!-- TODO: Update blank fragment layout -->
<TextView
android:id="#+id/textView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/btnAddPerson"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:src="#android:drawable/ic_input_add"
app:backgroundTint="#FFFFFF"
app:layout_constraintBottom_toBottomOf="#+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.825"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.896"
app:rippleColor="#FFFFFF" />
</androidx.constraintlayout.widget.ConstraintLayout>
RegisterPersonFrag.kt
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
/**
* A simple [Fragment] subclass.
*/
class RegisterPersonFrag : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_register_person, container, false)
}
}
fragment_register_person.xml
<androidx.constraintlayout.widget.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:id="#+id/frameLayout3"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RegisterPersonFrag">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="409dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.161">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Name" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
import MyAdapter
import android.os.Bundle
import com.google.android.material.tabs.TabLayout
import androidx.viewpager.widget.ViewPager
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
var tabLayout: TabLayout? = null
var viewPager: ViewPager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tabLayout = findViewById<TabLayout>(R.id.tabs)
viewPager = findViewById<ViewPager>(R.id.view_pager)
tabLayout!!.addTab(tabLayout!!.newTab().setText("UPCOMING"))
tabLayout!!.addTab(tabLayout!!.newTab().setText("PAST"))
tabLayout!!.addTab(tabLayout!!.newTab().setText("GIFTS TO BUY"))
tabLayout!!.tabGravity = TabLayout.GRAVITY_FILL
val adapter = MyAdapter(this, supportFragmentManager, tabLayout!!.tabCount)
viewPager!!.adapter = adapter
viewPager!!.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout))
tabLayout!!.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
viewPager!!.currentItem = tab.position
}
override fun onTabUnselected(tab: TabLayout.Tab) {
}
override fun onTabReselected(tab: TabLayout.Tab) {
}
})
}
}
One tip for you :
Do not use !! operator, it's code smell. Instead of it you can declare your variable tabLayout as lateinit var tabLayout: TabLayout so in future after it declaration tabLayout = findViewById<TabLayout>(R.id.tabs) you can have access without any conversion from nullable to non-nullable.
According to your question if you want to open new activity via Intent the second paramert should be Activity , not a Fragment (your class RegisterPersonFrag.kt is Fragment)! So here you have two way :
Create new Activity and call it like you do.
Read about fragmentManager and replace or add your fragment in particular container.