Labelling material-calendarview with Events - android

I'm trying to use prolificinteractive's material-calendarview to achieve something similar to the image I've attached, which has events added to the calendar. I'm thinking I could do this by creating a custom Span class as shown here in another post.
However, my issue is that DayViewDecorator.decorate doesn't know which date it's currently decorating, so I can't pass specific event names to it. Is there a way I could achieve this using material-calendarview or are there other alternatives? Cheers
What I'm trying to achieve
MainActivity.kt
I thought decorate would be called after shouldDecorate, so I could define calendarDay in shouldDecorate, then call for its corresponding Event value in mapOfCalendarDays. But decorate is actually called first, and only once, before shouldDecorate is called for each day in the Calendar. This results in a crash, though, since calendarDay has not yet been initialized.
calendarView.addDecorator(object: DayViewDecorator {
lateinit var calendarDay: CalendarDay
override fun shouldDecorate(day: CalendarDay?): Boolean {
return if (mapOfCalendarDays.containsKey(day)) { // check if 'day' is in mapOfCalendarDays (a map of CalendarDay to Event)
calendarDay = day!!
true
} else false
}
override fun decorate(view: DayViewFacade?) {
val event: Event? = mapOfCalendarDays[calendarDay]
if (event != null) {
view?.addSpan(AddTextToDates(event.name))
}
}
})
Event.kt
data class Event(
val name: String,
val date: LocalDate
)
AddTextToDates.kt
class AddTextToDates(text: String): LineBackgroundSpan {
private val eventName = text
override fun drawBackground(
canvas: Canvas,
paint: Paint,
left: Int,
right: Int,
top: Int,
baseline: Int,
bottom: Int,
text: CharSequence,
start: Int,
end: Int,
lnum: Int
) {
canvas.drawText(eventName, ((left+right)/4).toFloat(), (bottom+15).toFloat(), paint)
}
}

Related

Remember mutableStateOf custom class in Android Compose

I created a android compose component and to avoid multiple params, it takes only one parameter : an object
Here is my component :
#Composable
fun ValidationButton(validationButtonModel: ValidationButtonModel)
My object :
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
#Parcelize
data class ValidationButtonModel(
var title: Int,
var contentDescription: Int,
var rfidReadingStarted: Boolean,
var progression: Float,
var read: Int,
var expected: Int,
) : Parcelable
Here is how i define it to compose for being remember between recomposition AND being a state (launch recomposition when change) :
val validationButtonModelState by rememberSaveable() {
mutableStateOf(
ValidationButtonModel(
R.string.common_validate,
R.string.common_contentDescription,
true,
0.1f,
7,
10
)
)
}
But if i try to update it with for example this :
ValidationButton(
validationButtonModelState,
)
Button(onClick =
{
validationButtonModelState.rfidReadingStarted = true
if (validationButtonModelState.progression < 1.0f) {
validationButtonModelState.progression += 0.1f
validationButtonModelState.read += 1
}
}, content = {
Text("INCREMENT")
})
There is no re composition.
I try to add a Saver but doesn't work too :
val validationButtonModelSaver = listSaver<ValidationButtonModel, Any>(
save = { listOf(it.rfidReadingStarted, it.read, it.progression, it.expected, it.title, it.contentDescription) },
restore = { ValidationButtonModel(rfidReadingStarted = it[0] as Boolean, read = it[1] as Int, progression = it[2] as Float, expected = it[4] as Int, title = it[5] as Int, contentDescription = it[6] as Int)}
)
val validationButtonModelState by rememberSaveable(validationButtonModelSaver) {
mutableStateOf(
ValidationButtonModel(
R.string.common_validate,
R.string.common_contentDescription,
true,
0.1f,
7,
10
)
)
}
Am i missing something ? Following this : https://developer.android.com/jetpack/compose/state#parcelize
It should works
If you only change the fields inside your model, mutableStateOf(ModValidationButtonModel) it won't work, as you are still updating your old instance. You need to pass in a new instance of your model effectively.
try
validationButtonModelState = ModValidationButtonModel()
or since it is a data class you can also do
validationButtonModelState = validationButtonModelState.copy(
read = validationButtonModelState.read+1
)

set length character Emoji Edittext Android Kotlin

i have 1 edittext maxLength = 30, but i can only type 6 character emoji dog => 1 emoji dog = 6 regular character. So please help me type 30 emoji dog. Thanks everyone.
[enter image description here][1]
When someone types an emoji, you can call .length on that emoji, then increase your max character count by that amount. (You would have to remember your original character count and use that on your UI if you wanted to hide the magic).
i.e. when someone types a "dog" then you increase your max count from 30 to 35. (1 has been used and a dog usually counts for 6)
Ref:
https://twitter.com/riggaroo/status/1148278279713017858
https://developer.android.com/reference/java/text/BreakIterator
https://lemire.me/blog/2018/06/15/emojis-java-and-strings/
https://developer.android.com/reference/kotlin/androidx/emoji/text/EmojiCompat
editText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
oldTextString = charSequence.toString()
}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun afterTextChanged(editable: Editable) {
var newTextString = editable.toString()
if (!oldTextString.equals(newTextString)) {
if (Character.codePointCount(
newTextString,
0,
newTextString.length
) > maxCharactersAllowed
) {
newTextString = oldTextString
}
editText.setText(newTextString)
editText.setSelection(newTextString.length)
}
}
})

No visibility when extending function for a data class from its companion object

