Change CardBackgroundColor on RecyclerView - android

I have the following code on my RecyclerView:
class TaskViewHolder(v: View) : RecyclerView.ViewHolder(v), View.OnClickListener
{
var mId: TextView = v.task_id
var mDescription: TextView = v.task_description
var mCard: CardView = v.task_card
var mView: View = v
}
override fun onBindViewHolder(holder: TaskViewHolder, position: Int)
{
// Initialize ViewHolder content
holder.mId.text = items[position].getID().toString()
holder.mDescription.text = items[position].getDescription()
holder.mTask = items[position]
for (elem in priorities)
{
if (elem.getID() == items[position].getPriority())
{
holder.mCard.setCardBackgroundColor(Color.parseColor("#c2c2c2"))
break
}
}
}
I don't know why, but CardView backgroundColor isn't changing.
If I use the following code, it works correctly:
holder.mCard.setCardBackgroundColor(ContextCompat.getColor(holder.mView.context, R.color.priority3))
What I should do to set CardBackgroundColor progrmatically?

First of all, You need to provide else part as transparent or another color to avoid color duplicate render issue. second, you have to pass context from your activity or fragment to adapter and that context will be used to get color like below.
if (elem.getID() == items[position].getPriority())
{
holder.mCard.setCardBackgroundColor(ContextCompat.getColor(mContext, [first color]))
} else {
holder.mCard.setCardBackgroundColor(ContextCompat.getColor(mContext,[second color]))
}

Try replacing "#c2c2c2" with "#ffc2c2c2" to make sure you provide a correct alpha for the background colour. On android, this extra byte added at the beginning represents the alpha of the colour:
#ffc2c2c2
ff: alpha
c2: red
c2: green
c2: blue

Related

Data disappears when scrolling in recycler view

Good day. So I currently have data in my recycler view. It is for now only static data. I still have to do the code where I import. My problem however is I have a button that changes the background of a text view. This happens in my adapter. And when I scroll through my list the bg color change gets reverted back to what it was before the button click. I have read a lot of similar problems but could not really find one that explains clearly or work for me. From what I read the data gets reset to the static data because it is currently happening in my onBindViewHolder and I think this changes the data on every new data read(scrolling). I read that I should create a link or a listener and then call it. But It does not make sense to me because if a link is called the same amount of times as the code is executed then it will be the same will it not. Maybe having a condition listener but not sure if this is the way to go.
I am somewhat new to android and kotlin. Have been working with it for a month now. I dont know everything I am doing but I got given a deadline. So sadly there was no time to go and learn the basics. Thank you for any and all help. Please let me know if you need any additional code/information
my adapter
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RowViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.table_list_item, parent, false)
return RowViewHolder(itemView)
}
private fun setHeaderBg(view: View) {
view.setBackgroundResource(R.drawable.table_header_cell_bg)
}
private fun setContentBg(view: View) {
view.setBackgroundResource(R.drawable.table_content_cell_bg)
}
override fun onBindViewHolder(holder: RowViewHolder, position: Int) {
// (TableViewAdapter.DataviewHolder) .bind()
val rowPos = holder.adapterPosition
if (rowPos == 0) {
// Header Cells. Main Headings appear here
holder.itemView.apply {
setHeaderBg(txtWOrder)
setHeaderBg(txtDElNote)
setHeaderBg(txtCompany)
// setHeaderBg(txtAddress)
setHeaderBg(txtWeight)
setHeaderBg(txtbutton1)
setHeaderBg(txtbutton2)
setHeaderBg(txttvdone)
txtWOrder.text = "WOrder"
txtDElNote.text = "DElNote"
txtCompany.text = "Company"
// txtAddress.text = "Address"
txtWeight.text = "Weight"
txtbutton1.text = "Delivered"
txtbutton2.text = "Exception"
txttvdone.text = ""
}
} else {
val modal = Tripsheetlist[rowPos - 1]
holder.itemView.apply {
setContentBg(txtWOrder)
setContentBg(txtDElNote)
setContentBg(txtCompany)
// setContentBg(txtAddress)
setContentBg(txtWeight)
setContentBg(txtbutton1)
setContentBg(txtbutton2)
setContentBg(txttvdone)
txtWOrder.text = modal.WOrder.toString()
txtDElNote.text = modal.DElNote.toString()
txtCompany.text = modal.Company.toString()
// txtAddress.text = modal.Address.toString()
txtWeight.text = modal.Weight.toString()
txtbutton1.text = modal.Button1.toString()
txtbutton2.text = modal.Button2.toString()
txttvdone.text = modal.tvdone.toString()
}
}
holder.apply {
txtbutton1.setOnClickListener {
Log.e("Clicked", "Successful delivery")
txttvdone.setBackgroundResource(R.color.green)
txttvdone.setText("✓")
}
txtbutton2.setOnClickListener {
Log.e("Clicked", "Exception on delivery")
txttvdone.setBackgroundResource(R.color.orange)
txttvdone.setText("x")
}
}
}
class RowViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val txttvdone:TextView = itemView.findViewById<TextView>(R.id.txttvdone)
val txtbutton1:Button = itemView.findViewById<Button>(R.id.txtbutton1)
val txtbutton2:Button = itemView.findViewById<Button>(R.id.txtbutton2)
} class MyViewHolder(val view: View) : RecyclerView.ViewHolder(view){
var txtbutton1 = view.findViewById<Button>(R.id.txtbutton1)
val txtbutton2:Button = itemView.findViewById<Button>(R.id.txtbutton2)
var txttvdone = view.findViewById<TextView>(R.id.txttvdone)
}
I tried (TableViewAdapter.DataviewHolder) .bind() doing this and creating another class as I saw that was done in another thread(Why do values ​disappear after scrolling in Recycler View?) Its a lot like my problem. I just can't seem to implement his solution to make mine work. ( don't understand his solution fully)
//I am also aware that I am using android extensions which will expire at the end of the year. But for now it works and once I have the code up and running I will start to move over to the newer versions of kotlin.
A RecyclerView, as its name implies, will recycle the views when they go off screen. This means that when the view for an item comes into view, it gets recreated and the onBindViewHolder() is called to fill in the details.
Your onClickListener inside your adapter changes the background of one of the subviews for your cell view. However, that cell will be redrawn if it leaves the screen and comes back.
To get around this, your onClickListener should be changing a property on the data item, and your onBindViewHolder should check that property to determine what background color to display for the subview:
enum class DataState {
Unselected,
Success,
Failure
}
data class DataItem(var state: DataState = DataState.Unselected)
class MyAdapter : RecyclerView.Adapter<MyViewHolder>() {
var dataItems: List<DataItem> = emptyList()
fun updateData(data: List<DataItem>) {
dataItems = data
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val dataItem = dataItems[position]
holder.txttvdone.apply {
setBackgroundResource(when (dataItem.state) {
DataState.Unselected -> android.R.color.transparent
DataState.Success -> R.color.green
DataState.Failure -> R.color.orange
})
text = when (dataItem.state) {
DataState.Unselected -> ""
DataState.Success -> "✓"
DataState.Failure -> "x"
}
}
holder.apply {
txtbutton1.setOnClickListener {
Log.e("Clicked", "Successful delivery")
dataItem.state = DataState.Success
notifyDataSetChanged()
}
txtbutton2.setOnClickListener {
Log.e("Clicked", "Exception on delivery")
dataItem.state = DataState.Failure
notifyDataSetChanged()
}
}
}
}

