How to display Fullscreen image from RecyclerView in Kotlin? - android

i am getting data from API and display it in RecyclerView and everything is working fine
but now i'm a bit confused on how can i display the images that i'm getting from the API in full screen if the user clicked on it
this is the activity:
LlmNoteDetailsActivity:
class LlmNoteDetailsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_ilm_note_detail)
val EventName = intent.getStringExtra("EventName")
val EventLink = intent.getStringExtra("EventLink")
val isFavourite = intent.getBooleanExtra("isFavourite" , false)
val eventId = intent.getIntExtra("eventId",0)
val eventDate = intent.getStringExtra("eventDate")
val eventDescription = intent.getStringExtra("eventDescription")
val eventLocation = intent.getStringExtra("eventLocation")
val eventLink = intent.getStringExtra("eventLink")
val EventImage = intent.getStringExtra("EventImage")
val ImageViewTop: ImageView = findViewById(R.id.ImageViewTop)
val LinkText: TextView = findViewById(R.id.LocationTx)
val EventNameText: TextView = findViewById(R.id.EventNameText)
val eventDateTxt: TextView = findViewById(R.id.eventDateTxt)
val eventDescriptionText: TextView = findViewById(R.id.eventDescriptionText)
val butttoRegiiste: Button = findViewById(R.id.butttoRegiiste)
val detailback_btn: ImageButton = findViewById(R.id.detailback_btn)
LinkText.text = EventLink
EventNameText.text = EventName
eventDateTxt.text = eventDate
eventDescriptionText.text = eventDescription
butttoRegiiste.setOnClickListener {
openLinkInBrowser(eventLink!!)
}
detailback_btn.setOnClickListener {
onBackPressed()
}
ImageViewTop.setOnClickListener {
//display image in fullscreen
}
Picasso.get()
.load(EventImage)
.error(R.drawable.qiblacompass)
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
.networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE)
.into(ImageViewTop)
}
fun openLinkInBrowser(url: String) {
try {
val url = if (Uri.parse(url).scheme == null || !url.startsWith("https://") && !url.startsWith(
"http://"
)) {
"https://$url"
} else url
val webPage: Uri = Uri.parse(url)
val intent = Intent(Intent.ACTION_VIEW, webPage)
this.startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
this its layout(fragment_ilm_note_detail):
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
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="wrap_content"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_marginTop="-100dp"
android:id="#+id/ImageViewTop"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:contentDescription="login background"
android:src="#drawable/sampledetail" />
<com.google.android.flexbox.FlexboxLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="350dp"
android:layout_alignParentBottom="true"
android:orientation="vertical"
app:flexDirection="column">
<LinearLayout
android:background="#drawable/rounded_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="#dimen/dimen_16"
android:paddingLeft="#dimen/dimen_16"
android:paddingRight="#dimen/dimen_16"
android:elevation="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical"
android:padding="20dp">
<com.google.android.flexbox.FlexboxLayout
android:layout_width="match_parent"
app:justifyContent="space_between"
android:layout_height="wrap_content">
<com.google.android.flexbox.FlexboxLayout
app:alignItems="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_marginRight="10dp"
android:src="#drawable/pin"
android:layout_width="18dp"
android:layout_height="25dp"/>
<TextView
android:id="#+id/LocationTx"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/poppins"
android:text="Ara Damansara"
android:textColor="#color/black"
android:textSize="15sp" />
</com.google.android.flexbox.FlexboxLayout>
<ImageView
android:layout_marginRight="10dp"
android:src="#drawable/star"
android:layout_width="25dp"
android:layout_height="25dp"/>
</com.google.android.flexbox.FlexboxLayout>
<TextView
android:id="#+id/EventNameText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:fontFamily="#font/poppins"
android:text="Perfecting My Solah"
android:textColor="#color/teal_600"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="#+id/eventDateTxt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:fontFamily="#font/poppins_semibold"
android:text="Sabtu 15 Ogos 2021"
android:textColor="#color/black"
android:textSize="15sp"
android:textStyle="bold" />
<TextView
android:id="#+id/eventDescriptionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:fontFamily="#font/poppins"
android:text=""
android:textColor="#color/black"
android:textSize="10sp" />
<Button
android:id="#+id/butttoRegiiste"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="#dimen/dimen_32"
android:backgroundTint="#FFB248"
android:textAllCaps="false"
android:text="Link to Register"
app:rippleColor="#FFB248" />
</LinearLayout>
</LinearLayout>
</com.google.android.flexbox.FlexboxLayout>
<ImageButton
android:id="#+id/detailback_btn"
android:layout_width="18dp"
android:layout_marginLeft="#dimen/_20sdp"
android:layout_marginTop="#dimen/_50sdp"
android:layout_height="20dp"
android:background="#drawable/chevron_left" />
</RelativeLayout>
</ScrollView>
this is the adapter:
IimfinderAdapter:
class IimfinderAdapter(var countryList: List<ilmFinders>, private val iimfinderAdapterCallback: MainActivity): RecyclerView.Adapter<IimfinderAdapter.ViewHolder>(){
lateinit var context: Context
val CategoryID: Int = AppPreferences.heightInCentimeters ?: 170
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IimfinderAdapter.ViewHolder {
context = parent.context!!
val view = LayoutInflater.from(context).inflate(R.layout.list_item2, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return countryList[CategoryID].ilmFinders.size
}
override fun onBindViewHolder(holder: IimfinderAdapter.ViewHolder, position: Int) {
var categoryIDis = countryList[CategoryID].categoryID
Log.e("CategoryID",categoryIDis.toString())
Log.e("Categorycount ",countryList[CategoryID].ilmFinders.size.toString())
holder.eventName.text = countryList[CategoryID].ilmFinders[position].eventName
holder.eventLink.text = countryList[CategoryID].ilmFinders[position].eventLink
Log.d("ggggggggggggggg", countryList[CategoryID].ilmFinders[position].eventName)
var ilmfinderimage = countryList[CategoryID].ilmFinders[position].eventPhoto.toString()
var newatri = ilmfinderimage.replace("[", "").replace("" +
"]", "")
Log.d("ffffffffff", newatri)
Picasso.get()
.load(newatri)
.error(R.drawable.qiblacompass)
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
.networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE)
.into(holder.eventPhoto)
holder.list_card.setOnClickListener {
countryList[CategoryID].ilmFinders[position].eventName
countryList[CategoryID].ilmFinders[position].eventLink
newatri
//context.startActivity(Intent(context, LlmNoteDetailsActivity::class.java))
var intent = Intent(context, LlmNoteDetailsActivity::class.java)
intent.putExtra("EventName",countryList[CategoryID].ilmFinders[position].eventName)
intent.putExtra("EventLink",countryList[CategoryID].ilmFinders[position].eventLink)
intent.putExtra("isFavourite",countryList[CategoryID].ilmFinders[position].isFavourite)
intent.putExtra("eventId",countryList[CategoryID].ilmFinders[position].eventId)
intent.putExtra("eventDate",countryList[CategoryID].ilmFinders[position].eventDate)
intent.putExtra("eventDescription",countryList[CategoryID].ilmFinders[position].eventDescription)
intent.putExtra("eventLocation",countryList[CategoryID].ilmFinders[position].eventLocation)
intent.putExtra("eventLink",countryList[CategoryID].ilmFinders[position].eventLink)
intent.putExtra("EventImage",newatri)
context.startActivity(intent)
}
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val eventName: TextView = itemView.findViewById(R.id.title_tv2)
val eventLink: TextView = itemView.findViewById(R.id.tv_url2)
val eventPhoto: ImageView = itemView.findViewById(R.id.thumbnail_tv2)
val list_card: ConstraintLayout = itemView.findViewById(R.id.list_constraint)
}
}
and this is its layout(list_item2):
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:id="#+id/frame_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.bigman.wmzx.customcardview.library.CardView
android:id="#+id/list_card"
android:layout_width="wrap_content"
android:layout_height="250dp"
app:cardCornerRadius="4dp"
app:contentPadding="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/list_constraint"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/thumbnail_tv2"
android:layout_width="150dp"
android:layout_height="150dp"
android:src="#drawable/insta"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="MissingConstraints"
/>
<TextView
android:id="#+id/title_tv2"
android:layout_width="150dp"
android:textColor="#4B4B4B"
android:fontFamily="#font/poppins"
android:textStyle="bold"
android:textSize="12dp"
android:maxLength="20"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/thumbnail_tv2" />
<TextView
android:id="#+id/tv_url2"
android:maxLength="20"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="10dp"
android:drawableLeft="#drawable/linkiimfinder"
android:layout_marginTop="8dp"
android:fontFamily="#font/poppins"
android:text="TextView"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title_tv2"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.bigman.wmzx.customcardview.library.CardView>
</FrameLayout>
so in (fragment_ilm_note_detail) layout there is an ImageView (id:ImageViewTop) i'm trying to display this image in full screen if the user click on it
i tried to create new activity and layout to show only the image and i added setOnClickListener if the user click the image but i faced few errors
what is the easiest way to achieve this?
"please be patient with me becasue i'm new to Kotlin"
i have been searching online for almost 3 days for this problem and i couldn't find the right solution for my case that's why i'm asking here, please relate to my code if you have a solution

You don't create a new activity inside holder.list_card.setOnClickListener. Instead, create a new fragment, with it's own xml layout with fullScreen imageView. For example, the kotlin code for fragment:
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
class FullScreenImageFragment: Fragment() {
companion object {
val TAG: String = "FullScreenImageFragment"
val INPUT_IMAGE: String = "INPUT_IMAGE"
fun getInstance(inputImageUrl: String): FullScreenImageFragment {
val fullScreenImageFragment = FullScreenImageFragment()
val args = Bundle()
args.putString(INPUT_IMAGE, inputImageUrl)
fullScreenImageFragment.arguments = args
return fullScreenImageFragment
}
}
private var inputImageUrl: String = ""
private lateinit var fragmentRootView: View
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// return super.onCreateView(inflater, container, savedInstanceState)
this.fragmentRootView = inflater.inflate(R.layout.full_screen_image, container, false)
this.fragmentRootView.setOnClickListener { /* empty listener to prevent click propagation */ }
return fragmentRootView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initArgument()
val imagePreview: ImageView = fragmentRootView.findViewById(R.id.imagePreview)
Picasso.get()
.load(inputImageUrl)
.error(R.drawable.qiblacompass)
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
.networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE)
.into(imagePreview)
}
private fun initArgument() {
if(arguments!=null) {
this.inputImageUrl = requireArguments().getString(INPUT_IMAGE, "")
}
}
}
The xml layout:
<?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">
<ImageView
android:id="#+id/imagePreview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
And finally, inside your adapter:
holder.list_card.setOnClickListener{
val activity: Activity = holder.context as Activity
activity.supportFragmentManager.beginTransaction()
.replace(android.R.id.content, FullScreenImageFragment.getInstance(newatri)) // "www.some_image_url.com"
.commit()
}
This should do the work. Consider studying some more on fragments to further modify the code.

