Chords
A Kotlin multi-platform view library for displaying stringed instrument chord diagrams
Install / Use
/learn @chRyNaN/ChordsREADME
chords
An easily customizable Kotlin multi-platform View to display guitar (and other stringed instrument) chords. Simple to use and beautifully designed.
Current Version:
<img alt="Sample Screenshot" src="https://github.com/chRyNaN/chords/blob/master/sample-android/screenshots/device-2020-01-18-180759.png" width="300"></img> <img alt="Sample Screenshot" src="https://github.com/chRyNaN/chords/blob/master/sample-android/screenshots/device-2020-01-18-183742.png" width="300"></img>
This library has been updated significantly from its original version and the process is detailed in this blog post which was featured in Android Weekly issue #398. <a href="https://androidweekly.net/issues/issue-398" title="Android Weekly Issue 398"> <img alt="Badge" src="https://androidweekly.net/issues/issue-398/badge" height="20px"></img> </a>
Building the library
The library is provided through Repsy.io. Checkout the releases page to get the latest version. <br /> <img alt="GitHub tag (latest by date)" src="https://img.shields.io/github/v/tag/chRyNaN/chords">
Repository
repositories {
maven { url = uri("https://repo.repsy.io/mvn/chrynan/public") }
}
Dependencies
core:
implementation("com.chrynan.chords:chords-core:VERSION")
compose:
implementation("com.chrynan.chords:chords-compose:VERSION")
Usage
There are a few main components to using the library:
ChordWidgetis theChordViewimplementation that displays the chord.ChordChartis a class that represents information about the chord chart that will be displayed.Chordis a class that represents the markers on a chord that will be displayed.
Creating an instance of ChordWidget
Android:
<!-- Specify an exact size (MATCH_PARENT, MATCH_CONSTRAINTS, DP value). -->
<com.chrynan.chords.widget.ChordWidget
android:id="@+id/chordWidget"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Kotlin JS:
val widget = ChordWidget(htmlCanvas)
Jetpack Compose:
ChordWidget(
modifier = Modifier.size(width = 200.dp, height = 200.dp),
chord = chord,
chart = chart
)
Assigning a ChordChart to a ChordWidget
chordWidget?.chart = ChordChart(
fretStart = FretNumber(1),
fretEnd = FretNumber(2),
stringCount = 6,
stringLabels = setOf(
StringLabel(string = StringNumber(1), label = "e"),
StringLabel(string = StringNumber(2), label = "B"),
StringLabel(string = StringNumber(3), label = "G"),
StringLabel(string = StringNumber(4), label = "D"),
StringLabel(string = StringNumber(5), label = "A"),
StringLabel(string = StringNumber(6), label = "E")
)
)
Creating a Chord using the DSL
val chord = chord("G") {
+ChordMarker.Note(
fret = FretNumber(3),
finger = Finger.MIDDLE,
string = StringNumber(6)
)
+ChordMarker.Note(
fret = FretNumber(2),
finger = Finger.INDEX,
string = StringNumber(5)
)
+ChordMarker.Open(string = StringNumber(4))
+ChordMarker.Open(string = StringNumber(3))
+ChordMarker.Note(
fret = FretNumber(3),
finger = Finger.RING,
string = StringNumber(2)
)
+ChordMarker.Note(
fret = FretNumber(3),
finger = Finger.PINKY,
string = StringNumber(1)
)
}
Assigning a Chord to a ChordWidget
chordWidget?.chord = chord
Note: This library doesn't try to coerce values to fit into a chart or exclude values that exceed the chart bounds.
If the ChordChart and Chord have inconsistent values, the ChordWidget may look peculiar. It's important for the
user of the library to properly handle coordinating the different components.
Parsing Chords from other formats
The ChordParser interface takes in an input type and outputs a ChordParseResult. This interface can be implemented
for different format input types. There are a couple provided ChordParser implementations.
AsciiChordParser:
AsciiChordParser parses a String input of an ASCII Chord Diagram and outputs a ChordParseResult containing
a Chord.
val chordDiagram = """
C
e |-----0------|
B |-----1------|
G |-----0------|
D |-----2------|
A |-----3------|
E |------------|
""".trimIndent()
val parser = AsciiChordParser()
launch {
// parse() is a suspending function and needs to be called from another suspending
// function or a coroutine
val result = parser.parse(chordDiagram)
val chord: Chord = result.chord
val stringLabels: Set<StringLabel> = result.stringLabels
val baseFret: FretNumber? = result.baseFret
}
ChordProParser:
ChordProParser parses a String input of a Chord Pro (Chord or Define) Directive and outputs a ChordParseResult
containing a Chord.
val chordDiagram = "{define: Bes base-fret 1 frets 1 1 3 3 3 1 fingers 1 1 2 3 4 1}"
val parser = ChordProParser()
launch {
// parse() is a suspending function and needs to be called from another suspending
// function or a coroutine
val result = parser.parse(chordDiagram)
val chord: Chord = result.chord
val stringLabels: Set<StringLabel> = result.stringLabels
val baseFret: FretNumber? = result.baseFret
}
Customizing the appearance
ChordWidget implements the ChordView interface which contains properties to alter the appearance of the view.
ChordView:
interface ChordView {
...
var fitToHeight: Boolean
var showFretNumbers: Boolean
var showFingerNumbers: Boolean
var stringLabelState: StringLabelState
var mutedStringText: String
var openStringText: String
var fretColor: ColorInt
var fretLabelTextColor: ColorInt
var stringColor: ColorInt
var stringLabelTextColor: ColorInt
var noteColor: ColorInt
var noteLabelTextColor: ColorInt
...
}
Updating properties directly on ChordWidget:
chordWidget?.noteColor = Color.BLUE
chordWidget?.openStringText = "o"
Note: That in Kotlin JS, you have to explicitly call the render() function after updating properties on
the ChordWidget.
Updating properties using a ViewModel and Binder:
val binder = ChordViewBinder(chordWidget)
val viewModel = ChordViewModel(
fretColor = Color.BLACK,
fretLabelTextColor = Color.WHITE,
stringLabelTextColor = Color.BLACK,
stringColor = Color.BLACK,
noteColor = Color.BLACK,
noteLabelTextColor = Color.WHITE
)
binder.bind(viewModel)
Note: That in Kotlin JS, there is a convenience function ChordViewBinder.bindAndRender() which will bind the
properties from the ChordViewModel to the ChordWidget and call render().
Updating properties in Android XML:
<com.chrynan.chords.widget.ChordWidget
android:id="@+id/chordWidget"
android:layout_width="200dp"
android:layout_height="300dp"
app:stringLabelState="label"
app:showFretNumbers="false"/>
Available Android XML Attributes:
<attr name="fretColor" format="color"/>
<attr name="fretLabelTextColor" format="color"/>
<attr name="stringColor" format="color"/>
<attr name="stringLabelTextColor" format="color"/>
<attr name="noteColor" format="color"/>
<attr name="noteLabelTextColor" format="color"/>
<attr name="mutedStringText" format="string"/>
<attr name="openStringText" format="string"/>
<attr name="showFingerNumbers" format="boolean"/>
<attr name="showFretNumbers" format="boolean"/>
<attr name="stringLabelState" format="enum">
<enum name="number" value="0"/>
<enum name="label" value="1"/>
<enum name="hide" value="2"/>
</attr>
<attr name="fitToHeight" format="boolean"/>
<attr name="fretLabelTypeface" format="reference"/>
<attr name="noteLabelTypeface" format="reference"/>
<attr name="stringLabelTypeface" format="reference"/>
Selectable Chord names in Text using Android Spans
The library comes with a ChordSpan which allows the pairing of text with a Chord. And when the ChordSpan is
selected, a listener is alerted with the Chord.
Adding a ChordSpan to a TextView:
val text = SpannableString("G")
val span = ChordSpan(chord, this) // "this" refers to the listener
text.setSpan(span, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
textView?.text = text
// Need to specify LinkTouchMovementMethod as the movement method for clicks to work
textView?.movementMethod = LinkTouchMovementMethod()
Using DSL functions to add a ChordSpan to a TextView:
val textBuilder = buildSpannableString {
+chordSpan(chord, listener)
+chordSpan("G", chord) {
// Handle click event
}
+styledChordSpan(chord, listener) {
this.backgroundColor = Color.BLUE
}
}
textView?.text = textBuilder
// Need to specify LinkTouchMovementMethod as the movement method for clicks to work
textView?.movementMethod = LinkTouchMovementMethod()
Listening to Chord selected events:
class MainActivity : AppCompatActivity(),
ChordSpan.ChordSelectedListener {
override fun onChordSpanSelected(chord: Chord) {
// Perform action with chord
}
}
Customizing the ChordSpan appearance:
ChordSpan extends from TouchableSpan which inherits from TouchableSpanView and has the following customizable
properties:
var backgroundColor = Color.TRANSPARENT
var selectedBackgroundColor = Color.TRANSPARENT
var textColor = Color.BLUE
var selectedTextColor = Color.BLUE
var isUnderlined = false
var isUnderlinedWhenSelected = false
var textTypeface = Typeface
Related Skills
node-connect
337.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
337.3kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.2kCommit, push, and open a PR
