I wanted to implement searchview inside bottomsheetdialog fragment but i don't know how. I have this full screen bottom sheet dialog layout with a SearchView and Recyclerview in it:
<?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"
android:background="#color/white">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="17dp"
android:layout_marginEnd="19dp"
android:src="#drawable/ic_close_green"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tv_institution_name"
style="#style/bo_texto_regular"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="21dp"
android:layout_marginTop="66dp"
android:letterSpacing="0.01"
android:text="Nome"
android:textColor="#757575"
android:textSize="14sp"
android:textStyle="normal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.SearchView
android:id="#+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginStart="21dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="27dp"
app:layout_constraintEnd_toEndOf="#id/tv_institution_name"
app:layout_constraintStart_toStartOf="#id/tv_institution_name"
app:layout_constraintTop_toBottomOf="#id/tv_institution_name"
app:searchIcon="#drawable/magnifying_glass" />
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="#ececec"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/searchView"
tools:listitem="#layout/search_item_list" />
</androidx.constraintlayout.widget.ConstraintLayout>
And this is my full screen BottomSheetDialogFragment :
import android.view.ViewGroup
import android.view.WindowManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.koin.android.ext.android.inject
class SearchDialog() : BottomSheetDialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = BottomSheetDialog(requireContext(), theme)
dialog.setOnShowListener {
val bottomSheetDialog = it as BottomSheetDialog
val parentLayout =
bottomSheetDialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
parentLayout?.let { it ->
val behaviour = BottomSheetBehavior.from(it)
setupFullHeight(it)
behaviour.state = BottomSheetBehavior.STATE_EXPANDED
}
}
return dialog
}
private fun setupFullHeight(bottomSheet: View) {
val layoutParams = bottomSheet.layoutParams
layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT
bottomSheet.layoutParams = layoutParams
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
var view = inflater.inflate(R.layout.dialog_search, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}
I wanted to make the data come from an array and that i could search it using searchview
My adapter :
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Filter
import android.widget.Filterable
import androidx.recyclerview.widget.RecyclerView
class SearchAdapter(private val list: List<InstitutionModel>) : RecyclerView.Adapter<SearchViewHolder>() , Filterable{
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
val inflater = LayoutInflater.from(parent.context)
return SearchViewHolder(inflater, parent)
}
override fun getItemCount(): Int = list.size
override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
val dialogData: InstitutionModel = list[position]
holder.bind(dialogData)
}
override fun getFilter(): Filter {
TODO("Not yet implemented")
}
}
And this is the setOnQueryListener:
searchViewInstitutions.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
// searchView.clearFocus()
// if (data.toString().contains(query)) {
// mAdapter.filter.filter(query)
// }
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
mAdapter.filter.filter(newText)
return false
}
})
}
Related
list_users.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/user_list_bg"
android:layout_margin="10dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/user_profile_image_view"
android:layout_width="90sp"
android:layout_height="90sp"
android:src="#drawable/user_image_placeholder"
android:layout_margin="12dp"
app:border_width="1sp"
android:clickable="true"
android:focusable="true">
</de.hdodenhof.circleimageview.CircleImageView>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="#+id/user_name_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hardik Dhuri"
android:layout_marginBottom="10dp"
android:textSize="24sp"/>
<TextView
android:id="#+id/last_message_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello Bro"
android:textSize="16sp"/>
</LinearLayout>
</LinearLayout>
fragment_chats.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context=".main.fragments.ChatsFragment"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/list_users_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="#layout/list_users"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</FrameLayout>
ListUsersAdapter.kt
package com.example.samvach.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.samvach.R
import com.example.samvach.models.User
class ListUsersAdapter(private val userList: ArrayList<User>) : RecyclerView.Adapter<ListUsersAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.list_users, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = userList[position]
holder.name.text = item.name
}
override fun getItemCount(): Int {
return userList.size
}
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val name: TextView = itemView.findViewById(R.id.user_name_tv)
}
}
ChatsFragment.kt
Here I am getting data from firebase. I checked the data by logging it and it seems fine only thing i can't figure out is where is the actual error in my recycler view. I create few users manually to test and they don't show up either.
package com.example.samvach.main.fragments
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import com.example.samvach.R
import com.example.samvach.adapters.ListUsersAdapter
import com.example.samvach.databinding.FragmentChatsBinding
import com.example.samvach.models.User
import com.google.firebase.database.*
class ChatsFragment : Fragment() {
private lateinit var binding: FragmentChatsBinding
private lateinit var dbref: DatabaseReference
private lateinit var usersArrayList: ArrayList<User>
companion object {
fun newInstance() = ChatsFragment()
}
private lateinit var viewModel: ChatsViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_chats, container, false)
}
#Deprecated("Deprecated in Java")
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(this)[ChatsViewModel::class.java]
binding = FragmentChatsBinding.inflate(layoutInflater)
binding.listUsersRv.setHasFixedSize(true)
usersArrayList = arrayListOf(
User(
name = "Banana"
),
User(
name = "Apple"
),
User(
name = "Kiwi"
),
User(
name = "Orange"
)
)
binding.listUsersRv.adapter = ListUsersAdapter(usersArrayList)
// getUserData()
}
private fun getUserData() {
dbref = FirebaseDatabase.getInstance().getReference("users")
dbref.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
usersArrayList.clear()
if (snapshot.exists()) {
for (userSnapshot in snapshot.children) {
val user = userSnapshot.getValue(User::class.java)
Log.i("USER", "${user!!.name}")
usersArrayList.add(user)
}
binding.listUsersRv.adapter = ListUsersAdapter(usersArrayList)
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
}
User.kt
package com.example.samvach.models
data class User (
var email: String?= null,
var name: String?= null,
var profilePicture: String?= null,
var uid: String?= null,
)
You have an error in binding = FragmentChatsBinding.inflate(layoutInflater), try to init your binding in onCreateView like this:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_chats, container, false)
binding = FragmentChatsBinding.bind(view)
return binding.root
}
or move your code of onActivityCreated to onViewCreated and use view instance
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_chats, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentChatsBinding.bind(view)
viewModel = ViewModelProvider(this)[ChatsViewModel::class.java]
binding.listUsersRv.setHasFixedSize(true)
usersArrayList = arrayListOf(
User(
name = "Banana"
),
User(
name = "Apple"
),
User(
name = "Kiwi"
),
User(
name = "Orange"
)
)
binding.listUsersRv.adapter = ListUsersAdapter(usersArrayList)
// getUserData()
}
I am trying to implement onEditPost and onDeletePost inside of my RecyclerViewFragment:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.MainActivity
import com.example.projectdrivemark.R
import com.example.projectdrivemark.databinding.FragmentListBinding
import com.example.projectdrivemark.databinding.PostBinding
class RecyclerViewFragment: Fragment(R.layout.fragment_list), PostAdapter.OnPostClickListener {
private lateinit var binding: FragmentListBinding
val dummyList = MockDatabase.createMockData()
val adapter = PostAdapter(dummyList, this)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreate(savedInstanceState)
binding = FragmentListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<RecyclerView>(R.id.recycleViewMain).apply{
layoutManager = LinearLayoutManager(requireActivity())
adapter = PostAdapter(dummyList, this#RecyclerViewFragment)
}
}
override fun onEditPost(position: Int){
val clickedPost = dummyList[position]
clickedPost.title = "Updated title"
clickedPost.body = "Updated body"
adapter.notifyItemChanged(position)
}
override fun onDeletePost(position: Int) {
dummyList.removeAt(position)
adapter.notifyItemRemoved(position)
}
}
Here is how my Adapter looks like:
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.MainActivity
import com.example.projectdrivemark.R
class PostAdapter(val dummyData: ArrayList<Post>, val myListener: OnPostClickListener) : RecyclerView.Adapter<PostAdapter.PostViewHolder>() {
inner class PostViewHolder(postView: View) : RecyclerView.ViewHolder(postView), View.OnClickListener{
val iconImage: ImageView = postView.findViewById(R.id.icon_image_view)
val title: TextView = postView.findViewById(R.id.title)
val body: TextView = postView.findViewById(R.id.body)
val deleteIcon: ImageView = postView.findViewById(R.id.delete_post_image)
val editIcon: ImageView = postView.findViewById(R.id.edit_post_image)
init {
deleteIcon.setOnClickListener(this)
editIcon.setOnClickListener(this)
}
override fun onClick(v: View?){
val position = adapterPosition
if(v?.id == editIcon.id){
myListener.onEditPost(position)
}else{
myListener.onDeletePost(position)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostViewHolder {
val postView = LayoutInflater.from(parent.context).inflate(R.layout.post, parent, false)
return PostViewHolder(postView)
}
override fun onBindViewHolder(holder: PostViewHolder, position: Int) {
val currentPost = dummyData[position]
holder.iconImage.setImageResource(currentPost.image)
holder.title.text = currentPost.title
holder.body.text = currentPost.body
}
override fun getItemCount(): Int {
return dummyData.size
}
interface OnPostClickListener{
fun onEditPost(position: Int)
fun onDeletePost(position: Int)
}
}
And here is how my XML File Post looks like:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:padding="5dp">
<ImageView
android:id="#+id/icon_image_view"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:src="#drawable/ic_baseline_ac_unit"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginBottom="8dp"
android:text="Title of blog post"
android:textColor="#android:color/black"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="#+id/textView2"
app:layout_constraintStart_toEndOf="#+id/icon_image_view"
app:layout_constraintTop_toTopOf="#+id/icon_image_view" />
<TextView
android:id="#+id/body"
android:layout_width="285dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="Body of first blog post"
app:layout_constraintBottom_toBottomOf="#+id/icon_image_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.050"
app:layout_constraintStart_toEndOf="#+id/icon_image_view" />
<LinearLayout
android:id="#+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="end">
<ImageView
android:id="#+id/edit_post_image"
android:layout_width="60dp"
android:layout_height="50dp"
android:src="#drawable/ic_baseline_edit" />
<ImageView
android:id="#+id/delete_post_image"
android:layout_width="60dp"
android:layout_height="50dp"
android:src="#drawable/ic_baseline_delete_forever" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
As you see in the RecyclerViewFragment, I have 2 functions that is onEditPost and onDeletePost. I want that functionality to work when I press it. Right now, it only works when I press and change the fragment view. I want it to work instantly. If anyone see my mistake or Am I missing something? please do tell me. All help is appreciate
You are creating Adapter twice, you already have an Adapter initialised globally use it in onViewCreated instead of assign again. because you are using global adapter to notify changes.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<RecyclerView>(R.id.recycleViewMain).apply{
layoutManager = LinearLayoutManager(requireActivity())
adapter = this#RecyclerViewFragment.adapter
}
}
I'm using Tmdb API and displaying it in a RecyclerView. The RecyclerView is not scrolling smoothly initially but then is working fine. I've tried to change Recyclerview height to 0dp or match_parent but it's still the same. I've tried also android:nestedScrollingEnabled="true". Please help me to fix this problem in the RecyclerView. Thank you in advance.
fragment_add.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.AddFragment">
<androidx.appcompat.widget.AppCompatAutoCompleteTextView
android:id="#+id/fragment_add_movieSeriesACT"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:background="#drawable/edit_text_background"
android:hint="#string/movie_or_series"
android:imeOptions="actionDone"
android:inputType="textEmailAddress"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="#color/colorPrimary"
android:textColorHint="#color/colorEight"
android:textCursorDrawable="#null"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/fragment_add_mainRV"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/fragment_add_movieSeriesACT" />
<ProgressBar
android:id="#+id/fragment_add_mainPB"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:indeterminateTint="#color/colorOne"
android:indeterminateTintMode="src_atop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
AddFragment.kt
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import com.martiandeveloper.muuvi.R
import com.martiandeveloper.muuvi.adapter.RecyclerViewMovieAdapter
import com.martiandeveloper.muuvi.viewmodel.AddViewModel
import kotlinx.android.synthetic.main.fragment_add.*
class AddFragment : Fragment() {
private lateinit var viewModel: AddViewModel
private val adapter = RecyclerViewMovieAdapter(arrayListOf())
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_add, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initUI()
}
private fun initUI() {
viewModel = ViewModelProviders.of(this).get(AddViewModel::class.java)
setRecyclerView()
observe()
viewModel.refreshData()
}
private fun setRecyclerView() {
fragment_add_mainRV.layoutManager = LinearLayoutManager(context)
fragment_add_mainRV.adapter = adapter
}
private fun observe() {
viewModel.movieList.observe(viewLifecycleOwner, Observer { movieList ->
movieList?.let {
adapter.updateMovieList(it)
}
})
viewModel.isError.observe(viewLifecycleOwner, Observer { isError ->
isError?.let {
setProgress(1F, true, View.GONE)
if (it) {
setToast(resources.getString(R.string.something_went_wrong_please_try_again_later))
}
}
})
viewModel.isLoading.observe(viewLifecycleOwner, Observer { isLoading ->
isLoading?.let {
if (it) {
setProgress(.5F, false, View.VISIBLE)
} else {
setProgress(1F, true, View.GONE)
}
}
})
}
private fun setProgress(alpha: Float, enable: Boolean, visible: Int) {
fragment_add_movieSeriesACT.alpha = alpha
fragment_add_mainRV.alpha = alpha
fragment_add_movieSeriesACT.isEnabled = enable
fragment_add_mainPB.visibility = visible
}
private fun setToast(text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
}
AddViewModel.kt
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.martiandeveloper.muuvi.model.MovieResult
import com.martiandeveloper.muuvi.model.Movie
import com.martiandeveloper.muuvi.service.MovieService
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.observers.DisposableSingleObserver
import io.reactivex.schedulers.Schedulers
class AddViewModel : ViewModel() {
private val movieService = MovieService()
private val disposable = CompositeDisposable()
val movieList = MutableLiveData<List<Movie>>()
val isError = MutableLiveData<Boolean>()
val isLoading = MutableLiveData<Boolean>()
fun refreshData() {
isLoading.value = true
disposable.add(
movieService.getData().subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<MovieResult>() {
override fun onSuccess(t: MovieResult) {
movieList.value = t.results
isError.value = false
isLoading.value = false
}
override fun onError(e: Throwable) {
isError.value = true
isLoading.value = false
}
})
)
}
}
RecyclerViewMovieAdapter.kt
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.martiandeveloper.muuvi.R
import com.martiandeveloper.muuvi.model.Movie
import kotlinx.android.synthetic.main.recyclerview_movie_item.view.*
class RecyclerViewMovieAdapter(private val movieList: ArrayList<Movie>) :
RecyclerView.Adapter<RecyclerViewMovieAdapter.RecyclerViewMovieViewHolder>() {
lateinit var context: Context
class RecyclerViewMovieViewHolder(var view: View) : RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RecyclerViewMovieViewHolder {
context = parent.context
val view = LayoutInflater.from(context)
.inflate(R.layout.recyclerview_movie_item, parent, false)
return RecyclerViewMovieViewHolder(view)
}
override fun getItemCount(): Int {
return movieList.size
}
override fun onBindViewHolder(holder: RecyclerViewMovieViewHolder, position: Int) {
holder.view.recyclerview_movie_item_movieTitleMTV.text =
movieList[position].movieTitle
holder.view.recyclerview_movie_item_movieVoteAverageMTV.text =
movieList[position].movieVoteAverage.toString()
Glide.with(context)
.load("https://image.tmdb.org/t/p/w300${movieList[position].moviePosterPath}")
.into(holder.view.recyclerview_movie_item_moviePosterIV)
}
fun updateMovieList(newMovieList: List<Movie>) {
movieList.clear()
movieList.addAll(newMovieList)
notifyDataSetChanged()
}
}
recyclerview_movie_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:paddingBottom="8dp">
<androidx.cardview.widget.CardView
android:layout_width="80dp"
android:layout_height="80dp"
app:cardBackgroundColor="#android:color/transparent"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<ImageView
android:id="#+id/recyclerview_movie_item_moviePosterIV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="#string/app_name"
android:scaleType="centerCrop"
app:srcCompat="#drawable/default_user_image" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="#+id/recyclerview_movie_item_movieTitleMTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/colorPrimary"
android:textSize="18sp"
android:textStyle="bold" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/recyclerview_movie_item_movieVoteAverageMTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/colorPrimaryDark" />
</LinearLayout>
</LinearLayout>
I believe this is due to size of the image , I had similar issue try to fit the image you obtain using glide into imageview, I have added centerCrop():
Glide.with(context).load("https://image.tmdb.org/t/p/w300${movieList[position].moviePosterPath}") .centerCrop().into(holder.view.recyclerview_movie_item_moviePosterIV);
If this does not work try to change imageview scaletype or centerCrop() to fitCenter()
I have a plain basic recycler view and its adapter.In the single recycler view row, I got a textView which are floating numbers.The textView width length shall fixed in proportional sizes, i.e. 30 % of the screen.I want to put OnGlobalLayoutListener to auto resize text downward passively whenever the text exceed over the separator lineso that it fits in the textView box prepared.I don't want WRAP the numbers
So I add OnGlobalLayoutListener on each ViewHolder and expect to detect & tracing each row items if the costTextView is Overlapped to the separator line.
However what happened is it does not resize all rows, but part of items that exposed on user screen only. Example 4-7 does not affected, 8-12 is affected, so on.
MainFragment.kt
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.elliot.financetracker.databinding.MainFragmentBinding
class MainFragment : Fragment() {
companion object {
private val TAG = "HomeFragment"
}
private lateinit var thisView: View
private lateinit var binding: MainFragmentBinding
private var cashList: ArrayList<Cash> = ArrayList()
private lateinit var recyclerView: RecyclerView
private lateinit var mAdapter: CashAdapter
init {
cashList = prepareMovieData()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding =
DataBindingUtil.inflate<MainFragmentBinding>(
inflater,
R.layout.main_fragment, container, false
)
thisView = binding.root
return thisView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView = binding.rvCash
mAdapter = CashAdapter(cashList, action)
val mLayoutManager: RecyclerView.LayoutManager = LinearLayoutManager(thisView.context)
recyclerView.layoutManager = mLayoutManager
recyclerView.itemAnimator = DefaultItemAnimator()
recyclerView.adapter = mAdapter
}
private val action = object : CashAdapter.ClickedListener {
override fun onItemClick(cash: Cash) {
val bundle = bundleOf("cash" to cash)
this#MainFragment.findNavController().navigate(R.id.edit_frag, bundle)
}
}
private val actionAddNewRecord = object : View.OnClickListener {
override fun onClick(v: View?) {
val arg = Bundle()
arg.putInt("sss", 43)
this#MainFragment.findNavController().navigate(R.id.edit_frag, arg)
}
}
private fun prepareMovieData(): ArrayList<Cash> {
for (i in 0..29) {
cashList.add(
Cash(
"-2000000000000000000000000.00",
"Action & Adventure $i"
)
)
}
return cashList
}
}
CashAdapter.kt
import android.graphics.Rect
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.elliot.financetracker.CashAdapter.CashViewHolder
import com.elliot.financetracker.databinding.CashListRowBinding
import com.elliot.financetracker.utils.*
//import com.example.recyclerviewbinding.databinding.MovieListRowBinding;
class CashAdapter(
private val cashList: List<Cash>,
private val clickedAction: ClickedListener
) : RecyclerView.Adapter<CashViewHolder>() {
companion object {
private var TAG = "CashAdapter"
}
inner class CashViewHolder(private val binding: CashListRowBinding) :
RecyclerView.ViewHolder(binding.root) {
var positionss: Int = -1
fun bind(cash: Cash, clickedAction: ClickedListener, positionss: Int) {
this.positionss = positionss
binding.cash = cash
binding.cvCashView.setOnClickListener {
clickedAction.onItemClick(binding.cash!!)
}
// ---------- NOTE HERE I ADD GLOBAL LAYOUT LISTENER ---------------------------------------------
this.binding.cvCashView.viewTreeObserver.addOnGlobalLayoutListener(globalObserver)
binding.executePendingBindings()
}
private val globalObserver = object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
val overlapped = isOverlapped(binding.tvItemCost, binding.separatorCost)
if (overlapped) {
Log.d(CashAdapter.TAG, "${CashAdapter.TAG}: isOverlapping : $positionss")
binding.tvItemCost.textSize = resizeTextDown(binding.tvItemCost)
// always assumed values in SP units
} else {
Log.d(CashAdapter.TAG, "${CashAdapter.TAG}: isNotOverlapping : $positionss")
}
}
}
private fun resizeTextDown(target: View): Float {
val t = target as TextView
val z = t.textSize
val a = z.pxToSp()
val b = a - 2.0F
// leave it as SP unit will do**
return b
}
}
interface ClickedListener {
fun onItemClick(cash: Cash)
}
fun isOverlapped(tvItemCost: View, g20: View): Boolean {
val firstPosition = IntArray(2)
val secondPosition = IntArray(2)
tvItemCost.getLocationOnScreen(firstPosition)
g20.getLocationOnScreen(secondPosition)
val rectFirstView = Rect(
firstPosition[0],
firstPosition[1],
firstPosition[0] + tvItemCost.measuredWidth,
firstPosition[1] + tvItemCost.measuredHeight
)
val rectSecondView = Rect(
secondPosition[0],
secondPosition[1],
secondPosition[0] + g20.measuredWidth,
secondPosition[1] + g20.measuredHeight
)
return rectFirstView.intersect(rectSecondView)
}
// plain basic recycler view adapter's methods
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CashViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = CashListRowBinding.inflate(layoutInflater, parent, false)
return CashViewHolder(binding)
}
override fun onBindViewHolder(holder: CashViewHolder, position: Int) {
val cash = cashList[position]
holder.bind(cash, clickedAction, position)
}
override fun getItemCount(): Int {
return cashList.size
}
}
cash_list_row.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>
<variable
name="cash"
type="com.elliot.financetracker.Cash" />
</data>
<androidx.cardview.widget.CardView
android:id="#+id/cv_cash_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="#color/colorLightGreen"
app:cardElevation="10dp"
app:cardCornerRadius="#dimen/default_card_corner_radius"
app:cardUseCompatPadding="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp">
<TextView
android:id="#+id/tv_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="#{cash.description}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="#id/g_20"
app:layout_constraintTop_toBottomOf="#id/tv_date"
tools:text="Item description Item description Item description Item description" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/g_20"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.7"/>
<View
android:id="#+id/separator_cost"
android:layout_width="1dp"
android:layout_height="0dp"
android:padding="20dp"
android:background="#android:color/darker_gray"
app:layout_constraintLeft_toRightOf="#id/g_20"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="#+id/tv_item_cost"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="#{cash.amountString}"
android:gravity="center_vertical"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="RM -2000000000.00" />
<!-- removed out ...........-->
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</layout>
This can be perfectly solved by the auto resizing feature of TextView
use something like this
<TextView
android:layout_width="100dp"
android:layout_height="40dp"
android:maxLines="1"
android:text="hello world google"
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="1sp"
app:autoSizeMaxTextSize="25sp"
android:gravity="center_vertical"
/>
I am trying to create a tabular layout for my Android application through an adapter class. However, the layout represented in the ViewPager does not change when I click on different tabs. Below is my code:
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
android:background="#ffffff"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay"/>
<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="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
tools:context=".MainActivity"
app:tabTextAppearance="#style/MyCustomTextAppearance"
>
<!-- The tabular layout -->
<com.google.android.material.tabs.TabLayout
android:id="#+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:textAllCaps="false"
android:background="#ffffff"
>
</com.google.android.material.tabs.TabLayout>
<!-- Contains the fragment corresponding to each tab -->
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
total_fragment.xml, individual_fragment.xml, usage_fragment.xml are all very basic layouts with just a TextView in them.
Below is my Kotlin code (MainActivity.kt):
import PageAdapter
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import android.util.Log
import androidx.viewpager.widget.ViewPager
import com.google.android.material.tabs.TabLayout
var tabLayout: TabLayout? = null
var viewPagerReference: ViewPager? = null
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tabLayout = findViewById<TabLayout>(R.id.tablayout)
viewPagerReference = findViewById<ViewPager>(R.id.viewPager)
tabLayout!!.addTab(tabLayout!!.newTab().setText("TabA"))
tabLayout!!.addTab(tabLayout!!.newTab().setText("TabB"))
tabLayout!!.addTab(tabLayout!!.newTab().setText("TabC"))
tabLayout!!.tabGravity = TabLayout.GRAVITY_FILL
val adapter = PageAdapter(this, supportFragmentManager, tabLayout!!.tabCount)
viewPager!!.adapter = adapter
viewPager!!.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout))
tabLayout!!.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
Log.i("TextStats","NEW TAB SELECTED: " + tab.position)
viewPager!!.currentItem = tab.position
}
override fun onTabUnselected(tab: TabLayout.Tab) {
}
override fun onTabReselected(tab: TabLayout.Tab) {
}
})
}
}
For the adapter class (PageAdapter.kt):
import android.content.Context
import android.util.Log
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import com.example.textstats.IndividualFragment
import com.example.textstats.TotalFragment
import com.example.textstats.UsageFragment
class PageAdapter(private val myContext: Context, fm: FragmentManager, internal var totalTabs: Int) : FragmentPagerAdapter(fm) {
// this is for fragment tabs
override fun getItem(position: Int): Fragment? {
Log.i("TextStats", "POSITION = " + position);
when (position) {
0 -> {
// val homeFragment: HomeFragment = HomeFragment()
return TotalFragment()
}
1 -> {
return IndividualFragment()
}
2 -> {
// val movieFragment = MovieFragment()
return UsageFragment()
}
else -> return null
}
}
// this counts total number of tabs
override fun getCount(): Int {
return totalTabs
}
}
TotalFragment, UsageFragment, and IndividualFragment have all the same code below, except for the layout id being different (R.layout.[layout name]):
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
class TotalFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater!!.inflate(R.layout.total_fragment, container, false)
}
}
add this line after set adapter
tabLayout.setupWithViewPager(viewPager)
class LayoutPagerAdapter internal
constructor(var mContext: Context):PagerAdapter() {
override fun isViewFromObject(view: View, `object`: Any): Boolean {
return view === `object` as ImageView
}
private val sliderImageId = intArrayOf(
R.drawable.sym_action_call,R.drawable.presence_audio_away
)
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val imageView = ImageView(mContext)
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP)
imageView.setImageResource(sliderImageId[position])
(container as ViewPager).addView(imageView, 0)
return imageView
}
override fun destroyItem(
container: ViewGroup,
position: Int,
`object`: Any
) {
(container as ViewPager).removeView(`object` as ImageView)
}
override fun getCount(): Int {
return sliderImageId.size
}
}