Android Buttons in Fragments to not get triggered - android

Why do buttons in Fragments to not get triggered? I've already spent about 5 hours on this, but no onClick / onFocusListener will trigger
Edit: Code
So this Fragment(s) is on a ViewPager2 on my MainActivity and
it just wont trigger any Callback Methods. I have also tried to but those onClick initializations everywhere
Fragment:
class SearchFragment : Fragment() {
private lateinit var searchBinding: FragmentSearchBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
searchBinding = FragmentSearchBinding.inflate(layoutInflater)
interface OnSearch {
fun onArticleSelected(position: Int)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_search, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var test = activity?.findViewById<TabLayout>(R.id.tab_layout)
searchBinding.searchBar.setOnFocusChangeListener{view, hasFocus ->
if (hasFocus){
if (test != null) {
test.isVisible = false
}
Log.d("SearchFragment", "onFocus")
}else{
if (test != null) {
test.isVisible = true
}
}
}
}
}
XML:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".ui.main.search.SearchFragment"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="#+id/testButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<EditText
android:id="#+id/search_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:hint="Search"
android:focusedByDefault="true"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/searchRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</FrameLayout>

Try something like this
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) {
super.onCreateView(view, savedInstanceState)
val binding = FragmentSearchBinding.inflate(inflater, container, false);
val view = binding.root
// Set binding
return view;
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById(R.id.searchBar).setOnFocusChangeListener { view, hasFocus ->
}
}

Related

Fragment incide AppCompatDialog

I have simple DialogWindow
XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dp"
android:layout_height="150dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<fragment
android:id="#+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/navigation"/>
</LinearLayout>
Code:
class DialogWindowBuyingStock(context: Context, private val viewModel: MainViewModel, private val stock: Stock) : AppCompatDialog(context) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
setContentView(R.layout.buy_stock_dialogwindow)
}
}
In which i have some fragments for example the Main one:
class MainFragment : Fragment() {
private var _binding: MainFragmentBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val homeViewModel =
ViewModelProvider(this)[MainFragmentViewModel::class.java]
_binding = MainFragmentBinding.inflate(inflater, container, false)
val root: View = binding.root
return root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
main_buy_button.setOnClickListener {
Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_buyFragment)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
It works perfectly for the first time i start Dialog Window, but when i close it and start again, i got this llegalArgumentException: Binary XML file line #20: Duplicate id 0x7f080136, tag null, or parent id 0xffffffff with another fragment for androidx.navigation.fragment.NavHostFragment
How can i fix that?

On click of the Button I want Fragment 1 layout to be replaced with Fragment 2 layout but its throwing: java.lang.NullPointerException

First Fragment's code
class MainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val mainInflater = inflater.inflate(R.layout.fragment_main, container, false)
return mainInflater
}
fun thisdata():String{
return "Hello from MainFragment"
}
}
First Fragment XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".MainFragment">
<EditText
android:id="#+id/etSavedData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="60dp"
android:hint="Enter the Text"/>
<Button
android:id="#+id/btnSaveData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="120dp"
android:text="Save"/>
</RelativeLayout>
Main Activity's code
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Set replace Main Activity content with the Fragment1 content
val mainFragment = MainFragment()
supportFragmentManager.beginTransaction().add(R.id.contain_fragment, mainFragment).commit()
val thedata = mainFragment.thisdata()
Log.e("Main Frag to Activity", thedata)
btnSaveData.setOnClickListener {
val secondFragment = SecondFragment()
supportFragmentManager.beginTransaction().add(R.id.contain_fragment, secondFragment).commit()
}
}
}
Main Activity XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:id="#+id/contain_fragment"
tools:context=".MainActivity">
</RelativeLayout>
Second Fragment's code
class SecondFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val secondInflater = inflater.inflate(R.layout.fragment_second, container, false)
return secondInflater
}
}
Second Fragment XML
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
android:background="#android:color/holo_blue_dark"
tools:context=".SecondFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:id="#+id/tvDisplayText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Text displayed here." />
</FrameLayout>
Following Exception showing up in Logcat
Caused by: java.lang.NullPointerException: Attempt to invoke virtual
method 'void
android.widget.Button.setOnClickListener(android.view.View$OnClickListener)'
on a null object reference
at com.example.demo2.MainActivity.onCreate(MainActivity.kt:22)*
You either need to move your btnSaveData button into the MainActivity, or you should move the onClickListener into the MainFragment, along with some other changes. Something like:
class MainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val mainInflater = inflater.inflate(R.layout.fragment_main, container, false)
btnSaveData.setOnClickListener {
val secondFragment = SecondFragment()
activity.supportFragmentManager.beginTransaction().add(R.id.contain_fragment, secondFragment).commit()
}
return mainInflater
}
fun thisdata():String{
return "Hello from MainFragment"
}
}
**Thanks Tash, that actually make sense, unfortunately it was still showing the same exception but I finally manage to resolve the issue by doing the following. **
class MainFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val mainInflater = inflater.inflate(R.layout.fragment_main, container, false)
mainInflater.btnSaveData.setOnClickListener {
val secondFragment = SecondFragment()
secondFragment?.let {
activity?.supportFragmentManager?.beginTransaction()?.replace(R.id.contain_fragment, secondFragment)?.commit()
}
}
return mainInflater
}
fun thisdata():String{
return "Hello from MainFragment"
}
}

