I am trying to create a method that will listen to the item that is clicked and if the category is clicked, it should open the activity selected. I have a Grid Layout setup, but I am not really sure how I would set an onClickListener or an onRecyclerViewItemClick for this purpose. I do have a pseudocode that I believe could be implemented, but would need guidance for this.
Here is my MainActivity.kt:
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
private lateinit var appBarConfiguration: AppBarConfiguration
private var mAuthListener : FirebaseAuth.AuthStateListener? = null
lateinit var myApi: IMyAPI
lateinit var txt_user_name:TextView
lateinit var txt_email_address:TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
myApi = RetrofitClient.getInstance().create(IMyAPI::class.java)
Log.d("__MAIN__", "OnCreate")
}
private fun initView() {
recycler_view.layoutManager = GridLayoutManager(this, 2)
recycler_view.addItemDecoration(GridItemDecoration(10, 2))
val categoryListAdapter = CategoryListGridRecyclerAdapter()
recycler_view.adapter = categoryListAdapter
categoryListAdapter.setProductList(generateDummyData())
val navView: NavigationView = findViewById(R.id.nav_view)
navView.setNavigationItemSelectedListener(this)
navView.bringToFront()
}
private fun generateDummyData(): List<Category> {
val listOfCategory = mutableListOf<Category>()
var categoryModel = Category(1, "Products", R.drawable.kumo_logo, 1.90)
listOfCategory.add(categoryModel)
categoryModel = Category(2, "Food", R.drawable.kumo_logo, 1.90)
listOfCategory.add(categoryModel)
categoryModel = Category(3, "Technology", R.drawable.kumo_logo, 1.90)
listOfCategory.add(categoryModel)
categoryModel = Category(4, "News", R.drawable.kumo_logo, 1.90)
listOfCategory.add(categoryModel)
categoryModel = Category(5, "Economy", R.drawable.kumo_logo, 1.90)
listOfCategory.add(categoryModel)
categoryModel = Category(6, "Sports", R.drawable.kumo_logo, 1.90)
listOfCategory.add(categoryModel)
return listOfCategory
}
//Pseudocode:
/*override fun onRecyclerViewItemClick(category: Category, position: Int)
{
lateinit var catClick: Intent
if(category.id == 1)
{
catClick = Intent(this, ShowProducts::class.java)
}
startActivity(catClick) //start activity selected
}*/
override fun onNavigationItemSelected(item: MenuItem):Boolean {
when (item.itemId) {
R.id.nav_sign_out -> {
Log.d("Testing logout ", "user")
FirebaseAuth.getInstance().signOut()
startActivity(Intent(this#MainActivity, LoginActivity::class.java))
finish()
}
}
return true
}
}
GridItemDecoration.kt:
class GridItemDecoration(gridSpacingPx: Int, gridSize: Int) : RecyclerView.ItemDecoration() {
private var mSizeGridSpacingPx: Int = gridSpacingPx
private var mGridSize: Int = gridSize
private var mNeedLeftSpacing = false
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val frameWidth = ((parent.width - mSizeGridSpacingPx.toFloat() * (mGridSize - 1)) / mGridSize).toInt()
val padding = parent.width / mGridSize - frameWidth
val itemPosition = (view.getLayoutParams() as RecyclerView.LayoutParams).viewAdapterPosition
if (itemPosition < mGridSize) {
outRect.top = 0
} else {
outRect.top = mSizeGridSpacingPx
}
if (itemPosition % mGridSize == 0) {
outRect.left = 0
outRect.right = padding
mNeedLeftSpacing = true
} else if ((itemPosition + 1) % mGridSize == 0) {
mNeedLeftSpacing = false
outRect.right = 0
outRect.left = padding
} else if (mNeedLeftSpacing) {
mNeedLeftSpacing = false
outRect.left = mSizeGridSpacingPx - padding
if ((itemPosition + 2) % mGridSize == 0) {
outRect.right = mSizeGridSpacingPx - padding
} else {
outRect.right = mSizeGridSpacingPx / 2
}
} else if ((itemPosition + 2) % mGridSize == 0) {
mNeedLeftSpacing = false
outRect.left = mSizeGridSpacingPx / 2
outRect.right = mSizeGridSpacingPx - padding
} else {
mNeedLeftSpacing = false
outRect.left = mSizeGridSpacingPx / 2
outRect.right = mSizeGridSpacingPx / 2
}
outRect.bottom = 0
}
}
CategoryListGridRecyclerAdapter.kt
class CategoryListGridRecyclerAdapter:RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var listOfCategory = listOf<Category>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return CategoryListViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.product_row, parent, false))
}
override fun getItemCount(): Int = listOfCategory.size
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
val productViewHolder = viewHolder as CategoryListViewHolder
productViewHolder.bindView(listOfCategory[position])
}
fun setProductList(listOfCategory: List<Category>) {
this.listOfCategory = listOfCategory
notifyDataSetChanged()
}
}
You can do something like this:
lateinit var mOnItemClickListener: (category: Category,pos: Int)-> Unit
fun setOnItemClickListener(callback: (category: Category,pos: Int)-> Unit){
mOnItemClickListener = callback
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return CategoryListViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.product_row, parent, false))
.listen {pos,_ ->
if(::mOnItemClickListener.isInitialized){
mOnItemClickListener(listOfCategory[pos],pos)
}
}
}
//I took this code from a post a while back, don't know the author
fun <T : RecyclerView.ViewHolder> T.listen(event: (position: Int, type: Int) -> Unit): T {
itemView.setOnClickListener {
event.invoke(adapterPosition, itemViewType)
}
return this
}
On MainActivity
categoryListAdapter.setOnItemClickListener { category, pos ->
...
}
Related
i am not able to do the lazy loading for the images
this is my code of the adapter. I am providing it all the urls from my database in the form of list please help
class Horizontal_lower_recycler(private val images:List<Image>,private val context: Context,private val recyclerView: RecyclerView):
RecyclerView.Adapter<Horizontal_lower_recycler.MyViewHolder>() {
private var isLoading = false
init {
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val visibleItemCount = layoutManager.childCount
val totalItemCount = layoutManager.itemCount
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
if (!isLoading) {
if (visibleItemCount + firstVisibleItemPosition >= totalItemCount
&& firstVisibleItemPosition >= 0
&& totalItemCount >= PAGE_SIZE
) {
isLoading = true
}
}
}
})
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val image1: ImageView = itemView.findViewById(R.id.lower_image1)
val image2: ImageView = itemView.findViewById(R.id.lower_image2)
val image3: ImageView = itemView.findViewById(R.id.lower_image3)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.bootm_recycler,
parent, false
)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return images.size / 3 + images.size % 3
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val images = images.subList(position * 3, min(position * 3 + 3, images.size))
for (i in images.indices) {
when (i) {
0 -> {
Glide.with(holder.itemView).load(images[i].url).apply(RequestOptions().format(
DecodeFormat.PREFER_RGB_565).disallowHardwareConfig())
.into(holder.image1)
holder.image1.setOnClickListener {
val intent = Intent(context, image_full_size_avtivity::class.java)
intent.putExtra("url", images[i].url)
context.startActivity(intent)
}
}
1 -> {
Glide.with(holder.itemView).load(images[i].url).apply(RequestOptions().format(
DecodeFormat.PREFER_RGB_565).disallowHardwareConfig())
.into(holder.image2)
holder.image2.setOnClickListener {
val intent = Intent(context, image_full_size_avtivity::class.java)
intent.putExtra("url", images[i].url)
context.startActivity(intent)
}
}
2 -> {
Glide.with(holder.itemView).load(images[i].url).apply(RequestOptions().format(
DecodeFormat.PREFER_RGB_565).disallowHardwareConfig())
.into(holder.image3)
holder.image3.setOnClickListener {
val intent = Intent(context, image_full_size_avtivity::class.java)
intent.putExtra("url", images[i].url)
context.startActivity(intent)
}
}
}
}
}
}
Help me in doing the lazy loading. When i open the app all the images are loaded at once without scrolling the recycler view.
I am unable to used pagination in HeterogeneousRecyclerview.it contents horizontal and vertical recycleview. I want to use pagination in VerticalRecycleview endless. When I try to do that it will not satisfy conditon.I will return visibleItemCount,totalItemCount and firstVisibleItemPosition that will not help to load more data. Please suggest me.
Recycle view:
val recyclerView = findViewById<RecyclerView>(R.id.rvMain)
val adapter = MainAdapter(this, getObject())
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
recycleViewScroll(recyclerView)
private fun getObject(): ArrayList<Any> {
jsonHelper?.readHorizontalItemsJSON()?.get(0)?.let { objects.add(it) }
jsonHelper?.readVerticalItemsJSON()?.get(0)?.let { objects.add(it) }
jsonHelper?.readHorizontalItemsJSON()?.get(0)?.let { objects.add(it) }
return objects as ArrayList<Any>
}
private fun recycleViewScroll(recyclerView: RecyclerView) {
recyclerView.addOnScrollListener(object :
RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val visibleItemCount = recyclerView.layoutManager!!.childCount
val totalItemCount = recyclerView.layoutManager!!.itemCount
val firstVisibleItemPosition =
(recyclerView.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
val lastpositionView = (recyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition()
//val lastpositionView = (recyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition()
println("dsfdsfdsfdfd----->" + visibleItemCount + "," + totalItemCount + "," + firstVisibleItemPosition!! +","+lastpositionView)
if (!isLoading && !isLastPage) {//
if (visibleItemCount + firstVisibleItemPosition!! >= totalItemCount && firstVisibleItemPosition!! >= 0 && totalItemCount >= PAGE_SIZE) {
loadMoreItems()
}
}
}
})
}
}`
Adapter:
class MainAdapter(private val context: Context, private val items: ArrayList<Any>?=null) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val jsonHelper = JsonHelper(context)
//this method returns the number according to the Vertical/Horizontal object
override
fun getItemViewType(position: Int): Int {
if (items?.get(position) is HorizontalModel)
return HORIZONTAL
return if (items?.get(position) is VerticalModel) VERTICAL else -1
}
override
fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(context)
val view: View
val holder: RecyclerView.ViewHolder
when (viewType) {
HORIZONTAL -> {
view = inflater.inflate(R.layout.layout_horizontal_recycler_view, parent, false)
holder = HorizontalViewHolder(view)
}
VERTICAL -> {
view = inflater.inflate(R.layout.layout_vertical_recycler_view, parent, false)
holder = VerticalViewHolder(view)
}
else -> {
view = inflater.inflate(R.layout.layout_horizontal_recycler_view, parent, false)
holder = HorizontalViewHolder(view)
}
}
return holder
}
override
fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder.itemViewType == HORIZONTAL)
horizontalView(holder as HorizontalViewHolder)
else if (holder.itemViewType == VERTICAL)
verticalView(holder as VerticalViewHolder)
}
private fun horizontalView(holder: HorizontalViewHolder) {
val adapter = HorizontalAdapter(context, jsonHelper.readHorizontalItemsJSON()!!)
holder.recyclerView.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
holder.recyclerView.adapter = adapter
}
private fun verticalView(holder: VerticalViewHolder) {
val adapter = VerticalAdapter(context, jsonHelper.readVerticalItemsJSON()!!)
holder.recyclerView.layoutManager = LinearLayoutManager(context)
holder.recyclerView.adapter = adapter
}
override
fun getItemCount(): Int {
return items?.size ?:0
}
inner class HorizontalViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal var recyclerView: RecyclerView = itemView.findViewById(R.id.rvHorizontal)
}
inner class VerticalViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal var recyclerView: RecyclerView = itemView.findViewById(R.id.rvVertical)
}
}
I have recyclerview with horizontal scroll and it snaps by default with LinearSnapHelper or PagerSnapHelper to the center of item. I want to snap to the left side of the each item. Is it possible?
You can easily extend PagerSnapHelper to align items by left side. There is one trick only needed with last item.
My solution is:
class AlignLeftPagerSnapHelper : PagerSnapHelper() {
private var horizontalHelper: OrientationHelper? = null
override fun findSnapView(layoutManager: RecyclerView.LayoutManager?): View? {
return getStartView(layoutManager as LinearLayoutManager, getHorizontalHelper(layoutManager))
}
private fun getStartView(layoutManager: LinearLayoutManager, helper: OrientationHelper): View? {
val firstVisibleChildPosition = layoutManager.findFirstVisibleItemPosition()
val lastCompletelyVisibleChildPosition = layoutManager.findLastCompletelyVisibleItemPosition()
val lastChildPosition = layoutManager.itemCount - 1
if (firstVisibleChildPosition != RecyclerView.NO_POSITION) {
var childView = layoutManager.findViewByPosition(firstVisibleChildPosition)
if (helper.getDecoratedEnd(childView) < helper.getDecoratedMeasurement(childView) / 2) {
childView = layoutManager.findViewByPosition(firstVisibleChildPosition + 1)
} else if (lastCompletelyVisibleChildPosition == lastChildPosition) {
childView = layoutManager.findViewByPosition(lastChildPosition)
}
return childView
}
return null
}
override fun calculateDistanceToFinalSnap(layoutManager: RecyclerView.LayoutManager, targetView: View): IntArray =
intArrayOf(distanceToStart(targetView, getHorizontalHelper(layoutManager)), 0)
override fun findTargetSnapPosition(
layoutManager: RecyclerView.LayoutManager,
velocityX: Int,
velocityY: Int
): Int {
val currentView = findSnapView(layoutManager) ?: return RecyclerView.NO_POSITION
val currentPosition = layoutManager.getPosition(currentView)
return if (velocityX < 0) {
(currentPosition - 1).coerceAtLeast(0)
} else {
(currentPosition + 1).coerceAtMost(layoutManager.itemCount - 1)
}
}
private fun distanceToStart(targetView: View, helper: OrientationHelper): Int =
helper.getDecoratedStart(targetView) - helper.startAfterPadding
private fun getHorizontalHelper(layoutManager: RecyclerView.LayoutManager): OrientationHelper {
if (horizontalHelper == null) {
horizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager)
}
return horizontalHelper!!
}
}
As above pic show, The items will be misplaced momently while i refresh.
smartRefresh.setOnRefreshListener {
mViewModel.observeComposition(ConnectivityUtil.isNetworkReachable(this),mLabelId.toInt(),lifecycleScope).observe(this, Observer {
mCompositionAdapter.submitList(it)
smartRefresh.finishRefresh()
})
}
My Adapter
class CompositionsAdapter(context: Context) : PagedListAdapter<CompositionAbstract, CompositionsAdapter.ViewHolder>(CompositionAbstractDiffCallback()) {
private val mScreenWidth by lazy {
val displayMetrics = DisplayMetrics()
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
windowManager.defaultDisplay.getRealMetrics(displayMetrics)
displayMetrics.widthPixels
}
val width by lazy {
(mScreenWidth - 15.px * 3) / 2
}
val height by lazy {
val srcWidth = 165f.px
val srcHeight = 155f.px
width * (srcHeight / srcWidth)
}
class ViewHolder(private val binding: HomepageItemGroupCompositionBinding)
: RecyclerView.ViewHolder(binding.root) {
fun bind(item: CompositionAbstract) {
binding.apply {
composition = item.briefComposition
itemView.setOnClickListener {
ARouter.getInstance().build(HomePageArouterConstants.PATH_AUDIO_PLAYER).withString(HomePageArouterConstants.KEY_ID, item.briefComposition.id).navigation()
}
executePendingBindings()
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = HomepageItemGroupCompositionBinding.inflate(LayoutInflater.from(parent.context))
binding.imageView.layoutParams = LinearLayout.LayoutParams(width, height.toInt())
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
getItem(position)?.let {
holder.bind(it)
}
}
}
My Itemdecoration
class GridSpacingItemDecoration(private val spanCount: Int,
private val verticalSpacing: Int,
private val topSpacing: Int
) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val position = parent.getChildAdapterPosition(view)
if (position / spanCount == 0) {
outRect.top = topSpacing
}
if (position % spanCount == 1) {
outRect.left = 7.px
}
outRect.bottom = verticalSpacing
}
}
My diffUtil
class CompositionAbstractDiffCallback : DiffUtil.ItemCallback<CompositionAbstract>() {
override fun areItemsTheSame(oldItem: CompositionAbstract, newItem: CompositionAbstract): Boolean {
return oldItem.briefComposition.id == newItem.briefComposition.id
}
override fun areContentsTheSame(oldItem: CompositionAbstract, newItem: CompositionAbstract): Boolean {
return oldItem == newItem
}
}
Any ideas for this problem? Thanks for first!
I had a issue with PositionalDataSource, were loadRange not getting called once i invalidate the data source, this issue happens only if i add the item decoration for my recyclerview for sticky header
Without item decoration for recyclerview works fine
Here is my datasource
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<SomeData>) {
getData(0, params.requestedLoadSize)
.doOnSubscribe {
loading.set(true)
}
.map {
callback.onResult(it.data, 0)
}
.doOnComplete {
loading.set(false)
}
.subscribe()
.addTo(disposal)
}
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<SomeData>) {
getData(params.startPosition, params.loadSize)
.doOnSubscribe {
loading.set(true)
}
.map {
callback.onResult(it.data)
}
.doOnComplete {
loading.set(false)
}
.subscribe()
.addTo(disposal)
}
I invalidate the datasource from getData(int,int)
Here is my sticky header item decoration class
class StickyHeaderItemDecoration constructor(listener : StickyHeader) : RecyclerView.ItemDecoration() {
private val mListener: StickyHeader = listener
private var mStickyHeaderHeight = 0
override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDrawOver(canvas, parent, state)
val topChild = parent.getChildAt(0) ?: return
val topChildPosition = parent.getChildAdapterPosition(topChild)
if (topChildPosition == RecyclerView.NO_POSITION) {
return
}
val headerPos = mListener.getHeaderPositionFor(topChildPosition)
val currentHeader = getHeaderViewForItem(headerPos, parent)
fixLayoutSize(parent, currentHeader)
val contactPoint = currentHeader.bottom
val childInContact = getChildInContact(parent, contactPoint, headerPos)
if (childInContact != null && mListener.isHeaderView(
parent.getChildAdapterPosition(
childInContact
)
)
) {
moveHeader(canvas, currentHeader, childInContact)
return
}
drawHeader(canvas, currentHeader)
}
private fun getHeaderViewForItem(headerPosition: Int, parent: RecyclerView): View {
val layoutResId = mListener.getHeaderLayoutIdFor(headerPosition)
val header =
LayoutInflater.from(parent.context).inflate(layoutResId, parent, false)
// mListener.bindHeaderData(header, headerPosition)
return header
}
private fun drawHeader(c: Canvas, header: View) {
c.save()
c.translate(0F, 0F)
header.draw(c)
c.restore()
}
private fun moveHeader(c: Canvas, currentHeader: View, nextHeader: View) {
c.save()
c.translate(0F, (nextHeader.top - currentHeader.height)*1.0F)
currentHeader.draw(c)
c.restore()
}
private fun getChildInContact(
parent: RecyclerView,
contactPoint: Int,
currentHeaderPos: Int
): View? {
var childInContact: View? = null
for (i in 0 until parent.childCount) {
var heightTolerance = 0
val child = parent.getChildAt(i)
//measure height tolerance with child if child is another header
if (currentHeaderPos != i) {
val isChildHeader =
mListener.isHeaderView(parent.getChildAdapterPosition(child))
if (isChildHeader) {
heightTolerance = mStickyHeaderHeight - child.height
}
}
//add heightTolerance if child top be in display area
var childBottomPosition: Int
childBottomPosition = if (child.top > 0) {
child.bottom + heightTolerance
} else {
child.bottom
}
if (childBottomPosition > contactPoint) {
if (child.top <= contactPoint) { // This child overlaps the contactPoint
childInContact = child
break
}
}
}
return childInContact
}
/**
* Properly measures and layouts the top sticky header.
* #param parent ViewGroup: RecyclerView in this case.
*/
private fun fixLayoutSize(
parent: ViewGroup,
view: View
) { // Specs for parent (RecyclerView)
val widthSpec =
View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.EXACTLY)
val heightSpec =
View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.UNSPECIFIED)
// Specs for children (headers)
val childWidthSpec = ViewGroup.getChildMeasureSpec(
widthSpec,
parent.paddingLeft + parent.paddingRight,
view.layoutParams.width
)
val childHeightSpec = ViewGroup.getChildMeasureSpec(
heightSpec,
parent.paddingTop + parent.paddingBottom,
view.layoutParams.height
)
view.measure(childWidthSpec, childHeightSpec)
view.layout(
0,
0,
view.measuredWidth,
view.measuredHeight.also { mStickyHeaderHeight = it }
)
}
}
Can anyone guess, what may be the issue ?