Animate single item in RecyclerView on data change

I have complex and generic RecyclerView design and List Adapter.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
val layoutInflater: LayoutInflater = LayoutInflater.from(parent.context)
val binding: ViewDataBinding =
DataBindingUtil.inflate(layoutInflater, viewType, parent, false)
return object : BaseViewHolder(binding) {
override fun bindData(position: Int) {
val model = getItem(position).data
itemBinding.setVariable(BR.model, model)
viewModel?.let {
itemBinding.setVariable(BR.viewModel, it)
}
}
}
}
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
holder.run {
bindData(position)
if (itemBinding.hasPendingBindings()) {
itemBinding.executePendingBindings()
}
}
}
It has RecyclerView inside RecyclerView as item and handle multi layout by itself. I update list and items with databinding adapters. When I need to update single item; I search all tree in LiveData list, modify value and post value updated list to LiveData again.
I want to update each view with animation(item inside of RecyclerView inside of RecyclerView) when it's value changed.
here is my update code;
#BindingAdapter("setTransactionBgAnimation")
fun View.setTransactionBgAnimation(ratio: Double?) {
ratio?.let { value ->
val colorAnim = ObjectAnimator.ofInt(
this, "backgroundColor", getEvaluateColor(context, value), Color.WHITE
)
colorAnim.duration = 500
colorAnim.repeatCount = 1
colorAnim.start()
val alphaAnim = ObjectAnimator.ofFloat(
this, "alpha", 0.40f, 0.0f
)
alphaAnim.duration = 500
alphaAnim.repeatCount = 1
alphaAnim.start()
}
}
When value updated; it has called from all views for each change.
I tried to give unique tag to view and check tag in binding adapter but it is not worked for me.
I solve the problem with not -so clean- way.
First of all; animation was called for every visible item's count for each row, I fix it by controlling with giving view tag with changing value and check that tag that is same with new value.
After first fix, only really changed item animated but it animates multiple times. It was causing because of ObjectAnimator's backgroundColor animations. I have no idea why did I even change backgroundColor with animation. I remove it and multiple flickering animation fixed too.
For better understanding please see my code part
fun View.setTransactionBgAnimation(ratio: Double?) {
if (tag != ratio.toString()) {
ratio?.let { value ->
setBackgroundColor(getEvaluateColor(context, value))
val alphaAnim = ObjectAnimator.ofFloat(
this, "alpha", 0.40f, 0.0f
)
alphaAnim.duration = 500
alphaAnim.start()
}
tag = ratio.toString()
}
}

My adapter in recyclerview change background color of 2 items instead of 1

I have 10 items like this in recyclerview. My problem is when I click one item I change the background color but for some reason another item changes its background color too. I don't know why this is happening.
For example, if I click the first item and change the color, the item in position 8 also changes its color. I only want the item that I click on to change its color.
This is my code:
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val titulo = holder.itemView.findViewById(R.id.txt_categoria) as TextView
titulo.text = categories[position].name_category!!
holder.itemView.setOnClickListener{
when{
textView == null ->{
textView = holder.itemView.txt_categoria
textView!!.setBackgroundResource(R.drawable.design_categories_project)
textView!!.setTextColor(Color.WHITE)
fragmentProyectos.filterProjects(categories[position].id_categorie!!,
categories[position].name_category!!)
}
posClicked != position ->{
holder.itemView.txt_categoria.setBackgroundResource(R.drawable.design_categories_project)
holder.itemView.txt_categoria.setTextColor(Color.WHITE)
textView!!.setBackgroundResource(R.drawable.design_categories_project_white)
textView!!.setTextColor((Color.parseColor("#343434")))
textView = holder.itemView.txt_categoria
fragmentProyectos.filterProjects(categories[position].id_categorie!!,
categories[position].name_category!!)
}
else ->{
textView!!.setBackgroundResource(R.drawable.design_categories_project_white)
textView!!.setTextColor((Color.parseColor("#343434")))
textView = null
fragmentProyectos.getData()
}
}
posClicked = position
}
}
Try setting the color like this:
titulo.setTextColor((Color.parseColor("#343434")))

Make UITexftField like android using subclass in swift without any third party

How to creat Uitextfield look like android text field
I found this solution, it's working for me.
Follow below step for customise UITextField like android textfield using subclass.
Create new filed with subclass UITextField
for Border below UITextField : set tint color and lineWidthas you want.
override var tintColor: UIColor! {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
let startingPoint = CGPoint(x: rect.minX, y: rect.maxY)
let endingPoint = CGPoint(x: rect.maxX, y: rect.maxY)
let path = UIBezierPath()
path.move(to: startingPoint)
path.addLine(to: endingPoint)
path.lineWidth = 2.0
tintColor.setStroke()
path.stroke()
}
assign UItextFieldDelegate
Enter below code for asking delegate for this subclass
override func awakeFromNib() {
self.delegate = self
}
Also enter text field delegate method textFieldDidBeginEditing and textFieldDidEndEditing.
//MARK: - Textfield Delegate
func textFieldDidBeginEditing(_ textField: UITextField) {
for view in (textField.superview?.subviews)!{
if view is UILabel && view.tag == 0{
let lbl = view as! UILabel
lbl.isHidden = false
textField.placeholder = ""
}
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if newdelegate != nil{
newdelegate?.textFieldDidEndEditing!(textField)
}
if textField.text?.length == 0{
for view in (textField.superview?.subviews)!{
if view is UILabel && view.tag == 0{
let lbl = view as! UILabel
lbl.isHidden = true
textField.placeholder = lbl.text
}
}
}
}
Make structure in storyboard and assign this subclass to this UItextField
Hope it's helpful to you
Thank you enjoy. :)
First in your xib file go to attributes inspection.
Open right pane.
Select attributes inspection
In border style apply none i.e select one with dotted lines.
After that in your swift file add following method:
func textViewUnderline(textField: UITextField)**
{
let border = CALayer()
let width = CGFloat(1.0)
border.borderColor = UIColor.yourColorforBorderHere.cgColor
border.frame = CGRect(x: 0, y: textField.frame.size.height - width,
width: textField.frame.size.width, height: textField.frame.size.height)
border.borderWidth = width
textField.layer.masksToBounds = true
textField.layer.addSublayer(border)
}
Call this method by passing your uitextfield in viewDidLoad as:
override func viewDidLoad()
{
super.viewDidLoad()
textViewUnderline(textField: yourUITextField)
}

How to use selectableButtonBackground on Anko?

How do I use selectableButtonBackground attribute on a custom View that uses Anko's apply() method inside its constructor like the following structure?
class XPTO(context: Context) : CardView(context) {
init {
this.apply {
// I'd like to invoke selectableButtonBackground here
}
}
I've tried to do context.obtainStyledAttributes(arrayOf(R.attr.selectableItemBackground).toIntArray()).getDrawable(0) but with no success.
I just created an extension function to get the resource ids for attributes.
val Context.selectableItemBackgroundResource: Int get() {
return getResourceIdAttribute(R.attr.selectableItemBackground)
}
fun Context.getResourceIdAttribute(#AttrRes attribute: Int) : Int {
val typedValue = TypedValue()
theme.resolveAttribute(attribute, typedValue, true)
return typedValue.resourceId
}
This way you can also add more attributes if needed. Example to put it in anko:
frameLayout {
textView {
text = "Test"
backgroundResource = selectableItemBackgroundResource
isClickable = true
}
}
Don't forget the isClickable, else you won't see anything when you're clicking the textView
Another way to achieve this with Anko:
val backgroundResource = attr(R.attr.selectableItemBackgroundBorderless).resourceId

Categories

Resources