Fragments inside other fragment: crash with "has not been attached yet."

Hi I have a strange problem. MainActivity has a framelayout and a main fragment EnterFragment.
EnterFragment has a framelayout and i'd like to see other 2 fragments. SignupFragment and LoginFragment.
When EnterFragment starts, it shows SignupFragment.java.. after MainActivity invoke a method of EnterFragment to change fragment to LoginFragment but app crash:
java.lang.IllegalStateException: Fragment EnterFragment{95bc7de (70f980b2-eff8-4e4c-a644-e95544b73584)} has not been attached yet.
at androidx.fragment.app.Fragment.getChildFragmentManager(Fragment.java:923)
Here the project: https://www.dropbox.com/s/qq1kl4qw688ipjr/example.zip?dl=0
Here the code:
//MainActivity
class MainActivity : AppCompatActivity() {
private var enterFragment: EnterFragment?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
showEnter()
}
private fun showEnter() {
enterFragment= EnterFragment.newInstance()
this.replaceFragmentTransition(R.id.fragmentContainer, EnterFragment.newInstance(), EnterFragment.TAG )
var handler= Handler()
handler.postDelayed(Runnable {gotoLoginFromSignup() }, 8000)
}
private fun gotoLoginFromSignup() {
enterFragment?.gotoLogin()
}
}
//EnterFragment
class EnterFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_enrer, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
this.replaceFragmentTransition(R.id.fmContainer, SignuFragment.newInstance(), SignuFragment.TAG )
}
fun gotoLogin(){
this.replaceFragmentTransition(R.id.fmContainer, LoginFragment.newInstance(), SignuFragment.TAG )
}
companion object {
val TAG: String="EnterFragment"
#JvmStatic
fun newInstance() = EnterFragment()
}
}
//SignupFragment
class SignuFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_signup, container, false)
}
companion object {
val TAG: String="SignuFragment"
#JvmStatic
fun newInstance() = SignuFragment()
}
}
//LoginFragment
class LoginFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_login, container, false)
}
companion object {
val TAG: String="LoginFragment"
#JvmStatic
fun newInstance() = LoginFragment()
}
}
//EXTENSIONS
fun FragmentActivity.replaceFragmentTransition(
container: Int,
fragment: Fragment?,
tag: String
) {
fragment?.let {
var transition = this.supportFragmentManager
.beginTransaction()
transition.replace(container, it, tag)
.commitNowAllowingStateLoss()
}
}
fun Fragment.replaceFragmentTransition(
container: Int,
fragment: Fragment?,
tag: String
) {
fragment?.let {
var transition = this.childFragmentManager
.beginTransaction()
transition.replace(container, it, tag)
.commitNowAllowingStateLoss()
}
}
//main activity layout
<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">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragmentContainer"
android:layout_width="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_height="0dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
//enterFragment
<androidx.constraintlayout.widget.ConstraintLayout 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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".EnterFragment">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fmContainer"
android:layout_width="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_height="0dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
Replace this
this.replaceFragment(R.id.fragmentContainer, FatherFragment.newInstance(), FatherFragment.TAG )
with
this.replaceFragment(R.id.fragmentContainer, fatherFragment!!, FatherFragment.TAG )
while transaction, you are passing different instance & calling openLogin() with another instance.
FatherFragment.newInstance() will give new instance.

ViewPager is not working correctly after device rotation

After rotation, one old page is displayed, followed by new pages.
Before rotation 1.
After rotation 2.
Layout with ViewPager:
<RelativeLayout ...
<androidx.viewpager.widget.ViewPager
android:id="#+id/pager"
android:overScrollMode="never"
android:layout_marginStart="16dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="10dp"
android:layout_height="match_parent"
android:layout_width="match_parent">
</RelativeLayout>
Fragment with this layout:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val view = inflater.inflate(layoutId(), container, false)
view.findViewById<ViewPager>(R.id.pager).adapter = TextPagerAdapter(activity!!.supportFragmentManager, listOf())
return view
}
TextPagerAdapter:
override fun getItem(position: Int): Fragment = PageFragment.newInstance("sssssss ssss $position")
override fun getCount() = 5//pageText.size
}
PageFragment:
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {
val view = inflater.inflate(layoutId(), container, false)
view.findViewById<TextView>(R.id.page_content_textView).text = arguments!!.getCharSequence(PAGE_TEXT)
return view
}
I forgot to check if the content fragment is already created.
The solution is to check if the savedInstanceState == null. And create new fragment if that condition is true.
In MyActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if(savedInstanceState == null) this.supportFragmentManager.beginTransaction()
.add(containerViewId, CollectionDemoFragment()).commit()
}

How to click a button in a fragment to switch to other Tab in Kotlin?

The following code is based by Android Studio Wizard 3.2.1 for creating Tabbed Activity.
There is a button1 on PlaceholderFragment1 connected Tab1, and a button2 on PlaceholderFragment2 connected Tab2
I hope to click button1 to switch to Tab2, and click button2 to switch to Tab1, How can I do ?
BTW, I have read How to change tab on button click in Android? and How to programmatically switch tabs using buttonclick in Android
Code
class MainActivity : AppCompatActivity() {
private var mSectionsPagerAdapter: SectionsPagerAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mSectionsPagerAdapter = SectionsPagerAdapter(supportFragmentManager)
container.adapter = mSectionsPagerAdapter
container.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabs))
tabs.addOnTabSelectedListener(TabLayout.ViewPagerOnTabSelectedListener(container))
}
inner class SectionsPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
val fragment: Fragment
when (position) {
0 -> fragment = PlaceholderFragment1() //Tab1
1 -> fragment = PlaceholderFragment2() //Tab2
else -> throw IllegalArgumentException("Invalid section number")
}
return fragment
}
override fun getCount(): Int {
return 2
}
}
class PlaceholderFragment1 : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.fragment_main1, container, false)
return rootView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
button1.setOnClickListener {
//Switch to Tab2
}
}
}
class PlaceholderFragment2 : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.fragment_main2, container, false)
return rootView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
button2.setOnClickListener {
//Switch to Tab1
}
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="#dimen/appbar_padding_top"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TabItem
android:id="#+id/tabItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/tab_text_1"/>
<android.support.design.widget.TabItem
android:id="#+id/tabItem2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/tab_text_2"/>
</android.support.design.widget.TabLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
fragment_main1.xml
<?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:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:text="Button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/button1"/>
</android.support.constraint.ConstraintLayout>
My way
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
...
setControl(view)
}
private fun setControl(view: View){
button1.setOnClickListener {
var viewPager=view.parent as ViewPager
viewPager.setCurrentItem(1,true)
}
}
manually binding these events is not required...
you just have to setup the TabLayout with the Viewpager:
container.adapter = mSectionsPagerAdapter
tabs.setupWithViewPager(container);
then these events are being handled by the framework, in a rather convenient way.
PS: container is a rather unfortunate variable name for a ViewPager.
I think you should use Livedata and ViewModel.
Create View Model class with livedata.
Observer Livedata in MainActivity.
Update livedata value from your fragment.
For example:
class TabChangerViewModel : ViewModel() {
val colorResource = MutableLiveData<Int>()
.........
}
In your MainActivity class:
val tabChangerViewModel = ViewModelProviders.of(this).get(TabChangerViewModel::class.java) // initialize view model
tabChangerViewModel.colorResource.observe(this, android.arch.lifecycle.Observer {
//mViewPager.setCurrentItem(it)
})
Now update live data value from your fragment:
val tabChangerViewModel = ViewModelProviders.of(activity).get(TabChangerViewModel::class.java) // initialize view model
tabChangerViewModel.colorResource.value = TAB_POSITION_WANT_TO_SELECT

Categories

Resources