I am implementing a function which is to close the application by clicking back button twice. It shows the toast message when it is first clicked. However, the problem is it shows infinite loop error then is termiated. I do not really understand the reason after two days of browsing. Any help will be greatly appreciated.
My fragment code
class RegisteredMainFragment : Fragment() {
private lateinit var binding : FragmentRegisteredMainBinding
private val viewModel : RegisteredMainViewModel by inject()
private lateinit var mContext: MainActivity
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context as MainActivity
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_registered_main, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.viewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner
onBackPressedCallback()
}
private fun onBackPressedCallback() {
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner,
object : OnBackPressedCallback(true ) {
override fun handleOnBackPressed() {
if (doubleBackToExitPressedOnce) {
requireFragmentManager().popBackStack()
return
}
doubleBackToExitPressedOnce = true
Toast.makeText(requireContext(), "click back button again", Toast.LENGTH_SHORT).show()
Handler().postDelayed({ doubleBackToExitPressedOnce = false }, 2000)
}
})
}
override fun onResume() {
super.onResume()
viewModel.onAuthExist()
}
override fun onDestroyView() {
super.onDestroyView()
PushObserverService.unregisterObserver(this)
}
}
I solved the problem by writing it in override fun onAttach
Please check the code below
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context as MainActivity
callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (doubleBackToExitPressedOnce) {
activity?.finish()
}
doubleBackToExitPressedOnce = true
Handler().postDelayed({ doubleBackToExitPressedOnce = false }, 2000)
showSnackBar(
context = mContext,
layout = binding.layoutMain,
type = Constants.SnackBarTypes.Warn,
message = mContext.getString(R.string.tap_twice_to_terminate)
)
}
}
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
}
Related
I have a project with an interface, a main activity and a fragment. Here is the interface:
interface OnClickInterface {
fun onClick(proceed: Boolean)
}
Here is the fragment. It has one button:
class FragmentMain() : Fragment() {
private lateinit var binding: FragmentMainBinding
var listener: OnClickInterface? = null
lateinit var btn: Button
fun initOnClickInterface(listener: OnClickInterface) {
this.listener = listener
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = FragmentMainBinding.inflate(layoutInflater)
btn = binding.button
btn.setOnClickListener()
{
if (listener == null)Toast.makeText(activity, "NULL", Toast.LENGTH_SHORT).show()
listener?.onClick(true) // Trigger the call back
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
return binding.root
}
companion object {
#JvmStatic
fun newInstance() = FragmentMain().apply { }
}
}
My problem is that callback listener is allways null. How can I initialize the listener? Thanks
so I want to pass data from one to another. I'm using ViewPager to make fragments change when I click some trigger which is a button. What I want is when I click the buttonLanjut from MainActivity, I can set data from MerchantTambahBarangFragment and get that data to MerchantTambahKategoriFragment. I'm confused because the trigger is on MainActivity not in the fragment. Code below
ParentActivity which is same as MainActivity
class MerchantTambahParentActivty : AppCompatActivity(){
lateinit var binding: ActivityMerchantTambahParentActivtyBinding
var currentFragPosition = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMerchantTambahParentActivtyBinding.inflate(layoutInflater)
setContentView(binding.root)
//Toolbar
setToolbar()
//Tabpager
val pagerAdapter = PagerAdapterTambahBaran(this, supportFragmentManager)
binding.viewPager.adapter = pagerAdapter
nextOrBackPageAction()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
startActivity(Intent(this, MerchantKelolaBarangActivity::class.java))
return true
}
}
return super.onOptionsItemSelected(item)
}
private fun setToolbar() {
supportActionBar?.apply {
title = "Tambah Barang"
setDisplayHomeAsUpEnabled(true)
}
}
#SuppressLint("SetTextI18n")
private fun nextOrBackPageAction(){
when (currentFragPosition) {
0 -> {
binding.buttonKembali.visibility = View.GONE
}
}
binding.buttonLanjut.setOnClickListener {
binding.buttonKembali.visibility = View.VISIBLE
if(currentFragPosition <= 3){
currentFragPosition++
Toast.makeText(this, currentFragPosition.toString(), Toast.LENGTH_SHORT).show()
if(currentFragPosition == 4){
binding.buttonLanjut.text = "SUBMIT"
binding.buttonLanjut.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)
}
binding.viewPager.setCurrentItem(currentFragPosition, true)
}
}
binding.buttonKembali.setOnClickListener {
if(currentFragPosition > 0){
currentFragPosition--
if(currentFragPosition == 0){
binding.buttonKembali.visibility = View.GONE
}
binding.buttonLanjut.text = "Lanjut"
binding.buttonLanjut.setCompoundDrawablesWithIntrinsicBounds(0, 0, id.co.leholeh.R.drawable.ic_arrow_next, 0)
binding.viewPager.setCurrentItem(currentFragPosition, true)
}
}
}
}
MerchantTambahBarangFragment.kt
class MerchantTambahBarangFragment : Fragment() {
lateinit var binding : FragmentMerchantTambahBarangBinding
private lateinit var cacheUtil: CacheUtil
private lateinit var auth: Login
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = FragmentMerchantTambahBarangBinding.inflate(inflater,container,false)
cacheUtil = CacheUtil()
cacheUtil.start(requireActivity(), ConstantAuth.PREFERENCES)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (TextUtils.isEmpty(this.cacheUtil.get(ConstantAuth.AUTH))) {
startActivity(Intent(requireActivity(), LoginActivity::class.java))
} else {
auth = getAuth(cacheUtil)
//setDataCategories()
binding.inputCover.setOnClickListener {
ImagePicker.create(this)
.single()
.start()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (ImagePicker.shouldHandle(requestCode, resultCode, data)) {
val image: Image = ImagePicker.getFirstImageOrNull(data)
Glide.with(binding.imageViewCover).load(image.uri).into(binding.imageViewCover)
binding.imageViewCover.visibility = View.VISIBLE
}
super.onActivityResult(requestCode, resultCode, data)
}
}
MerchantTambahKategoriFragment.kt
class MerchantTambahKategoriFragment : Fragment() {
lateinit var binding : FragmentTambahKategoriBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = FragmentTambahKategoriBinding.inflate(inflater, container, false)
return binding.root
}
}
PagerAdapterTambahBarang.kt
class PagerAdapterTambahBaran(
private val context: Context,
fragmentManager: FragmentManager ) :
FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getCount() = 5
override fun getItem(position: Int): Fragment = when (position) {
0 -> MerchantTambahBarangFragment()
1 -> MerchantTambahKategoriFragment()
2 -> MerchantTambahLokasiFragment()
3 -> MerchantTambahFotoFragment()
4 -> MerhchantSubmitBarangFragment()
else-> Fragment()
}
}
You can solve this by using a SharedViewModel between the activity and the fragments, where in the activity when you click on the button you can update some LiveData where the children fragments observe.
you can check this answer for more info about this approach
I'm trying to add my fragment into stack. So when the next button is clicked, the fragment will added to stack. But then I got some problem that the container id is not found. Here is my code in the next button :
override fun onNextClicked(callback: StepperLayout.OnNextClickedCallback?) {
val myFragment = MotorFragment()
val fragmentManager = fragmentManager
val fragmentTransaction = fragmentManager!!.beginTransaction()
fragmentTransaction.add(R.id.container, myFragment)
fragmentTransaction.addToBackStack("firstFrag")
fragmentTransaction.commit()
Toast.makeText(context,"firstFrag Added", Toast.LENGTH_SHORT).show()
callback?.goToNextStep()
}
I google it but none of the solution work for me. I try to change the
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getReligion()
}
into this :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(null)
getReligion()
}
and still got the same error.
And here is my on create code :
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater.inflate(R.layout.fragment_spk_motor, container, false)
return v
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getReligion()
}
I expect when I click the next button will not force close and success adding into stack. Help please.
My full code :
class MotorFragment : Fragment(), Step, BlockingStep {
var datareligion: ArrayList<MasterReligion?>? = null
lateinit var dataManager: DataManager
val myCalendar = Calendar.getInstance()
companion object {
fun newInstance(): MotorFragment {
return MotorFragment()
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater.inflate(R.layout.fragment_spk_motor, container, false)
return v
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getReligion()
}
fun getReligion() {
API.getreligion().enqueue(object : Callback<ArrayList<MasterReligion>> {
override fun onResponse(call: Call<ArrayList<MasterReligion>>, response: Response<ArrayList<MasterReligion>>) {
if (response.code() == 200) {
datareligion = ArrayList()
datareligion?.add(0, null)
response.body()?.forEach { datareligion?.add(it) }
val adapter = MyStepFragment.CustomAdapter<MasterReligion?>(activity, R.layout.spinner_custom, R.layout.spinner_dropdown_item, datareligion?.toTypedArray()!!)
spnOTR.adapter = adapter
}else{
Toast.makeText(activity, "Error", Toast.LENGTH_LONG).show()
}
}
override fun onFailure(call: Call<ArrayList<MasterReligion>>, throwable: Throwable) {
Toast.makeText(activity, "Please check your connection", Toast.LENGTH_LONG).show()
}
})
spnOTR?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parentView: AdapterView<*>, selectedItemView: View?, position: Int, id: Long) {
if (selectedItemView == null) {
Toast.makeText(context, "Tipe harga jual tidak terpilih", Toast.LENGTH_SHORT).show()
} else {
val data = position
val prefs = PreferenceManager.getDefaultSharedPreferences(activity!!.baseContext) //context
val prefEditor = prefs.edit()
prefEditor.putInt("savedValue", data)
prefEditor.apply()
}
}
override fun onNothingSelected(parentView: AdapterView<*>) {
Toast.makeText(context, "Nothing selected", Toast.LENGTH_SHORT).show()
}
}
}
override fun verifyStep(): VerificationError? {
return null
}
override fun onSelected() {}
override fun onError(error: VerificationError) {}
override fun onBackClicked(callback: StepperLayout.OnBackClickedCallback?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onCompleteClicked(callback: StepperLayout.OnCompleteClickedCallback?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onNextClicked(callback: StepperLayout.OnNextClickedCallback?) {
val myFragment = MotorFragment()
val fragmentManager = fragmentManager
val fragmentTransaction = fragmentManager!!.beginTransaction()
fragmentTransaction.add(R.id.container, myFragment)
fragmentTransaction.addToBackStack("firstFrag")
fragmentTransaction.commit()
Toast.makeText(context,"firstFrag Added", Toast.LENGTH_SHORT).show()
callback?.goToNextStep()
}
}
Try this
// Define framelayout in your activity xml , then try to add
fragment
<FrameLayout
xmlns
:android="http://schemas.android.com/apk/res/android
android:layout_width="match_parent
android:layout_height="match_parent
android:id="#+id/container"/>
I want develop one application with Rxjava, Dagger, Kotin and MVP.
I write below codes but when run application show me nullPointerExecetpion error.
I know nullPointerExeception for my codes bug, but i try to found my bug i can't it!
My base fragment codes:
abstract class BaseFragment : Fragment(), BaseView {
var presenter: BasePresenter<*>? = null
abstract fun initializeDagger()
abstract fun initializePresenter()
abstract var layoutID: Int
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(layoutID, container, false)
initializeDagger()
initializePresenter()
return view
}
override fun onDetach() {
super.onDetach()
presenter?.onDestroy()
}
}
HomePresenter code:
class HomeTodayPresenter #Inject constructor(
val repositoryUseCase: RepositoryUseCase, disposable: CompositeDisposable
) : BasePresenter<HomeTodayView>(disposable) {
private var todayList = ArrayList<Today>()
fun onCreate() {
view?.initRepositoryList(todayList)
getTodayList()
}
fun onTodayRefreshList() {
getTodayList()
}
private fun getTodayList() {
compositeDisposable.add(
repositoryUseCase.getAuctionsToday()
.subscribe({ responseResult ->
view?.hideLoader()
responseResult?.let { itResponse ->
itResponse.res?.let { itRes ->
itRes.today?.let { itToday ->
if (itToday.size > 0) {
todayList.clear()
todayList.addAll(itToday)
view?.loadRepositoryList()
}
}
}
}
}, { e ->
view?.let { itView ->
e.message?.let { itErr ->
itView.showErrorMessage(itErr)
}
itView.hideLoader()
}
})
)
}
}
Home Fragment code:
class HomeTodayFragment : BaseFragment(), HomeTodayView {
#Inject
lateinit var homeTodayPresenter: HomeTodayPresenter
lateinit var todayAuctionsAdapter: TodayAuctionsAdapter
lateinit var layoutManager: LinearLayoutManager
private val swipeRefreshListener = SwipeRefreshLayout.OnRefreshListener {
homeTodayPresenter.onTodayRefreshList()
}
override fun initRepositoryList(list: ArrayList<Today>) {
layoutManager = LinearLayoutManager(requireContext())
todayAuctionsAdapter = TodayAuctionsAdapter(list)
requireContext().initRecyclerView(homeFragToday_list, layoutManager, todayAuctionsAdapter)
}
override fun loadRepositoryList() {
todayAuctionsAdapter.notifyDataSetChanged()
}
override fun hideLoader() {
homeFragToday_loader.visibility = View.GONE
}
override fun showErrorMessage(msg: String) {
Log.e("responseErr", msg)
}
override fun initializeDagger() {
AndroidInjection.inject(requireActivity())
}
override fun initializePresenter() {
super.presenter = homeTodayPresenter
homeTodayPresenter.view = this
}
override var layoutID: Int = R.layout.fragment_home_today
private lateinit var toolbarTile: TextView
lateinit var handler: Handler
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
homeTodayPresenter.onCreate()
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Initialize
activity?.let {
toolbarTile = it.findViewById(R.id.homePage_toolbarTitle)
}
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
if (isVisibleToUser) {
//Initialize
handler = Handler()
//Set delay
handler.postDelayed({
//Set title
toolbarTile.text = resources.getString(R.string.today)
toolbarTile.setTextColor(ResourcesCompat.getColor(resources, R.color.green_active, null))
}, 10)
}
}
}
LogCat error :
kotlin.UninitializedPropertyAccessException: lateinit property homeTodayPresenter has not been initialized
at com.app.applisttestapp.UI.Home.Fragments.Today.HomeTodayFragment.onCreateView(HomeTodayFragment.kt:69)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2539)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:875)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1227)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1293)
Show me error for this line : homeTodayPresenter.onCreate()
How can i fix it?
I have OnClickInterface (with method fun onClickShape()) Main.class, and FlipFragment.class and ImageView (which called image in my code). My goal is make listener for image.
interface OnClickInterface {
fun onClickShape()
}
MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initTabs()
var flip = FlipFragment()
flip.listener = object : OnClickInterface {
override fun onClickShape() {
Log.d("MainActivity", "Shape Pressed")
ToastUtils.showSuccessMessage(baseContext, "sometext")
}
}
}
fun initTabs() {
var adapter = TabsPagerFragmentAdapter(supportFragmentManager)
mViewPager.adapter = adapter
mTabLayout.setupWithViewPager(mViewPager)
}
}
onCreate in FlipFragment
var image = view.findViewById<ImageView>(R.id.fShapeView)
image.setOnClickListener(View.OnClickListener {
Log.d("FlipFragment", "PRESSED")
if (listener != null)
listener!!.onClickShape()
})
App was loading well, without errors. But when I pressed in the image I show in my log FlipFragment: PRESSED that's mean that my application call method from FragmentFlip, not override method from MainActivity. Why?
I searched error . My app show NPE here.
flip.listener = object : OnClickInterface {
override fun onClickShape() {
Log.d("MainActivity", "Shape Pressed")
ToastUtils.showSuccessMessage(baseContext, "someText")
}}
Why listener = null . I defined it with anonymous class.
All code in FlipFragment
class FlipFragment : Fragment() {
private var layout = R.layout.view_flip
var listener: OnClickInterface? = null
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
var view: View
view = inflater!!.inflate(layout, container, false)
var image = view.findViewById<ImageView>(R.id.fShapeView)
image.setOnClickListener(View.OnClickListener {
Log.d("FlipFragment", "PRESSED")
if (listener != null){
listener!!.onClickShape()}
})
return view
}
companion object {
fun getInstanse(): FlipFragment {
var args = Bundle()
var flipFragment = FlipFragment()
flipFragment.arguments = args
return flipFragment
}
}
}
If you need all code it is FragmentPagerAdapter.class
class TabsPagerFragmentAdapter(fm: FragmentManager?) : FragmentPagerAdapter(fm) {
var tabs: Array<String> = arrayOf("Flip", "Multi")
override fun getItem(position: Int) = when(position){
0 -> FlipFragment.getInstanse()
1 -> Mulit.getInstanse() //it is empty now
else -> FlipFragment.getInstanse()
}
override fun getPageTitle(position: Int) = tabs[position]
override fun getCount() = tabs.size
}