My first time asking questions here I think I'm going insane. Working on a school project, I am creating an app like Goodreads, I have recyclerView which lists all the books from database(reading data works fine) and a different fragment where you can write a review for each book. So I have 3 layouts(main_activity layout,review layout and a layout that designs an item for recyclerView).
The problem is when I click save button which is supposed to save a review ni database it does nothing. I have a collection called "books" which has 8 documents and fields such as title,author,rating,image,review. All fields are string types. My rules allow writing and reading data. I have no idea what to do if anyone has better insight I will be really thankful
Main activity:
class MainActivity : AppCompatActivity(),BookRecyclerAdapter.ContentListener {
private lateinit var recyclerAdapter: BookRecyclerAdapter
private val db = Firebase.firestore
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(layout.activity_main)
val recyclerView = findViewById<RecyclerView>(id.booksList)
db.collection("books")
.get() //da dohvati sve dokumente
.addOnSuccessListener { //unutar ovoga pristup svim podadcima koji su se ucitali
val items: ArrayList<Book> = ArrayList()
for (data in it.documents) { //stvori novu data varijablu za svaki dohvaceni dokument(element?)
val book =
data.toObject(Book::class.java) //sve podatke pretvaramo u model preko toObject u Person
if (book != null) {
book.id = data.id //postavljanje ida
items.add(book) //dodavanje gotovog eprsona u listu
}
}
recyclerAdapter = BookRecyclerAdapter(items, this#MainActivity)
recyclerView.apply {
layoutManager = LinearLayoutManager(this#MainActivity)
adapter = recyclerAdapter
}
}.addOnFailureListener { exception ->
Log.w("MainActivity", "Error getting documents", exception)
}
}
override fun onItemButtonClick(index: Int, item: Book, clickType: ItemClickType) {
if (clickType == ItemClickType.SAVE) {
db.collection("books").document(item.id)
.set(item)
.addOnSuccessListener { Log.d(TAG, "DocumentSnapshot successfully written!") }
.addOnFailureListener { e -> Log.w(TAG, "Error writing document", e) }
}
else if (clickType == ItemClickType.REVIEW) {
val newFragment: Fragment = ReviewFragment()
val transaction: FragmentTransaction? = supportFragmentManager.beginTransaction()
transaction?.replace(R.id.container, newFragment);
transaction?.addToBackStack(null);
transaction?.commit();
}
}
}
BookRecyclerAdapter
package hr.ferit.enasalaj.zavrsni
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.ImageView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
enum class ItemClickType {
SAVE,
REVIEW
}
class BookRecyclerAdapter(
val items:ArrayList<Book>,
val listener: ContentListener,
): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val mainView = LayoutInflater.from(parent.context).inflate(R.layout.recycler_view_books, parent, false)
val reviewView = LayoutInflater.from(parent.context).inflate(R.layout.review, parent, false)
return BookViewHolder(mainView, reviewView)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is BookViewHolder -> {
holder.bind(position, listener, items[position])
}
}
}
override fun getItemCount(): Int {
return items.size
}
class BookViewHolder(val view: View,val review: View): RecyclerView.ViewHolder(view) {
private val bookImage =
view.findViewById<ImageView>(R.id.image)
private val bookAuthor =
view.findViewById<EditText>(R.id.bookAuthor)
private val bookTitle =
view.findViewById<EditText>(R.id.bookTitle)
private val bookRating =
view.findViewById<EditText>(R.id.bookRating)
private val reviewButton =
view.findViewById<Button>(R.id.writeReviewButton)
public val saveButton =
review.findViewById<Button>(R.id.saveButton)
public val bookReview =
review.findViewById<EditText>(R.id.saveReview)
fun bind(
index: Int,
listener: ContentListener,
item: Book,
) {
Glide.with(view.context).load(item.image).into(bookImage)
bookAuthor.setText(item.author)
bookTitle.setText(item.title)
bookRating.setText(item.rating)
bookReview.setText(item.review)
reviewButton.setOnClickListener {
listener.onItemButtonClick(index,item,ItemClickType.REVIEW)
}
saveButton.setOnClickListener{
listener.onItemButtonClick(index,item,ItemClickType.SAVE)
}
}
}
interface ContentListener {
fun onItemButtonClick(index: Int, item: Book, clickType: ItemClickType)
}
}
And here is my gradle file:
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.google.gms.google-services'
}
android {
namespace 'hr.ferit.enasalaj.zavrsni'
compileSdk 32
defaultConfig {
applicationId "hr.ferit.enasalaj.zavrsni"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
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 {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.firebase:firebase-firestore-ktx:24.4.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
implementation 'com.github.bumptech.glide:glide:4.14.2'
implementation "com.google.firebase:firebase-auth:9.6.1"
implementation 'com.google.firebase:firebase-database:9.6.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'
apply plugin: 'com.google.gms.google-services'
}
Related
when the app is ran the Edit Text's text is not being accessed and passed for some reason instead this is what it shows in the recycler view and my room database:
https://imgur.com/a/7XjDodL
Sorry if this question doesn't make much sense I didn't really know how to phrase it. Any help is greatly appreciated.
Activity Main
package com.example.todoit
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.todoit.data.Todo
import com.example.todoit.data.TodoDataBase
import com.example.todoit.databinding.ActivityMainBinding
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
private lateinit var todoAdapter: TodoAdapter
private lateinit var todoDB: TodoDataBase
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
todoDB = TodoDataBase.getInstance(this)
todoAdapter = TodoAdapter(mutableListOf())
val rvTodoItems = binding.rvTodoItems
val btnAddTodo = binding.btnAddTodo
val btnDeleteTodo = binding.btnDeleteTodo
rvTodoItems.layoutManager = LinearLayoutManager(this)
rvTodoItems.adapter = todoAdapter
btnAddTodo.setOnClickListener {
val todoTitle = binding.etTodoTitle.toString()
if (todoTitle.isNotEmpty()) {
val todo = Todo(null, todoTitle, false)
GlobalScope.launch {
todoDB.todoDao().insertAll(todo)
}
todoAdapter.addTodo(todo)
binding.etTodoTitle.text.clear()
Toast.makeText(this, "Successfully written data", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(this, "There was an error while writing data", Toast.LENGTH_LONG)
.show()
}
}
btnDeleteTodo.setOnClickListener {
todoAdapter.deleteDoneTodos()
Toast.makeText(this, "Selected Todo(s) Deleted", Toast.LENGTH_LONG).show()
}
}
}
TodoAdapter
package com.example.todoit
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.todoit.data.Todo
import kotlinx.android.synthetic.main.item_todo.view.*
class TodoAdapter(
private val todos: MutableList<Todo>,
) : RecyclerView.Adapter<TodoAdapter.TodoViewHolder>() {
class TodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
return TodoViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.item_todo,
parent,
false
)
)
}
fun addTodo(todo: Todo) {
todos.add(todo)
notifyItemInserted(todos.size - 1)
}
fun deleteDoneTodos() {
todos.removeAll { todo ->
todo.isChecked
}
notifyDataSetChanged()
}
private fun toggleStrikeThrough(tvTodoTitle: TextView, isChecked: Boolean) {
if(isChecked) {
tvTodoTitle.paintFlags = tvTodoTitle.paintFlags or STRIKE_THRU_TEXT_FLAG
} else {
tvTodoTitle.paintFlags = tvTodoTitle.paintFlags and STRIKE_THRU_TEXT_FLAG.inv()
}
}
override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
val curTodo = todos[position]
holder.itemView.apply {
tvTodoTitle.text = curTodo.title
cbDone.isChecked = curTodo.isChecked
toggleStrikeThrough(tvTodoTitle, curTodo.isChecked)
cbDone.setOnCheckedChangeListener { _, isChecked ->
toggleStrikeThrough(tvTodoTitle, isChecked)
curTodo.isChecked = !curTodo.isChecked
}
}
}
override fun getItemCount(): Int {
return todos.size
}
}
Gradle(app)
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id "kotlin-android-extensions"
}
apply plugin: 'kotlin-kapt'
android {
compileSdk 32
defaultConfig {
applicationId "com.example.todoit"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildFeatures{
viewBinding = true
}
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 {
//ROOM
def roomVersion = "2.4.2"
implementation "androidx.room:room-ktx:$roomVersion"
kapt "androidx.room:room-compiler:$roomVersion"
// Navigation Component
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// Lifecycle components
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
// Kotlin components
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72"
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5"
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5"
}
If you need anymore code then please let me know.
You might want to change the following assignment:
val todoTitle = binding.etTodoTitle.toString()
to this:
val todoTitle = binding.etTodoTitle.text.toString()
Otherwise any new Todo objects created by the onClick will store the View ID of the editText instead of the value you typed inside it...
When the app is ran, the following errors occur:
Errors:
C:\Users\John\AndroidStudioProjects\Todoit 2\app\build\tmp\kapt3\stubs\debug\com\example\todoit\data\TodoDao.java:11: error: Not sure how to handle insert method's return type.
public abstract java.lang.Object addTodo(#org.jetbrains.annotations.NotNull()
C:\Users\John\AndroidStudioProjects\Todoit 2\app\build\tmp\kapt3\stubs\debug\com\example\todoit\data\TodoDao.java:13: error: Type of the parameter must be a class annotated with #Entity or a collection/array of it.
kotlin.coroutines.Continuation<? super kotlin.Unit> continuation);
Any help would be greatly appreciated.
Code:
Todo
package com.example.todoit.data
import androidx.room.Entity
import androidx.room.PrimaryKey
#Entity(tableName = "todo_data")
data class Todo (
#PrimaryKey val id: Int,
val title: String,
var isChecked: Boolean = false
)
TodoDao
package com.example.todoit.data
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
#Dao
interface TodoDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun addTodo(todo: Todo)
#Query("SELECT * FROM todo_data ORDER BY id ASC")
fun readAllData(): LiveData<List<Todo>>
}
TodoDataBase
package com.example.todoit.data
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
#Database(entities = [Todo::class],version = 1, exportSchema = false)
abstract class TodoDataBase: RoomDatabase() {
abstract fun todoDao(): TodoDao
companion object{
#Volatile
private var INSTANCE: TodoDataBase? = null
fun getDataBase(context: Context):TodoDataBase{
val tempInstance = INSTANCE
if(tempInstance != null){
return tempInstance
}
synchronized(this){
val instance = Room.databaseBuilder(
context.applicationContext,
TodoDataBase::class.java,
"todo_database"
).build()
INSTANCE = instance
return instance
}
}
}
}
TodoRepository
package com.example.todoit.data
import androidx.lifecycle.LiveData
class TodoRepository(private val todoDao:TodoDao) {
val readAllData: LiveData<List<Todo>> = todoDao.readAllData()
suspend fun addTodo(todo:Todo) {
todoDao.addTodo(todo)
}
}
TodoViewModel
package com.example.todoit.data
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class TodoViewModel(application: Application) : AndroidViewModel(application) {
private val readAllData: LiveData<List<Todo>>
private val repository: TodoRepository
init {
val todoDao = TodoDataBase.getDataBase(application).todoDao()
repository = TodoRepository(todoDao)
readAllData = repository.readAllData
}
fun addTodoToDataBase(todo: Todo) {
viewModelScope.launch(Dispatchers.IO) {
repository.addTodo(todo)
}
}
}
MainActivity
package com.example.todoit
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.todoit.data.Todo
import com.example.todoit.data.TodoViewModel
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private lateinit var todoAdapter: TodoAdapter
private lateinit var todoViewModel: TodoViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
todoViewModel = ViewModelProvider(this).get(TodoViewModel::class.java)
todoAdapter = TodoAdapter(mutableListOf())
rvTodoItems.layoutManager = LinearLayoutManager(this)
rvTodoItems.adapter = todoAdapter
btnAddTodo.setOnClickListener {
val todoTitle = etTodoTitle.text.toString()
if (todoTitle.isNotEmpty()) {
val todo = Todo(0,todoTitle,false)
etTodoTitle.text.clear()
insertDataToDataBase(todo)
todoAdapter.addTodo(todo)
}
btnDeleteTodo.setOnClickListener {
todoAdapter.deleteDoneTodos()
}
}}
private fun insertDataToDataBase(todo: Todo) {
val todoTitle = etTodoTitle.text.toString()
if(todoTitle.isNotEmpty()) {
//Add data to database
todoViewModel.addTodoToDataBase(todo)
}
}
}
TodoAdapter
package com.example.todoit
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.todoit.data.Todo
import kotlinx.android.synthetic.main.item_todo.view.*
class TodoAdapter(
private val todos: MutableList<Todo>,
) : RecyclerView.Adapter<TodoAdapter.TodoViewHolder>() {
class TodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
return TodoViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.item_todo,
parent,
false
)
)
}
fun addTodo(todo: Todo) {
todos.add(todo)
notifyItemInserted(todos.size - 1)
}
fun deleteDoneTodos() {
todos.removeAll { todo ->
todo.isChecked
}
notifyDataSetChanged()
}
private fun toggleStrikeThrough(tvTodoTitle: TextView, isChecked: Boolean) {
if (isChecked) {
tvTodoTitle.paintFlags = tvTodoTitle.paintFlags or STRIKE_THRU_TEXT_FLAG
} else {
tvTodoTitle.paintFlags = tvTodoTitle.paintFlags and STRIKE_THRU_TEXT_FLAG.inv()
}
}
override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
val curTodo = todos[position]
holder.itemView.apply {
tvTodoTitle.text = curTodo.title
cbDone.isChecked = curTodo.isChecked
toggleStrikeThrough(tvTodoTitle, curTodo.isChecked)
cbDone.setOnCheckedChangeListener { _, isChecked ->
toggleStrikeThrough(tvTodoTitle, isChecked)
curTodo.isChecked = !curTodo.isChecked
}
}
}
override fun getItemCount(): Int {
return todos.size
}
}
Gradle(Module)
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id "kotlin-android-extensions"
}
apply plugin: 'kotlin-kapt'
android {
compileSdk 32
defaultConfig {
applicationId "com.example.todoit"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
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 {
//ROOM
def roomVersion = "2.4.2"
implementation 'androidx.room:room-ktx:2.2.1'
kapt "androidx.room:room-compiler:2.2.1"
// Navigation Component
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// Lifecycle components
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
// Kotlin components
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72"
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5"
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5"
}
Gradle(Project)
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '7.2.0' apply false
id 'com.android.library' version '7.2.0' apply false
id 'org.jetbrains.kotlin.android' version '1.5.30' apply false
}
task clean(type: Delete) {
delete rootProject.buildDir
}
I think you should set the dependencies for room as follows:
def roomVersion = "2.4.2"
implementation "androidx.room:room-ktx:$roomVersion"
kapt "androidx.room:room-compiler:$roomVersion"
I have created a Sales Order menu, the menu uses the Activity and Fragment files,
The problem is when I display the Customer List page to find customer data that will be used on the SalesOrder page (Create Data tab (use Fragment))
CustomerList page not closed,
Here's my code
First run show
enter image description here
Then I clik Sales Order show like this, its file SalesOrderActivity.kt
Page SalesOrder use TabLayout and Fragment , ie (fragment for Create Data and Fragment List Data)
enter image description here
File SalesOrderActivity.kt
class SalesOrderActivity : AppCompatActivity() {
lateinit var viewPager : ViewPager
lateinit var tabs : TabLayout
private var appContext: Context? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sales_order)
CommonCtx.setViewXml2(contentView)
setSupportActionBar(findViewById(R.id.my_toolbar))
supportActionBar!!.title = "Sales Order"
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
viewPager = findViewById(R.id.viewPager)
tabs = findViewById(R.id.tabs)
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
val position = tab?.position
if (position == 1) {
println("get selected")
}
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabReselected(tab: TabLayout.Tab?) {
}
})
appContext = this
CommonCtx.setActivity(this)
setUpTabs()
}
//SHOW FRAGMENT CREATE DATA AND LIST DATA
private fun setUpTabs(){
val adapter = com.myapp.sfa.adapter.ViewPagerAdapter(supportFragmentManager)
adapter.addFragment(SalesOrderCreateDataFragment(), "Create Data")
adapter.addFragment(CustomerListDataFragment(), "List Data")
viewPager.adapter = adapter
tabs.setupWithViewPager(viewPager)
setupTabIcons()
}
private fun setupTabIcons() {
tabs.getTabAt(0)!!.setIcon(tabIcons.get(0))
tabs.getTabAt(1)!!.setIcon(tabIcons.get(1))
}
private val tabIcons = intArrayOf(
R.drawable.add_sign,
R.drawable.list_48
)
override fun onSupportNavigateUp(): Boolean {
finish()
return true
}
}
in page Sales Order I used fragment for show tab Create Data and List Data
this
file SalesOderCreateDataFragment.kt
class SalesOrderCreateDataFragment : Fragment() {
private var appContext: Context? = null
private lateinit var txtvCustId : TextView
public lateinit var edtCustCode : EditText
private lateinit var btnCustomer : Button
lateinit var DBHelper : DBHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
appContext = this.getActivity()
DBHelper = DBHelper(appContext!!))
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_sales_order_create_data,container,false)
txtvCustId = view.findViewById<TextView>(R.id.txtvCustId)
edtCustCode = view.findViewById<EditText>(R.id.edtCustCode)
btnCustomer = view.findViewById(R.id.btnCustomer)
btnCustomer.setOnClickListener {
showCustomer()
}
appContext = this.getActivity()
return view
}
//SHOW LIST CUSTOMER event clik Cust. Code
fun showCustomer() {
val intent = Intent(this.getActivity(), CustomerListActivity::class.java)
//intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
CommonCtx.getContext().startActivity(intent)
CustomerListActivity().finish()
}
fun getCustomer(idCustomer: String){
var customerPojo = CustomerPojo()
customerPojo= SQLCustomer().getCustomerById(idCustomer)
}
}
while I click button Cust Code
enter image description here
Will show page CustomerListActivity, like this picture
Code in Cust code event
enter image description here
File CustomerListActivity.kt
class CustomerListActivity : AppCompatActivity() {
private lateinit var recyclerView : RecyclerView
lateinit var DBHelper : DBHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_customer_list)
setSupportActionBar(findViewById(R.id.my_toolbar))
supportActionBar!!.title = "Customer List"
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
recyclerView =findViewById(R.id.recyclerView)
DBHelper = DBHelper(applicationContext)//this)
//get data customer in database local
var listCustomerPojo : MutableList<CustomerPojo> = ArrayList()
listCustomerPojo = SQLCustomer().getCustomerList()
//show data customer in Recycle Card View
showListDataToRecycleCardView(listCustomerPojo);
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.main, menu)
val searchItem = menu.findItem(R.id.menu_search)
if(searchItem != null){
val searchView = searchItem.actionView as SearchView
val editext = searchView.findViewById<EditText>
(androidx.appcompat.R.id.search_src_text)
editext.hint = "Search here..."
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
var listCustomerPojo: ArrayList<CustomerPojo> = ArrayList()
if (newText!!.isNotEmpty()) {
val search = newText.toLowerCase()
listCustomerPojo = SQLCustomer().getCustomerByName(newText)
}
showListDataToRecycleCardView(listCustomerPojo);
return true
}
})
}
return super.onCreateOptionsMenu(menu)
}
fun showListDataToRecycleCardView(listCustomerPojo: MutableList<CustomerPojo>){
var customerListDC = arrayListOf<CustomerDataModel>()
for (i in 0 until listCustomerPojo.size) {
var custPojo: CustomerPojo = listCustomerPojo[i]
val cdm = CustomerDataModel(R.drawable.shop_on_ballon_36,
custPojo.custId.toString(),
custPojo.custCode.toString(),
custPojo.custName.toString(),
custPojo.address.toString())
customerListDC.add(cdm)
}
val recyclerAdapter = CustomerListRecyclerAdapter(customerListDC)
recyclerView.apply {
adapter = recyclerAdapter
addItemDecoration(DividerItemDecoration(CommonCtx.getContext(),
DividerItemDecoration.VERTICAL))
setHasFixedSize(true)
}
}
override fun onSupportNavigateUp(): Boolean {
finish()
return true
}
}
file CustomerListRecyclerAdapter.kt
class CustomerListRecyclerAdapter(private var customerListData: ArrayList<CustomerDataModel>)
: RecyclerView.Adapter<CustomerListRecyclerAdapter.ViewHolder>(){
var appContext: Context? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater.inflate(R.layout.recycler_customer_list, parent, false)
appContext = parent.context
return ViewHolder(view)
}
override fun getItemCount(): Int = customerListData.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val movie = customerListData[position]
holder.bind(movie)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val intent: Intent? = null
init {
itemView.setOnClickListener {
var textvCustId = itemView.findViewById<TextView>(R.id.textvCustId)
var textvCustName = itemView.findViewById<TextView>(R.id.textvCustName)
}
}
fun bind(model: CustomerDataModel) {
var imgViewIcon = itemView.findViewById<ImageView>(R.id.imgViewIcon)
var textvCustId = itemView.findViewById<TextView>(R.id.textvCustId)
var textvCustCode = itemView.findViewById<TextView>(R.id.textvCustCode)
var textvCustName = itemView.findViewById<TextView>(R.id.textvCustName)
var textvAddress = itemView.findViewById<TextView>(R.id.textvAddress)
var btnCreateSO = itemView.findViewById<Button>(R.id.btnCreateSO)
var btnCreateSR = itemView.findViewById<Button>(R.id.btnCreateSR)
imgViewIcon.setImageResource(model.iconPict)
textvCustId.text = model.custId
textvCustCode.text = model.custCode
textvCustName.text = model.custName
textvAddress.text = model.address
textvCustId.setVisibility(View.INVISIBLE)
btnCreateSO.setOnClickListener {
SalesOrderCreateDataFragment().getCustomer(textvCustId.text.toString())
}
}
}//end class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
File CommonCtx.kt
#SuppressLint("StaticFieldLeak")
abstract class CommonCtx {
companion object {
#Volatile
private lateinit var appContext : Context
private lateinit var mainActivity : Activity
private lateinit var myActivity : Activity
private lateinit var myView : View
private lateinit var myView2 : View
private lateinit var myView3 : View
private lateinit var myContainer: ViewGroup
fun setContext(context: Context) {
appContext = context
}
fun getFilesDir():String {
return appContext.filesDir.absolutePath
}
fun getOpenFileInput(fileName : String): FileInputStream {
return appContext.openFileInput(fileName)
}
fun getOpenFileOutput(fileName : String): FileOutputStream {
return appContext.openFileOutput(fileName, MODE_PRIVATE)
}
fun getContext():Context{
return appContext
}
fun getContext2():Context{
return appContext.applicationContext
}
fun setMainActivity(activity: Activity) {
mainActivity = activity
}
fun getMainActivity():Activity{
return mainActivity
}
fun setActivity(activity: Activity) {
myActivity = activity
}
fun getActivity():Activity{
return myActivity
}
fun setViewXml(view : View) {
myView = view
}
fun getViewXml():View{
return myView
}
fun setViewXml2(view: View?) {
if (view != null) {
myView2 = view
}
}
fun getViewXml2():View{
return myView2
}
fun setViewXml3(view: View?) {
if (view != null) {
myView3 = view
}
}
fun getViewXml3():View{
return myView3
}
fun setContainer(container : ViewGroup) {
myContainer = container
}
fun getContainer():ViewGroup{
return myContainer
}
}
}
File AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.myapp.sfa">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:networkSecurityConfig="#xml/network_security_config"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.MyAppSFA">
<activity android:name=".view.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ui.view.NetworkSettingActivity"
android:screenOrientation="portrait" />
<activity android:name=".ui.view.salesman.SalesmanProfileActivity"
android:screenOrientation="portrait" />
<activity android:name=".ui.view.sales.SalesOrderActivity"
android:launchMode="singleTop"/>
<activity android:name=".ui.view.sales.SalesReturnActivity"/>
<activity android:name=".view.ui.customer.CustomerActivity" />
<activity android:name=".view.ui.list.CustomerListActivity"
android:noHistory="true"/>
<activity android:name=".view.ui.list.ProductItemListActivity" />
</application>
</manifest>
Problem , while I click button Create SO in page CustomerListActivity
Page CustomerListActivity is not Closed and back to Page SalesOrderActivity
Thanks
enter image description here
in this code, file CustomerListRecyclerAdapter.kt
is not working, CustomerListActivity.kt not close
btnCreateSO.setOnClickListener {
SalesOrderCreateDataFragment().getCustomer(textvCustId.text.toString())
}
this is my file build.gradle(:app)
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.myapp.sfa"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
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'
}
buildFeatures {
viewBinding true
}
dataBinding {
enabled = true
}
sourceSets {
main {
res.srcDirs = [
'src/main/res',
'src/main/res/layout',
'src/main/res/layout/fragments',
'src/main/res/layout/salesaction',
'src/main/res/layout/salesman',
'src/main/res/layout/setting',
'src\\main\\res', 'src\\main\\res\\layout\\itemrecycler', 'src\\main\\res\\layout\\product', 'src\\main\\res\\layout\\customer'
]
}
//debug {
// res.srcDirs = ['resources/debug']
//}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.anko:anko:0.10.8"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
implementation "com.squareup.retrofit2:converter-moshi:2.4.0"
implementation 'com.squareup.okhttp3:logging-interceptor:4.4.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'androidx.lifecycle:lifecycle-common:2.2.0'
implementation 'androidx.lifecycle:lifecycle-runtime:2.2.0'
implementation 'android.arch.lifecycle:extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation "androidx.viewpager:viewpager:1.0.0"
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.appcompat:appcompat:1.3.0-rc01'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation 'com.github.bumptech.glide:glide:4.9.0'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation "org.jetbrains.kotlin:kotlin-test-
junit:$kotlin_version"
}
any other ideas, should i change the way to display the Customer list?
Thanks
i have add code (itemView.context as Activity).finish() in CustomerListRecycleAdapter.kt
btnCreateSO.setOnClickListener {
SalesOrderActivity().selectedTabForUpdate(textvCustId.text.toString())
(itemView.context as Activity).finish()
}
and this work, Customer List close
Thanks all
I have a Room database that returns a Flow of objects. When I insert a new item into the database, the Flow's collect function only triggers if the insert was performed from the same Fragment/ViewModel.
I have recorded a quick video showcasing the issue: https://www.youtube.com/watch?v=7HJkJ7M1WLg
Here is my code setup for the relevant files:
AchievementDao.kt:
#Dao
interface AchievementDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(achievement: Achievement)
#Query("SELECT * FROM achievement")
fun getAllAchievements(): Flow<List<Achievement>>
}
AppDB.kt:
#Database(entities = [Achievement::class], version = 1, exportSchema = false)
abstract class AppDB : RoomDatabase() {
abstract fun achievementDao(): AchievementDao
}
AchievementRepository.kt:
class AchievementRepository #Inject constructor(appDB: AppDB) {
private val achievementDao = appDB.achievementDao()
suspend fun insert(achievement: Achievement) {
withContext(Dispatchers.IO) {
achievementDao.insert(achievement)
}
}
fun getAllAchievements() = achievementDao.getAllAchievements()
}
HomeFragment.kt:
#AndroidEntryPoint
class HomeFragment : Fragment() {
private val viewModel: HomeViewModel by viewModels()
private lateinit var homeText: TextView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bindViews()
subscribeObservers()
}
private fun bindViews() {
homeText = requireView().findViewById(R.id.txt_home)
requireView().findViewById<ExtendedFloatingActionButton>(R.id.fab_add_achievement).setOnClickListener {
AddAchievementBottomSheet().show(parentFragmentManager, "AddAchievementDialog")
}
requireView().findViewById<ExtendedFloatingActionButton>(R.id.fab_add_achievement_same_fragment).setOnClickListener {
viewModel.add()
}
}
private fun subscribeObservers() {
viewModel.count.observe(viewLifecycleOwner, { count ->
if(count != null) {
homeText.text = count.toString()
} else {
homeText.text = resources.getString(R.string.app_name)
}
})
}
}
HomeViewModel.kt:
class HomeViewModel #ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
ViewModel() {
private val _count = MutableLiveData<Int>(null)
val count = _count as LiveData<Int>
init {
viewModelScope.launch {
achievementRepository.getAllAchievements()
.collect { values ->
// FIXME this is only called when inserting from the same Fragment
_count.postValue(values.count())
}
}
}
fun add() {
viewModelScope.launch {
achievementRepository.insert(Achievement(0, 0, "Test"))
}
}
}
AddAchievementBottomSheet.kt:
#AndroidEntryPoint
class AddAchievementBottomSheet : BottomSheetDialogFragment() {
private val viewModel: AddAchievementViewModel by viewModels()
private lateinit var addButton: MaterialButton
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_add_achievement, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
addButton = requireView().findViewById(R.id.btn_add_achievement)
addButton.setOnClickListener {
viewModel.add(::close)
}
}
private fun close() {
dismiss()
}
}
AddAchievementBottomSheetViewModel.kt:
class AddAchievementViewModel #ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
ViewModel() {
fun add(closeCallback: () -> Any) {
viewModelScope.launch {
achievementRepository.insert(Achievement(0, 0, "Test"))
closeCallback()
}
}
}
build.gradle (app):
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
compileSdkVersion 30
defaultConfig {
applicationId "com.marcdonald.achievementtracker"
minSdkVersion 23
targetSdkVersion 30
versionCode 1
versionName "1.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
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 {
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'
implementation 'androidx.core:core-ktx:1.3.2'
// Android
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation "androidx.activity:activity-ktx:1.1.0"
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
// Navigation
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
// Testing
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
// Dagger Hilt
implementation 'com.google.dagger:hilt-android:2.29.1-alpha'
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
kapt 'com.google.dagger:hilt-android-compiler:2.29.1-alpha'
// Timber for logging
implementation 'com.jakewharton.timber:timber:4.7.1'
// Room
implementation 'androidx.room:room-runtime:2.2.5'
implementation 'androidx.room:room-ktx:2.2.5'
kapt 'androidx.room:room-compiler:2.2.5'
androidTestImplementation 'androidx.room:room-testing:2.2.5'
}
build.gradle (project):
buildscript {
ext.kotlin_version = "1.4.10"
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.0-alpha16'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0'
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.29.1-alpha'
}
}
allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
I'm not sure if my understanding of Kotlin Flow is to blame or whether my setup is incorrect in some way, but I'd appreciate some help with the issue.
Make sure you use the same instance of your RoomDatabase. Add a #Singleton where you provide AppDB might do the trick.
Try calling subscribeObservers() in the onStart() lifecycle.
You need to add this dependency:
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
Then don't collect the Flow in your ViewModel. Instead map it to your needs and expose it as LiveData like this:
class HomeViewModel #ViewModelInject constructor(private val achievementRepository: AchievementRepository) :
ViewModel() {
val count: LiveData<Int> = achievementRepository
.getAllAchievements()
.map {it.size}
.asLiveData()
fun add() {
viewModelScope.launch {
achievementRepository.insert(Achievement(0, 0, "Test"))
}
}
}
Flow is a cold stream which means you have to manually call Flow.collect{} to get the data.
To continuously observe changes in the database,
Option 1) convert Flow to Livedata,
val count: LiveData<Int> = achievementRepository.getAllAchivements().map {
it.count()
}.asLiveData()
Checkout the solution code in Google Codelab, "Android Room with a View - Kotlin"
Option 2) convert Flow to StateFlow which is a hot stream that you can observe on with StateFlow.collect {}
I'm trying to use android databinding library with Kotlin and want provide custom attribute for RecyclerView. For this I've created a file with this code:
#BindingAdapter("config")
fun configRecyclerView(recyclerView: RecyclerView, config: RecyclerViewConfig) {
config.apply(recyclerView)
}
Where RecyclerViewConfig is:
class RecyclerViewConfig private constructor(
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>,
val layoutManager: RecyclerView.LayoutManager,
val isNestedScrollingEnabled: Boolean,
val hasFixedSize: Boolean
) {
fun apply(recyclerView: RecyclerView) {
recyclerView.layoutManager = layoutManager
recyclerView.isNestedScrollingEnabled = isNestedScrollingEnabled
recyclerView.setHasFixedSize(hasFixedSize)
recyclerView.adapter = adapter
}
class Builder {
private lateinit var adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>
private lateinit var layoutManager: RecyclerView.LayoutManager
private var isNestedScrollingEnabled = true
private var hasFixedSize = true
fun adapter(adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>): Builder {
this.adapter = adapter
return this
}
fun layoutManager(layoutManager: RecyclerView.LayoutManager): Builder {
this.layoutManager = layoutManager
return this
}
fun nestedScrollingEnabled(isNestedScrollingEnabled: Boolean): Builder {
this.isNestedScrollingEnabled = isNestedScrollingEnabled
return this
}
fun hasFixedSize(hasFixedSize: Boolean): Builder {
this.hasFixedSize = hasFixedSize
return this
}
fun build() = RecyclerViewConfig(adapter, layoutManager, isNestedScrollingEnabled, hasFixedSize)
}
}
Also I have my ownViewModel with RecyclerViewConfig inside:
class AdvertPageViewModel(val context: Context) : RecyclerViewModel {
private val adapter: RealtyAdvertAdapter
private val listener: OnPageContentsClickListener
private val config: RecyclerViewConfig
init {
listener = createListener()
config = createConfig()
adapter = RealtyAdvertAdapter(createAdvert().toPageItems(), listener)
}
override fun getConfig() = config
private fun createConfig() =
RecyclerViewConfig.Builder()
.adapter(adapter)
.layoutManager(LinearLayoutManager(context))
.build()
private fun createListener() = object: OnPageContentsClickListener {
override fun onCallButtonClick(phone: String) {
Log.i(TAG, "onCallButtonClick")
}
override fun onWriteButtonClick(phone: String) {
Log.i(TAG, "onWriteButtonClick")
}
}
}
I create it like this:
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = AdvertPageViewModel(context)
if (view != null) {
val binding = FragmentAdvertPageBinding.bind(view)
binding.viewModel = viewModel
}
}
And there's layout for Fragment:
<?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">
<data>
<variable
name="viewModel"
type="ru.am.realty.ui.fragments.advertpage.AdvertPageViewModel"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:config="#{viewModel.getConfig()}"/>
</LinearLayout>
</layout>
The problem is that when I try to build application, there's an error occured:
Error:(20, 27) Cannot find the setter for attribute 'app:config' with
parameter type ru.am.realty.ui.base.viewmodel.RecyclerViewConfig on
android.support.v7.widget.RecyclerView.
So, I was looking for any solutions, but without success. And I have no idea, what's wrong with my code
UPDATE: there's gradle file
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 26
defaultConfig {
minSdkVersion 17
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
dataBinding {
enabled = 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:recyclerview-v7:26.1.0'
// Rx
implementation 'io.reactivex.rxjava2:rxkotlin:2.2.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
//di
implementation 'com.google.dagger:dagger:2.15'
kapt 'com.google.dagger:dagger-compiler:2.15'
kapt 'com.android.databinding:compiler:3.0.1'
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'
}