I've been scratching my head about this problem. I have a recyclerview inside a recyclerview that shows a grid layout where you can scroll both vertically and horizontally.
I decided to enlarge the width and height of each item. Previously, width and height were: 80 x 100
<?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:id="#+id/book_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/inner_dropshadow"
android:layout_width="80dp"
android:layout_height="100dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/bookdropshadow" />
</android.support.constraint.ConstraintLayout>
And then I enlarged it to 150x 180. And then Level 4-6 row completely disappeared no matter how much I scroll up. I only expected the recyclerview to display larger images. Why does this happen?
[EDIT] I ran a debug and it seems only 3 positions are bound in the outer adapter when the app is run on a phone but there are 4 positions when run on a tablet.
<?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:id="#+id/book_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/inner_dropshadow"
android:layout_width="150dp"
android:layout_height="180dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/bookdropshadow" />
</android.support.constraint.ConstraintLayout>
Please note that this also happens when I run the app on a device. In a tablet, it shows all the Levels, while on a phone, it only shows Levels 1-3.
Outer recyclerview layout
<?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:id="#+id/mainlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/color_white">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/swiperefresh"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#+id/toolbar">
<ScrollView
android:id="#+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="always">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/browsebooksheader_layout"
android:id="#+id/bb_header"
app:layout_constraintBottom_toTopOf="#+id/guidelinetop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="parent"/>
<android.support.constraint.Guideline
android:id="#+id/guidelinetop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.30"/>
<Button
android:id="#+id/btn_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:background="#drawable/search_btn_border"
android:padding="8dp"
android:text="Search"
android:textSize="#dimen/browse_search_textsize"
android:textColor="#android:color/darker_gray"
app:layout_constraintBottom_toTopOf="#+id/recyclerview_main"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/bb_header" />
<ProgressBar
android:id="#+id/main_progressbar"
style="?android:attr/progressBarStyleSmall"
android:layout_marginTop="16dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:indeterminateTint="#color/color_grey"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btn_search"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:nestedScrollingEnabled="false"
android:background="#color/color_white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btn_search"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.constraint.ConstraintLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="#+id/toolbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/swiperefresh"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<include layout="#layout/toolbar_layout_wo_learn"/>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
Inner recyclerview layout
<?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="wrap_content">
<TextView
android:id="#+id/book_category"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Category Title"
android:textColor="#color/defaultdark_color"
android:textSize="#dimen/browse_category_textsize"
android:fontFamily="#font/vagroundedstd_bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="#dimen/browse_seeall_imagesize"
android:layout_height="#dimen/browse_seeall_imagesize"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
android:layout_marginEnd="8dp"
android:id="#+id/see_all_btn"
android:src="#drawable/seeall"
app:layout_constraintBottom_toTopOf="#+id/inner_recyclerview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.v7.widget.RecyclerView
android:id="#+id/inner_recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:background="#color/color_white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/book_category"/>
<TextView
android:id="#+id/h_line"
android:layout_width="match_parent"
android:layout_height="0.8dp"
android:background="#color/color_grey"
android:gravity="center"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:alpha="0.5"
app:layout_constraintTop_toBottomOf="#id/inner_recyclerview"/>
</android.support.constraint.ConstraintLayout>
Outer Adapter
class BrowseBooksAdapter : RecyclerView.Adapter<BrowseBooksAdapter.SimpleViewHolder>() {
private var sortedList : SortedMap<String, List<ResourcesList>> = emptyMap<String, List<ResourcesList>>().toSortedMap()
fun setData(sortedList : SortedMap<String, List<ResourcesList>>) {
if (this.sortedList != sortedList) {
this.sortedList = sortedList
notifyDataSetChanged()
}
}
override fun getItemCount(): Int {
return sortedList.count()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SimpleViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.browsebooks_layout, parent, false)
return SimpleViewHolder(view)
}
override fun onBindViewHolder(holder: SimpleViewHolder, position: Int) {
val dataList = ArrayList(sortedList.values)[position]
holder.category_text.text = ArrayList(sortedList.keys)[position]
holder.booksByCategory = dataList
holder.horizontalAdapter.setData(dataList)
}
class SimpleViewHolder(v: View, var booksByCategory : List<ResourcesList>? = null) : RecyclerView.ViewHolder(v) {
var recyclerView : RecyclerView = v.findViewById(R.id.inner_recyclerview)
var category_text : TextView = v.findViewById(R.id.book_category)
var fragmentCallback: FragmentCallback? = null
val horizontalAdapter: BooksByCategoryAdapter
init {
recyclerView.layoutManager = object : LinearLayoutManager(v.context, LinearLayoutManager.HORIZONTAL, false) {
override fun canScrollHorizontally(): Boolean {
return true
}
override fun canScrollVertically(): Boolean {
return false
}
}
horizontalAdapter = BooksByCategoryAdapter()
recyclerView.adapter = horizontalAdapter
fragmentCallback = v.context as FragmentCallback
var mLastClickTime : Long = 0
v.see_all_btn.setOnClickListener {
val category = v.book_category.text.toString()
// double-clicking prevention, using threshold of 1000 ms
if (!(SystemClock.elapsedRealtime() - mLastClickTime < 1000)){
mLastClickTime = SystemClock.elapsedRealtime()
if(fragmentCallback != null){
fragmentCallback!!.callBackSeeAllFragment(booksByCategory!!, category)
}
}
}
}
}
}
Inner Adapter
class BooksByCategoryAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var booksByCategory: List<ResourcesList> = emptyList()
fun setData(data: List<ResourcesList>) {
if (this.booksByCategory != data) {
this.booksByCategory = data
notifyDataSetChanged()
}
}
override fun getItemCount(): Int {
return booksByCategory.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.bookcontainer_layout, parent, false)
return CustomVH(cellForRow)
}
override fun onBindViewHolder(rawHolder: RecyclerView.ViewHolder, position: Int) {
val holder = rawHolder as CustomVH
val booksProcessModel = BooksProcessModel()
booksProcessModel.setItem(booksByCategory[position])
holder.booksByCategory = booksByCategory[position]
Picasso.get().load(booksByCategory[position].coverURI).into(holder.inner_bookcover)
}
private inner class CustomVH(v: View, var booksByCategory : ResourcesList? = null): RecyclerView.ViewHolder(v) {
var inner_bookcover : ImageView = v.findViewById(R.id.inner_bookcover)
var fragmentCallback: FragmentCallback? = null
var ctx : Context = v.context
var mLastClickTime : Long = 0
init {
fragmentCallback = ctx as FragmentCallback
v.setOnClickListener {
// double-clicking prevention, using threshold of 1000 ms
if (!(SystemClock.elapsedRealtime() - mLastClickTime < 1000)){
mLastClickTime = SystemClock.elapsedRealtime()
if(fragmentCallback != null){
fragmentCallback!!.callBackDownloadBookFragment(booksByCategory!!)
}
}
}
}
}
class BooksProcessModel {
private var processBooks : ResourcesList? = null
fun setItem(books: ResourcesList) {
this.processBooks = books
}
fun setVisibility(ctx: Context) : Int {
val sp = PreferenceManager.getDefaultSharedPreferences(ctx)
val page = sp.getString(processBooks!!.id.toString(), "")
if (page != "") {
return View.VISIBLE
}
return View.INVISIBLE
}
}
}
You need to change the layout_height of the outer recycler view to wrap_content.
Nevermind, figured it out. I should be using a NestedScrollView instead of a ScrollView. The missing items were still there. It's just that I couldn't scroll the nested recyclerview.
In my case it was because I had the RecyclerView directly and even though it scrolled (horizontally), the same thing happened to me if for example the keyboard appeared.
Solution: Put the RecyclerView inside a NestedScrollView and delegate the scroll to it, like this:
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="#layout/item"/>
</androidx.core.widget.NestedScrollView>
Related
I am trying to build a layout in which there is a recyclerview where each item has three edittexts horizontally aligned. I want to add the next item when a button is pressed.
This the design I am trying to build
This is the Adapter class for the recycler view
class AddProjectAdapter(private val itemsList : ArrayList<ItemDetails>) : RecyclerView.Adapter<AddProjectAdapter.AddProjectViewHolder>() {
inner class AddProjectViewHolder(private val binding : AddProjectItemViewBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item : ItemDetails){
binding.item = item
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddProjectViewHolder{
return AddProjectViewHolder(AddProjectItemViewBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: AddProjectViewHolder, position: Int) {
holder.bind(itemsList[position])
}
override fun getItemCount(): Int = itemsList.size
}
Layout file for the item add_project_item_view.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">
<data>
<variable
name="item"
type="com.example.hero.models.ItemDetails" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.card.MaterialCardView
android:id="#+id/item_name_cardview"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:flow_horizontalBias="3"
app:cardBackgroundColor="#color/light_background"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="#id/g_v_1"
android:layout_marginRight="5dp">
<EditText
android:id="#+id/item_name_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Item Name"
android:text="#={item.itemName}"
android:textColor="#color/t_dark"
android:fontFamily="#font/metropolislight"
android:textSize="14dp"
android:textColorHint="#color/t_dark"
android:background="#null"
android:padding="10dp" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="#+id/rate_unit_cardview"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:flow_horizontalBias="2"
android:layout_marginRight="5dp"
app:cardBackgroundColor="#color/light_background"
app:layout_constraintStart_toEndOf="#id/g_v_1"
app:layout_constraintEnd_toEndOf="#id/g_v_2">
<EditText
android:id="#+id/rate_unit_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Rate/Unit"
android:text="#={item.rate}"
android:textColorHint="#color/t_dark"
android:textColor="#color/t_dark"
android:fontFamily="#font/metropolislight"
android:textSize="14dp"
android:padding="10dp"
android:background="#null" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="#+id/unit_cardview"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:flow_horizontalBias="2"
app:cardBackgroundColor="#color/light_background"
app:layout_constraintStart_toEndOf="#id/g_v_2"
app:layout_constraintEnd_toEndOf="parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/unit_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:background="#null"
android:fontFamily="#font/metropolislight"
android:hint="Unit"
android:padding="10dp"
android:text="#={item.unit}"
android:textColor="#color/t_dark"
android:textColorHint="#color/t_dark"
android:textSize="14dp" />
<ImageView
android:id="#+id/drop_down_imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:src="#drawable/dropdown"
android:layout_centerVertical="true" />
</RelativeLayout>
</com.google.android.material.card.MaterialCardView>
<androidx.constraintlayout.widget.Guideline
android:id="#+id/g_v_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.4" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/g_v_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.8" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
`
//code of the activity in which the recycler view is present
private val itemsList : ArrayList<ItemDetails> = ArrayList()
private lateinit var addProjectAdapter: AddProjectAdapter
private lateinit var binding: ActivityAddProjectBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAddProjectBinding.inflate(layoutInflater)
setContentView(binding.root)
setAdapter()
setListner()
}
private fun setAdapter(){
addProjectAdapter = AddProjectAdapter(itemsList)
binding.itemDetailsRecyclerview.apply {
layoutManager = LinearLayoutManager(this#AddProject)
adapter = addProjectAdapter
}
}
override fun onClick(v : View?) {
when(v?.id){
binding.addItemButton.id -> {
itemsList.add(ItemDetails())
addProjectAdapter.notifyItemInserted(itemsList.size-1)
}
}
}
private fun setListner(){
binding.addItemButton.setOnClickListener(this)
}
data class ItemDetails(
var itemName : String = "",
var rate : String = "",
var unit : String = "")
And my recyclerview id is item_details_recyclerview.
Output for the above code
When I click the add button the item added is not visible but the add button moves down.
Can someone help me out with this issue? And also if you could provide some better approach it would be great.
Thanks in advance.
Change height of ConstraintLayout from add_project_item_view.xml
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
to this
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
As the official material.io documentation says:
Chips are compact elements that represent an input, attribute, or action.
I want to add chips as attributes (tags) for any blog post, a recyclerview is used to show the list of blog posts:
item_post.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"
android:id="#+id/mcv_container_item_blog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:clickable="true"
android:background="?selectableItemBackground"
app:cardUseCompatPadding="true">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="#+id/llc_container_item_project"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/item_margin"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/aciv_item_post_image"
android:layout_width="match_parent"
android:layout_height="#dimen/item_project_Image_size"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
app:srcCompat="#drawable/drawer_bg"/>
<com.google.android.material.textview.MaterialTextView
android:id="#+id/mtv_item_post_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="#dimen/item_margin"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:textColorHighlight="#color/design_default_color_background"
android:text="Chameleon UI Kit" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/mtv_item_post_date"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#color/red_lighten"
android:layout_marginTop="#dimen/user_name_margin_start"
android:layout_marginEnd="#dimen/user_name_margin_start"
android:layout_marginBottom="#dimen/user_name_margin_start"
android:text="10 Sept"/>
<com.google.android.material.textview.MaterialTextView
android:id="#+id/mtv_item_post_short_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="#style/TextAppearance.AppCompat.Small"
android:text="#string/about_me" />
<include layout="#layout/item_divider"/>
<HorizontalScrollView
android:id="#+id/hsv_item_post_tags"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<com.google.android.material.chip.ChipGroup
android:id="#+id/cg_item_post_tags"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:singleLine="true"/>
</HorizontalScrollView>
</androidx.appcompat.widget.LinearLayoutCompat>
</com.google.android.material.card.MaterialCardView>
then the PostAdapter class:
class PostAdapter(
private val postList: List<Post>
): RecyclerView.Adapter<PostAdapter.PostViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_post, parent, false)
return PostViewHolder(view)
}
override fun onBindViewHolder(holder: PostViewHolder, position: Int) {
val post = postList[position]
holder.postTitle.text = post.postTitle
holder.postDescription.text = post.postDescription
holder.postDate.text = post.postDate
val tagChip = Chip(holder.itemView.context).apply {
id = View.generateViewId()
text = "label"
}
holder.postTagsChipGroup.addView(tagChip)
}
override fun getItemCount(): Int {
return postList.size
}
inner class PostViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val postTitle: MaterialTextView = itemView.findViewById(R.id.mtv_item_post_title)
val postDescription: MaterialTextView = itemView.findViewById(R.id.mtv_item_post_short_description)
val postTagsChipGroup: ChipGroup = itemView.findViewById(R.id.cg_item_post_tags)
val postDate: MaterialTextView = itemView.findViewById(R.id.mtv_item_post_date)
}
}
at this stage a weird thing happens, when I 'scroll recyclerview' other Chips are added from nowhere as you can see this behavior in gif below:
how to avoid such behavior, what changes should I make in adapters onBindViewHolder method?
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>
Using LiveData<PagedList> to display a recycler view that hits a Room database. When I do a search on screen, the number of items returned is less, however, there seems to be a lot of extra white space between the recycler view and the other buttons. The recycler view itself shrinks, and then moves down on the screen.
Here is the way it looks before the data is refreshed (before I do a search):
Here is after:
Here is my adapter for the recyclerView:
class WorkPackagesRecyclerAdapter(
private val onWorkPackageClickListener: OnClickWorkPackage
) : PagedListAdapter<Workpackage,
WorkPackagesRecyclerAdapter.WorkPackagesViewHolder>(
REPO_COMPARATOR
) {
interface OnClickWorkPackage {
fun clickWorkPackage(workPackageId: String)
}
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): WorkPackagesViewHolder {
val inflater = LayoutInflater.from(viewGroup.context)
val binding = inflate<ItemWorkPackageBinding>(
inflater,
R.layout.item_work_package,
viewGroup,
false
)
return WorkPackagesViewHolder(binding, viewGroup.context)
}
override fun onBindViewHolder(holder: WorkPackagesViewHolder, position: Int)
{
getItem(position)?.let {
holder.bind(it)
}
}
inner class WorkPackagesViewHolder(
internal var binding: ItemWorkPackageBinding,
internal val context: Context
) : RecyclerView.ViewHolder(binding.root), KoinComponent {
fun bind(data: Workpackage) {
val itemWorkPackagesViewModel: ItemWorkPackagesViewModel by inject{ parametersOf(data)}
this.binding.listItem.setOnClickListener {
onWorkPackageClickListener.clickWorkPackage(data.id)
}
this.binding.viewmodel = itemWorkPackagesViewModel
this.binding.executePendingBindings()
}
}
companion object {
private val REPO_COMPARATOR =
object : DiffUtil.ItemCallback<Workpackage>() {
override fun areItemsTheSame(
oldItem: Workpackage,
newItem: Workpackage
): Boolean =
oldItem.id == newItem.id
override fun areContentsTheSame(
oldItem: Workpackage,
newItem: Workpackage
): Boolean =
oldItem == newItem
}
}
Here is how I set up the PagedList:
val searchQuery: MutableLiveData<SearchAndSort> = MutableLiveData(
SearchAndSort("",
WorkpackagesRepository.Companion.SortedBy.WorkPackageNumber,
AscendDescend.ASC))
var workPackagesList = Transformations.switchMap(searchQuery) { searchQuery ->
val factory = workPackageStorageDao.searchWorkpackages(
searchQuery.searchText,
searchQuery.sortBy.type + " " + searchQuery.ascendDescend.text
)
val pagedListBuilder = LivePagedListBuilder<Int, Workpackage>(factory, pagingLimit)
pagedListBuilder.build()
}
Here is where I am observing the adapter:
workPackagesViewModel.workPackagesList.observe(this, Observer { wpList ->
wpList ?: return#Observer
adapter = WorkPackagesRecyclerAdapter(this)
adapter.submitList(wpList)
binding.workPackagesRecyclerView.adapter = adapter
adapter.notifyDataSetChanged()
})
Here is the layout of the recycler view
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.bechtel.pf.ui.workpackages.WorkPackagesViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="727dp"
tools:layout_editor_absoluteY="1dp">
<EditText
android:id="#+id/search_bar_edit_text"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_marginTop="#dimen/work_package_margin"
android:hint="#string/work_packages_search_hint"
app:layout_constraintBottom_toBottomOf="#id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<ImageButton
android:id="#+id/search_bar_magnifying_glass"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginTop="#dimen/work_package_margin"
android:background="#drawable/icons_search"
app:layout_constraintBottom_toBottomOf="#id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintGuide_begin="#dimen/work_package_search_guideline_start"
/>
<com.google.android.material.button.MaterialButton
android:id="#+id/btnSortBy"
android:layout_width="0dp"
android:layout_height="45dp"
android:layout_marginTop="5dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:backgroundTint="#color/colorPrimary"
app:layout_constraintEnd_toStartOf="#+id/btnAscendDescend"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/guideline"
app:layout_constraintHorizontal_chainStyle="spread"
android:text="#string/work_packages_sort"
/>
<com.google.android.material.button.MaterialButton
android:id="#+id/btnAscendDescend"
android:layout_width="0dp"
android:layout_height="45dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:backgroundTint="#color/colorPrimary"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintEnd_toStartOf="#+id/btnFilterBy"
app:layout_constraintStart_toEndOf="#+id/btnSortBy"
app:layout_constraintTop_toBottomOf="#+id/guideline"
app:layout_constraintHorizontal_chainStyle="spread"
android:text="#string/work_packages_ascend"
android:onClick="#{() -> viewModel.ascendingDescending()}"
/>
<com.google.android.material.button.MaterialButton
android:id="#+id/btnFilterBy"
android:layout_width="0dp"
android:layout_height="45dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:backgroundTint="#color/colorPrimary"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/btnAscendDescend"
app:layout_constraintTop_toBottomOf="#+id/guideline"
android:text="#string/work_packages_filter"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/workPackagesRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:layout_marginTop="75dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btnFilterBy">
</androidx.recyclerview.widget.RecyclerView>
</androidx.constraintlayout.widget.ConstraintLayout>
Anyone ever seen anything like this before? I've also made sure that I am using layout_height = wrap_content in my recyclerview layout and item.xml files.
Screenshot inspector:
OK, it looks like I had set the recyclerview layout_constraintBottom_toBottomOf was set to 'parent' and since the total height of the recyclerview shrunk when the number of items shrunk, it was moving the view to the bottom of the screen. I also could get rid of the top margin attribute and the recycler view stayed below the buttons. So now I have:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/workPackagesRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btnFilterBy"/>
And it all looks good!
activity_main.xml code:
<?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="sampleex_recycleview.himanshu.recycleview.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/RecViewId"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Data List for RecyclerView in main activity, list_row.xml code:
<?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:background="#color/cardview_shadow_end_color"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="375dp"
android:layout_height="140dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="4dp"
android:layout_marginTop="8dp"
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_toTopOf="parent"
app:layout_constraintVertical_bias="0.0">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/imageViewPhoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginStart="13dp"
android:padding="25dp"
app:srcCompat="#mipmap/ic_launcher_round" />
<TextView
android:id="#+id/AgeTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_below="#+id/NameTextView"
android:layout_marginTop="14dp"
android:layout_toRightOf="#id/imageViewPhoto"
android:hint="age"
android:padding="5dp" />
<TextView
android:id="#+id/NameTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/imageViewPhoto"
android:layout_marginTop="22dp"
android:layout_toEndOf="#+id/imageViewPhoto"
android:hint="name"
android:padding="5dp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
Main Activity code here :
class MainActivity : AppCompatActivity() {
private var adapter : PersonListAdapter? = null
private var layoutManager : RecyclerView.LayoutManager? = null
private var personList : ArrayList<Person>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
personList = ArrayList<Person>()
layoutManager = LinearLayoutManager(this)
adapter = PersonListAdapter(personList!!, this)
//set up recycle view
RecViewId.layoutManager = layoutManager
RecViewId.adapter = adapter
// loading data
for(i in 1..15){
var person = Person()
person.name = "Person " + i
person.age = 21 + i
}
adapter!!.notifyDataSetChanged()
}
}
Adapter Class' code (exists in package name 'Data') :
// adapter class for the inflating layout for list in the recycler view and parsing data into the feild components of the layout
class PersonListAdapter(private var list : ArrayList<Person>, var context: Context) :
RecyclerView.Adapter<PersonListAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.list_row, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
holder?.bindItem(list[position])
}
override fun getItemCount(): Int {
return list.size
}
class ViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView) {
fun bindItem(person: Person){
var Name : TextView = itemView.findViewById(R.id.NameTextView) as TextView
var Age : TextView = itemView.findViewById(R.id.AgeTextView) as TextView
Name.text = person.name
Age.text = person.age.toString()
}
}
}
Person Class' code (exists in different package name 'Model'):
// class for the layout of the data to be shown in the data list in the Recycler view in the main activity
class Person{
var name : String? = null
var age : Int? = null
}
Your just creating person object. Those objects need to be added to the personList.
for(i in 1..15){
var person = Person()
person.name = "Person " + i
person.age = 21 + i
personList.add(person) // Add person object to the list
}
Also you aren't correctly initializing the RecyclerView in the above code.
//set up recycle view
val recView = findViewById<RecyclerView>(R.id.RecViewId)
recView.layoutManager = layoutManager
recView.adapter = adapter
Let's try this code:
<android.support.v7.widget.RecyclerView
android:id="#+id/RecViewId"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
and set fixed size for RecyclerView