Last active
January 10, 2022 23:07
-
-
Save webserveis/53a31b1665cf0f7cfeda5264ac49fab6 to your computer and use it in GitHub Desktop.
RadioGridGroupLayout
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
<?xml version="1.0" encoding="utf-8"?> | |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:orientation="vertical" | |
tools:context=".FirstFragment"> | |
<com.webserveis.app.testradiogridlayout.RadioGridGroupLayout | |
android:id="@+id/gridRadioGroup" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
app:columnCount="2"> | |
<androidx.appcompat.widget.AppCompatRadioButton | |
android:id="@+id/option_1" | |
style="@style/GridRadioButton" | |
android:checked="true" | |
android:drawableTop="@drawable/small_yoga1" | |
android:text="Beginner essentials" | |
app:layout_columnWeight="1" | |
app:layout_rowWeight="1" /> | |
<androidx.appcompat.widget.AppCompatRadioButton | |
android:id="@+id/option_2" | |
style="@style/GridRadioButton" | |
android:drawableTop="@drawable/small_yoga2" | |
android:text="Intermediate essentials" | |
app:layout_columnWeight="1" | |
app:layout_rowWeight="1" | |
tools:checked="true" /> | |
<androidx.appcompat.widget.AppCompatRadioButton | |
android:id="@+id/option_3" | |
style="@style/GridRadioButton" | |
android:drawableTop="@drawable/small_yoga3" | |
android:text="Advanced essentials" | |
app:layout_columnWeight="1" | |
app:layout_rowWeight="1" | |
tools:checked="true" /> | |
<androidx.appcompat.widget.AppCompatRadioButton | |
android:id="@+id/option_4" | |
style="@style/GridRadioButton" | |
android:enabled="false" | |
android:text="Sun salutations" | |
app:drawableTopCompat="@drawable/small_yoga4" | |
app:layout_columnWeight="1" | |
app:layout_rowWeight="1" /> | |
</com.webserveis.app.testradiogridlayout.RadioGridGroupLayout> | |
</LinearLayout> |
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
package com.webserveis.app.testradiogridlayout | |
import android.content.Context | |
import android.util.AttributeSet | |
import android.view.View | |
import android.view.ViewGroup | |
import android.view.accessibility.AccessibilityEvent | |
import android.view.accessibility.AccessibilityNodeInfo | |
import android.widget.CompoundButton | |
import androidx.appcompat.widget.AppCompatRadioButton | |
import androidx.gridlayout.widget.GridLayout | |
import java.util.concurrent.atomic.AtomicInteger | |
/* | |
https://gist.github.com/saiaspire/a73135cfee1110a64cb0ab3451b6ca33 | |
*/ | |
class RadioGridGroupLayout : GridLayout { | |
var checkedCheckableImageButtonId = -1 | |
private set | |
private var mChildOnCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null | |
private var mProtectFromCheckedChange = false | |
private var mOnCheckedChangeListener: OnCheckedChangeListener? = null | |
private var mPassThroughListener: PassThroughHierarchyChangeListener? = null | |
constructor(context: Context?) : super(context) { | |
init() | |
} | |
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) { | |
init() | |
} | |
private fun init() { | |
mChildOnCheckedChangeListener = CheckedStateTracker() | |
mPassThroughListener = PassThroughHierarchyChangeListener() | |
super.setOnHierarchyChangeListener(mPassThroughListener) | |
} | |
override fun setOnHierarchyChangeListener(listener: OnHierarchyChangeListener?) { | |
mPassThroughListener!!.mOnHierarchyChangeListener = listener | |
} | |
override fun onFinishInflate() { | |
super.onFinishInflate() | |
if (checkedCheckableImageButtonId != -1) { | |
mProtectFromCheckedChange = true | |
setCheckedStateForView(checkedCheckableImageButtonId, true) | |
mProtectFromCheckedChange = false | |
setCheckedId(checkedCheckableImageButtonId) | |
} | |
} | |
override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams?) { | |
if (child is AppCompatRadioButton) { | |
if (child.isChecked) { | |
mProtectFromCheckedChange = true | |
if (checkedCheckableImageButtonId != -1) { | |
setCheckedStateForView(checkedCheckableImageButtonId, false) | |
} | |
mProtectFromCheckedChange = false | |
setCheckedId(child.id) | |
} | |
} | |
super.addView(child, index, params) | |
} | |
private fun check(id: Int) { | |
if (id != -1 && id == checkedCheckableImageButtonId) { | |
return | |
} | |
if (checkedCheckableImageButtonId != -1) { | |
setCheckedStateForView(checkedCheckableImageButtonId, false) | |
} | |
if (id != -1) { | |
setCheckedStateForView(id, true) | |
} | |
setCheckedId(id) | |
} | |
fun setCheckedId(id: Int) { | |
checkedCheckableImageButtonId = id | |
if (mOnCheckedChangeListener != null) { | |
mOnCheckedChangeListener?.onCheckedChanged(this, checkedCheckableImageButtonId) | |
} | |
} | |
private fun setCheckedStateForView(viewId: Int, checked: Boolean) { | |
val checkedView: View = findViewById(viewId) | |
if (checkedView is AppCompatRadioButton) { | |
checkedView.isChecked = checked | |
} | |
} | |
fun clearCheck() { | |
check(-1) | |
} | |
fun setOnCheckedChangeListener(listener: OnCheckedChangeListener?) { | |
mOnCheckedChangeListener = listener | |
} | |
override fun onInitializeAccessibilityEvent(event: AccessibilityEvent) { | |
super.onInitializeAccessibilityEvent(event) | |
event.className = RadioGridGroupLayout::class.java.name | |
} | |
override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) { | |
super.onInitializeAccessibilityNodeInfo(info) | |
info.className = RadioGridGroupLayout::class.java.name | |
} | |
interface OnCheckedChangeListener { | |
fun onCheckedChanged(group: RadioGridGroupLayout?, checkedId: Int) | |
} | |
private inner class CheckedStateTracker : CompoundButton.OnCheckedChangeListener { | |
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { | |
if (mProtectFromCheckedChange) { | |
return | |
} | |
mProtectFromCheckedChange = true | |
if (checkedCheckableImageButtonId != -1) { | |
setCheckedStateForView(checkedCheckableImageButtonId, false) | |
} | |
mProtectFromCheckedChange = false | |
val id = buttonView.id | |
setCheckedId(id) | |
} | |
} | |
private inner class PassThroughHierarchyChangeListener : | |
OnHierarchyChangeListener { | |
var mOnHierarchyChangeListener: OnHierarchyChangeListener? = null | |
override fun onChildViewAdded(parent: View, child: View) { | |
if (parent === this@RadioGridGroupLayout && child is AppCompatRadioButton) { | |
var id: Int = child.getId() | |
// generates an id if it's missing | |
if (id == View.NO_ID) { | |
id = generateViewId() | |
child.setId(id) | |
} | |
if (!child.isEnabled) child.alpha = .5F | |
child.setOnCheckedChangeListener( | |
mChildOnCheckedChangeListener | |
) | |
} | |
mOnHierarchyChangeListener?.onChildViewAdded(parent, child) | |
} | |
override fun onChildViewRemoved(parent: View, child: View?) { | |
if (parent === this@RadioGridGroupLayout && child is AppCompatRadioButton) { | |
child.setOnCheckedChangeListener(null) | |
} | |
mOnHierarchyChangeListener?.onChildViewRemoved(parent, child) | |
} | |
} | |
companion object { | |
private val sNextGeneratedId: AtomicInteger = AtomicInteger(1) | |
fun generateViewId(): Int { | |
while (true) { | |
val result: Int = sNextGeneratedId.get() | |
// aapt-generated IDs have the high byte nonzero; clamp to the range under that. | |
var newValue = result + 1 | |
if (newValue > 0x00FFFFFF) newValue = 1 // Roll over to 1, not 0. | |
if (sNextGeneratedId.compareAndSet(result, newValue)) { | |
return result | |
} | |
} | |
} | |
} | |
} |
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
<?xml version="1.0" encoding="utf-8"?> | |
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | |
<item | |
android:drawable="@drawable/yoga1" | |
android:width="128dp" | |
android:height="128dp"> | |
</item> | |
</layer-list> |
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
<?xml version="1.0" encoding="utf-8"?> | |
<shape xmlns:android="http://schemas.android.com/apk/res/android"> | |
<solid android:color="?attr/colorSurface"/> | |
<stroke android:width="2dp" android:color="?android:colorButtonNormal" /> | |
<corners android:radius="16dp"/> | |
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" /> | |
</shape> |
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
<?xml version="1.0" encoding="utf-8"?> | |
<shape xmlns:android="http://schemas.android.com/apk/res/android"> | |
<solid android:color="?attr/colorSurface"/> | |
<stroke android:width="3dp" android:color="?android:colorAccent" /> | |
<corners android:radius="16dp"/> | |
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" /> | |
</shape> |
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
<style name="GridRadioButton"> | |
<item name="android:layout_width">0dp</item> | |
<item name="android:layout_height">wrap_content</item> | |
<item name="android:layout_margin">10dp</item> | |
<item name="android:background">@drawable/button_option_background_selector</item> | |
<item name="android:drawablePadding">16dp</item> | |
<item name="android:button">@null</item> | |
<item name="android:gravity">center_horizontal</item> | |
<item name="android:padding">20dp</item> | |
<item name="android:textColor">@drawable/button_option_text_color_selector</item> | |
<item name="android:textSize">14sp</item> | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment