Last active
April 17, 2025 19:07
-
-
Save Dev-Husnain/1f22903e3afe8a099b076fe264434080 to your computer and use it in GitHub Desktop.
CustomRadioView & CustomRadioGroup (Customize you views).
This file contains hidden or 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 CustomRadioGroup @JvmOverloads constructor( | |
| context: Context, attrs: AttributeSet? = null | |
| ) : ConstraintLayout(context, attrs) { | |
| private var checkedView: CustomRadioView? = null | |
| private var listener: ((CustomRadioView) -> Unit)? = null | |
| fun setOnCheckedChangeListener(listener: (CustomRadioView) -> Unit) { | |
| this.listener = listener | |
| } | |
| fun onChildChecked(checked: CustomRadioView) { | |
| if (checkedView == checked) return | |
| checkedView?.setChecked(false) | |
| checkedView = checked | |
| checked.setChecked(true) | |
| listener?.invoke(checked) | |
| } | |
| fun clearSelection() { | |
| checkedView?.setChecked(false) | |
| checkedView = null | |
| } | |
| fun getCheckedRadioView(): CustomRadioView? = checkedView | |
| fun getCheckedText(): String? = checkedView?.getText() | |
| fun getCheckedIndex(): Int = indexOfChild(checkedView) | |
| } |
This file contains hidden or 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 CustomRadioView @JvmOverloads constructor( | |
| context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 | |
| ) : ConstraintLayout(context, attrs, defStyleAttr) { | |
| private var isChecked = false | |
| private var defaultChecked = false | |
| private var textView: AppCompatTextView | |
| // Style properties | |
| private var selectedCornerRadius = 0f | |
| private var unselectedCornerRadius = 0f | |
| private var selectedBgColor = 0 | |
| private var unselectedBgColor = 0 | |
| private var selectedStrokeWidth = 0 | |
| private var unselectedStrokeWidth = 0 | |
| private var selectedStrokeColor = 0 | |
| private var unselectedStrokeColor = 0 | |
| private var selectedTextColor = 0 | |
| private var unselectedTextColor = 0 | |
| private var selectedTextSize = 0f | |
| private var unselectedTextSize = 0f | |
| private var selectedFontStyle = Typeface.NORMAL | |
| private var unselectedFontStyle = Typeface.NORMAL | |
| init { | |
| LayoutInflater.from(context).inflate(R.layout.view_custom_radio, this, true) | |
| textView = findViewById(R.id.radioText) | |
| context.theme.obtainStyledAttributes(attrs, R.styleable.CustomRadioView, 0, 0).apply { | |
| try { | |
| selectedCornerRadius = | |
| getDimension(R.styleable.CustomRadioView_cornerRadiusSelected, 0f) | |
| unselectedCornerRadius = | |
| getDimension(R.styleable.CustomRadioView_cornerRadiusUnselected, 0f) | |
| selectedBgColor = getColor(R.styleable.CustomRadioView_backgroundColorSelected, 0) | |
| unselectedBgColor = | |
| getColor(R.styleable.CustomRadioView_backgroundColorUnselected, 0) | |
| selectedStrokeWidth = | |
| getDimensionPixelSize(R.styleable.CustomRadioView_strokeWidthSelected, 0) | |
| unselectedStrokeWidth = | |
| getDimensionPixelSize(R.styleable.CustomRadioView_strokeWidthUnselected, 0) | |
| selectedStrokeColor = getColor(R.styleable.CustomRadioView_strokeColorSelected, 0) | |
| unselectedStrokeColor = | |
| getColor(R.styleable.CustomRadioView_strokeColorUnselected, 0) | |
| selectedTextColor = getColor(R.styleable.CustomRadioView_textColorSelected, 0) | |
| unselectedTextColor = getColor(R.styleable.CustomRadioView_textColorUnselected, 0) | |
| selectedTextSize = | |
| getDimension(R.styleable.CustomRadioView_textSizeSelected, textView.textSize) | |
| unselectedTextSize = | |
| getDimension(R.styleable.CustomRadioView_textSizeUnselected, textView.textSize) | |
| defaultChecked = getBoolean(R.styleable.CustomRadioView_defaultChecked, false) | |
| selectedFontStyle = | |
| getInt(R.styleable.CustomRadioView_fontStyleSelected, Typeface.NORMAL) | |
| unselectedFontStyle = | |
| getInt(R.styleable.CustomRadioView_fontStyleUnselected, Typeface.NORMAL) | |
| val text = getString(R.styleable.CustomRadioView_android_text) | |
| textView.text = text ?: "" | |
| } finally { | |
| recycle() | |
| } | |
| } | |
| post { | |
| setDefaultChecked(defaultChecked) | |
| } | |
| setOnClickListener { | |
| if (!isChecked) { | |
| setChecked(true) | |
| } else { | |
| (parent as? CustomRadioGroup)?.onChildChecked(this) | |
| } | |
| } | |
| refreshView() | |
| } | |
| private fun refreshView() { | |
| val drawable = GradientDrawable().apply { | |
| cornerRadius = if (isChecked) selectedCornerRadius else unselectedCornerRadius | |
| setColor(if (isChecked) selectedBgColor else unselectedBgColor) | |
| setStroke( | |
| if (isChecked) selectedStrokeWidth else unselectedStrokeWidth, | |
| if (isChecked) selectedStrokeColor else unselectedStrokeColor | |
| ) | |
| } | |
| background = drawable | |
| textView.setTextColor(if (isChecked) selectedTextColor else unselectedTextColor) | |
| textView.setTextSize( | |
| TypedValue.COMPLEX_UNIT_PX, | |
| if (isChecked) selectedTextSize else unselectedTextSize | |
| ) | |
| textView.setTypeface(null, if (isChecked) selectedFontStyle else unselectedFontStyle) | |
| } | |
| fun setChecked(checked: Boolean) { | |
| if (checked == isChecked) return | |
| isChecked = checked | |
| refreshView() | |
| (parent as? CustomRadioGroup)?.onChildChecked(this) | |
| } | |
| fun getText(): String = textView.text.toString() | |
| private fun setDefaultChecked(defaultChecked: Boolean) { | |
| isChecked = defaultChecked | |
| post { refreshView() } | |
| if (defaultChecked) { | |
| (parent as? CustomRadioGroup)?.onChildChecked(this) | |
| } | |
| } | |
| fun isChecked(): Boolean = isChecked | |
| } |
This file contains hidden or 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
| 📌 CustomRadioView & CustomRadioGroup (Kotlin – Android) | |
| ✅ Overview | |
| CustomRadioView is a highly customizable view designed to behave like a RadioButton, with support for rich visual customization such as: | |
| ->different corner radius | |
| ->Background color | |
| ->Stroke | |
| ->Text style | |
| ->Text size and font style | |
| It is designed to work inside a CustomRadioGroup that manages exclusive selection behavior — only one item is selected at a time, similar to native RadioGroup. | |
| 🎯 Features | |
| ✅ Fully customizable for selected and unselected states: | |
| Corner radius | |
| Background color | |
| Stroke color & width | |
| Text color | |
| Text size | |
| Font style (bold, italic, normal) | |
| Elevation | |
| Default selection | |
| ✅ Use standard Android attributes: | |
| android:text | |
| android:elevation | |
| ✅ Can contain other child views inside it — built using ConstraintLayout, FrameLayout, or similar. | |
| ✅ Easily used in XML and programmatically controlled in Kotlin. | |
| 🧩 Attributes | |
| defaultChecked | boolean | Marks this view as selected by default | |
| selectedCornerRadius | dimension | Corner radius when selected | |
| unselectedCornerRadius | dimension | Corner radius when unselected | |
| selectedBackgroundColor | color | Background when selected | |
| unselectedBackgroundColor | color | Background when unselected | |
| selectedStrokeColor | color | Stroke when selected | |
| unselectedStrokeColor | color | Stroke when unselected | |
| selectedStrokeWidth | dimension | Stroke width when selected | |
| unselectedStrokeWidth | dimension | Stroke width when unselected | |
| selectedTextColor | color | Text color when selected | |
| unselectedTextColor | color | Text color when unselected | |
| selectedTextSize | dimension | Text size when selected | |
| unselectedTextSize | dimension | Text size when unselected | |
| selectedFontStyle | integer (0:normal, 1:bold, 2:italic) | Font style when selected | |
| unselectedFontStyle | integer | Font style when unselected |
This file contains hidden or 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
| //Add below to you xml file | |
| <com.all.languages.text.voice.image.translation.views.CustomRadioGroup | |
| android:id="@+id/radioGroup" | |
| android:layout_width="0dp" | |
| android:layout_height="wrap_content" | |
| android:layout_marginBottom="@dimen/_10sdp" | |
| app:layout_constraintBottom_toTopOf="@id/btnContinue" | |
| app:layout_constraintEnd_toEndOf="@id/btnContinue" | |
| app:layout_constraintStart_toStartOf="@id/btnContinue"> | |
| <com.all.languages.text.voice.image.translation.views.CustomRadioView | |
| android:id="@+id/radioWeekly" | |
| android:layout_width="0dp" | |
| android:layout_height="wrap_content" | |
| android:elevation="0.5dp" | |
| android:padding="@dimen/_7sdp" | |
| app:backgroundColorSelected="#D0E5FF" | |
| app:backgroundColorUnselected="#F4F3F8" | |
| app:cornerRadiusSelected="@dimen/_10sdp" | |
| app:cornerRadiusUnselected="@dimen/_10sdp" | |
| app:defaultChecked="false" | |
| app:layout_constraintEnd_toEndOf="parent" | |
| app:layout_constraintStart_toStartOf="parent" | |
| app:layout_constraintTop_toTopOf="parent" | |
| app:strokeColorSelected="#1A73E8" | |
| app:strokeColorUnselected="#ADADAD" | |
| app:strokeWidthSelected="@dimen/_1sdp" | |
| app:strokeWidthUnselected="@dimen/_1sdp"> | |
| <com.google.android.material.textview.MaterialTextView | |
| android:id="@+id/tvWeekly" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_marginStart="@dimen/_10sdp" | |
| android:text="@string/weekly" | |
| android:textColor="@color/black" | |
| android:textSize="@dimen/_13ssp" | |
| android:textStyle="bold" | |
| app:layout_constraintStart_toStartOf="parent" | |
| app:layout_constraintTop_toTopOf="parent" /> | |
| <com.google.android.material.textview.MaterialTextView | |
| android:id="@+id/tvPriceWeekly" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_marginEnd="@dimen/_10sdp" | |
| android:text="@string/dots" | |
| android:textColor="@color/black" | |
| android:textSize="@dimen/_14ssp" | |
| android:textStyle="bold" | |
| app:layout_constraintBottom_toBottomOf="parent" | |
| app:layout_constraintEnd_toEndOf="parent" | |
| app:layout_constraintTop_toTopOf="parent" /> | |
| <com.google.android.material.textview.MaterialTextView | |
| android:id="@+id/tvPayWeekly" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_marginStart="@dimen/_10sdp" | |
| android:text="@string/pay_for_weekly" | |
| android:textColor="@color/colorAB" | |
| android:textSize="@dimen/_12ssp" | |
| app:layout_constraintStart_toStartOf="parent" | |
| app:layout_constraintTop_toBottomOf="@id/tvWeekly" /> | |
| </com.all.languages.text.voice.image.translation.views.CustomRadioView> | |
| <com.all.languages.text.voice.image.translation.views.CustomRadioView | |
| android:id="@+id/radioMonthly" | |
| android:layout_width="0dp" | |
| android:layout_height="wrap_content" | |
| android:layout_marginTop="@dimen/_10sdp" | |
| android:elevation="0.5dp" | |
| android:padding="@dimen/_7sdp" | |
| app:backgroundColorSelected="#D0E5FF" | |
| app:backgroundColorUnselected="#F4F3F8" | |
| app:cornerRadiusSelected="@dimen/_10sdp" | |
| app:cornerRadiusUnselected="@dimen/_10sdp" | |
| app:defaultChecked="true" | |
| app:layout_constraintEnd_toEndOf="parent" | |
| app:layout_constraintStart_toStartOf="parent" | |
| app:layout_constraintTop_toBottomOf="@id/radioWeekly" | |
| app:strokeColorSelected="#1A73E8" | |
| app:strokeColorUnselected="#ADADAD" | |
| app:strokeWidthSelected="@dimen/_1sdp" | |
| app:strokeWidthUnselected="@dimen/_1sdp"> | |
| <com.google.android.material.textview.MaterialTextView | |
| android:id="@+id/tvMonthly" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_marginStart="@dimen/_10sdp" | |
| android:text="@string/monthly" | |
| android:textColor="@color/black" | |
| android:textSize="@dimen/_13ssp" | |
| android:textStyle="bold" | |
| app:layout_constraintStart_toStartOf="parent" | |
| app:layout_constraintTop_toTopOf="parent" /> | |
| <com.google.android.material.textview.MaterialTextView | |
| android:id="@+id/tvPriceMonthly" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_marginEnd="@dimen/_10sdp" | |
| android:text="@string/dots" | |
| android:textColor="@color/black" | |
| android:textSize="@dimen/_14ssp" | |
| android:textStyle="bold" | |
| app:layout_constraintBottom_toBottomOf="parent" | |
| app:layout_constraintEnd_toEndOf="parent" | |
| app:layout_constraintTop_toTopOf="parent" /> | |
| <com.google.android.material.textview.MaterialTextView | |
| android:id="@+id/tvPayMonthly" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_marginStart="@dimen/_10sdp" | |
| android:text="@string/pay_for_monthly" | |
| android:textColor="@color/colorAB" | |
| android:textSize="@dimen/_12ssp" | |
| app:layout_constraintStart_toStartOf="parent" | |
| app:layout_constraintTop_toBottomOf="@id/tvMonthly" /> | |
| </com.all.languages.text.voice.image.translation.views.CustomRadioView> | |
| <com.all.languages.text.voice.image.translation.views.CustomRadioView | |
| android:id="@+id/radioYearly" | |
| android:layout_width="0dp" | |
| android:layout_height="wrap_content" | |
| android:layout_marginTop="@dimen/_10sdp" | |
| android:elevation="0.5dp" | |
| android:padding="@dimen/_7sdp" | |
| app:backgroundColorSelected="#D0E5FF" | |
| app:backgroundColorUnselected="#F4F3F8" | |
| app:cornerRadiusSelected="@dimen/_10sdp" | |
| app:cornerRadiusUnselected="@dimen/_10sdp" | |
| app:defaultChecked="false" | |
| app:layout_constraintEnd_toEndOf="parent" | |
| app:layout_constraintStart_toStartOf="parent" | |
| app:layout_constraintTop_toBottomOf="@id/radioMonthly" | |
| app:strokeColorSelected="#1A73E8" | |
| app:strokeColorUnselected="#ADADAD" | |
| app:strokeWidthSelected="@dimen/_1sdp" | |
| app:strokeWidthUnselected="@dimen/_1sdp"> | |
| <com.google.android.material.textview.MaterialTextView | |
| android:id="@+id/tvYearly" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_marginStart="@dimen/_10sdp" | |
| android:text="@string/yearly" | |
| android:textColor="@color/black" | |
| android:textSize="@dimen/_13ssp" | |
| android:textStyle="bold" | |
| app:layout_constraintStart_toStartOf="parent" | |
| app:layout_constraintTop_toTopOf="parent" /> | |
| <com.google.android.material.textview.MaterialTextView | |
| android:id="@+id/tvPriceYearly" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_marginEnd="@dimen/_10sdp" | |
| android:text="@string/dots" | |
| android:textColor="@color/black" | |
| android:textSize="@dimen/_14ssp" | |
| android:textStyle="bold" | |
| app:layout_constraintBottom_toBottomOf="parent" | |
| app:layout_constraintEnd_toEndOf="parent" | |
| app:layout_constraintTop_toTopOf="parent" /> | |
| <com.google.android.material.textview.MaterialTextView | |
| android:id="@+id/tvPayYearly" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_marginStart="@dimen/_10sdp" | |
| android:text="@string/pay_for_yearly" | |
| android:textColor="@color/colorAB" | |
| android:textSize="@dimen/_12ssp" | |
| app:layout_constraintStart_toStartOf="parent" | |
| app:layout_constraintTop_toBottomOf="@id/tvYearly" /> | |
| </com.all.languages.text.voice.image.translation.views.CustomRadioView> | |
| </com.all.languages.text.voice.image.translation.views.CustomRadioGroup> |
This file contains hidden or 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
| //add below to you layouts folder | |
| <?xml version="1.0" encoding="utf-8"?> | |
| <merge xmlns:android="http://schemas.android.com/apk/res/android"> | |
| <androidx.appcompat.widget.AppCompatTextView | |
| android:id="@+id/radioText" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_gravity="center" | |
| android:layout_margin="8dp" | |
| android:gravity="center" | |
| android:textColor="@android:color/black" | |
| android:textSize="16sp" /> | |
| </merge> |
This file contains hidden or 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
| //add below to your attrs folder | |
| <declare-styleable name="CustomRadioView"> | |
| <attr name="android:text" /> | |
| <attr name="defaultChecked" format="boolean" /> | |
| <attr name="cornerRadiusSelected" format="dimension" /> | |
| <attr name="cornerRadiusUnselected" format="dimension" /> | |
| <attr name="backgroundColorSelected" format="color" /> | |
| <attr name="backgroundColorUnselected" format="color" /> | |
| <attr name="strokeWidthSelected" format="dimension" /> | |
| <attr name="strokeWidthUnselected" format="dimension" /> | |
| <attr name="strokeColorSelected" format="color" /> | |
| <attr name="strokeColorUnselected" format="color" /> | |
| <attr name="textColorSelected" format="color" /> | |
| <attr name="textColorUnselected" format="color" /> | |
| <attr name="textSizeSelected" format="dimension" /> | |
| <attr name="textSizeUnselected" format="dimension" /> | |
| <attr name="fontStyleSelected" format="enum"> | |
| <enum name="normal" value="0" /> | |
| <enum name="bold" value="1" /> | |
| <enum name="italic" value="2" /> | |
| </attr> | |
| <attr name="fontStyleUnselected" format="enum"> | |
| <enum name="normal" value="0" /> | |
| <enum name="bold" value="1" /> | |
| <enum name="italic" value="2" /> | |
| </attr> | |
| </declare-styleable> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment