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?
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
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)
}
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've been trying to follow the only good example with support that I could find, but in my case, it doesn't work.
I have a ViewModel that talks to a #Model in Composable, and changes a loading: Bool according to a MutableLiveData<Boolean> but it doesn't recompose.
class LoaderViewModel : ViewModel() {
val loadingLiveData = MutableLiveData<Boolean>(false)
fun fetch() {
viewModelScope.launch {
val flow = flowOf("result")
.onStart {
loadingLiveData.value = true
delay(2000)
}
.onCompletion {
loadingLiveData.value = false
}
.collect {
// Do something with the result
}
}
}
}
class LoaderFragment : Fragment() {
private val viewModel: LoaderViewModel by viewModel()
#Model
class ActivityLoadingState(var loading: Boolean = false)
private val activityLoadingState = ActivityLoadingState()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return FrameLayout(context ?: return null).apply {
layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
setContent {
Loader()
}
}
}
#Composable
fun Loader() = MaterialTheme {
val loadingModel = activityLoadingState
Container {
Center {
if (loadingModel.loading) {
CircularProgressIndicator(
color = Color(0xFFFF0000)
)
} else {
Container { }
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
subscribeUI()
viewModel.fetch()
}
private fun subscribeUI() {
viewModel.loadingLiveData.observe(viewLifecycleOwner) {
activityLoadingState.loading = it
}
}
}
What I am doing, is to have Flows in my ViewModel, and use function collectAsState() whitin composables.
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
}