I am trying to pass data from one fragment to another but I am facing issue with sending data from parcelable from one fragment to another.
class MainFragment : Fragment() {
companion object {
public val KEY_PARSE_DATA = "parseData"
}
private var parseData: ParseData? = null
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater!!.inflate(R.layout.fragment_main, container, false).apply {
val editName = findViewById(R.id.edit_Name) as EditText
val editSurname = findViewById(R.id.edit_Surname) as EditText
val buttonNext = findViewById(R.id.btn_Next) as Button
buttonNext.setOnClickListener(View.OnClickListener {
val fragment = AnotherFragment()
if (parseData != null) {
var parseData = ParseData(editName.text.toString(), editSurname.text.toString())
val fragment = AnotherFragment()
val bundle = Bundle()
bundle.putParcelable(KEY_PARSE_DATA, parseData)
fragment.setArguments(bundle)
fragmentManager.beginTransaction().add(R.id.fragment_container, fragment).commitAllowingStateLoss()
}
})
}
}
}// Required empty public constructor
Parcelable class for implementing it
#SuppressLint("ParcelCreator")
#Parcelize
data class ParseData(val firstName: String, val lastName: String) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString()) {
}
override fun toString(): String {
return "ParseData(firstName='$firstName', lastName='$lastName')"
}
companion object : Parceler<ParseData> {
override fun ParseData.write(parcel: Parcel, flags: Int) {
parcel.writeString(firstName)
parcel.writeString(lastName)
}
override fun create(parcel: Parcel): ParseData {
return ParseData(parcel)
}
}
}
And another fragment which grab data from parcelable class in android
class AnotherFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater!!.inflate(R.layout.fragment_another, container, false).apply {
val textName = findViewById<TextView>(R.id.textFirst)
val textSurname = findViewById<TextView>(R.id.textSecond)
val bundle = arguments
if (bundle != null) {
val parseData = bundle.getParcelable<ParseData>(KEY_PARSE_DATA)
textName.setText(parseData.firstName)
textSurname.setText(parseData.lastName)
}
}
}
}
I tried some example but I cant get clear idea how parcelable is implemented in andoid application based on kotlin and build.gradle file
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.assignment.ankitt.kotlinsecond"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
androidExtensions {
experimental = true
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation 'com.android.support:support-v4:26.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
Add below code into your App Level gradle.
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
androidExtensions {
experimental = true
}
Now it is enough to add 'kotlin-parcelize' plugin.
So for example:
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-parcelize'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
//
androidExtensions {
experimental = true
}
}
The order of plugin matters. kotlin-android must be before kotlin-android-extension. Then clean project.
Unable to add kotlin android extension
I'd recommend taking a look at the #Parcelize annotation provided with the Kotlin Android Extensions, it generates all necessary boilerplate for you so you don't have to write and maintain that code yourself.
Need to add these two in your app level Gradle
apply plugin: 'kotlin-android-extensions'
androidExtensions {
experimental = true`
}
Kotlin plugin should be enabled before 'kotlin-android-extensions'
so change the order to
apply plugin: 'com.android.application' #1
apply plugin: 'kotlin-android' #2
apply plugin: 'kotlin-android-extensions' #3
Hello All I had solved this question and there was issue with fragment transaction and parcelable
MainActivity to setup fragment
class MainActivity : AppCompatActivity() {
val manager = supportFragmentManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val fragment = DetailFragment()
manager.findFragmentById(R.id.fragmentContainer)
manager.beginTransaction().add(R.id.fragmentContainer, fragment).commit()
}
}
First fragment to fill data
class DetailFragment : Fragment(), View.OnClickListener {
var editTextName: EditText? = null
var editTextLast: EditText? = null
var buttonNextFragment: Button? = null
var firstName: String? = null
var lastName: String? = null
companion object {
val KEY_PARSE_DATA = "detailData"
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view: View = inflater!!.inflate(R.layout.fragment_detail, container, false)
editTextName = view.findViewById(R.id.ed_firstName) as EditText
editTextLast = view.findViewById(R.id.ed_lastName) as EditText
buttonNextFragment = view.findViewById(R.id.btn_nextfragment) as Button
buttonNextFragment!!.setOnClickListener(this)
return view
}
override fun onClick(v: View?) {
firstName = editTextName!!.text.toString()
lastName = editTextLast!!.text.toString()
Toast.makeText(context, firstName, Toast.LENGTH_SHORT).show()
// val viewFragment = ViewFragment()
// val transaction = fragmentManager.beginTransaction()
// transaction.replace(R.id.fragmentContainer, viewFragment)
// transaction.commit()
var details = Details(firstName!!, lastName!!)
val viewFragment = ViewFragment()
val bundle = Bundle()
bundle.putParcelable(KEY_PARSE_DATA, details)
viewFragment.setArguments(bundle)
val transaction = fragmentManager.beginTransaction()
transaction.replace(R.id.fragmentContainer, viewFragment)
transaction.commit()
}
}
Display data coming from parcelable
class ViewFragment : Fragment() {
var textViewName: TextView? = null
var textViewLastName: TextView? = null
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view: View = inflater!!.inflate(R.layout.fragment_view, container, false)
textViewName = view.findViewById(R.id.text_name_another) as TextView
textViewLastName = view.findViewById(R.id.text_surname_another) as TextView
val bundle = arguments
if (bundle != null) {
val details = bundle.getParcelable<Details>(KEY_PARSE_DATA)
textViewName!!.setText(details.firstName)
textViewLastName!!.setText(details.lastName)
}
return view
}
}
Model class for implementation of parcelable
#Parcelize
class Details(val firstName: String, val lastName: String) : Parcelable
and my gradle file
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.assignment.ankitt.kotlintwo"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
androidExtensions {
experimental = true
}
}
Related
to describe the problem simply, i have main activity where it has viewPager2 which displays 2 fragments. The main activity also obtains a toolbar with search MenuOptions. i am trying to search artist therefore it will automatically update the recycler view in the first fragment (videoCallHomefragment). i don't know where is the problem as this is my 3rd day in debugging this matter and i still ended up nowhere.
my assumptions
there must be conflict because i am using viewpager2 and trying to update recycler view in a fragment from an activity
dependencies versioning conflict that results in "notifyDataSetChanged()" not working, on the other hand, i tried an alternative using DiffUtil and still not working
p.s. the app successfully retrieves artists and displays them in recycler view which indicated that the function "setData" in the adapter is working, but when searching for artist, the "onBindViewHolder" and "onCreateViewHolder" doesn't get initiated or called when calling notifyDataSetChanged()
See Below code snippets
MainActivity
#AndroidEntryPoint
class MainActivity : AppCompatActivity(), ArtistsAdapter.OnArtistListener {
private lateinit var binding: ActivityMainBinding
private val mAdapter by lazy { ArtistsAdapter(this) }
private val mainViewModel: MainViewModel by viewModels()
private lateinit var viewPager2:ViewPager2
private lateinit var fragement: Fragment
private lateinit var artists: List<Artist>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
fragement = supportFragmentManager.findFragmentById(R.id.videoCallHomeFragment)!!
setSupportActionBar(findViewById(R.id.toolbar))
val tabLayout = findViewById<TabLayout>(R.id.tab_layout)
viewPager2 = findViewById(R.id.view_pager_2)
val adapter = ViewPagerAdapter(supportFragmentManager, lifecycle)
viewPager2.adapter = adapter
TabLayoutMediator(tabLayout, viewPager2){tab, postion ->
when(postion) {
0 -> {
tab.text="Artists"
}
1 -> {
tab.text = "Voice Calls"
}
}
}.attach()
viewPager2.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback(), SearchView.OnQueryTextListener {
#SuppressLint("ResourceType")
override fun onPageSelected(position: Int) {
if(position == 0)
{
val toolbar = findViewById<Toolbar>(R.id.mainToolbar)
toolbar.inflateMenu(R.menu.search_menu)
val search = toolbar.menu.findItem(R.id.menu_search)
val searchView = search?.actionView as? SearchView
searchView?.isSubmitButtonEnabled = true
searchView?.setOnQueryTextListener(this)
} else {
val toolbar = findViewById<Toolbar>(R.id.mainToolbar)
toolbar.menu.clear()
}
}
#SuppressLint("NotifyDataSetChanged")
override fun onQueryTextSubmit(query: String?): Boolean {
if(query != null){
// recyclerView.adapter = mAdapter
// recyclerView.layoutManager = LinearLayoutManager(applicationContext, LinearLayoutManager.VERTICAL, false)
searchArtist(query)
mAdapter.notifyDataSetChanged()
}
return true
}
#SuppressLint("NotifyDataSetChanged")
override fun onQueryTextChange(newText: String?): Boolean {
if(newText != null){
// recyclerView.adapter = mAdapter
// recyclerView.layoutManager = LinearLayoutManager(applicationContext, LinearLayoutManager.VERTICAL, false)
searchArtist(newText)
mAdapter.notifyDataSetChanged()
}
return true
}
})
requestPermissions()
val toolbar = findViewById<Toolbar>(R.id.mainToolbar)
toolbar.setBackgroundColor(Color.rgb(32, 4, 209))
toolbar.setTitleTextColor(Color.WHITE)
toolbar.setTitle(R.string.app_name)
}
private fun requestPermissions()
{
if (PermissionRequestUtil.hasCameraPermissions(this)) {
return
} else {
EasyPermissions.requestPermissions(
this,
"Please accept camera permissions to use this app.",
0,
Manifest.permission.CAMERA
)
}
}
#SuppressLint("NotifyDataSetChanged")
private fun searchArtist(query: String)
{
val searchQuery = "%$query%"
lifecycle.coroutineScope.launch {
mainViewModel.searchArtist(searchQuery).collect { it ->
mAdapter.setData(it)
mAdapter.notifyDataSetChanged()
Log.i(TAG, "new artist List set!!")
}
}
}
override fun onArtistClick(artist: Artist, position: Int) {
TODO("Not yet implemented")
}
}
VideoCallHomeFragment
package com.example.awfc.ui
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.*
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.coroutineScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.awfc.R
import com.example.awfc.adapters.ArtistsAdapter
import com.example.awfc.data.Artist
import com.example.awfc.viewmodels.MainViewModel
import com.todkars.shimmer.ShimmerRecyclerView
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
#AndroidEntryPoint
class VideoCallHomeFragment : Fragment() , ArtistsAdapter.OnArtistListener {
private lateinit var mainViewModel: MainViewModel
private val mAdapter by lazy { ArtistsAdapter(this)}
private lateinit var mView: View
private lateinit var artists: List<Artist>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
mView = inflater.inflate(R.layout.fragment_video_call_home, container, false)
setHasOptionsMenu(true)
//val shimmer = mView.findViewById<ShimmerRecyclerView>(R.id.recycler_view)
//shimmer.showShimmer()
setupRecyclerView()
mainViewModel = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)
lifecycle.coroutineScope.launch {
mainViewModel.getArtists().collect {
artists = it
mAdapter.setData(it)
//shimmer.hideShimmer()
}
}
return mView
}
#SuppressLint("CutPasteId")
fun setupRecyclerView()
{
mView.findViewById<RecyclerView>(R.id.recycler_view).adapter = mAdapter
mView.findViewById<RecyclerView>(R.id.recycler_view).layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
}
override fun onArtistClick(artist: Artist, position: Int) {
artists[position]
val intent = Intent(this.context, ArtistDetailsActivity::class.java)
intent.putExtra("artistName", artist.name)
intent.putExtra("arabicName", artist.name_arabic)
intent.putExtra("arabicDesc", artist.description_arabic)
intent.putExtra("artistDesc", artist.description)
intent.putExtra("artistImage", artist.image)
intent.putExtra("artistVideo1", artist.videoUrl1)
intent.putExtra("artistVideo2", artist.videoUrl2)
intent.putExtra("artistVideo3", artist.videoUrl3)
startActivity(intent)
}
}
Artist Adapter
package com.example.awfc.adapters
import android.annotation.SuppressLint
import android.content.ContentValues.TAG
import android.content.Intent
import android.service.autofill.OnClickAction
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.example.awfc.data.Artist
import com.example.awfc.databinding.ArtistRowLayoutBinding
import com.example.awfc.utils.ArtistsDiffUtil
class ArtistsAdapter(var artistListener: OnArtistListener) : RecyclerView.Adapter<ArtistsAdapter.MyViewHolder>() {
private var artists = emptyList<Artist>()
class MyViewHolder(private val binding: ArtistRowLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
fun init(artist: Artist, action: OnArtistListener)
{
itemView.setOnClickListener {
action.onArtistClick(artist, adapterPosition)
}
}
fun bind(modelClass: Artist) {
binding.result = modelClass
binding.executePendingBindings()
}
companion object {
fun from(parent: ViewGroup): MyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ArtistRowLayoutBinding.inflate(layoutInflater, parent, false)
return MyViewHolder(binding)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
Log.i(TAG, "OnCreateViewHolder initiated")
return MyViewHolder(
ArtistRowLayoutBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentResult = artists[position]
holder.bind(currentResult)
holder.init(currentResult, artistListener)
Log.i(TAG, "OnBindViewHOlder initiated")
}
override fun getItemCount(): Int {
return artists.size
}
#SuppressLint("NotifyDataSetChanged")
fun setData(newData: List<Artist>) {
val artistsDiffUtil = ArtistsDiffUtil(artists, newData)
val diffUtilResult = DiffUtil.calculateDiff(artistsDiffUtil)
artists = emptyList()
artists = newData
diffUtilResult.dispatchUpdatesTo(this)
this.notifyDataSetChanged()
//this.notifyDataSetChanged()
}
interface OnArtistListener {
fun onArtistClick(artist:Artist, position: Int)
}
}
artistDiffUtil
package com.example.awfc.utils
import androidx.recyclerview.widget.DiffUtil
class ArtistsDiffUtil<T>(
private val oldList: List<T>,
private val newList: List<T>
): DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] === newList[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}
gradleFile
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
id 'kotlin-parcelize'
id 'androidx.navigation.safeargs'
}
android {
compileSdk 31
buildFeatures {
viewBinding true
dataBinding true
}
defaultConfig {
applicationId "com.example.awfc"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments += ["room.schemaLocation":
"$projectDir/schemas".toString()]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
def room_version = "2.4.2"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
implementation "com.google.dagger:hilt-android:2.28.3-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28.3-alpha"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02"
kapt "androidx.hilt:hilt-compiler:1.0.0-alpha02"
implementation 'com.facebook.shimmer:shimmer:0.5.0'
implementation 'com.todkars:shimmer-recyclerview:0.4.1'
implementation 'de.hdodenhof:circleimageview:3.1.0'
// Image Loading library Coil
implementation "io.coil-kt:coil:0.13.0"
implementation 'com.squareup.picasso:picasso:2.71828'
// UI Tests
androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.1.1'
// When using a AppCompat theme
implementation "com.google.accompanist:accompanist-appcompat-theme:0.16.0"
implementation "com.google.android.material:compose-theme-adapter:1.1.1"
// Lifecycle
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
implementation 'com.yqritc:android-scalablevideoview:1.0.4'
implementation 'com.github.bumptech.glide:glide:4.11.0'
kapt 'com.github.bumptech.glide:compiler:4.11.0'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation("javax.inject:javax.inject:1")
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// viewpager2
implementation 'androidx.viewpager2:viewpager2:1.0.0'
//tablayout
implementation 'com.google.android.material:material:1.3.0-alpha04'
// For developers using AndroidX in their applications
implementation 'pub.devrel:easypermissions:3.0.0'
}
You're creating two separate instances of ArtistsAdapter in MainActivity and VideoCallHomeFragment. It sounds like the adapter works fine if setData is working inside the fragment that's actually displaying the RecyclerView, because that's the one that has access to the actual adapter that's displaying the data.
But in searchArtist in MainActivity, you're calling setData on a completely different adapter instance that isn't connected to the RecyclerView in any way, so nothing's gonna happen.
Instead of having the activity trying to talk to a widget hosted in a fragment, it would be better to have a LiveData or similar in your view model that contains the data that's supposed to be displayed. Make VideoCallHomeFragment observe that, and it can call setData on the adapter. Your activity can call viewModel.getArtists() or whatever, but that function should internally update the LiveData so that anything observing it will see the new data to display.
Here is my build.gradle
dependencies {
//viewmodel
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
//noinspection GradleDependency
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
implementation 'androidx.activity:activity-ktx:1.4.0'
implementation 'androidx.fragment:fragment-ktx:1.4.1'
}
viewModel
class AlarmViewModel(application: Application) : AndroidViewModel(application) {
private var repository : AlarmRepository = AlarmRepository(application)
var list : LiveData<List<Alarm>> = repository.list
}
Fragment
class AlarmListFragment : Fragment() {
private lateinit var viewModel: AlarmViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentAlarmListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(requireActivity(), ViewModelProvider.AndroidViewModelFactory.getInstance(activity!!.application)).get(AlarmViewModel::class.java)
}
}
}
Searching on google and trying to fix but it doesn't work
plz help me :<
I need ur helpe!!!
Android Studio 3.6
build.gradle:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: "kotlin-kapt"
android {
dataBinding {
enabled = true
}
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.myproject"
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
Now in Activity I can use this:
import kotlinx.android.synthetic.main.event_details_activity.*
class EventDetailsActivity : AppCompatActivity() {
private lateinit var dataBinding: EventDetailsActivityBinding
companion object {
val EVENT = EventDetailsActivity.javaClass.canonicalName + "_EVENT"
private val TAG = EventDetailsActivity::class.java.name
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dataBinding =
DataBindingUtil.setContentView(this, R.layout.event_details_activity)
// THIS WORK FINE
eventTitleTextView.text = "Test"
Nice.
Now I want to use Kotlin extension in Adapter.
I try this:
import kotlinx.android.synthetic.main.event_list_item.*
class EventAdapter(
context: Context?,
data: List<*>?
) : DataBindingRecyclerViewAdapter(context, data) {
private var listener: AdapterListener? = null
override fun onBindViewHolder(
holder: RecyclerView.ViewHolder,
position: Int
) {
super.onBindViewHolder(holder, position)
descriptionTextView.text = "TEST"
}
Here event_list_item.xml
<TextView
android:id="#+id/descriptionTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#{model.description}"
android:textColor="#android:color/darker_gray"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#+id/titleTextView"
app:layout_constraintStart_toStartOf="#+id/titleTextView"
app:layout_constraintTop_toBottomOf="#+id/titleTextView" />
java file:
public abstract class DataBindingRecyclerViewAdapter extends RecyclerView.Adapter {
But I get error:
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public val Activity.descriptionTextView: TextView! defined in kotlinx.android.synthetic.main.event_list_item
public val Dialog.descriptionTextView: TextView! defined in kotlinx.android.synthetic.main.event_list_item
public val android.app.Fragment.descriptionTextView: TextView! defined in kotlinx.android.synthetic.main.event_list_item
public val androidx.fragment.app.Fragment.descriptionTextView: TextView! defined in kotlinx.android.synthetic.main.event_list_item
I have the same problem and i solved it by add this into gradle build file
androidExtensions {
experimental = true
}
Android studio 3.6
build.gradle:
android {
dataBinding {
enabled = true
}
// Configure only for each module that uses Java 8
// language features (either in its source code or
// through dependencies). E.g. lambda expressions.
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.project.client"
minSdkVersion 23
targetSdkVersion 29
versionCode 80
versionName "0.0.80"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
// use e.g. in my custom RecyclerView.Adapter
//apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'io.fabric'
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
Here my RecyclerView adaptder:
class GazStationAdapter(newActivity: Activity, private val data: List<String>) :
RecyclerView.Adapter<GazStationAdapter.ViewHolder>() {
var activity: Activity? = null
init {
this.activity = newActivity
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val v = inflater.inflate(R.layout.gazstation_item_service_card, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val serivceURL = "http://www.gravatar.com/avatar/11111?s=40x40&d=identicon"
Debug.d("TAG", "onBindViewHolder: serivceURL = $serivceURL")
Glide.with(holder.itemView.context)
.load(serivceURL)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
Debug.d("TAG", "onBindViewHolder: glide_load_onLoadFailed")
return false;
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
Debug.d("TAG", "onBindViewHolder: glide_load_ready")
return false
}
})
.placeholder(R.drawable.profile)
.into(holder.image)
}
override fun getItemCount(): Int {
return data.size
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val image: ImageView
init {
image = itemView.findViewById<View>(R.id.gazStaionServiceimage) as ImageView
}
}
}
here gazstation_item_service_card.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:layout_margin="4dp"
android:orientation="vertical">
<ImageView
android:id="#+id/gazStaionServiceimage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
</LinearLayout>
But image not load by url
Here logcat:
D TAG : onBindViewHolder: serivceURL = http://www.gravatar.com/avatar/11111?s=40x40&d=identicon
W Glide : Failed to find GeneratedAppGlideModule. You should include an annotationProcessor compile dependency on com.github.bumptech.glide:compiler in your application and a #GlideModule annotated AppGlideModule implementation or LibraryGlideModules will be silently ignored
D TAG : onBindViewHolder: serivceURL = http://www.gravatar.com/avatar/11111?s=40x40&d=identicon
D TAG : onBindViewHolder: serivceURL = http://www.gravatar.com/avatar/11111?s=40x40&d=identicon
D TAG : onBindViewHolder: serivceURL = http://www.gravatar.com/avatar/11111?s=40x40&d=identicon
D TAG : onBindViewHolder: serivceURL = http://www.gravatar.com/avatar/11111?s=40x40&d=identicon
D TAG : onBindViewHolder: serivceURL = http://www.gravatar.com/avatar/11111?s=40x40&d=identicon
D TAG : onBindViewHolder: serivceURL = http://www.gravatar.com/avatar/11111?s=40x40&d=identicon
D TAG : onBindViewHolder: serivceURL = http://www.gravatar.com/avatar/11111?s=40x40&d=identicon
D TAG : onBindViewHolder: serivceURL = http://www.gravatar.com/avatar/11111?s=40x40&d=identicon
D TAG : onBindViewHolder: serivceURL = http://www.gravatar.com/avatar/11111?s=40x40&d=identicon
D TAG : onBindViewHolder: serivceURL = http://www.gravatar.com/avatar/11111?s=40x40&d=identicon
D TAG : onBindViewHolder: serivceURL = http://www.gravatar.com/avatar/11111?s=40x40&d=identicon
as you can see the methods onLoadFailed or onResourceReady NOT CALLED. The placeholder R.drawable.profilesuccess show.
If you have config apply plugin kotlin-kapt you have to also replace these line to gradle:
annotationProcessor 'com.github.bumptech.glide:compiler:[version]'
should be replaced by
kapt 'com.github.bumptech.glide:compiler:[version]'
I am using fragments inside my main activity and I want to send an object of my custom class "TaskWithUserAndProfile" to the TaskDetailsFragment
I found out that you can do it with Bundle and made it send a string, but things got complicated when I tried to send with Parcebale.
here are some parts of my code for better understanding:
TaskWithUserAndProfile.kt
class TaskWithUserAndProfile() : Parcelable{
override fun writeToParcel(p0: Parcel?, p1: Int) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
var profile = Profile()
var task = Task()
var user = User()
constructor(parcel: Parcel) : this() {
//profile = parcel.read
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<TaskWithUserAndProfile> {
override fun createFromParcel(parcel: Parcel): TaskWithUserAndProfile {
return TaskWithUserAndProfile(parcel)
}
override fun newArray(size: Int): Array<TaskWithUserAndProfile?> {
return arrayOfNulls(size)
}
}
}
HomeFragment.kt
//Inside onCreateView
adapter = TasksAdapter(tasksArray) { item ->
println(item.profile)
val bundle = Bundle()
bundle.putParcelable("MyItem", item)
val taskDetailsFragment = TaskDetailsFragment()
taskDetailsFragment.arguments = bundle
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.container, taskDetailsFragment)
fragmentTransaction.addToBackStack(null)
fragmentTransaction.commit()
}
How should my class that implements the Parcebale look like and how can I then send and receive the item object in fragments?
You don't need to use Parcelable even, just simply define an TaskWithUserAndProfile variable in your TaskDetailsFragment and set in in HomeFragment.
TaskDetailsFragment.kt
class TaskDetailsFragment : Fragment() {
var selectedTask: TaskWithUserAndProfile? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
}
}
HomeFragment.kt
//Inside onCreateView
adapter = TasksAdapter(tasksArray) { item ->
val taskDetailsFragment = TaskDetailsFragment()
taskDetailsFragment.selectedTask = item
val fragmentTransaction =
fragmentManager?.beginTransaction()
fragmentTransaction?.replace(R.id.container, taskDetailsFragment)
fragmentTransaction?.addToBackStack(null)
fragmentTransaction?.commit()
}
if you wanna keep using parcelize, just try this sample:
#Parcelize
data class TaskWithUserAndProfile(var profile:Profile, var task :Task, var user:User) : Parcelable{}
I could miss something from your class but the idea should looks like this, so use annotation #Parcelize and Parcelable implementation (do not need to override any method).
Update
Thanks for reminder. You will have to add this to your gradle file:
androidExtensions {
experimental = true
}
Use this plugin:
android-parcelable-intellij-plugin-kotlin
for TaskWithUserAndProfile, Profile, Task, User models.