SOLUTION:
so what i did here:
i created new Activity with its layout:
Activity:
val detailback_btn: ImageButton = findViewById(R.id.detailback_btn2)
val EventImage = intent.getStringExtra("EventImage")
val thumbnail_tv22: ImageView = findViewById(R.id.thumbnail_tv22)
Picasso.get()
.load(EventImage)
.error(R.drawable.qiblacompass)
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
.networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE)
.into(thumbnail_tv22)
detailback_btn.setOnClickListener {
onBackPressed()
}
}
i passed the image "val EventImage = intent.getStringExtra("EventImage")"
and from "LlmNoteDetailsActivity" i added setOnClickListener to open new Activity and passed the image:
LlmNoteDetailsActivity:
ImageViewTop.setOnClickListener {
val intent = Intent(this, LoginActivityV2::class.java)
intent.putExtra("EventImage",EventImage)
startActivity(intent)
}

Related

Kotlin Android onClickListener in getView only works on edge

I'm new to Kotlin and Android development in general. I have created a custom adapter that extends BaseAdapter and it works as I want it to, but as soon as I was to add a click listener to the view that gets returned in the getView, it doesn't seem to work as expected.
I have looked everywhere for an answer. I have seen people talking about clickable, duplicateParentState, focusable,... But nothing seems to work
The click listener does react if I click on the left/right border of my layout RelativeLayout, but I want the entire RelativeLayout to react when I click.
Am I missing something in my XML? Or is there some kind of "hack" that works. I'm a little confused as to why my click listener is not working as expected
My TokenAdapter (extends BaseAdapter)
class TokenAdapter(private val ctx: Context) : BaseAdapter() {
private val tokenPersistence: TokenPersistence = TokenPersistence(ctx)
private val clipboardManager: ClipboardManager =
ctx.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
var seconds: Int
var percentage: Int = 100
var shouldGenerateToken: Boolean = true
init {
seconds = getSecondsUntilRefresh()
}
fun getSecondsUntilRefresh() : Int{
val secondsElapsedInMinute = LocalDateTime.now().second
return if(secondsElapsedInMinute < 30) 30 - secondsElapsedInMinute else 60 - secondsElapsedInMinute
}
override fun getCount(): Int {
return tokenPersistence.length()
}
override fun getItem(position: Int): Token? {
return tokenPersistence.get(position)
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val v: View = if (convertView == null) {
val inflater: LayoutInflater =
ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
inflater.inflate(R.layout.list_item, parent, false)
} else {
convertView
}
val t: Token = getItem(position)!!
val title: TextView = v.findViewById(R.id.list_item_title)
val code: TextView = v.findViewById(R.id.list_item_subtitle)
title.text = t.getLabel()
if (shouldGenerateToken) {
var generatedCode : String = t.generateCode()
generatedCode = generatedCode.substring(0, 3) + " " + generatedCode.substring(3, generatedCode.length)
code.text = generatedCode
shouldGenerateToken = false
}
val countdown: ProgressBar = v.findViewById(R.id.progress_circular)
val countdownText: TextView = v.findViewById(R.id.progress_circular_text)
countdownText.text = seconds.toString()
countdown.progress = percentage
v.setOnClickListener {
val copyText = code.text.split(' ').joinToString("")
println("===================")
println("Copied: $copyText")
val clip: ClipData = ClipData.newPlainText("2FA Code", copyText)
clipboardManager.setPrimaryClip(clip)
Toast.makeText(ctx, "Copied: $copyText to clipboard", Toast.LENGTH_LONG).show()
}
return v
}
}
The list_item XML layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:id="#+id/list_item_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/list_item_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="16sp"
android:textStyle="bold"
android:paddingHorizontal="8dp"
android:paddingTop="16dp"
android:paddingBottom="0dp"
android:clickable="false"
/>
<TextView
android:id="#+id/list_item_subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="#id/list_item_title"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_toStartOf="#id/progress_circular_container"
android:layout_toLeftOf="#id/progress_circular_container"
android:paddingHorizontal="8dp"
android:paddingTop="0dp"
android:text="123456"
android:textColor="#color/primary"
android:textSize="32sp"
android:textStyle="bold"
android:clickable="false"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/progress_circular_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_below="#id/list_item_title"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:clickable="false">
<ProgressBar
android:id="#+id/progress_circular"
android:layout_width="48sp"
android:layout_height="48sp"
android:indeterminateOnly="false"
android:progressDrawable="#drawable/pb_circular_determinative"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:progress="100"/>
<TextView
android:id="#+id/progress_circular_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#android:color/black"
app:layout_constraintBottom_toBottomOf="#+id/progress_circular"
app:layout_constraintEnd_toEndOf="#+id/progress_circular"
app:layout_constraintStart_toStartOf="#+id/progress_circular"
app:layout_constraintTop_toTopOf="#+id/progress_circular"
tools:text="30" />
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>
So I managed to fix my problem.
I did 2 "major" things after which my problem was resolved:
I rewrote my list_item.xml making use of LinearLayout instead of RelativeLayout (although I doubt this did anything)
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="#+id/list_item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="16sp"
android:textStyle="bold"
android:paddingHorizontal="8dp"
android:paddingTop="16dp"
android:paddingBottom="0dp"
/>
<TextView
android:id="#+id/list_item_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="8dp"
android:paddingTop="0dp"
android:textColor="#color/primary"
android:textSize="32sp"
android:textStyle="bold"
/>
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<ProgressBar
android:id="#+id/progress_circular"
android:layout_width="48sp"
android:layout_height="48sp"
android:indeterminateOnly="false"
android:progressDrawable="#drawable/pb_circular_determinative"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:progress="100"/>
<TextView
android:id="#+id/progress_circular_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#android:color/black"
app:layout_constraintBottom_toBottomOf="#+id/progress_circular"
app:layout_constraintEnd_toEndOf="#+id/progress_circular"
app:layout_constraintStart_toStartOf="#+id/progress_circular"
app:layout_constraintTop_toTopOf="#+id/progress_circular"
tools:text="30" />
</androidx.constraintlayout.widget.ConstraintLayout>
I moved my ClickListener from my Adapter getView() to my MainActivity with an setOnItemClickListener on my ListView
val listview = findViewById<ListView>(R.id.tokenList)
listview.setOnItemClickListener { parent: AdapterView<*>, view: View, position: Int, id ->
val token = tokenAdapter.getItem(position)
if(token != null) {
val code = token.generateCode()
val clip: ClipData = ClipData.newPlainText("2FA Code", code)
clipboardManager.setPrimaryClip(clip)
Toast.makeText(this, "Copied: $code", Toast.LENGTH_LONG).show()
}
}
Using this method on my Listview I also didn't have to use clickable, or any of those kinds of properties on my XML layouts. If someone knows what exactly made it work, please tell me. Personally I think it is the Listener being an 'OnItemClick' and it being moved to the MainActivity, although don't take my word for it as it could just be something else random that I did.