Given the following data class:
data class Coords(val x: Int, val y: Int) {
companion object CoordsOps {
private val shiftTable: Map<Direction, Pair<Int, Int>> = mapOf(
Direction.North to Pair(0, 1),
Direction.East to Pair(1, 0),
Direction.South to Pair(0, -1),
Direction.West to Pair(-1, 0)
)
private operator fun Coords.plus(increment: Pair<Int, Int>): Coords =
Coords(x + increment.first, y + increment.second)
fun Coords.shift(direction: Direction): Coords = this + shiftTable.getValue(direction)
}
}
I'm not able to use the extension function shift() from outside that object. I would like to use Coords.shift(direction).
What am I doing wrong?
You should be able to. I copied your code exactly and was able to call extension function shift from another file.
Perhaps you're missing the import? Or calling it incorrectly? Try this
import <your_package>.Coords.CoordsOps.shift
val y = Coords(3, 4).shift(Direction.East)

recyclerview-selection: stop auto item deselection when touching to the blank space inside the recyclerview

I am trying to use the recyclerview-selection library in my project. I followed this tutorial:
https://proandroiddev.com/a-guide-to-recyclerview-selection-3ed9f2381504
Everything workes fine. But I have a problem. If I tap/touch any blank space inside the RecyclerView, all the selected elements got deselected! I don't find any method or solution to disable this. What should I do?
I am using implementation 'androidx.recyclerview:recyclerview-selection:1.1.0-rc01' in my project.
Edit 1:
I set the RecyclerView background red to describe my problem. Here, blue items are selected items. If I click any red area, then all the selected items got unselected! The select and deselect should only be done by clicking the items. So, I need to disable this feature (or bug!), that unselect all items!
Example project: https://github.com/ImaginativeShohag/multiselection
Solution is to have an out of context selection item like:
class ItemLookup(private val recyclerView: RecyclerView) : ItemDetailsLookup<Long>() {
private val outOfContextSelection = object : ItemDetails<Long>() {
override fun getPosition(): Int = OUT_OF_CONTEXT_POSITION.toInt()
override fun getSelectionKey() = OUT_OF_CONTEXT_POSITION
}
override fun getItemDetails(e: MotionEvent): ItemDetails<Long>? {
recyclerView.findChildViewUnder(e.x, e.y)?.let {
return (recyclerView.getChildViewHolder(it) as?
SelectorBarAdapter.SelectorBarViewHolder)?.itemDetails
}
return outOfContextSelection
}
companion object {
const val OUT_OF_CONTEXT_POSITION = 10000L
}
}
so that when a view which is not one of our clickable elements is clicked we can identify further on a predicate like follows:
class SingleSelectionPredicate : SelectionTracker.SelectionPredicate<Long>() {
override fun canSetStateForKey(key: Long, nextState: Boolean): Boolean {
// warranties that an item can't be unselected on click
// warranties that clicks out of the item's scope are disabled
return nextState && key != ItemLookup.OUT_OF_CONTEXT_POSITION
}
override fun canSetStateAtPosition(position: Int, nextState: Boolean) = true
override fun canSelectMultiple() = false
}
You create your own SelectionTracker.SelectionPredicate<Long>.
Override the method canSetStateForKey(key: Long, nextState: Boolean)
like this:
override fun canSetStateForKey(#NonNull key: Long, nextState: Boolean): Boolean {
rv.findViewHolderForItemId(key)?.let { holder -> adapter.canSetStateForItem(holder as YourItemHolder<YourItem>, nextState)}
return true
}
and in your ViewHolder check if the item is already selected if so return false or vice versa.
Edit:
You can also define a "Selection Hotspot" by inSelectionHotspot(e: MotionEvent) in your ItemDetailsLookup.ItemDetails.
In your ViewHolder you can then check if the TouchEvent is in an area that requires a select or unselect.
for example:
override fun inSelectionHotspot(e: MotionEvent): Boolean {
val rect = Rect()
itemView.getGlobalVisibleRect(rect)
if (rect.contains(e.rawX.toInt(), e.rawY.toInt())) {
// in select region
} else {
// not in select region
}
}

Hide string content in a span

I have EditText with content: #<m id="36c03920-f411-4919-a175-a5b1eb616592">Full name</m>, how are you doing?. I would like to hide tag with id from the user and keep only #Full name, how are you doing? when displaying text. Still getText should return full content.
I found ReplacementSpan useful to this approach. At first step, tried replacing only </m> but text is drawn twice in two lines. First line starts with # and second one starts with <. Also cannot insert new text. getSize is called multiple times and draw is called twice.
Is my approach correct? Should I try to found different solution and store ids in separate collection and do post processing on getText()?
Code:
inner class RemoveTagSpan : ReplacementSpan() {
private val regexEndTag = Regex("</m>")
override fun draw(canvas: Canvas, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
text?.replace(regexEndTag, "")?.let { canvas.drawText(it, start, it.length - 1, x, y.toFloat(), paint) }
}
override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: FontMetricsInt?): Int {
return text?.replace(regexEndTag, "")?.let { paint.measureText(it).roundToInt() } ?: 0
}
}
Not sure if this will give you exactly what you want, but generally displaying markup language in a TextView or EditText(?) can be done in this way:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
textView.setText(Html.fromHtml("#<m id="36c03920-f411-4919-a175-a5b1eb616592">Full name</m>, how are you doing?", Html.FROM_HTML_MODE_COMPACT));
} else {
textView.setText(Html.fromHtml("#<m id="36c03920-f411-4919-a175-a5b1eb616592">Full name</m>, how are you doing?"));

Categories

Resources