I am trying to add a listener to my button from a Fragment but it always give me this error:
Process: com.example.projectdrivemark, PID: 27776
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.projectdrivemark/com.example.MainActivity}: kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3341)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3485)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2045)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7478)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:549)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:941)
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized
at com.example.tempConverter.TempConverterFragment.<init>(TempConverterFragment.kt:16)
at com.example.MainActivity.onCreate(MainActivity.kt:28)
at android.app.Activity.performCreate(Activity.java:7989)
at android.app.Activity.performCreate(Activity.java:7978)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3316
Here is MainActivity code:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.projectdrivemark.R
import com.example.projectdrivemark.databinding.ActivityMainBinding
import com.example.recyclerView.MockDatabase.Companion.createMockData
import com.example.recyclerView.PostAdapter
import com.example.recyclerView.RecyclerViewFragment
import com.example.tempConverter.TempConverterFragment
import com.example.uploaderView.UploaderFragment
class MainActivity : AppCompatActivity(), PostAdapter.OnPostClickListener {
private lateinit var binding: ActivityMainBinding
val dummyList = createMockData()
val adapter = PostAdapter(dummyList, this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
title = "First Kotlin App"
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val recyclerView = RecyclerViewFragment()
val tempConverterView = TempConverterFragment()
val uploaderView = UploaderFragment(this)
setFragmentView(recyclerView)
binding.bottomNavBar.setOnNavigationItemSelectedListener {
when(it.itemId){
R.id.listView -> setFragmentView(recyclerView)
R.id.tempConverterView -> setFragmentView(tempConverterView)
R.id.videoUploaderView -> setFragmentView(uploaderView)
}
true
}
}
private fun setFragmentView(fragment: Fragment){
supportFragmentManager.beginTransaction().apply {
replace(R.id.main_fragment_view, fragment)
//Will return to previous page when tap "Back Button" on the phone
addToBackStack(null)
commit()
}
}
override fun onEditPost(position: Int){
val clickedPost = dummyList[position]
clickedPost.title = "Updated title"
clickedPost.body = "Updated body"
adapter.notifyItemChanged(position)
}
override fun onDeletePost(position: Int) {
dummyList.removeAt(position)
adapter.notifyItemRemoved(position)
}
}
Here is how my TempConverterFragment looks like:
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.example.MainActivity
import com.example.projectdrivemark.R
import com.example.projectdrivemark.databinding.FragmentTempConverterBinding
class TempConverterFragment: Fragment(){
private lateinit var binding: FragmentTempConverterBinding
val celsiusButton = binding.celsiusButton
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = inflater.inflate(R.layout.fragment_temp_converter, container, false)
celsiusButton.setOnClickListener {
celsiusFunction(celsiusButton)
}
return binding.rootView
}
fun celsiusFunction(view: View){
val fahrenheitView = binding!!.userTemp
val fahrenheitValue = fahrenheitView.text.toString()
if(!fahrenheitValue.isBlank()){
val celsiusCovertedValue = (fahrenheitValue.toDouble() - 32) * 5/9
val celsiusValue = String.format("%.2f", celsiusCovertedValue)
Toast.makeText(activity,
"$fahrenheitValue fahrenheit is $celsiusValue degrees celsius",
Toast.LENGTH_LONG).show()
}else {
Toast.makeText(activity, "You must enter a value to convert", Toast.LENGTH_LONG).show()
}
}
fun farenheitFunction(view: View){
val celsiusView = binding.userTemp
val celsiusValue = celsiusView.text.toString()
if(!celsiusValue.isBlank()){
val farenheitConvertedValue = celsiusValue.toDouble() * 9/5 + 32
val farenheitValue = String.format("%.2f", farenheitConvertedValue)
Toast.makeText(activity,
"$celsiusValue degrees celsius is $farenheitValue farenheit",
Toast.LENGTH_LONG).show()
}else{
Toast.makeText(activity, "You must enter a value to convert", Toast.LENGTH_LONG).show()
}
}
}
And here is how my Xml Fragment looks like:
<?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="com.example.MainActivity">
<ImageView
android:id="#+id/imageView"
android:layout_width="487dp"
android:layout_height="320dp"
android:layout_marginBottom="16dp"
android:adjustViewBounds="true"
android:cropToPadding="false"
android:scaleType="fitXY"
app:layout_constraintBottom_toTopOf="#+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="#drawable/termometer" />
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="62dp"
android:layout_marginLeft="62dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="76dp"
android:layout_marginRight="76dp"
android:text="Enter desired temperature to convert"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/imageView" />
<EditText
android:id="#+id/userTemp"
style="#android:style/Widget.DeviceDefault.EditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="140dp"
android:layout_marginLeft="140dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="140dp"
android:layout_marginRight="140dp"
android:ems="10"
android:hint="Enter Temperature Here"
android:inputType="numberDecimal"
android:selectAllOnFocus="false"
android:singleLine="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textView" />
<Button
android:id="#+id/celsiusButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="70dp"
android:layout_marginLeft="70dp"
android:layout_marginTop="32dp"
android:onClick="celsiusFunction"
android:text="to Celsius"
app:backgroundTint="#color/purple_200"
app:layout_constraintEnd_toStartOf="#+id/farenheitButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/userTemp" />
<Button
android:id="#+id/farenheitButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="41dp"
android:layout_marginLeft="41dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="72dp"
android:layout_marginRight="72dp"
android:onClick="farenheitFunction"
android:text="to Farenheit"
app:backgroundTint="#color/teal_200"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/celsiusButton"
app:layout_constraintTop_toBottomOf="#+id/userTemp" />
</androidx.constraintlayout.widget.ConstraintLayout>
How to initialize a lateinit binding? Or is it better just use view.findViewById?
remove the 'val' before binding in your onCreatView in your fragment. You have already declared it and initialise it like below
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
{
binding = FragmentTempConverterBinding.inflate(inflater, container, false)
return binding.root
}
Related
THIS IS THE VIEWMODEL FILE CODE
class HomePageDocVM(var dao: HomePageEventsDAO) : ViewModel() {
val eventNamevm = dao.getAll()
var newEventName : String = "event nameprop"
var newEventDate : String = "event dateprop"
var newEventTime : String = ""
var selectedTime : String = ""
fun addnew(){
viewModelScope.launch {
var evNamedb = HomePageEventsEntity(0)
evNamedb.EventName = newEventName
var evDatedb = HomePageEventsEntity(0)
evDatedb.EventDate = newEventDate
var evTimedb = HomePageEventsEntity(0)
evDatedb.EventTime = newEventTime
dao.insert(evNamedb, evTimedb, evDatedb)
}
}
}
THIS IS THE ADAPTER FILE CODE
package com.example.projectm.activitiesFragments
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.projectm.R
class DocUpcomingEventsAdapter : RecyclerView.Adapter<DocUpcomingEventsAdapter.DocUpcomingEventsViewHolder>() {
var data = listOf<HomePageEventsEntity>()
class DocUpcomingEventsViewHolder( val view: View) : RecyclerView.ViewHolder(view)
{
var eventName : TextView = view.findViewById<TextView>(R.id.text_of_evnt_rv_item)
var eventTime : TextView = view.findViewById<TextView>(R.id.text_of_time_rv_item)
var eventDate : TextView = view.findViewById<TextView>(R.id.text_of_date_rv_item)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
DocUpcomingEventsViewHolder
{
val linearlayout = LayoutInflater.from(parent.context)
.inflate(R.layout.doc_upcoming_events_rv_item, parent, false) as View
return DocUpcomingEventsViewHolder(linearlayout)
}
override fun onBindViewHolder(holder: DocUpcomingEventsViewHolder, position: Int) {
holder.eventName.text = data[position].EventName
holder.eventTime.text = data[position].EventTime
holder.eventDate.text = data[position].EventDate
}
override fun getItemCount(): Int = data.size
}
THIS IS THE MAIN FRAGMENT FILE CODE
package com.example.projectm.activitiesFragments
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.TextView
import android.widget.TimePicker
import androidx.fragment.app.Fragment
import com.example.projectm.databinding.HomePageDocBinding
import java.util.*
class HomePageDoc : Fragment()
{
private var _binding : HomePageDocBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View
{
_binding = HomePageDocBinding.inflate(inflater, container, false)
val writenewevnt : EditText = binding.addNewEvntEtv
val addevntbtn : TextView = binding.saveEvntBtn
val timetv : TextView = binding.timepickertv
val datetv : TextView = binding.datepickertv
val currentTime = Calendar.getInstance()
val hour = currentTime.get(Calendar.HOUR_OF_DAY)
val minute = currentTime.get(Calendar.MINUTE)
val day = currentTime.get(Calendar.DAY_OF_MONTH)
val month = currentTime.get(Calendar.MONTH)
val year = currentTime.get(Calendar.YEAR)
writenewevnt.setOnClickListener(){
}
timetv.setOnClickListener()
{
TimePickerDialog(requireContext(), { view : TimePicker, selectedhour: Int, selectedminute : Int
->
var theselectedtime = "$selectedhour : $selectedminute"
timetv.text = theselectedtime
}, hour, minute, true).show() }
addevntbtn.setOnClickListener()
{
/**Toast.makeText(requireContext(), "Hello" , Toast.LENGTH_LONG ).show()**/
}
datetv.setOnClickListener()
{
DatePickerDialog(requireContext(),
{view, selectedyear : Int, selectedmonth: Int, selecteddayOfMonth:Int ->
val selectedDate = "$selecteddayOfMonth/${selectedmonth+1}/$selectedyear"
datetv.text = selectedDate
}, year, month, day).show()
}
val adapter = DocUpcomingEventsAdapter()
binding.docScheduleRv.adapter = adapter
return binding.root
}
override fun onDestroyView()
{
super.onDestroyView()
_binding = null
}
}
THIS IS THE ENTITY FILE CODE
#Entity(tableName = "UpComingEvents")
data class HomePageEventsEntity(
#PrimaryKey(autoGenerate = true)
var ID : Int,
var EventName : String = "",
var EventTime : String = "",
var EventDate : String = "",
)
THIS IS THE DATABASE FILE CODE
package com.example.projectm.activitiesFragments
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
#Database(entities = [HomePageEventsEntity::class], version = 1, exportSchema = false)
abstract class HomePageEventsDatabase : RoomDatabase() {
abstract val eventsDaoval : HomePageEventsDAO
companion object{
#Volatile
private var INSTANCE : HomePageEventsDatabase? = null
fun getInstance(context : Context) : HomePageEventsDatabase{
synchronized(this){
var instance = INSTANCE
if(instance == null){
instance = Room.databaseBuilder(context.applicationContext,
HomePageEventsDatabase::class.java,
"HmPgEventDbname").build()
INSTANCE = instance
}
return instance
}
}
}
}
THIS IS THE DAO FILE CODE
#Dao
interface HomePageEventsDAO {
#Insert
suspend fun insert(EventName : HomePageEventsEntity, EventTime : HomePageEventsEntity, EventDate: HomePageEventsEntity )
#Update
suspend fun update(EventName: HomePageEventsEntity, EventTime: HomePageEventsEntity, EventDate: HomePageEventsEntity)
#Delete
suspend fun delete(EventName: HomePageEventsEntity, EventTime: HomePageEventsEntity, EventDate: HomePageEventsEntity)
#Query("SELECT * FROM UpComingEvents")
fun getAll() : List<HomePageEventsEntity>
}
THIS IS THE XML FILE CODE
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="editetv"
type="com.example.projectm.activitiesFragments.HomePageDocVM" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.google.android.material.card.MaterialCardView
android:id="#+id/doc_hmpg_today_stats_cv"
android:layout_width="match_parent"
android:layout_height="180dp"
android:layout_margin="#dimen/layout_margin"
app:cardCornerRadius="30dp"
app:strokeWidth="#dimen/strk_width"
app:strokeColor="#color/patronas"
app:cardElevation="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="#+id/doc_hmpg_stat_hding_tv"
android:layout_width="0dp"
android:layout_height="40dp"
android:background="#drawable/cardviewbg30"
android:gravity="center"
app:layout_constraintTop_toTopOf="#id/doc_hmpg_today_stats_cv"
app:layout_constraintRight_toRightOf="#id/doc_hmpg_today_stats_cv"
app:layout_constraintLeft_toLeftOf="#id/doc_hmpg_today_stats_cv"
android:text="#string/hmpg_stat_hding"
android:textColor="#color/white"
android:textSize="#dimen/ts30"/>
<TextView
android:id="#+id/hmpg_patient_treated"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#drawable/circularoutline"
android:layout_margin="0dp"
android:gravity="center"
app:layout_constraintTop_toBottomOf="#id/doc_hmpg_stat_hding_tv"
app:layout_constraintLeft_toLeftOf="#id/doc_hmpg_today_stats_cv"
app:layout_constraintRight_toLeftOf="#id/hmpg_patient_waiting"
app:layout_constraintBottom_toTopOf="#id/patient_treated_label"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintVertical_bias="0.5"
android:hint="#string/triple_num_hint"
android:textSize="#dimen/ts40"
android:textColor="#color/black"
/>
<TextView
android:id="#+id/hmpg_patient_waiting"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#drawable/circularoutline"
android:layout_margin="0dp"
android:gravity="center"
app:layout_constraintTop_toBottomOf="#id/doc_hmpg_stat_hding_tv"
app:layout_constraintLeft_toRightOf="#id/hmpg_patient_treated"
app:layout_constraintRight_toRightOf="#id/doc_hmpg_today_stats_cv"
app:layout_constraintBottom_toTopOf="#id/patient_watiting_label"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintVertical_bias="0.5"
android:hint="#string/triple_num_hint"
android:textSize="#dimen/ts40"
/>
<TextView
android:id="#+id/patient_treated_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
app:layout_constraintTop_toBottomOf="#id/hmpg_patient_treated"
app:layout_constraintLeft_toLeftOf="#id/hmpg_patient_treated"
app:layout_constraintRight_toRightOf="#id/hmpg_patient_treated"
app:layout_constraintBottom_toBottomOf="#id/doc_hmpg_today_stats_cv"
android:text="#string/patient_treated"
android:textSize="#dimen/ts20"
android:textColor="#color/black"
/>
<TextView
android:id="#+id/patient_watiting_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
app:layout_constraintTop_toBottomOf="#id/hmpg_patient_waiting"
app:layout_constraintRight_toRightOf="#id/hmpg_patient_waiting"
app:layout_constraintLeft_toLeftOf="#id/hmpg_patient_waiting"
app:layout_constraintBottom_toBottomOf="#id/doc_hmpg_today_stats_cv"
android:text="#string/patient_wating"
android:textSize="#dimen/ts20"
android:textColor="#color/black"
/>
<com.google.android.material.card.MaterialCardView
android:id="#+id/doc_schedule_cv"
android:layout_width="match_parent"
android:layout_height="500dp"
android:layout_margin="#dimen/layout_margin"
app:strokeWidth="#dimen/strk_width"
app:strokeColor="#color/patronas"
app:cardElevation="0dp"
app:layout_constraintTop_toBottomOf="#id/doc_hmpg_today_stats_cv"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:cardCornerRadius="30dp"
/>
<TextView
android:id="#+id/doc_schedule_hding_tv"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_margin="0dp"
android:background="#drawable/cardviewbg30"
android:gravity="center"
app:layout_constraintTop_toTopOf="#id/doc_schedule_cv"
app:layout_constraintLeft_toLeftOf="#id/doc_schedule_cv"
app:layout_constraintRight_toRightOf="#id/doc_schedule_cv"
android:text="#string/schedule"
android:textSize="#dimen/ts30"
android:textColor="#color/white"
/>
<EditText
android:id="#+id/add_new_evnt_etv"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_margin="#dimen/layout_margin"
android:hint="#string/Edittvschnew"
android:textSize="#dimen/ts20"
android:text="#={editetv.newEventName}"
app:layout_constraintLeft_toLeftOf="#id/doc_schedule_cv"
app:layout_constraintRight_toRightOf="#id/doc_schedule_cv"
app:layout_constraintTop_toBottomOf="#id/doc_schedule_hding_tv"
/>
<TextView
android:id="#+id/timepickertv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="#dimen/layout_margin"
android:background="#drawable/round_cv"
app:layout_constraintLeft_toLeftOf="#id/add_new_evnt_etv"
app:layout_constraintRight_toLeftOf="#id/datepickertv"
app:layout_constraintTop_toBottomOf="#id/add_new_evnt_etv"
android:textColor="#color/black"
android:textColorHint="#color/black"
android:hint="Pick A Time"
android:textSize="#dimen/ts20"
android:text="#={editetv.newEventTime}"
/>
<TextView
android:id="#+id/datepickertv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="#dimen/layout_margin"
android:background="#drawable/round_cv"
app:layout_constraintLeft_toRightOf="#id/timepickertv"
app:layout_constraintRight_toLeftOf="#id/save_evnt_btn"
app:layout_constraintTop_toBottomOf="#id/add_new_evnt_etv"
app:layout_constraintHorizontal_chainStyle="spread"
android:textColor="#color/black"
android:textColorHint="#color/black"
android:hint="Pick A Date"
android:textSize="#dimen/ts20"
android:text="#={editetv.newEventDate}"
/>
<TextView
android:id="#+id/save_evnt_btn"
android:layout_width= "100dp"
android:layout_height="wrap_content"
android:background="#drawable/round_cv"
android:padding="#dimen/layout_margin"
android:gravity="center"
app:layout_constraintRight_toRightOf="#id/add_new_evnt_etv"
app:layout_constraintLeft_toRightOf="#id/datepickertv"
app:layout_constraintTop_toBottomOf="#id/add_new_evnt_etv"
app:layout_constraintHorizontal_chainStyle="spread"
android:text="Save"
android:textSize="#dimen/ts20"
android:textColor="#color/black"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/doc_schedule_rv"
android:layout_width="0dp"
android:layout_height="300dp"
android:layout_margin="#dimen/layout_margin"
app:layout_constraintTop_toBottomOf="#id/timepickertv"
app:layout_constraintLeft_toLeftOf="#id/doc_schedule_cv"
app:layout_constraintRight_toRightOf="#id/doc_schedule_cv"
app:layout_constraintBottom_toBottomOf="#id/doc_schedule_cv"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="#layout/doc_upcoming_events_rv_item"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
LET ME KNOW IF YOU WANT TO KNOW ANYTHING ELSE REGARDING THIS PROBLEM AND SORRY IF BOLD TEXT SOUNDS RUDE :]
I am currently trying to make a app that calculates a loan plan. User plots in data like loan amount, downpayment years, and how much rent the loan should have. I have implemented edit text for the loan amount, two seekbars for years and rent number.
Since I am fairly new to android programing I am nautraly struggling with implementing MVVM to my app. I implemented databinding, viewmodel and wrote the code to link them togehter. I also get the value out if I use Init in viewmodel, but I dont seem to transfer Int value from seekbar to the next fragment. Do anyone has suggestion to my code?
I am gratefull for any help.
In AnnuitetFragment.kt fragment I have only implemented modelView to RenterAnnSeekBar since I use it for testing.
package com.example.lnognedbetalingsplan.annuitet
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.example.lnognedbetalingsplan.R
import com.example.lnognedbetalingsplan.databinding.FragmentAnnuitetBinding
import com.example.lnognedbetalingsplan.viewmodel.AnnuitetViewModel
class AnnuitetFragment : Fragment() {
private var _binding: FragmentAnnuitetBinding? = null
private val binding get() = _binding!!
private lateinit var viewModel: AnnuitetViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
_binding = FragmentAnnuitetBinding.inflate(inflater, container, false)
return binding.root
}
var startPoint = 0
var endPoint = 0
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = this
viewModel = ViewModelProvider(this)[AnnuitetViewModel::class.java]
binding.viewmodel = viewModel
binding.tilbakeTilVelkommen.setOnClickListener {
findNavController().navigate(R.id.action_annuitetFragment_to_velkommenFragment)
}
binding.BeregnLaanAnnuitet.setOnClickListener {
findNavController().navigate(R.id.action_annuitetFragment_to_annuitetPlanFragment)
}
binding.RenterAnnSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
binding.RenterTall.text = progress.toString()
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
if (seekBar != null){
startPoint = seekBar.progress
}
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
if (seekBar != null){
endPoint = seekBar.progress
}
}
})
binding.AarSerieSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
binding.AarTall.text = progress.toString()
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
if (seekBar != null){
startPoint = seekBar.progress
}
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
if (seekBar != null){
endPoint = seekBar.progress
}
}
})
}
}
AnnuitetViewModel.kt is the viewmodel class I am going to use to store livedata so it can share it between AnnuitetFragment.kt and AnnuitetPlanFragment.kt. If this code miss any please help me with some pointers.
package com.example.lnognedbetalingsplan.viewmodel
import android.widget.SeekBar
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
open class AnnuitetViewModel : ViewModel() {
val progress: MutableLiveData<Int> by lazy { MutableLiveData<Int>() }
val renterProgress: LiveData<Int>
get() = progress
init {
progress.value = 1
}
}
My plan AnnuitetPlanFragment.kt is to get data from AnnuitetFragment.kt and calculate the loan plan from user. But when I try to get the data from AnnuitViewModel.kt I get the mock data from Init but there is not data from val progress.
package com.example.lnognedbetalingsplan.annuitet
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.example.lnognedbetalingsplan.R
import com.example.lnognedbetalingsplan.databinding.FragmentAnnuitetBinding
import com.example.lnognedbetalingsplan.databinding.FragmentAnnuitetPlanBinding
import com.example.lnognedbetalingsplan.viewmodel.AnnuitetViewModel
import kotlin.math.pow
class AnnuitetPlanFragment : Fragment() {
private var _binding: FragmentAnnuitetPlanBinding? = null
private val binding get() = _binding!!
private lateinit var viewModel: AnnuitetViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
_binding = FragmentAnnuitetPlanBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = this
viewModel = ViewModelProvider(this).get(AnnuitetViewModel::class.java)
binding.viewmodel = viewModel
viewModel.renterProgress.observe(viewLifecycleOwner, Observer { renteTall->
binding.tvTest.text = renteTall.toString()
})
binding.tilbakeTilAnnuitet.setOnClickListener {
findNavController().navigate(R.id.action_annuitetPlanFragment_to_annuitetFragment)
}
}
}
Last one is my fragment_annuitet.xml where I implemented databinding and seekbar gets progress data and show it in viewCreate.
<layout
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">
<data>
<import type="android.view.View"/>
<variable
name="viewmodel"
type="com.example.lnognedbetalingsplan.viewmodel.AnnuitetViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/annuitet_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".annuitet.AnnuitetFragment">
<SeekBar
android:id="#+id/RenterAnnSeekBar"
android:layout_width="300dp"
android:layout_height="30dp"
android:max="50"
android:min="0"
android:progress="#={viewmodel.progress}"
app:layout_constraintBottom_toTopOf="#+id/AarSerieSeekBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.306"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.84" />
<SeekBar
android:id="#+id/AarSerieSeekBar"
android:layout_width="300dp"
android:layout_height="30dp"
android:max="50"
android:min="0"
android:progress="5"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.306"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
<EditText
android:id="#+id/laanebelop"
android:layout_width="196dp"
android:layout_height="56dp"
android:autofillHints=""
android:ems="10"
android:hint="#string/hint"
android:inputType="numberDecimal"
android:textColorHint="#757575"
android:visibility="visible"
android:text=""
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.641"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.248" />
<TextView
android:id="#+id/Laanebelop"
android:layout_width="103dp"
android:layout_height="40dp"
android:layout_marginTop="184dp"
android:text="#string/laaneBeløp"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.11"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/AarTall"
android:layout_width="35dp"
android:layout_height="30dp"
android:text="#string/_5"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.619"
app:layout_constraintStart_toEndOf="#+id/AarSerieSeekBar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
<TextView
android:id="#+id/RenterTall"
android:layout_width="35dp"
android:layout_height="30dp"
android:text="#={`` + viewmodel.progress}"
android:textSize="20sp"
android:visibility="#{viewmodel.progress != null? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.633"
app:layout_constraintStart_toEndOf="#+id/RenterAnnSeekBar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.383" />
<TextView
android:id="#+id/Rentenummer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/velg_rente"
app:layout_constraintBottom_toTopOf="#+id/RenterAnnSeekBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.097"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Laanebelop"
app:layout_constraintVertical_bias="0.692" />
<TextView
android:id="#+id/antallAar"
android:layout_width="62dp"
android:layout_height="21dp"
android:text="#string/antall_aar"
app:layout_constraintBottom_toTopOf="#+id/AarSerieSeekBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.097"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/RenterAnnSeekBar"
app:layout_constraintVertical_bias="0.666" />
<Button
android:id="#+id/BeregnLaanAnnuitet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="264dp"
android:text="#string/bergen_l_net"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.757"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/AarSerieSeekBar"
app:layout_constraintVertical_bias="1.0" />
<Button
android:id="#+id/tilbakeTilVelkommen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="68dp"
android:text="#string/tilbake"
app:layout_constraintBottom_toTopOf="#+id/laanebelop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<TextView
android:id="#+id/textView2"
android:layout_width="180dp"
android:layout_height="40dp"
android:layout_marginStart="32dp"
android:layout_marginTop="24dp"
android:text="#string/annuitet_l_n"
android:textStyle="bold"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="#+id/Laanebelop"
app:layout_constraintEnd_toStartOf="#+id/tilbakeTilVelkommen"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.066" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
I figured my own question out.
If found out if you share your modelview with the two fragments you want to share data bewteen, and with observer this is possible.
Added this code:
private val AnnuitetViewModel: AnnuitetViewModel by activityViewModels()
Also made observers for every val I wanted to transfer to the next fragment:
AnnuitetViewModel.loanNumber.observe(viewLifecycleOwner){loanNumber->
binding.laanebelop.setText(loanNumber)
}
AnnuitetViewModel.progressRenter.observe(viewLifecycleOwner){progressRenter->
binding.RenterTall.text = progressRenter
}
AnnuitetViewModel.progressAar.observe(viewLifecycleOwner){progressAar->
binding.AarTall.text = progressAar
}
A button listner trigger the transfer to livedata in the viewmodel:
binding.BeregnLaanAnnuitet.setOnClickListener {
AnnuitetViewModel.saveLoanNumber(binding.laanebelop.text.toString())
AnnuitetViewModel.saveProgressRenter(binding.RenterTall.text.toString())
AnnuitetViewModel.saveProgressAar(binding.AarTall.text.toString())
findNavController().navigate(R.id.action_annuitetFragment_to_annuitetPlanFragment)
}
In the viewModel I made some var and val that takes inn the data:
open class AnnuitetViewModel : ViewModel() {
var startPoint = 0
var endPoint = 0
val seekbarRenterProgress: MutableLiveData<Int> by lazy { MutableLiveData<Int>() }
val seekbarAarProgress: MutableLiveData<Int> by lazy { MutableLiveData<Int>() }
private var _loanNumber = MutableLiveData<String>()
val loanNumber: MutableLiveData<String> = _loanNumber
fun saveLoanNumber(newLoanNumber: String){
_loanNumber.value = newLoanNumber
}
private var _progressRenter = MutableLiveData<String>()
val progressRenter: MutableLiveData<String> = _progressRenter
fun saveProgressRenter(newProgressRenter: String){
_progressRenter.value = newProgressRenter
}
private var _progressAar = MutableLiveData<String>()
val progressAar: MutableLiveData<String> = _progressAar
fun saveProgressAar(newProgressAar: String){
_progressAar.value = newProgressAar
}
init {
seekbarRenterProgress.value = 5
seekbarAarProgress.value = 5
}
}
The seekBar and textview look like this in my xml:
<SeekBar
android:id="#+id/AarSerieSeekBar"
android:layout_width="300dp"
android:layout_height="30dp"
android:max="50"
android:min="0"
android:progress="#={viewmodel.seekbarAarProgress}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.306"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
<TextView
android:id="#+id/AarTall"
android:layout_width="35dp"
android:layout_height="30dp"
android:text="#={`` + viewmodel.seekbarAarProgress}"
android:textSize="20sp"
android:visibility="#{viewmodel.seekbarAarProgress != null? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.619"
app:layout_constraintStart_toEndOf="#+id/AarSerieSeekBar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
For test if the second fragment can read the data I wrote this code that observe the data and sends it to a textview that comes up when you run the app in a emulator:
AnnuitetViewModel.loanNumber.observe(viewLifecycleOwner, binding.tvTest::setText)
AnnuitetViewModel.progressRenter.observe(viewLifecycleOwner, binding.tvTest2::setText)
AnnuitetViewModel.progressRenter.observe(viewLifecycleOwner, binding.tvTest3::setText)
I am trying to implement onEditPost and onDeletePost inside of my RecyclerViewFragment:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.MainActivity
import com.example.projectdrivemark.R
import com.example.projectdrivemark.databinding.FragmentListBinding
import com.example.projectdrivemark.databinding.PostBinding
class RecyclerViewFragment: Fragment(R.layout.fragment_list), PostAdapter.OnPostClickListener {
private lateinit var binding: FragmentListBinding
val dummyList = MockDatabase.createMockData()
val adapter = PostAdapter(dummyList, this)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreate(savedInstanceState)
binding = FragmentListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<RecyclerView>(R.id.recycleViewMain).apply{
layoutManager = LinearLayoutManager(requireActivity())
adapter = PostAdapter(dummyList, this#RecyclerViewFragment)
}
}
override fun onEditPost(position: Int){
val clickedPost = dummyList[position]
clickedPost.title = "Updated title"
clickedPost.body = "Updated body"
adapter.notifyItemChanged(position)
}
override fun onDeletePost(position: Int) {
dummyList.removeAt(position)
adapter.notifyItemRemoved(position)
}
}
Here is how my Adapter looks like:
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.MainActivity
import com.example.projectdrivemark.R
class PostAdapter(val dummyData: ArrayList<Post>, val myListener: OnPostClickListener) : RecyclerView.Adapter<PostAdapter.PostViewHolder>() {
inner class PostViewHolder(postView: View) : RecyclerView.ViewHolder(postView), View.OnClickListener{
val iconImage: ImageView = postView.findViewById(R.id.icon_image_view)
val title: TextView = postView.findViewById(R.id.title)
val body: TextView = postView.findViewById(R.id.body)
val deleteIcon: ImageView = postView.findViewById(R.id.delete_post_image)
val editIcon: ImageView = postView.findViewById(R.id.edit_post_image)
init {
deleteIcon.setOnClickListener(this)
editIcon.setOnClickListener(this)
}
override fun onClick(v: View?){
val position = adapterPosition
if(v?.id == editIcon.id){
myListener.onEditPost(position)
}else{
myListener.onDeletePost(position)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostViewHolder {
val postView = LayoutInflater.from(parent.context).inflate(R.layout.post, parent, false)
return PostViewHolder(postView)
}
override fun onBindViewHolder(holder: PostViewHolder, position: Int) {
val currentPost = dummyData[position]
holder.iconImage.setImageResource(currentPost.image)
holder.title.text = currentPost.title
holder.body.text = currentPost.body
}
override fun getItemCount(): Int {
return dummyData.size
}
interface OnPostClickListener{
fun onEditPost(position: Int)
fun onDeletePost(position: Int)
}
}
And here is how my XML File Post looks like:
<?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="wrap_content"
android:layout_marginBottom="8dp"
android:padding="5dp">
<ImageView
android:id="#+id/icon_image_view"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:src="#drawable/ic_baseline_ac_unit"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginBottom="8dp"
android:text="Title of blog post"
android:textColor="#android:color/black"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="#+id/textView2"
app:layout_constraintStart_toEndOf="#+id/icon_image_view"
app:layout_constraintTop_toTopOf="#+id/icon_image_view" />
<TextView
android:id="#+id/body"
android:layout_width="285dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="Body of first blog post"
app:layout_constraintBottom_toBottomOf="#+id/icon_image_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.050"
app:layout_constraintStart_toEndOf="#+id/icon_image_view" />
<LinearLayout
android:id="#+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="end">
<ImageView
android:id="#+id/edit_post_image"
android:layout_width="60dp"
android:layout_height="50dp"
android:src="#drawable/ic_baseline_edit" />
<ImageView
android:id="#+id/delete_post_image"
android:layout_width="60dp"
android:layout_height="50dp"
android:src="#drawable/ic_baseline_delete_forever" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
As you see in the RecyclerViewFragment, I have 2 functions that is onEditPost and onDeletePost. I want that functionality to work when I press it. Right now, it only works when I press and change the fragment view. I want it to work instantly. If anyone see my mistake or Am I missing something? please do tell me. All help is appreciate
You are creating Adapter twice, you already have an Adapter initialised globally use it in onViewCreated instead of assign again. because you are using global adapter to notify changes.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<RecyclerView>(R.id.recycleViewMain).apply{
layoutManager = LinearLayoutManager(requireActivity())
adapter = this#RecyclerViewFragment.adapter
}
}
I have 3 fragments here I show them, they have a TextView with the id omitir_swipe, and I need to use it in the activity where I show those 3 fragments, how would I achieve it?:
3 fragments xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#color/colorWhite"
>
<ImageView
android:id="#+id/image_swipe"
android:layout_width="200dp"
android:layout_height="200dp"
android:src="#drawable/books_swipe"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.246"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.329"
/>
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="116dp"
android:text="Amantium irae amoris integratio est."
android:textAlignment="center"
android:textColor="#color/colorBlack"
android:textSize="16dp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="#+id/image_swipe"
app:layout_constraintEnd_toEndOf="#+id/image_swipe"
app:layout_constraintHorizontal_bias="0.465"
app:layout_constraintStart_toStartOf="#+id/image_swipe" />
<TextView
android:id="#+id/omitir_swipe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Omitir"
android:textSize="16sp"
android:textStyle="bold"
android:layout_marginRight="30dp"
android:textColor="#color/colorBlack"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintBottom_toBottomOf="#+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/textView"
app:layout_constraintTop_toTopOf="#+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>
The 3 are the same so I only upload 1, they have the same id, to only use 1, I don't know if this is good, could you tell me whether to change it or not
3 Fragments .kt kotlin:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.yr.iolite.R
class SwipeFragement1 : Fragment()
{
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_splash_screen_1, container, false)
}
}
in the 3 only change this number fragment_splash_screen_change this number
The activity where I show the 3 fragments 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=".Splash.SplashActivity">
<ImageView
android:id="#+id/bg_snow_splash"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="#drawable/bg_snow"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<ImageView
android:id="#+id/logo_splash"
android:layout_width="320dp"
android:layout_height="200dp"
android:layout_marginTop="20dp"
android:src="#drawable/logo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0"
/>
<com.airbnb.lottie.LottieAnimationView
android:id="#+id/penguin_splash"
android:layout_width="250dp"
android:layout_height="300dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.391"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/logo_splash"
app:layout_constraintVertical_bias="0"
app:lottie_autoPlay="true"
app:lottie_loop="true"
app:lottie_rawRes="#raw/party_penguin"
/>
<com.cuberto.liquid_swipe.LiquidPager
android:id="#+id/swipe_screen"
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"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
The .kt from the previous activity:
import android.content.Intent
import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.AlarmClock.EXTRA_MESSAGE
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.ImageView
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.viewpager.widget.ViewPager
import com.airbnb.lottie.LottieAnimationView
import com.yr.iolite.MainActivity
import com.yr.iolite.R
import com.yr.iolite.WriteProblem.MinOrMax
import kotlinx.android.synthetic.main.activity_splash.*
import kotlinx.android.synthetic.main.fragment_splash_screen_1.*
class SplashActivity : AppCompatActivity()
{
var bg: ImageView? = null
var logo: ImageView? = null
var penguin: LottieAnimationView? = null
var anim: Animation? = null
private var pagerAdapter : ScreenSliderPagerAdapter? = null
private val SPLASH_TIME_OUT : Int = 5500
private var sharedPref : SharedPreferences ? = null
private var omitir_swipe : TextView? = null
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
bg = findViewById(R.id.bg_snow_splash)
logo = findViewById(R.id.logo_splash)
penguin = findViewById(R.id.penguin_splash)
omitir_swipe = findViewById(R.id.omiti_swipe)
val viewPager : ViewPager = findViewById(R.id.swipe_screen)
pagerAdapter = ScreenSliderPagerAdapter(supportFragmentManager)
viewPager.adapter = pagerAdapter
anim = AnimationUtils.loadAnimation(this, R.anim.swipe_screen_anim)
viewPager.startAnimation(anim)
bg_snow_splash.animate().apply {
duration = 1000
startDelay = 4000
translationY(-1600f)
}
logo_splash.animate().apply {
duration = 1000
startDelay = 4000
translationY(-1400f)
}
penguin_splash.animate().apply {
duration = 1000
startDelay = 4000
translationY(1400f)
}
Handler(Looper.getMainLooper()).postDelayed({
var sharedPref= getSharedPreferences("SharedPref", MODE_PRIVATE)
var isFirstTime : Boolean = sharedPref.getBoolean("firstTime", true)
if(isFirstTime)
{
var editor : SharedPreferences.Editor = sharedPref.edit()
editor.putBoolean("firstTime", false)
editor.commit()
}
else
{
val intent = Intent(this, MinOrMax::class.java).apply {
putExtra(EXTRA_MESSAGE, "Intento con exito")
}
startActivity(intent)
finish()
}
}, 5000)
/*findViewById<TextView>(R.id.omitir_swipe).setOnClickListener{
val intent = Intent(this, MainActivity::class.java).apply {
putExtra(EXTRA_MESSAGE, "xd")
}
startActivity(intent)
finish()
}*/
}
private class ScreenSliderPagerAdapter (fm : FragmentManager) : FragmentStatePagerAdapter(fm)
{
val NUM_TABS = 3
override fun getItem(position: Int): Fragment
{
return when (position)
{
0 -> SwipeFragement1()
1 -> SwipeFragement2()
else -> SwipeFragement3()
}
}
override fun getCount(): Int = NUM_TABS
}
}
I don't have any errors but when I uncomment the setOnClickListener it doesn't work it gives me a null pointer exception error, and that is because the id of the omit_swipe is not in the activity xml as you can see above instead it is in the fragments.
You can start a new activity and finish the current from the fragment.
class SwipeFragement1 : Fragment(){
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_splash_screen_1, container, false)
view.findViewById<TextView>(R.id.omitir_swipe).setOnClickListener{
val intent = Intent(activity, MainActivity::class.java).apply {
putExtra(EXTRA_MESSAGE, "xd")
}
activity.startActivity(intent)
activity.finish()
}
return view
}
}
I'm using Tmdb API and displaying it in a RecyclerView. The RecyclerView is not scrolling smoothly initially but then is working fine. I've tried to change Recyclerview height to 0dp or match_parent but it's still the same. I've tried also android:nestedScrollingEnabled="true". Please help me to fix this problem in the RecyclerView. Thank you in advance.
fragment_add.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.AddFragment">
<androidx.appcompat.widget.AppCompatAutoCompleteTextView
android:id="#+id/fragment_add_movieSeriesACT"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:background="#drawable/edit_text_background"
android:hint="#string/movie_or_series"
android:imeOptions="actionDone"
android:inputType="textEmailAddress"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="#color/colorPrimary"
android:textColorHint="#color/colorEight"
android:textCursorDrawable="#null"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/fragment_add_mainRV"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/fragment_add_movieSeriesACT" />
<ProgressBar
android:id="#+id/fragment_add_mainPB"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:indeterminateTint="#color/colorOne"
android:indeterminateTintMode="src_atop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
AddFragment.kt
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import com.martiandeveloper.muuvi.R
import com.martiandeveloper.muuvi.adapter.RecyclerViewMovieAdapter
import com.martiandeveloper.muuvi.viewmodel.AddViewModel
import kotlinx.android.synthetic.main.fragment_add.*
class AddFragment : Fragment() {
private lateinit var viewModel: AddViewModel
private val adapter = RecyclerViewMovieAdapter(arrayListOf())
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_add, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initUI()
}
private fun initUI() {
viewModel = ViewModelProviders.of(this).get(AddViewModel::class.java)
setRecyclerView()
observe()
viewModel.refreshData()
}
private fun setRecyclerView() {
fragment_add_mainRV.layoutManager = LinearLayoutManager(context)
fragment_add_mainRV.adapter = adapter
}
private fun observe() {
viewModel.movieList.observe(viewLifecycleOwner, Observer { movieList ->
movieList?.let {
adapter.updateMovieList(it)
}
})
viewModel.isError.observe(viewLifecycleOwner, Observer { isError ->
isError?.let {
setProgress(1F, true, View.GONE)
if (it) {
setToast(resources.getString(R.string.something_went_wrong_please_try_again_later))
}
}
})
viewModel.isLoading.observe(viewLifecycleOwner, Observer { isLoading ->
isLoading?.let {
if (it) {
setProgress(.5F, false, View.VISIBLE)
} else {
setProgress(1F, true, View.GONE)
}
}
})
}
private fun setProgress(alpha: Float, enable: Boolean, visible: Int) {
fragment_add_movieSeriesACT.alpha = alpha
fragment_add_mainRV.alpha = alpha
fragment_add_movieSeriesACT.isEnabled = enable
fragment_add_mainPB.visibility = visible
}
private fun setToast(text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
}
AddViewModel.kt
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.martiandeveloper.muuvi.model.MovieResult
import com.martiandeveloper.muuvi.model.Movie
import com.martiandeveloper.muuvi.service.MovieService
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.observers.DisposableSingleObserver
import io.reactivex.schedulers.Schedulers
class AddViewModel : ViewModel() {
private val movieService = MovieService()
private val disposable = CompositeDisposable()
val movieList = MutableLiveData<List<Movie>>()
val isError = MutableLiveData<Boolean>()
val isLoading = MutableLiveData<Boolean>()
fun refreshData() {
isLoading.value = true
disposable.add(
movieService.getData().subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<MovieResult>() {
override fun onSuccess(t: MovieResult) {
movieList.value = t.results
isError.value = false
isLoading.value = false
}
override fun onError(e: Throwable) {
isError.value = true
isLoading.value = false
}
})
)
}
}
RecyclerViewMovieAdapter.kt
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.martiandeveloper.muuvi.R
import com.martiandeveloper.muuvi.model.Movie
import kotlinx.android.synthetic.main.recyclerview_movie_item.view.*
class RecyclerViewMovieAdapter(private val movieList: ArrayList<Movie>) :
RecyclerView.Adapter<RecyclerViewMovieAdapter.RecyclerViewMovieViewHolder>() {
lateinit var context: Context
class RecyclerViewMovieViewHolder(var view: View) : RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RecyclerViewMovieViewHolder {
context = parent.context
val view = LayoutInflater.from(context)
.inflate(R.layout.recyclerview_movie_item, parent, false)
return RecyclerViewMovieViewHolder(view)
}
override fun getItemCount(): Int {
return movieList.size
}
override fun onBindViewHolder(holder: RecyclerViewMovieViewHolder, position: Int) {
holder.view.recyclerview_movie_item_movieTitleMTV.text =
movieList[position].movieTitle
holder.view.recyclerview_movie_item_movieVoteAverageMTV.text =
movieList[position].movieVoteAverage.toString()
Glide.with(context)
.load("https://image.tmdb.org/t/p/w300${movieList[position].moviePosterPath}")
.into(holder.view.recyclerview_movie_item_moviePosterIV)
}
fun updateMovieList(newMovieList: List<Movie>) {
movieList.clear()
movieList.addAll(newMovieList)
notifyDataSetChanged()
}
}
recyclerview_movie_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:gravity="center_vertical"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:paddingBottom="8dp">
<androidx.cardview.widget.CardView
android:layout_width="80dp"
android:layout_height="80dp"
app:cardBackgroundColor="#android:color/transparent"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<ImageView
android:id="#+id/recyclerview_movie_item_moviePosterIV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="#string/app_name"
android:scaleType="centerCrop"
app:srcCompat="#drawable/default_user_image" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="#+id/recyclerview_movie_item_movieTitleMTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/colorPrimary"
android:textSize="18sp"
android:textStyle="bold" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/recyclerview_movie_item_movieVoteAverageMTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/colorPrimaryDark" />
</LinearLayout>
</LinearLayout>
I believe this is due to size of the image , I had similar issue try to fit the image you obtain using glide into imageview, I have added centerCrop():
Glide.with(context).load("https://image.tmdb.org/t/p/w300${movieList[position].moviePosterPath}") .centerCrop().into(holder.view.recyclerview_movie_item_moviePosterIV);
If this does not work try to change imageview scaletype or centerCrop() to fitCenter()