Facebook Shimmer library with Paging 3 library

In my app I load data from API which is using paginating so I have to use paging 3 library
and I use facebook shimmer library to show shimmer effect when items are being loaded
but the problem is always shimmer effect is shown even when items are completely loaded
ArticlePagingAdapter.kt
class ArticlePagingAdapter(private val listener: OnItemClickListener) :
PagingDataAdapter<Article, ArticlePagingAdapter.ViewHolder>(ArticleDiffUtil()) {
private val TAG = ArticlePagingAdapter::class.simpleName
private lateinit var context: Context
var showShimmer: Boolean = true
inner class ViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView), View.OnClickListener {
lateinit var item: Article
val shimmerLayout: ShimmerFrameLayout = itemView.shimmer_layout
val articleImageView: ShapeableImageView = itemView.findViewById(R.id.article_image_view)
val articleTitleTextView: TextView = itemView.findViewById(R.id.article_title_text_view)
val articleEstimatedTextView: TextView =
itemView.findViewById(R.id.article_estimated_read_time_text_view)
val clockImageView: ImageView = itemView.findViewById(R.id.clock_icon)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
if (showShimmer) {
holder.shimmerLayout.startShimmer()
} else {
holder.shimmerLayout.stopShimmer()
holder.shimmerLayout.setShimmer(null)
holder.articleImageView.background = null
holder.clockImageView.background = null
holder.articleEstimatedTextView.background = null
holder.articleTitleTextView.background = null
val item = getItem(position)
if (item != null) {
Log.d(TAG, "position $position, isNull != null ")
holder.item = item
holder.articleTitleTextView.text = item.title
holder.articleEstimatedTextView.text = (item.estimatedReadingTime.toString()
val requestOptions = RequestOptions().transform(
CenterCrop(), RoundedCorners(
context.resources.getDimension(R.dimen.item_article_image_view_radius)
.toInt()
)
)
Glide.with(context).load(item.imageUrl!!.toUri())
.placeholder(R.drawable.ic_article_placeholder).apply(requestOptions)
.into(holder.articleImageView)
holder.clockImageView.setImageResource(R.drawable.ic_clock)
} else {
Log.d(TAG, "position $position, isNull= null ")
}
}
}
}
ArticleFragmnet.kt
class LatestArticlesFragment : Fragment(), ArticlePagingAdapter.OnItemClickListener {
val TAG = LatestArticlesFragment::class.simpleName
lateinit var binding: FragmentLatestArticlesBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// some unrelated code
val adapter = ArticlePagingAdapter(this)
binding.recyclerView.adapter = adapter
viewModel.getArticles().observe(viewLifecycleOwner) { data ->
lifecycleScope.launch {
adapter.submitData(data)
Log.d(TAG,"data ${data.toString()}")
adapter.showShimmer = false
}
}
}
}
ArticleFragmentViewModel.kt
class LatestArticlesFragmentViewModel #AssistedInject constructor(
private val repository: ArticleRepository, ...) : ViewModel() {
fun getArticles(): LiveData<PagingData<Article>> {
currentArticles = repository.getArticlesStream().cachedIn(viewModelScope)
return currentArticles?.asLiveData()!!
}
}
item_article.xml
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView 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"
style="#style/card_view_style"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/small_margin"
android:clipToPadding="false"
android:paddingBottom="#dimen/_2sdp">
<com.facebook.shimmer.ShimmerFrameLayout
android:id="#+id/shimmer_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/very_small_padding">
<com.google.android.material.imageview.ShapeableImageView
android:id="#+id/article_image_view"
android:layout_width="0dp"
android:layout_height="#dimen/_70sdp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="3:2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/article_title_text_view"
app:layout_constraintTop_toTopOf="parent"
tools:src="#drawable/ic_icon" />
<TextView
android:id="#+id/article_title_text_view"
style="#style/Widget.MyWidget.TextView.VerySmall.Bold"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/very_small_margin"
android:layout_marginEnd="#dimen/very_small_margin"
android:ellipsize="end"
android:gravity="start"
android:maxLines="2"
android:minLines="2"
android:textAlignment="textStart"
android:textColor="?attr/textColor"
app:layout_constraintBottom_toTopOf="#id/clock_icon"
app:layout_constraintEnd_toStartOf="#id/article_image_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#id/article_image_view"
tools:text="This is a dummy title" />
<TextView
android:id="#+id/article_estimated_read_time_text_view"
style="#style/Widget.MyWidget.TextView.VerySmall"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/very_small_margin"
android:background="#color/shimmer_background"
android:textColor="?attr/textColor"
app:layout_constraintBottom_toBottomOf="#id/article_image_view"
app:layout_constraintEnd_toEndOf="#id/article_title_text_view"
app:layout_constraintStart_toEndOf="#id/clock_icon"
app:layout_constraintTop_toBottomOf="#id/article_title_text_view"
tools:text="5 min" />
<ImageView
android:id="#+id/clock_icon"
android:layout_width="#dimen/_10sdp"
android:layout_height="#dimen/_10sdp"
android:background="#color/shimmer_background"
app:layout_constraintBottom_toBottomOf="#id/article_estimated_read_time_text_view"
app:layout_constraintEnd_toStartOf="#id/article_estimated_read_time_text_view"
app:layout_constraintStart_toStartOf="#id/article_title_text_view"
app:layout_constraintTop_toTopOf="#id/article_estimated_read_time_text_view"
tools:src="#drawable/ic_clock" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.facebook.shimmer.ShimmerFrameLayout>
</com.google.android.material.card.MaterialCardView>
fragment_article_layout.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">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/root_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/articleBackground">
<TextView
android:id="#+id/article_text_view"
style="#style/Widget.MyWidget.TextView.Large.Bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/medium_margin"
android:text="#string/articles"
android:textColor="?attr/textColor"
app:layout_constraintStart_toStartOf="#id/left_guideline"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="#+id/line_separator"
style="#style/Divider"
android:layout_marginStart="#dimen/_10sdp"
app:layout_constraintBottom_toBottomOf="#id/article_text_view"
app:layout_constraintEnd_toEndOf="#id/right_guideline"
app:layout_constraintStart_toEndOf="#id/article_text_view"
app:layout_constraintTop_toTopOf="#id/article_text_view"
app:layout_constraintVertical_chainStyle="packed" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#android:color/transparent"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingHorizontal="#dimen/small_padding"
android:paddingTop="#dimen/small_padding"
android:paddingBottom="#dimen/small_padding"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="#id/article_text_view"
tools:itemCount="8"
tools:listitem="#layout/item_article" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="?attr/listFadeBackground"
app:layout_constraintTop_toTopOf="#id/recycler_view" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/left_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.03" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/right_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.97" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

EditText in RecyclerView with Kotlin not working

I'm new in app developpement and I have a problem with an EditText in recyclerview's item.
When user clicks on the editText nothing appens. I tried with an itemClickListener on EditText, click did worked but not edtion of text.
Here is my code :
the Activity :
class MyIngredientListActivity : AppCompatActivity() {
val ingerdientOfListArray = database.getAllIngredientOfList()
val adapter = IngredientOfListAdapter(ingerdientOfListArray)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.ingredient_list_recyclerview)
findViewById<Button>
(R.id.ingredient_list_activity_search_button).setOnClickListener {
val intent = Intent(this, IngredientSearchableActivity::class.java)
startActivity(intent)
}
val recyclerView = findViewById<RecyclerView>
(R.id.ingredientoflist_recyclerview)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = adapter
}
override fun onResume() {
super.onResume()
adapter.updateIngredientList()
}
The adapter :
class IngredientOfListAdapter(val ingredientOfListDisplay:
ArrayList<IngredientOfList>
): RecyclerView.Adapter<IngredientOfListAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val cardView = itemView.findViewById<CardView>
(R.id.ingredientoflist_cardview_name)
val name = itemView.findViewById<TextView>(R.id.ingredientoflist_name)
val checkbox = itemView.findViewById<CheckBox>
(R.id.ingredientoflist_checkbox)
val editText = itemView.findViewById<EditText>
(R.id.ingredientoflist_quantity)
val unitmeasure = itemView.findViewById<TextView>
(R.id.ingredientoflist_unitmeasure)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val viewItem = inflater.inflate(R.layout.ingredient_list_item, parent, false)
return ViewHolder(viewItem)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val ingredientOfList = ingredientOfListDisplay[position]
val ingredientFromId = database.readIngredientById(ingredientOfList).first()
val unitMeasureId = database.readUnitMeasureById(ingredientFromId).first()
holder.cardView?.tag = position
holder.name.text = ingredientFromId.name
holder.unitmeasure.text = unitMeasureId.symbol
holder.editText?.tag = position
holder.editText.setText(0)
}
override fun getItemCount(): Int {
return ingredientOfListDisplay.size
}
fun updateIngredientList(ingredientOfListArray: ArrayList<IngredientOfList> = database.getAllIngredientOfList()) {
ingredientOfListDisplay.clear()
ingredientOfListDisplay.addAll(ingredientOfListArray)
notifyDataSetChanged()
}
}
RelativeLayout here
cardview here
android:id="#+id/ingredientoflist_cardview_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardUseCompatPadding="true"
android:focusable="false"
android:clickable="false"
android:foreground="?android:attr/selectableItemBackground">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#C3FFFFFF">
<TextView
android:id="#+id/ingredientoflist_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:textColor="#403E3E"
android:textSize="17sp"
android:textStyle="bold"
tools:text="ingredient"/>
<EditText
android:id="#+id/ingredientoflist_quantity"
android:layout_width="50sp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:layout_alignParentBottom="true"
android:layout_marginStart="250dp"
android:layout_marginLeft="250dp"
android:layout_marginBottom="8dp"
android:clickable="true"
android:focusable="true"
android:inputType="number"
android:imeOptions="actionDone"
android:saveEnabled="true"
android:hint="00"
android:textColor="#403E3E"
android:textSize="17sp"
android:textStyle="bold"
android:gravity="center_horizontal"
android:background="#drawable/abc_vector_test" />
<TextView
android:id="#+id/ingredientoflist_unitmeasure"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:layout_marginStart="305dp"
android:layout_marginLeft="305dp"
android:hint="cl"
android:textColor="#403E3E"
android:textSize="17sp"
android:textStyle="bold"/>
<CheckBox
android:id="#+id/ingredientoflist_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:layout_marginStart="350dp"
android:layout_marginLeft="350dp"
android:gravity="center_vertical"/>
</RelativeLayout>
</CardView>
RecyclerView Layout:
<RelativeLayout
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"
style="#style/NoActionBar"
android:background="#000000">
<ImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_below="#+id/ingredient_search_activity_search_button"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:paddingBottom="60dp"
app:srcCompat="#drawable/baseline_no_food_white_48"
app:tint="#40FFFFFF" />
<Button
android:id="#+id/ingredient_search_activity_search_button"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#android:color/white"
android:drawableStart="#android:drawable/ic_search_category_default"
android:drawableLeft="#android:drawable/ic_search_category_default"
android:gravity="start|center_vertical"
android:text="#string/my_searchables_ingredients"
android:textAlignment="textStart"
android:focusable="false"
android:clickable="true"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/ingredientoflist_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#C3FFFFFF"
app:queryHint="your ingredients"
android:layout_below="#id/ingredient_search_activity_search_button"
android:clickable="false"
android:focusable="false"/>
</RelativeLayout>
Thanks and have a good day !
I think adding
holder.editText.setOnClickListener(Object : View.OnClickListener {
override fun OnClick(v: View?) {
v.requestFocus()
}
}
Should do the trick. I'm not very good at Kotlin, so there might be a syntax error there.

Populate UI from a ViewModel in Kotlin

This is the first time I am using Android Architecture. I have decided to go with MVVM structure but I am stuck at the point where I have no idea how to set the textViews in my XML with the data I pull from the database.
Checking the logs, I have seen that the function calls to my database (Firestore) does retrieve the correct data documents. Do I set the UI elements from the Activity,ViewModel or the Fragment? (Please note that I'm using a Navigation bar and controller)
Please assist me, my code is as follows:
My Single Activity:
class HomeActivity : AppCompatActivity() {
// Create the three objects for the fragments
lateinit var homeFragment: HomeFragment
lateinit var visitsFragment: VisitsFragment
lateinit var profileFragment: ProfileFragment
// ViewModels
lateinit var customerViewModel: CustomerViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
// Initialize the bottom nav bar and navigation controller and then merge them
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.btm_nav)
val navigationController = findNavController(R.id.fragmentHost)
bottomNavigationView.setupWithNavController(navigationController)
// Create app bar config object so that you can rename the bar ontop with the tab name
val appBarConfiguration = AppBarConfiguration(setOf(R.id.homeFragment,R.id.visitsFragment,R.id.profileFragment))
setupActionBarWithNavController(navigationController,appBarConfiguration)
// View Model
customerViewModel = ViewModelProvider(this).get(CustomerViewModel::class.java)
customerViewModel.retrieveCustomer().observe(this, Observer { it })
}
// This function creates the menu object by inflating it with the resource we gave it (menu resource).
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_main,menu);
return true
}
// This function checks which menu item was selected and performs the task associated with the item selected.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId;
// If Log Out menu item was selected
if (id == R.id.menuLogOut){
// Sign the user out
FirebaseAuth.getInstance().signOut()
// Finish this activity
finish()
// Start the initial activity
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
// Display the message to the user
Toast.makeText(this, "Successfully Signed Out", Toast.LENGTH_SHORT).show()
return true
}
return super.onOptionsItemSelected(item)
}
}
My View Model:
class CustomerViewModel: ViewModel() {
val TAG = "CustomerViewModel"
var db = Firebase.firestore
var user = FirebaseAuth.getInstance().currentUser
var liveData = MutableLiveData<List<Customer>>()
var cusArray = arrayListOf<Customer>()
var docRef = user?.uid
fun retrieveCustomer(): MutableLiveData<List<Customer>>
{
db.collection("users").document(docRef.toString())
.get()
.addOnSuccessListener { document ->
if (document != null)
{
val data = document
// Set the data
val name = data.get("name") as String
val surname = data.get("surname") as String
val email = data.get("email") as String
val contactNo = data.get("contact no") as String
val customer = Customer(name, surname, email, contactNo)
cusArray.add(customer)
liveData.value = cusArray
Log.d(TAG, "DocumentSnapshot data: ${document.data}")
}
else
{
Log.d(TAG, "No such document")
}
}
.addOnFailureListener { exception ->
Log.d(TAG, "get failed with " + exception.message, exception)
}
return liveData
}
}
My Object Class
package com.CleanWheels.cleanwheels.DataClasses
data class Customer(
val name: String?,
val surname: String?,
val email: String?,
val contactNo: String?
)
My XML file (profile_fragment):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".Fragments.ProfileFragment">
<TextView
android:id="#+id/banner"
android:layout_width="499dp"
android:layout_height="290dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginStart="-6dp"
android:layout_marginTop="0dp"
android:layout_marginEnd="0dp"
android:background="#color/colorPrimary"
android:layout_marginLeft="-6dp"
android:layout_alignParentRight="true"
android:layout_alignParentLeft="true"
android:layout_marginRight="0dp" />
<ImageView
android:id="#+id/image"
android:layout_width="154dp"
android:layout_height="159dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="53dp"
android:layout_marginEnd="132dp"
android:layout_marginRight="132dp"
android:layout_marginBottom="78dp"
android:src="#drawable/ic_action_profile" />
<LinearLayout
android:id="#+id/layout_1"
android:layout_width="280dp"
android:layout_height="40dp"
android:layout_below="#id/banner"
android:layout_marginLeft="80dp"
android:layout_marginTop="100dp"
android:layout_marginRight="20dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Name:"
android:textSize="18sp"/>
<TextView
android:id="#+id/profileNameUI"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="18sp"/>
</LinearLayout>
<LinearLayout
android:id="#+id/layout_2"
android:layout_width="280dp"
android:layout_height="40dp"
android:layout_below="#id/layout_1"
android:layout_marginLeft="80dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Surname:"
android:textSize="18sp"/>
<TextView
android:id="#+id/profileSurnameUI"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="18sp"/>
</LinearLayout>
<LinearLayout
android:id="#+id/layout_3"
android:layout_width="280dp"
android:layout_height="40dp"
android:layout_below="#id/layout_2"
android:layout_marginLeft="80dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Email Address:"
android:textSize="18sp"/>
<TextView
android:id="#+id/profileEmailUI"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="18sp"/>
</LinearLayout>
<LinearLayout
android:id="#+id/layout_4"
android:layout_width="280dp"
android:layout_height="40dp"
android:layout_below="#id/layout_3"
android:layout_marginLeft="80dp"
android:layout_marginBottom="50dp"
android:layout_marginTop="20dp"
android:layout_marginRight="20dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Contact No:"
android:textSize="18sp" />
<TextView
android:id="#+id/profileContactUI"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:id="#+id/layout_5"
android:layout_width="65dp"
android:layout_height="220dp"
android:layout_below="#id/banner"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_marginStart="0dp"
android:layout_marginLeft="0dp"
android:layout_marginTop="90dp"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:layout_marginBottom="5dp"
android:layout_weight="1"
android:src="#drawable/ic_action_profile_name_ui"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_weight="1"
android:src="#drawable/ic_action_profile_surname_ui"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_weight="1"
android:src="#drawable/ic_action_profile_email_ui"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="0dp"
android:layout_weight="1"
android:src="#drawable/ic_action_profile_contact_ui"/>
</LinearLayout>
The profile_fragment class file:
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [ProfileFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class ProfileFragment : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_profile, container, false)
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment ProfileFragment.
*/
// TODO: Rename and change types and number of parameters
#JvmStatic
fun newInstance(param1: String, param2: String) =
ProfileFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
}
}
Then finally, the View I am trying to populate
You need to move all the logic to ProfileFragment once you navigate to ProfileFragment data will be set.
Example:
ProfileFragment
class ProfileFragment : Fragment() {
private val customerViewModel: CustomerViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_profile, container, false)
val name = view.findViewById<TextView>(R.id.name)
val surname = view.findViewById<TextView>(R.id.surname)
val email = view.findViewById<TextView>(R.id.email)
val contact = view.findViewById<TextView>(R.id.contact)
//calling initially here
customerViewModel.retrieveCustomer()
customerViewModel.liveData.observe(viewLifecycleOwner, Observer {
//customer index at 0
val customer = it[0]
name.text = customer.name
surname.text = customer.surname
email.text = customer.email
contact.text = customer.contactNo
})
return view
}
CustomerViewModel.kt
class CustomerViewModel(application: Application) : AndroidViewModel(application) {
var liveData = MutableLiveData<List<Customer>>()
fun retrieveCustomer(){
//Your logic to get data from Firebase or any remote or db
val listOfCustomer = mutableListOf<Customer>()
val customer = Customer("name", "surname","email", "contqct")
listOfCustomer.add(customer)
liveData.postValue(listOfCustomer)
}
}
fragment_profile.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Name"
android:padding="8dp"
android:textSize="24dp"
/>
<TextView
android:id="#+id/surname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Surname"
android:padding="8dp"
android:textSize="24dp"
/>
<TextView
android:id="#+id/email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Name"
android:padding="8dp"
android:textSize="24dp"
/>
<TextView
android:id="#+id/contact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Name"
android:padding="8dp"
android:textSize="24dp"
/>
</LinearLayout>

