I am currently creating a chat application for a project in Android Studio using Kotlin, Firebase Cloud Storage and Cloud Firestore, and Glide, and I am following a few tutorials to do so. Everything was fine until I created a fragment to handle the user's account settings, such as name, bio, etc. The fragment appears to work and the edit text fields and profile picture can all be edited and interacted with, but neither of the buttons for saving and signing out seem to be displayed, and I can't figure out what the issue is exactly. As a result, I can't actually test the code until I can these buttons to be displayed. Any ideas?
This is what the fragment is supposed to look like, according to the layout: https://i.stack.imgur.com/0qR0C.png
This is what the fragment looks like when the application is actually running: https://i.stack.imgur.com/vAgLG.png
This is what the fragment layout looks like in code:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragment.MyAccountFragment">
<ImageView
android:id="#+id/imageView_profile_picture"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="155dp"
android:layout_marginTop="50dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="#drawable/ic_baseline_account_circle_24" />
<EditText
android:id="#+id/editText_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="225dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="0dp"
android:hint="Your Name"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/imageView_profile_picture"
app:layout_constraintVertical_bias="0.025" />
<EditText
android:id="#+id/editText_bio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="275dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="0dp"
android:hint="Your Bio"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/imageView_profile_picture"
app:layout_constraintVertical_bias="0.025" />
<Button
android:id="#+id/btn_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="550dp"
android:backgroundTint="?attr/colorAccent"
android:text="Save"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.971"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.982"
tools:visibility="visible" />
<Button
android:id="#+id/btn_sign_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="310dp"
android:layout_marginTop="550dp"
android:backgroundTint="?attr/colorPrimaryDark"
android:text="Sign Out"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.028"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.982"
tools:visibility="visible" />
</FrameLayout>
This is the code for the fragment itself:
class MyAccountFragment : Fragment() {
// Request code:
private val RC_SELECT_IMAGE = 2
// Variable for the selected image:
private lateinit var selectedImageBytes: ByteArray
// Picture change flag:
private var pictureJustChanged = false
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_my_account, container, false)
view.apply {
findViewById<ImageView>(R.id.imageView_profile_picture).setOnClickListener{
val intent = Intent().apply {
type = "image/*"
action = Intent.ACTION_GET_CONTENT
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/jpeg", "image/png"))
}
startActivityForResult(Intent.createChooser(intent, "Select Image"), RC_SELECT_IMAGE)
}
findViewById<Button>(R.id.btn_save).setOnClickListener{
if(::selectedImageBytes.isInitialized){
CloudStorageUtil.uploadProfilePicture(selectedImageBytes) { imagePath ->
CloudFirestoreUtil.updateCurrentUser(findViewById<EditText>(R.id.editText_name).text.toString(),
findViewById<EditText>(R.id.editText_bio).text.toString(),
imagePath)
}
}
else {
CloudFirestoreUtil.updateCurrentUser(findViewById<EditText>(R.id.editText_name).text.toString(),
findViewById<EditText>(R.id.editText_bio).text.toString(),
null)
}
}
findViewById<Button>(R.id.btn_sign_out).setOnClickListener {
FirebaseAuth.getInstance().signOut()
val intent = Intent(this#MyAccountFragment.context, LoginOrRegisterActivity::class.java)
intent.flags = (Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
}
return view
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?){
if(requestCode == RC_SELECT_IMAGE
&& resultCode == Activity.RESULT_OK
&& data != null
&& data.data != null) {
val selectedImagePath = data.data
val selectedImageBmp = MediaStore.Images.Media.getBitmap(activity?.contentResolver, selectedImagePath)
val outputStream = ByteArrayOutputStream()
selectedImageBmp.compress(Bitmap.CompressFormat.JPEG, 90, outputStream)
selectedImageBytes = outputStream.toByteArray()
GlideApp.with(this).load(selectedImageBytes).into(view?.findViewById(R.id.imageView_profile_picture))
pictureJustChanged = true
}
}
override fun onStart() {
super.onStart()
CloudFirestoreUtil.getCurrentUser { user ->
if(this#MyAccountFragment.isVisible){
if (user != null) {
view?.findViewById<EditText>(R.id.editText_name)?.setText(user.name)
view?.findViewById<EditText>(R.id.editText_bio)?.setText(user.bio)
if(!pictureJustChanged && user.profilePicturePath != null){
GlideApp.with(this)
.load(CloudStorageUtil.pathToReference(user.profilePicturePath))
.placeholder(R.drawable.ic_baseline_account_circle_24)
.into(view?.findViewById(R.id.imageView_profile_picture))
}
}
}
}
}
}
Related
I just started working with Android Studio, and I've been trying to figure out what's wrong with this code for a few days now. When I run the program it shows nothing, and also there is no errors from Android Studio. I read the posts on the subject, but unfortunately I found nothing appropriate.
This is my activity class.
package ie.wit.donationx.activities
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.firebase.database.*
import ie.wit.donationx.adapters.EventsAdapter
import ie.wit.donationx.databinding.ActivityEventsFeedBinding
import ie.wit.donationx.models.EventData
class EventsFeed : AppCompatActivity() {
lateinit var mDataBase: DatabaseReference
private lateinit var eventList:ArrayList<EventData>
private lateinit var mAdapter: EventsAdapter
private lateinit var binding: ActivityEventsFeedBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_events_feed)
binding = ActivityEventsFeedBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
eventList = ArrayList()
mAdapter = EventsAdapter(this,eventList)
var linearLayoutManager = LinearLayoutManager(this)
binding.recyclerEvents.layoutManager = linearLayoutManager
binding.recyclerEvents.setHasFixedSize(true)
binding.recyclerEvents.adapter = mAdapter
getEventsData()
}
private fun getEventsData() {
mDataBase = FirebaseDatabase.getInstance().getReference("Events")
mDataBase.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
for (eventSnapshot in snapshot.children) {
val event = eventSnapshot.getValue(EventData::class.java)
eventList.add(event!!)
}
binding.recyclerEvents.adapter = mAdapter
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
}
This is my Adapter
package ie.wit.donationx.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import ie.wit.donationx.R
import ie.wit.donationx.databinding.ItemListBinding
import ie.wit.donationx.models.EventData
class EventsAdapter(
var c: Context, var eventList: ArrayList<EventData>)
: RecyclerView.Adapter<EventsAdapter.EventViewHolder>() {
inner class EventViewHolder(var v: ItemListBinding): RecyclerView.ViewHolder(v.root){}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EventViewHolder {
val infilter = LayoutInflater.from(parent.context)
val v = DataBindingUtil.inflate<ItemListBinding>(
infilter,R.layout.item_list,parent,
false
)
return EventViewHolder(v)
}
override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
val newList = eventList[position]
holder.v.isEvent = eventList[position]
holder.adapterPosition
}
override fun getItemCount(): Int {
return eventList.size
}
}
XML recyclerView
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.EventsFeed">
<com.google.android.material.imageview.ShapeableImageView
android:id="#+id/main_shapeable"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:background="#color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="#+id/main_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:ems="10"
android:hint="search"
android:inputType="textPersonName"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="#+id/main_shapeable"
app:layout_constraintEnd_toEndOf="#+id/main_shapeable"
app:layout_constraintStart_toStartOf="#+id/main_shapeable"
app:layout_constraintTop_toTopOf="#+id/main_shapeable" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerEvents"
tools:listitem="#layout/item_list"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/main_shapeable"/>
</androidx.constraintlayout.widget.ConstraintLayout>
XML file for items in recyclerView
<?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="isEvent"
type="ie.wit.donationx.models.EventData" />
</data>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="5dp"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_margin="10dp"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/eventImg"
android:imageUrl="#{isEvent.image}"
android:scaleType="centerCrop"
android:layout_width="140dp"
android:layout_height="140dp"/>
<LinearLayout
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/eventTitle"
android:textColor="#color/black"
android:textSize="20sp"
android:gravity="center"
android:textStyle="bold|normal"
android:layout_gravity="center"
android:text="#{isEvent.evenTitle}"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="#+id/eventInfo"
android:textColor="#android:color/darker_gray"
android:textSize="15sp"
android:gravity="center"
android:textStyle="bold|normal"
android:layout_gravity="center"
android:text="#{isEvent.info}"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
<View
android:background="#android:color/darker_gray"
android:layout_width="match_parent"
android:layout_height="1dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</layout>
Data file
package ie.wit.donationx.models
class EventData {
var evenTitle: String? = null
var info: String? = null
var image: String? = null
constructor(){}
constructor(
evenTitle:String?,
info:String?,
image:String?
){
this.evenTitle = evenTitle
this.info = info
this.image = image
}
}
Firebase DB structure
When adding new list to the RecyclerView Adapter you need to call NotifyDataSetChanged()
Also, i advise to remove the list from the constructor of the adapter, and make a public function inside the adapter class that will update the private list created in the class.
private var items: MutableList<EventData> = mutableListOf()
fun setItems(newItemsList : List<EventData>){
eventList.clear()
eventList.addAll(newItemsList)
notifyDataSetChanged()
}
If you only update part of the list, you can create a separate function for that and use the more efficient notifyItemRangeInserted(), notifyItemInserted() or notifyItemChanged() \ notifyItemRangeChanged
You don't need to setAdaper again after getting EventsData
Just replace this line in getEventsData() :
binding.recyclerEvents.adapter = mAdapter
With :
if (eventList.size > 0) {
mAdapter.notifyDataSetChanged()
}
Edit:
You should not to add
android:imageUrl="#{isEvent.image}" in the ImageView,
you can use library to download the images from URL Like:
Picasso.
or
Fresco.
For Example using Picasso in your adapter:
override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
val newList = eventList[position]
holder.v.isEvent = eventList[position]
holder.adapterPosition
// here to download image for URL
Picasso.get().load(eventList[position].image)
.into(holder.v.eventImg)
}
Don't forget to add dependencies
implementation 'com.squareup.picasso:picasso:2.8'
I am currently working on a mobile application in my uni classes that utilizes a seekBar to let the user decide to use a timer for a quiz. This application uses main to host all of the fragments. currently I just want the textbox to display where the user scrolled the seek bar to but am struggling to find a solution. any advice would be greatly appreciated. this is the code I have within the
TitleFragment.kt:
package com.example.android.guesstheword.screens.title
import android.os.Bundle
import android.view.DragEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
import android.widget.Switch
import android.widget.TextView
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.databinding.adapters.SeekBarBindingAdapter
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.example.android.guesstheword.R
import com.example.android.guesstheword.databinding.TitleFragmentBinding
import kotlinx.android.synthetic.main.title_fragment.*
/**
* Fragment for the starting or title screen of the app
*/
class TitleFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
// Inflate the layout for this fragment
val binding: TitleFragmentBinding = DataBindingUtil.inflate(
inflater, R.layout.title_fragment, container, false)
binding.timerSwitch.setOnClickListener {
if(binding.timerSwitch.isChecked){
binding.timerBar.visibility = View.VISIBLE
}
else{
binding.timerBar.visibility = View.INVISIBLE
}
}
val seekBar = binding.timerBar
seekBar.setOnSeekBarChangeListener(binding)
binding.playGameButton.setOnClickListener {
findNavController().navigate(TitleFragmentDirections.actionTitleToGame())
}
return binding.root
}
}
private fun SeekBar.setOnSeekBarChangeListener(binding: TitleFragmentBinding) {
val seconds = binding.timerSeconds
seconds.visibility = View.VISIBLE
seconds.text = binding.timerBar.progress.toString()
}
title_fragment.xml:
<?xml version="1.0" encoding="utf-8"?><!--
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/title_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".screens.title.TitleFragment">
<TextView
android:id="#+id/get_ready_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:fontFamily="sans-serif"
android:text="#string/get_ready"
android:textColor="#color/black_text_color"
android:textSize="14sp"
android:textStyle="normal"
app:layout_constraintBottom_toTopOf="#+id/title_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="#+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:fontFamily="sans-serif"
android:text="#string/title_text"
android:textColor="#color/black_text_color"
android:textSize="34sp"
android:textStyle="normal"
app:layout_constraintBottom_toTopOf="#+id/play_game_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/get_ready_text"
app:layout_constraintVertical_chainStyle="packed" />
<Button
android:id="#+id/play_game_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:text="#string/play_button"
android:theme="#style/GoButton"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Switch
android:id="#+id/timer_switch"
android:layout_width="95dp"
android:layout_height="52dp"
android:text="Timer"
android:min="5"
android:max="360"
app:layout_constraintBottom_toTopOf="#+id/play_game_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title_text"
app:layout_constraintVertical_bias="0.064"/>
<SeekBar
android:id="#+id/timerBar"
android:layout_width="132dp"
android:layout_height="85dp"
android:clickable="false"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="#+id/play_game_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/timer_switch"
app:layout_constraintVertical_bias="0.144" />
<TextView
android:id="#+id/timer_seconds"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="#+id/play_game_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/timerBar"
app:layout_constraintVertical_bias="0.17" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
You created an extension function called setOnSeekBarChangeListener which does not actually set it.
In order to set the listener, you need to do something like this:
binding.timerBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
// here, you react to the value being set in seekBar
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
// you can probably leave this empty
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
// you can probably leave this empty
}
})
But keep in mind that this is NOT the extension function you created.
When I Click on Home navigation fragment(there is recyclerview) recycler view disappears with error message: E/RecyclerView: No adapter attached; skipping layout
when the app launchs it appears but when i move to another fragment and return again to home fragment, recycler view just disappears. even If I dont move on another fragment and click on home fragment, it still disappears.
What could be the problem and what can I do to fix it?
My codes:
fragment_home.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
android:paddingBottom="65dp"
app:reverseLayout="false" />
</LinearLayout>
Posts.kt
data class Posts(
var id: Int,
var imageUrl: String,
var title: String,)
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var recyclerViewPersonAdapter: RecyclerViewPersonAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView = findViewById<BottomNavigationView>(R.id.bottomNavMenu)
val controller = findNavController(R.id.nav_host_fragment)
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.homeFragment,
R.id.addFragment,
R.id.userFragment
)
)
setupActionBarWithNavController(controller, appBarConfiguration)
navView.setupWithNavController(controller)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = RecyclerViewPersonAdapter(getData())
}
private fun getData() : List<Posts>{
val list = ArrayList<Posts>()
list.add(
Posts(
1,
"https://www.myanbiz-consulting.com/wp-content/uploads/2019/04/4214892-news-images.jpg",
"Best news in the world\nfor you"
)
)
list.add(
Posts(
2,
"https://www.myanbiz-consulting.com/wp-content/uploads/2019/04/4214892-news-images.jpg",
"very very gooood\nnews for you"
)
)
list.add(
Posts(
3,
"https://www.myanbiz-consulting.com/wp-content/uploads/2019/04/4214892-news-images.jpg",
"very very gooood\nnews for you"
)
)
list.add(
Posts(
4,
"https://www.myanbiz-consulting.com/wp-content/uploads/2019/04/4214892-news-images.jpg",
"very very gooood\nnews for you"
)
)
return list
}
}
RecyclerViewPersonAdapter.kt
class RecyclerViewPersonAdapter(private val list: List<Posts>) : RecyclerView.Adapter<RecyclerViewPersonAdapter.PersonViewHolder>() {
class PersonViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val imageView: ImageView
val textView: TextView
init {
imageView = itemView.findViewById(R.id.imageView)
textView = itemView.findViewById(R.id.textView)
}
fun setData(posts: Posts){
Glide.with(itemView.context)
.load(posts.imageUrl)
.into(imageView)
textView.text = posts.title
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PersonViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.news_post, parent, false)
return PersonViewHolder(itemView)
}
override fun onBindViewHolder(holder: PersonViewHolder, position: Int) {
val posts = list[position]
holder.setData(posts)
}
override fun getItemCount(): Int {
return list.size
}
}
news_post.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="400dp"
android:layout_height="100dp"
android:background="#color/teal_200"
android:layout_marginTop="15dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:orientation="horizontal">
<ImageView
android:id="#+id/imageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="#drawable/img" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="22dp"
android:orientation="vertical">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lineSpacingExtra="8sp"
android:text="Best news in the world\nfor you"
android:textColor="#color/black"
android:textSize="16sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_gravity="center"
android:src="#drawable/ic_baseline_thumb_up_alt_24" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:lineSpacingExtra="7sp"
android:text="2,687"
android:textColor="#333333"
android:textSize="13sp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="22dp"
android:layout_weight="1"
android:orientation="horizontal">
<ImageView
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_gravity="center"
android:src="#drawable/ic_baseline_remove_red_eye_24" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:lineSpacingExtra="7sp"
android:text="32,577"
android:textColor="#333333"
android:textSize="13sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
Your RecyclerView is in fragment_home.xml.
But you declare it in MainActivity.
Just do that in your HomeFragment instead of MainActivity.
class HomeFragment : Fragment() {
private lateinit var recyclerView: RecyclerView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
recyclerView = view.findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(requireContext())
recyclerView.adapter = RecyclerViewPersonAdapter(getData())
...
I have two classes AdminPinActivity.kt and AdminPinActivityPresenter.kt
AdminPinActivity.kt:
class AdminPinActivity : InjectableAppCompatActivity() {
#Inject
lateinit var adminPinActivityPresenter: AdminPinActivityPresenter
private var input_Pin: String = ""
private var input_Confirm_Pin: String = ""
companion object {
fun createAdminPinActivityIntent(context: Context, profileId: Int, colorRgb: Int): Intent {
val intent = Intent(context, AdminPinActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
intent.putExtra(KEY_ADMIN_PIN_PROFILE_ID, profileId)
intent.putExtra(KEY_ADMIN_PIN_COLOR_RGB, colorRgb)
return intent
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityComponent.inject(this)
adminPinActivityPresenter.handleOnCreate(savedInstanceState)
}
override fun onSaveInstanceState(outState: Bundle) {
adminPinActivityPresenter.handleOnSavedInstanceState(outState)
super.onSaveInstanceState(outState)
}
override fun onSupportNavigateUp(): Boolean {
finish()
return false
}
}
AdminPinActivityPresenter.kt:
/** The presenter for [AdminPinActivity]. */
#ActivityScope
class AdminPinActivityPresenter #Inject constructor(
private val context: Context,
private val activity: AppCompatActivity,
private val profileManagementController: ProfileManagementController,
private val viewModelProvider: ViewModelProvider<AdminPinViewModel>
) {
private val adminViewModel by lazy {
getAdminPinViewModel()
}
private var input_Pin: String = ""
private var input_Confirm_Pin: String = ""
/** Binds ViewModel and sets up text and button listeners. */
fun handleOnCreate(savedInstanceState: Bundle?) {
activity.title = activity.getString(R.string.add_profile_title)
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
activity.supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp)
val binding =
DataBindingUtil.setContentView<AdminPinActivityBinding>(activity, R.layout.admin_pin_activity)
binding.apply {
lifecycleOwner = activity
viewModel = adminViewModel
}
if (savedInstanceState != null) {
if (savedInstanceState.getString("InputPin") != null) {
Log.i("Input pin from savedInstanceState",savedInstanceState.getString("InputPin"))
binding.inputPin.input.setText(savedInstanceState.getString("InputPin"))
}
if (savedInstanceState.getString("InputConfirmPin") != null) {
binding.inputConfirmPin.input.setText(savedInstanceState.getString("InputConfirmPin"))
}
binding.executePendingBindings()
}
binding.inputPin.addTextChangedListener(object :TextWatcher{
override fun afterTextChanged(p0: Editable?) {}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
input_Pin=p0.toString().trim()
}
})
binding.inputConfirmPin.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
input_Confirm_Pin=p0.toString().trim()
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
})
}
fun handleOnSavedInstanceState(bundle: Bundle) {
bundle.putString("InputPin", input_Pin)
Log.i("input_Pin",input_Pin)
bundle.putString("InputConfirmPin", input_Confirm_Pin)
}
private fun getAdminPinViewModel(): AdminPinViewModel {
return viewModelProvider.getForActivity(activity, AdminPinViewModel::class.java)
}
}
Log.i("Input pin from savedInstanceState",savedInstanceState.getString("InputPin")) and Log.i("input_Pin",input_Pin) are getting logged on every rotation. That means the code is working fine, but the EditText values are not getting updated i.e. binding.inputPin.input.setText(savedInstanceState.getString("InputPin")) and binding.inputConfirmPin.input.setText(savedInstanceState.getString("InputConfirmPin")) are not working properly.
When I use binding.inputPin.input.setText("Something") outside the if statements i.e inside the handleOnCreateMethod then it is updating the EditText Value with "Something" once. But on screen rotation that is lost too.
I hope you understand the question.
I have searched everywhere for almost two days now and can't find the solution to it. I don't want to use the Two-Way data binding.
admin_pin_activity.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:profile="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="org.oppia.app.profile.AdminPinViewModel" />
</data>
<ScrollView
android:id="#+id/scrollViewAdminPin"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/addProfileBackground">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/admin_auth_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/addProfileBackground">
<TextView
android:id="#+id/admin_pin_main_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/admin_pin_requirement_description"
android:textColor="#color/oppiaPrimaryText"
android:textSize="16sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="32dp"
android:paddingStart="32dp"
android:paddingEnd="32dp"/>
<TextView
android:id="#+id/admin_pin_warning_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/admin_pin_pin_description"
android:textSize="16sp"
android:textColor="#color/oppiaPrimaryText"
app:layout_constraintTop_toBottomOf="#+id/admin_pin_main_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp"
android:paddingStart="32dp"
android:paddingEnd="32dp"/>
<org.oppia.app.profile.ProfileInputView
android:id="#+id/input_pin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="#+id/admin_pin_warning_text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:label="#string/admin_pin_new_pin"
app:isPasswordInput="true"
app:inputLength="5"
profile:error="#{viewModel.pinErrorMsg}"/>
<org.oppia.app.profile.ProfileInputView
android:id="#+id/input_confirm_pin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#+id/input_pin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:label="#string/admin_pin_new_confirm"
app:isPasswordInput="true"
app:inputLength="5"
profile:error="#{viewModel.confirmPinErrorMsg}"/>
<Button
android:id="#+id/submit_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/admin_pin_submit"
style="#style/StateButtonActive"
app:layout_constraintTop_toBottomOf="#+id/input_confirm_pin"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="20dp"
android:layout_marginEnd="32dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>
profile_input_view.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="28dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="28dp"
android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true"
android:orientation="vertical">
<TextView
android:id="#+id/label_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:fontFamily="sans-serif"
android:textAllCaps="true"
android:textColor="#color/oppiaPrimaryText"
android:textSize="12sp" />
<EditText
android:id="#+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:background="#drawable/add_profile_edit_text_background"
android:fontFamily="sans-serif"
android:padding="8dp"
android:textColor="#color/oppiaPrimaryText"
android:textSize="14sp"
android:textStyle="italic" />
<TextView
android:id="#+id/error_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/red" />
</LinearLayout>
</layout>
I am new kotlin and recently started working with retrofit and I have followed this tutorial http://velmm.com/kotlin-retrofit-android-example-with-recyclerview/
but when I run code it is showing empty white screen below
empty screenshot
below my MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var recyclerView: RecyclerView
lateinit var recyclerAdapter: RecyclerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recyclerview)
recyclerAdapter = RecyclerAdapter(this)
recyclerview.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = recyclerAdapter
val apiInterface = ApiInterface.create().getMovies()
//apiInterface.enqueue( Callback<List<Movie>>())
apiInterface.enqueue( object : Callback<List<Movie>> {
override fun onResponse(call: Call<List<Movie>>?, response: Response<List<Movie>>?) {
if(response?.body() != null)
recyclerAdapter.setMovieListItems(response.body()!!)
}
override fun onFailure(call: Call<List<Movie>>?, t: Throwable?) {
}
})
}
}
below Adapter class
class RecyclerAdapter(val context: Context) : RecyclerView.Adapter() {
var movieList : List<Movie> = listOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_adapter,parent,false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return movieList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.tvMovieName.text = movieList.get(position).title
Glide.with(context).load(movieList.get(position).image)
.apply(RequestOptions().centerCrop())
.into(holder.image)
}
fun setMovieListItems(movieList: List<Movie>){
this.movieList = movieList;
notifyDataSetChanged()
}
class MyViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView!!) {
val tvMovieName: TextView = itemView!!.findViewById(R.id.title)
val image: ImageView = itemView!!.findViewById(R.id.image)
}
}
below ApiInterface.kt
interface ApiInterface {
#GET("volley_array.json")
fun getMovies() : Call<List<Movie>>
companion object {
var BASE_URL = "http://35.200.174.74/apis/"
fun create() : ApiInterface {
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
return retrofit.create(ApiInterface::class.java)
}
}
}
below Movie.kt
data class Movie(var title: String, var image: String)
below recyclerview_adapter.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="150dp">
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#android:color/white"
android:textStyle="bold"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
below activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
There is an issue at the recyclerview_adapter.xml file. You should not use match_parent inside constraint layout but 0dp, and when doing it you must take into account you need to constraint start and end if you want full width, or top and bottom if you want to do it vertically. Here how the file should look like:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="150dp">
<ImageView
android:id="#+id/image"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="#+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="#android:color/white"
android:textStyle="bold"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
And the activity should look like this:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>