I have a recycler view that contain progress bar
I am animating these progress bar with this animation
fun setProgresAnim(progress: ProgressBar, value: Int) {
val anim = ProgressBarAnimation(progress, 0, value)
anim.duration = 2000
progress.startAnimation(anim)
}
This is MyViewHolder in the adapter
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(deviceScan: DeviceScan) {
if (deviceScan.percentage > 0) {
itemView.title.text = deviceScan.name
itemView.tv_percentage.text = deviceScan.percentage.toString() + "%"
setProgresAnim(itemView.progress, deviceScan.percentage)}}}
The problem is when I scroll up and down the animation restart again.
I want to do this animation once.
You should avoid calling setProgresAnim(itemView.progress, deviceScan.percentage) within onBindViewHolder , because each time you will scroll up or down it will call your animation again . it's better to call it outside your RecyclerView Adapter .
Related
When I click on an image within a RecyclerView, I try to enlarge it. So far everything works wonderfully. The only problem I have now is that the image of its output position is always the same. In other words, if I click on the 3rd picture, the 2nd picture enlarges with the correct content but in the wrong position. The same applies to the click on the 1st picture.
This is my AdapterDetail.kt class:
class AdapterDetail(val context: Context, private val listImg:ArrayList<String>,private var listen: CustomerAdapter.OnItemClickListener): RecyclerView.Adapter<AdapterDetail.MyCustomViewHolder>() {
...
inner class MyCustomViewHolder(view: View):RecyclerView.ViewHolder(view){
val preview_img:ImageView = view.findViewById(R.id.preview_img)
fun bind(item: String){
imgView = itemView.findViewById(R.id.preview_img)
imgView.setOnClickListener {
listen.onItemClick(imgView, adapterPosition)
}
}
}
override fun onBindViewHolder(holder: MyCustomViewHolder, position: Int) {
val item = listImg[position]
holder.bind(item)
}
In the Activity i need the correct ImageView position to inflate them:
adapterDetail = AdapterDetail(this, house!!.objektBilder as ArrayList<String>,object: CustomerAdapter.OnItemClickListener{
override fun onItemClick(view: View, position: Int) {
//Here I need the ImageView that was clicked on in the RecyclerView
var viewPosition = view
zoomImageFromThumb(viewPosition, house!!.objektBilder!![position])
}
})
Sounds like an off by one issue, since you are using the deprecated adapterPosition. Use bindingAdapterPosition instead, which gives the position as RecyclerView sees it:
listen.onItemClick(imgView, bindingAdapterPosition)
I am working on an idea, which is make a RecyclerView auto scrolling but allow user to click item without stop scrolling.
First, I create a custom LayoutManager to disable manual scroll, also change the speed of scroll to a certain position
class CustomLayoutManager(context: Context, countOfColumns: Int) :
GridLayoutManager(context, countOfColumns) {
// Custom smooth scroller
private val smoothScroller = object : LinearSmoothScroller(context) {
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float =
500f / displayMetrics.densityDpi
}
// Disable manual scroll
override fun canScrollVertically(): Boolean = false
// Using custom smooth scroller to control the duration of smooth scroll to a certain position
override fun smoothScrollToPosition(
recyclerView: RecyclerView,
state: RecyclerView.State?,
position: Int
) {
smoothScroller.targetPosition = position
startSmoothScroll(smoothScroller)
}
}
Then I do the initial work for the RecyclerView and start smooth scroll after 1 sec
viewBinding.list.apply {
// initial recycler view
setHasFixedSize(true)
customLayoutManager = CustomLayoutManager(context = context, countOfColumns = 2)
layoutManager = customLayoutManager
// data list
val dataList = mutableListOf<TestModel>()
repeat(times = 100) { dataList.add(TestModel(position = it, clicked = false)) }
// adapter
testAdapter =
TestAdapter(clickListener = { testAdapter.changeVhColorByPosition(position = it) })
adapter = testAdapter
testAdapter.submitList(dataList)
// automatically scroll after 1 sec
postDelayed({ smoothScrollToPosition(dataList.lastIndex) }, 1000)
}
Everything goes as my expected until I found that the auto scrolling stopped when I clicked on any item on the RecycelerView, the function when clickListener triggered just change background color of the view holder in TestAdapter
fun changeVhColor(position: Int) {
position
.takeIf { it in 0..itemCount }
?.also { getItem(it).clicked = true }
?.also { notifyItemChanged(it) }
}
here is the screen recording screen recording
issues I encounter
auto scrolling stopped when I tap any item on the ReycelerView
first tap make scrolling stopped, second tap trigger clickListener, but I expect to trigger clickListener by one tap
Can anybody to tell me how to resolve this? Thanks in advance.
There is a lot going on here. You should suspect the touch handling of the RecyclerView and, maybe, the call to notifyItemChanged(it), but I believe that the RecyclerView is behaving correctly. You can look into overriding the touch code in the RecyclerView to make it do what you want - assuming you can get to it and override it.
An alternative would be to overlay the RecyclerView with another view that is transparent and capture all touches on the transparent view. You can then write code for the transparent view that interacts with the RecyclerView in the way that meets your objectives. This will also be tricky and you will have to make changes to the RecyclerView as it is constantly layout out views as scrolling occurs. Since you have your own layout manager, this might be easier if you queue changes to occur pre-layout as scrolling occurs.
After tried several ways, found that the key of keep recycler view scrolling automatically is override onInterceptTouchEvent
Example
class MyRecyclerView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : RecyclerView(context, attrs, defStyle) {
override fun onInterceptTouchEvent(e: MotionEvent?): Boolean = false
}
that will make the custom RecyclerView ignore all touch event
I am attempting to write a recyclerview which has some of the Viewholders inside it as stacked ontop of one another. The idea is that you can drag the topmost view above the stacked list and have drop it above where it becomes separate.
I managed to get this working using a Recyclerview with a custom RecyclerView.ItemDecoration. However, after I drop the item, i have the adapter call notifyDataSetChange to update the background code. This causes the the next item in the stack to appear to be the wrong one (though this does change sometimes if you touch the item and start scrolling, then it displays the correct one).
The custom RecyclerView.ItemDecoration class:
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
)
{
val itemPosition = parent.getChildAdapterPosition(view)
val adapter = parent.adapter
if (adapter is BaseRecVAdapter)
{
val item = adapter.getDataModel(itemPosition)
if (item is DragDropModel && item.mStackedPos != PMConsts.negNum)
{
if (item.mStackedPos != 0)
{
val context = view.context
val top = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 148f, context.resources.displayMetrics).toInt()
outRect.set(0, -top, 0, 0)
return
}
}
}
super.getItemOffsets(outRect, view, parent, state)
}
The drag interface I made for the Adapter and the ItemTouchHelper.Callback can be found below:
interface ItemTouchHelperListener
{
fun onItemMove(fromPosition: Int, toPosition: Int): Boolean
fun onClearView(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder?)
}
The onItem move code is as follows:
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean
{
var newToPosition = toPosition
if (toPosition <= mDragUpLimit)
{//Prevent items from being dragged above maximum movement.
newToPosition = mDragUpLimit + 1
}
else if (toPosition >= mDragDownLimit)
{//Cannot drag below stacked List...
newToPosition = mDragDownLimit - 1
}
if (fromPosition < newToPosition)
{
for (i in fromPosition until newToPosition)
{
swap(mDataList, i, i + 1)
}
}
else
{
for (i in fromPosition downTo newToPosition + 1)
{
swap(mDataList, i, i - 1)
}
}
notifyItemMoved(fromPosition, newToPosition)
return true
}
I have a simple viewholder which is an invisible bar which i mark as the position you need to drag above in order to make a valid change to the list order.
I have the code call notifyDataSetChanged after the onClearView() method is called as I need to update the background features so that the next item in the stack is draggable and the background data feeding into the adapter is also updated. It seems the simplest way to keep the data updating smoothly, but I wonder if it is causing my problems
If someone would be able to give me a hand with this, I would be most grateful. I am tearing my hair out somewhat. I thought I had a good system setup but it was not quite working. I hope that this is enough information to get some help with this issue.
Thank you in advance
I have a recyclerview where when I swipe left I open a bottom sheet and select one value and then I need to update my room DB table and also disable swipe.
But currently im not able to do both things at the same time, only the disable swipe is working not able to update recyclerview item
swipeController = SwipeController(object:SwipeControllerActions() {
override fun onRightClicked(position:Int) {
val bottomSheetFragment = BottomModalFragment()
bottomSheetFragment.show(parentFragmentManager, bottomSheetFragment.tag)
var homeTaskModel: HomeTaskModel = homeAdapter.getWordAtPosition(position)!!
TASK_STATUS.observe(viewLifecycleOwner, Observer {
homeTaskModel.task_status = it //passing value
homeViewModel.updateSwipeType(1,0) //disable swipe
homeAdapter.notifyItemChanged(position)
})
}
Below is my onbindviewholder code
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val tasks = taskslist[position]
var task__status = tasks.task_status
holder.Taskbinding.taskdata = taskslist[position]
holder.Taskbinding.txtTaskname.text = tasks.taskname
holder.Taskbinding.txtTaskStatus.text = task__status
//hide unhide navigation imageview
if (task__status.equals("Travel To Site"))
holder.Taskbinding.imgNavigation.visibility = View.VISIBLE
else
holder.Taskbinding.imgNavigation.visibility = View.GONE
}
getItemViewType() is called before onBindViewHolder where I am updating my views
override fun getItemViewType(position: Int): Int {
return taskslist.get(position).swipe_type
}
How to acheieve both the scenarios???
create refreshList in adapter class the refresh the list
fun refreshList(list:arrayOf()){
tasklist = list
}
call this function from mainActivity where you want to notify the adapter
homeAdapter.refreshList(pass your updated list here)
thats it!!
I am following this tutorial and source code to implement Collapsing Toolbar by using Motion Layout.
When you do "fast scroll" on recyclerView and then do click event on any item of the recyclerView, this event only works after you the 2nd click. Or you have to wait 2-3 second then do the click event works. Otherwise click event can not be dispatch to the recyclerView. (Assuming it waits to finish animation on MotionLayout). How can we fix this issue?
Anyone can simulate the issue by adding below code to the adapter of the article's source code. And do fast scroll.
class ViewHolder(
view: View,
private val textView: TextView = view.findViewById(android.R.id.text1)
) : RecyclerView.ViewHolder(view) {
var text: CharSequence
get() = textView.text
set(value) {
textView.text = value
}
init {
view.setOnClickListener { showMessage(it) }
}
private fun showMessage(view: View) {
Toast.makeText(view.context, "OnClickListener: item ${adapterPosition + 1}", Toast.LENGTH_SHORT).show()
}
}
Update ConstraintLayout to version 2.0.0-beta2
https://issuetracker.google.com/issues/128914828