Android Binding Adapter attribute not found

I am using a binding adapter to have mutable text in one of my views. I believe I have it implemented correctly (and it's working in other places), but for the case of mutableText, it's getting the error AAPT: error: attribute mutableText not found
I've looked through some other answers on here, but none of them have been able to solve the issue.
Here is my layout file:
<?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="viewModel"
type="com.example.nhlstats.ui.game.GameViewModel" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/awayTeam"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="12dp">
<ImageView
android:id="#+id/awayTeamLogo"
android:layout_height="48dp"
android:layout_width="0dp"
android:layout_weight="1"
tools:src="#drawable/ic_launcher_background"/>
<TextView
android:id="#+id/awayTeamName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:layout_gravity="center_vertical"
app:mutableText="#{viewModel.getAwayTeamName()}"
tools:text="CHI Blackhawks"/>
<TextView
android:id="#+id/awayScore"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
app:mutableText="#{viewModel.getAwayTeamScore().toString()}"
tools:text="0"/>
<TextView
android:id="#+id/gameTime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
app:mutableText="#{viewModel.getTimeRemaining()"
tools:text="14:26 3rd"/>
</LinearLayout>
<LinearLayout
android:id="#+id/homeTeam"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp">
<ImageView
android:id="#+id/homeTeamLogo"
android:layout_height="48dp"
android:layout_width="0dp"
android:layout_weight="1"
tools:src="#drawable/ic_launcher_background"/>
<TextView
android:id="#+id/homeTeamName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:layout_gravity="center_vertical"
app:mutableText="#{viewModel.getHomeTeamName()}"
tools:text="CAR Hurricanes"/>
<TextView
android:id="#+id/homeScore"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="2"
app:mutableText="#{viewModel.getHomeTeamScore().toString()}"
tools:text="4"/>
</LinearLayout>
</LinearLayout>
</layout>
and my BindingAdpater function:
#BindingAdapter("mutableText")
fun setMutableText(view: TextView, text: MutableLiveData<String>?) {
val parentActivity:AppCompatActivity? = view.getParentActivity()
if (parentActivity != null && text != null) {
text.observe(parentActivity, Observer { value ->
view.text = value?:""
})
}
}
GameViewModel:
class GameViewModel:BaseViewModel() {
private val awayTeamName = MutableLiveData<String>()
private val homeTeamName = MutableLiveData<String>()
private val awayTeamScore = MutableLiveData<Int>()
private val homeTeamScore = MutableLiveData<Int>()
private val timeRemaining = MutableLiveData<String>()
fun bind(response: Game) {
awayTeamName.value = response.gameData.teams.get(0).name
homeTeamName.value = response.gameData.teams.get(1).name
awayTeamScore.value = response.liveData.linescore.teams.get(1).goals
homeTeamScore.value = response.liveData.linescore.teams.get(0).goals
timeRemaining.value = response.liveData.linescore.currentPeriodOrdinal + " " + response.liveData.linescore.currentPeriodTimeRemaining
}
fun getAwayTeamName(): MutableLiveData<String> {
return awayTeamName
}
fun getHomeTeamName(): MutableLiveData<String> {
return homeTeamName
}
fun getAwayTeamScore(): MutableLiveData<Int> {
return awayTeamScore
}
fun getHomeTeamScore(): MutableLiveData<Int> {
return homeTeamScore
}
fun getTimeRemaining(): MutableLiveData<String> {
return timeRemaining
}
}
MainActivity:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: GameListViewModel
private var errorSnackbar: Snackbar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.gameList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
viewModel = ViewModelProviders.of(this).get(GameListViewModel::class.java)
viewModel.errorMessage.observe(this, Observer { errorMessage ->
if (errorMessage != null)
showError(errorMessage)
else
hideError()
})
binding.viewModel = viewModel
}
private fun showError(#StringRes errorMessage:Int) {
errorSnackbar = Snackbar.make(binding.root, errorMessage, Snackbar.LENGTH_INDEFINITE)
errorSnackbar?.setAction(R.string.retry, viewModel.errorClickListener)
errorSnackbar?.show()
}
private fun hideError() {
errorSnackbar?.dismiss()
}
}
Thanks in advance.
As per your GameViewModel, I don't think you need any custom binding adapter.
Please check the edited code.
GameViewModel
class GameViewModel:BaseViewModel() {
// Make the variables public and access directly instead of exposing them via get() method.
val awayTeamName = MutableLiveData<String>()
val homeTeamName = MutableLiveData<String>()
val awayTeamScore = MutableLiveData<String>() // Change to String
val homeTeamScore = MutableLiveData<String>() // Change to String
val timeRemaining = MutableLiveData<String>()
fun bind(response: Game) {
awayTeamName.value = response.gameData.teams.get(0).name
homeTeamName.value = response.gameData.teams.get(1).name
awayTeamScore.value = response.liveData.linescore.teams.get(1).goals.toString() // Convert to String from here only instead of writing it in XML!
homeTeamScore.value = response.liveData.linescore.teams.get(0).goals.toString()
timeRemaining.value = response.liveData.linescore.currentPeriodOrdinal + " " + response.liveData.linescore.currentPeriodTimeRemaining
}
}
Your layout file(Notice I accessed the variables directly and removed the toString() as we are now only exposing MutableLiveData with String and yes, we are not using your custom binding adapter.
<data>
<variable
name="viewModel"
type="com.example.nhlstats.ui.game.GameViewModel" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/awayTeam"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="12dp">
<ImageView
android:id="#+id/awayTeamLogo"
android:layout_height="48dp"
android:layout_width="0dp"
android:layout_weight="1"
tools:src="#drawable/ic_launcher_background"/>
<TextView
android:id="#+id/awayTeamName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:layout_gravity="center_vertical"
android:text="#{viewModel.awayTeamName}"
tools:text="CHI Blackhawks"/>
<TextView
android:id="#+id/awayScore"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:text="#{viewModel.awayTeamScore}"
tools:text="0"/>
<TextView
android:id="#+id/gameTime"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:text="#{viewModel.timeRemaining}"
tools:text="14:26 3rd"/>
</LinearLayout>
<LinearLayout
android:id="#+id/homeTeam"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp">
<ImageView
android:id="#+id/homeTeamLogo"
android:layout_height="48dp"
android:layout_width="0dp"
android:layout_weight="1"
tools:src="#drawable/ic_launcher_background"/>
<TextView
android:id="#+id/homeTeamName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:layout_gravity="center_vertical"
android:text="#{viewModel.homeTeamName}"
tools:text="CAR Hurricanes"/>
<TextView
android:id="#+id/homeScore"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="2"
android:text="#{viewModel.homeTeamScore}"
tools:text="4"/>
</LinearLayout>
</LinearLayout>
I haven't tested this so let me know how this goes for you.
Your binding adapter receives a nullable value, while the viewModel returns a non-null value. Change the text parameter to be non-null, just like the function in the viewModel returns it.
You can also do shorter encapsulation, for example:
private val _data = MutableLiveData<String>()
val data: LiveData<String>
get() = _data
This way you don't have to write long and boring getters like in Java.

Categories

Resources