How the JapaneseKeyboard Project Implements Keyboard View Architecture Using GridLayout and Custom View Controllers
The JapaneseKeyboard app employs a modular architecture where GridLayout containers position key views, specialized controller classes isolate gesture handling, and a dynamic theme engine applies consistent styling across flick, ten-key, and tablet keyboard variants.
The kazumaproject/japanesekeyboard repository demonstrates a sophisticated Android input method that supports diverse keyboard families through a unified design pattern. This article examines the keyboard view architecture using GridLayout and custom view controllers that enables deterministic cell placement, reusable gesture logic, and runtime theme adaptation across Flick, Ten-key, and Tablet implementations.
Architectural Layers of the Keyboard System
The implementation separates concerns into four distinct layers: layout containers, key views, behavior controllers, and the theme engine.
Layout Containers and Grid Implementation
Three container strategies manage the spatial arrangement of keys depending on the keyboard variant.
FlickKeyboardView extends Android's native GridLayout to create a static, non-scrolling grid for the primary flick keyboard. In FlickKeyboardView.kt, the setKeyboard() method receives a KeyboardLayout object and iterates over layout.keys to populate the grid deterministically.
CustomSymbolKeyboardView and the QWERTY/Number keyboards utilize a RecyclerView with GridLayoutManager for dynamic grid generation. This approach enables efficient view recycling when displaying large symbol sets without sacrificing the grid metaphor.
TabletKeyboardView wraps a ConstraintLayout that inflates keyboard_layout.xml (located in tenkey/src/main/res/layout/). This static XML approach supports complex positioning for large-screen devices while maintaining compatibility with the controller attachment system.
Key View Implementation
Individual keys instantiate as either AutoSizeButton (extending Button) for text labels or AppCompatImageButton for icon-based keys. The createKeyView() function in FlickKeyboardView constructs these views based on KeyData properties, applying rowSpec and columnSpec parameters defined in GridLayout.LayoutParams to position each cell within the grid.
Custom View Controllers for Gesture Handling
Gesture logic is isolated from UI code through specialized controller classes attached via attachKeyBehavior(). The architecture implements several controller types:
- CustomAngleFlickController: Manages circular flick pop-up gestures
- CrossFlickInputController: Handles cross-shaped four-directional flicks
- StandardFlickInputController: Processes standard five-direction flicks for alphanumeric keys
- GridFlickInputController: Powers the petal-style flick interface used by the Symbol keyboard
Each controller receives the key view instance and configures touch listeners in controller.attach(), forwarding high-level events such as onFlick, onKey, and onAction to the registered OnKeyboardActionListener supplied by the host IME.
Theme Engine and Dynamic Updates
The applyKeyboardTheme() method in base view classes manages visual styling across Neumorphism, Material-You, and custom color modes. Runtime state changes propagate through dynamicKeyMap, which stores references to keys requiring visual updates. When updateDynamicKey() triggers, the system rebuilds the specific key view and reattaches the appropriate controller while preserving its grid position.
Keyboard Construction Workflow
The assembly process follows a strict initialization sequence when setKeyboard() executes.
-
Grid Initialization: The view calls
removeAllViews()and configurescolumnCountandrowCountfrom theKeyboardLayoutmetadata. -
View Instantiation: For each
KeyDataentry,createKeyView()determines whether to instantiate anAutoSizeButtonorAppCompatImageButtonbased onkeyData.isSpecialKeyandkeyData.drawableResId. -
Controller Attachment:
attachKeyBehavior()inspectskeyData.keyType(values includeCIRCULAR_FLICK,STANDARD_FLICK,CROSS_FLICK,PETAL_FLICK, orNORMAL) to instantiate the matching controller fromcustom_keyboard/src/main/java/com/kazumaproject/custom_keyboard/controller/. The controller configures flick range, color themes, and listener interfaces before attaching to the view. -
Event Propagation: Touch events route through the controller's gesture recognition logic, which translates physical movements into semantic actions forwarded to the IME service via the
OnKeyboardActionListenerinterface.
Design Rationale
Why GridLayout?
The architecture leverages GridLayout for the Flick keyboard because it provides deterministic cell placement matching the physical Japanese keyboard matrix. Unlike RecyclerView, GridLayout requires no adapter overhead, minimizing touch latency for gesture-intensive input. The direct ownership of LayoutParams by the grid simplifies background drawable application and margin adjustments during theme changes.
Why Custom Controllers?
Isolating gesture logic into controller classes enables the same StandardFlickInputController to function across QWERTY, Ten-key, and Symbol keyboards without code duplication. This separation of concerns keeps view classes focused on layout and rendering while making gesture patterns testable and extensible. Adding a new flick pattern requires only a new controller subclass, leaving the grid container untouched.
Implementation Examples
The following Kotlin examples demonstrate typical usage patterns:
// Initialize a flick keyboard in an IME service
val flickView = FlickKeyboardView(context)
flickView.setOnKeyboardActionListener(object :
FlickKeyboardView.OnKeyboardActionListener {
override fun onKey(text: String, isFlick: Boolean) {
inputConnection.commitText(text, 1)
}
override fun onAction(action: KeyAction, view: View, isFlick: Boolean) {
// Handle space, delete, enter actions
}
})
// Load layout and display
flickView.setKeyboard(myJapaneseLayout)
Applying custom themes through the architecture:
flickView.applyKeyboardTheme(
themeMode = "custom",
currentNightMode = resources.configuration.uiMode,
isDynamicColorEnabled = false,
customBgColor = Color.parseColor("#FAFAFA"),
customKeyColor = Color.parseColor("#EEEEEE"),
customSpecialKeyColor = Color.parseColor("#DDDDDD"),
customKeyTextColor = Color.BLACK,
customSpecialKeyTextColor = Color.DKGRAY,
liquidGlassEnable = false,
customBorderEnable = true,
customBorderColor = Color.GRAY,
liquidGlassKeyAlphaEnable = 230,
borderWidth = 2
)
Ten-key implementation using RecyclerView with GridLayoutManager:
val tenKeyRecycler = RecyclerView(context).apply {
layoutManager = GridLayoutManager(
context,
3,
RecyclerView.HORIZONTAL,
false
)
adapter = TenKeyAdapter(myTenKeyLayout)
}
Summary
-
GridLayout containers in
FlickKeyboardViewprovide zero-overhead view hierarchies for latency-sensitive flick gestures, whileRecyclerViewwithGridLayoutManagerhandles larger datasets in the Symbol and Ten-key variants located insymbol_keyboard/andtenkey/modules. -
Custom view controllers encapsulate gesture recognition into reusable classes like
StandardFlickInputControllerandCustomAngleFlickController, enabling consistent behavior across different keyboard modes without modifying view code. -
Key views instantiate as
AutoSizeButtonorAppCompatImageButtonbased onKeyDataspecifications, with layout parameters managed by the parent grid container viarowSpecandcolumnSpec. -
Dynamic theming applies through
applyKeyboardTheme()andupdateDynamicKey(), allowing runtime visual updates without reconstructing the entire view hierarchy. -
Tablet optimization uses
TabletKeyboardViewwith a static XML grid defined inkeyboard_layout.xml, demonstrating how the controller system adapts to different layout containers while maintaining gesture consistency.
Frequently Asked Questions
What is the difference between FlickKeyboardView and CustomSymbolKeyboardView?
FlickKeyboardView extends GridLayout directly for static, non-scrolling keyboards optimized for flick gestures, while CustomSymbolKeyboardView uses a RecyclerView with GridLayoutManager to efficiently handle larger symbol sets that require view recycling. Both utilize the same controller classes for gesture handling but differ in their container implementations to suit their specific content needs and performance characteristics.
How does the architecture handle different flick gesture patterns?
The system implements specialized controllers—StandardFlickInputController for five-direction flicks, CrossFlickInputController for cross-shaped inputs, and GridFlickInputController for petal-style arrangements—that attach to key views via attachKeyBehavior() in FlickKeyboardView. Each controller interprets touch events independently and forwards high-level actions to the OnKeyboardActionListener, allowing the grid container to remain agnostic of gesture specifics.
Can the keyboard appearance change dynamically without rebuilding the entire layout?
Yes, the dynamicKeyMap in FlickKeyboardView maintains references to keys that require runtime updates. When updateDynamicKey(keyId, stateIndex) triggers, the system rebuilds only the affected key view and reattaches its controller, preserving the grid structure and maintaining the existing layout parameters defined during initial setKeyboard() execution.
Where are the gesture controllers defined in the source code?
Controller classes reside in custom_keyboard/src/main/java/com/kazumaproject/custom_keyboard/controller/, including CustomAngleFlickController.kt, StandardFlickInputController.kt, and CrossFlickInputController.kt. These classes implement the touch recognition logic that attaches to views created in FlickKeyboardView.kt through the attachKeyBehavior() method, ensuring separation between visual layout and input behavior.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →