Created
November 16, 2019 14:06
-
-
Save AndSky90/cddbcb48127357991623e47441800c95 to your computer and use it in GitHub Desktop.
CustomView editable Form factory
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DateAttrDelegate(field: EditText) : | |
DynamicAttrDelegate(field) { | |
init { | |
field.isClickable = false | |
field.isFocusable = false | |
field.setOnEditorActionListener { _: TextView, _: Int, _: KeyEvent -> true } | |
field.setOnClickListener { openDateSelector() } | |
} | |
private var dateIsoString: String = "" | |
override fun isFieldValid(): Boolean? = | |
field.text.isValid(ValidatorType.VALIDATE_DATE, data.required) | |
override fun setFieldData(attribute: FormComponent) { | |
data = attribute | |
dateIsoString = getAnswerFromCache() ?: data.value.orEmpty() | |
setFormattedDateToField(dateIsoString) | |
} | |
private fun setFormattedDateToField(dateIsoString : String) { | |
field.setText(formatOnlyDate(dateIsoString, true), TextView.BufferType.EDITABLE) | |
} | |
private fun openDateSelector() { | |
val timeDate: GregorianCalendar = getDateFromIsoString(dateIsoString) | |
val year = timeDate.get(Calendar.YEAR) | |
val month = timeDate.get(Calendar.MONTH) | |
val day = timeDate.get(Calendar.DAY_OF_MONTH) | |
val datePicker = DatePickerDialog(field.context, R.style.DatePickerTheme, onDateSelected, year, month, day).apply { | |
setCancelable(true) | |
setButton(BUTTON_NEUTRAL, " ") { _, _ -> | |
dateIsoString = "" | |
saveAnswerToCache(dateIsoString) | |
setFormattedDateToField(dateIsoString) | |
dismiss() | |
} | |
} | |
datePicker.setOnShowListener { | |
val button = datePicker.getButton(BUTTON_NEUTRAL) | |
val drawable = ContextCompat.getDrawable(field.context, R.drawable.ic_delete_red_24) | |
drawable!!.setBounds( | |
(drawable.intrinsicWidth * 0.5).toInt(), | |
0, | |
(drawable.intrinsicWidth * 1.5).toInt(), | |
drawable.intrinsicHeight | |
) | |
button.setCompoundDrawables(drawable, null, null, null) | |
} | |
datePicker.show() | |
} | |
private val onDateSelected = | |
DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth -> | |
dateIsoString = serialiseDateTime(year, month, dayOfMonth) | |
saveAnswerToCache(dateIsoString) | |
setFormattedDateToField(dateIsoString) | |
} | |
override fun getFieldValue(): Pair<String, String> { | |
return Pair(data.guid, formatForSendToApi(dateIsoString)) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
abstract class DynamicAttrDelegate(val field: EditText) : IDynamicAttrField { | |
lateinit var data: FormComponent | |
fun saveAnswerToCache(value: String?) { | |
MemoryCache.setCachedAttribute(data.guid, value) | |
} | |
fun getAnswerFromCache(): String? = | |
MemoryCache.getCachedAttribute(data.guid) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object DynamicAttrFieldFactory { | |
fun configureFieldDelegate( | |
field: EditText, attribute: FormComponent | |
): DynamicAttrDelegate { | |
val inputField: DynamicAttrDelegate = when (attribute.type) { | |
AttributeTypes.FORM_STATIC_ATTR -> NameAttrDelegate(field) | |
AttributeTypes.FORM_TEXT -> TextAttrDelegate(field) | |
AttributeTypes.FORM_NUMBER -> NumberAttrDelegate(field) | |
AttributeTypes.FORM_PHONE -> PhoneAttrDelegate(field) | |
AttributeTypes.FORM_LINK -> LinkAttrDelegate(field) | |
AttributeTypes.FORM_EMAIL -> EmailAttrDelegate(field) | |
AttributeTypes.FORM_DATE -> DateAttrDelegate(field) | |
AttributeTypes.FORM_SELECT -> SelectorAttrDelegate(field) | |
else -> TextAttrDelegate(field) | |
} | |
inputField.setFieldData(attribute) | |
return inputField | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class EditorAttributeView(context: Context, attrs: AttributeSet?) : | |
ConstraintLayout(context, attrs), | |
IEditorAttributeView { | |
init { | |
inflate(R.layout.dynamic_attr_editor, true) | |
setOnClickListener { | |
attrField.requestFocus() | |
attrField.performClick() | |
} | |
setOnFocusChangeListener { v, hasFocus -> | |
if (hasFocus) { | |
divider.setBackgroundColor(ContextCompat.getColor(context, R.color.colorAccent)) | |
errorText.text = "" | |
} else { | |
v.hideKeyboard() | |
divider.setBackgroundColor(ContextCompat.getColor(context, R.color.colorDivider)) | |
errorText.text = "" | |
} | |
} | |
} | |
private lateinit var fieldDelegate: IDynamicAttrField | |
override fun setData(attribute: FormComponent) { | |
attrTitle.text = | |
if (attribute.required == true) | |
TextUtils.concat( | |
attribute.title, getRedString(attrTitle.context, " *") | |
) else | |
attribute.title | |
fieldDelegate = DynamicAttrFieldFactory.configureFieldDelegate(attrField, attribute) | |
} | |
override fun isValidOrShowError(): Boolean { | |
val valid = fieldDelegate.isFieldValid() | |
setValidationErrorVisibility(valid) | |
return (valid == true) | |
} | |
override fun getAttributeValueData(): Pair<String, String> { | |
return fieldDelegate.getFieldValue() | |
} | |
/**Внутренний метод, отображение ошибки*/ | |
private fun setValidationErrorVisibility(valid: Boolean?) { | |
when (valid) { | |
true -> { | |
divider.setBackgroundColor(ContextCompat.getColor(context, R.color.colorDivider)) | |
errorText.text = "" | |
} | |
false -> { | |
divider.setBackgroundColor(ContextCompat.getColor(context, R.color.colorAccentRed)) | |
errorText.text = context.getString(R.string.error_wrong_input_hint) | |
} | |
null -> { | |
divider.setBackgroundColor(ContextCompat.getColor(context, R.color.colorAccentRed)) | |
errorText.text = context.getString(R.string.error_empty_input_hint) | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface IDynamicAttrField { | |
fun setFieldData(attribute: FormComponent) | |
fun isFieldValid(): Boolean? | |
fun getFieldValue(): Pair<String, String> | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object ProfileEditorAttrFactory { | |
private const val KEY_NAME = "Name" | |
private const val KEY_SURNAME = "Surname" | |
private const val KEY_PATRONYMIC = "Patronymic" | |
fun createDynamicAttributes(containerView: ViewGroup, attribute: FormComponent) { | |
if (attribute.type == "form:group") { | |
if (!attribute.attributes.isNullOrEmpty()) | |
attribute.attributes?.forEach { | |
addAttribute(containerView, it) | |
} | |
else log("Пустая группа атрибутов пользователя ${attribute.guid}, ${attribute.title}") | |
} else addAttribute(containerView, attribute) | |
} | |
fun createStaticAttributes( | |
containerView: ViewGroup, surname: String, name: String, patronymic: String | |
) { | |
val surnameComponent = FormComponent( | |
guid = KEY_SURNAME, | |
type = FORM_STATIC_ATTR, | |
name = KEY_SURNAME, | |
value = surname, | |
required = true, | |
title = containerView.context.getString(R.string.title_surname_text) | |
) | |
val nameComponent = FormComponent( | |
guid = KEY_NAME, | |
type = FORM_STATIC_ATTR, | |
name = KEY_NAME, | |
value = name, | |
required = true, | |
title = containerView.context.getString(R.string.title_name_text) | |
) | |
val patronymicComponent = FormComponent( | |
guid = KEY_PATRONYMIC, | |
type = FORM_STATIC_ATTR, | |
name = KEY_PATRONYMIC, | |
value = patronymic, | |
required = false, | |
title = containerView.context.getString(R.string.title_patronymic_text) | |
) | |
addAttribute(containerView, surnameComponent) | |
addAttribute(containerView, nameComponent) | |
addAttribute(containerView, patronymicComponent) | |
} | |
private fun addAttribute(containerView: ViewGroup, attribute: FormComponent) { | |
val view: EditorAttributeView = | |
EditorAttributeView(containerView.context, null) | |
.apply { setData(attribute) } | |
containerView.addView(view) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SelectorAttrDelegate(field: EditText) : | |
DynamicAttrDelegate(field) { | |
init { | |
field.isClickable = false | |
field.isFocusable = false | |
field.setOnEditorActionListener { _: TextView, _: Int, _: KeyEvent -> true } | |
field.setOnClickListener { openSelector() } | |
} | |
private var selectedGuid = "" | |
private var selectedIndex = -1 | |
private var optionsTitleArray = listOf<String>() | |
override fun isFieldValid(): Boolean? = | |
if (selectedGuid.isBlank()) { | |
if (data.required == true) null else true | |
} else { | |
data.options?.firstOrNull { it.guid == selectedGuid } != null | |
} | |
override fun setFieldData(attribute: FormComponent) { | |
data = attribute | |
selectedGuid = getAnswerFromCache() ?: getAnswerFromModel().orEmpty() | |
if (selectedGuid.isNotBlank()) | |
selectedIndex = data.options?.indexOfFirst { it.guid == selectedGuid } ?: -1 | |
if (selectedIndex >= 0) | |
field.setText(data.options?.get(selectedIndex)?.title, TextView.BufferType.EDITABLE) | |
optionsTitleArray = data.options?.map { it.title.orEmpty() } ?: listOf() | |
} | |
private fun getAnswerFromModel(): String? = | |
data.options?.firstOrNull { it.selected == true }?.guid | |
private fun openSelector() { | |
var alertDialog : AlertDialog? = null | |
val listView = RecyclerView(field.context) | |
listView.layoutParams = ViewGroup.LayoutParams( | |
AbsListView.LayoutParams.WRAP_CONTENT, | |
AbsListView.LayoutParams.WRAP_CONTENT | |
) | |
listView.setPadding(0, 8.dpToPx(), 0, 8.dpToPx()) | |
listView.clipToPadding = true | |
listView.setBackgroundColor(Color.WHITE) | |
listView.layoutManager = LinearLayoutManager(field.context) | |
val adapter = SelectorDialogAdapter( | |
listItems = optionsTitleArray, | |
onClick = { position -> | |
selectedIndex = position | |
val selected = data.options?.get(selectedIndex) | |
selectedGuid = selected?.guid.orEmpty() | |
saveAnswerToCache(selectedGuid) | |
Handler().postDelayed({ | |
field.setText(selected?.title, TextView.BufferType.EDITABLE) | |
alertDialog?.dismiss() | |
},300) | |
} | |
) | |
adapter.checked = selectedIndex | |
listView.adapter = adapter | |
val builder = AlertDialog.Builder(field.context) | |
builder.setView(listView) | |
alertDialog = builder.create() | |
alertDialog.show() | |
} | |
override fun getFieldValue(): Pair<String, String> { | |
return Pair(data.guid, selectedGuid) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SelectorDialogAdapter( | |
val listItems: List<String>, | |
val onClick: (Int) -> Unit | |
) : RecyclerView.Adapter<SelectorDialogAdapter.SelectorDialogViewHolder>() { | |
var checked = -1 | |
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = | |
SelectorDialogViewHolder(parent.inflate(R.layout.dialog_attr_selector, false), onClick) | |
override fun onBindViewHolder(holder: SelectorDialogViewHolder, position: Int) = | |
holder.bindItem(listItems[position]) | |
override fun getItemCount() = listItems.size | |
inner class SelectorDialogViewHolder( | |
val view: View, | |
val onClick: (Int) -> Unit | |
) : RecyclerView.ViewHolder(view) { | |
fun bindItem(title: String) { | |
(itemView as CheckedTextView).text = title | |
itemView.isChecked = (checked == adapterPosition) | |
itemView.setOnClickListener { | |
if (checked != adapterPosition) | |
notifyItemChanged(checked) | |
checked = adapterPosition | |
onClick(adapterPosition) | |
notifyItemChanged(adapterPosition) | |
} | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment