项目作者: chRyNaN

项目描述 :
A Kotlin multi-platform view library for displaying stringed instrument chord diagrams
高级语言: Kotlin
项目地址: git://github.com/chRyNaN/chords.git
创建时间: 2016-02-09T21:14:06Z
项目社区:https://github.com/chRyNaN/chords

开源协议:Apache License 2.0

下载


chords

An easily customizable Kotlin multi-platform View to display guitar (and other stringed instrument) chords. Simple to
use and beautifully designed.

Current Version: GitHub tag (latest by date)

Sample Screenshot
Sample Screenshot

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.

Badge

Building the library

The library is provided through Repsy.io. Checkout
the releases page to get the latest version.

GitHub tag (latest by date)

Repository

  1. repositories {
  2. maven { url = uri("https://repo.repsy.io/mvn/chrynan/public") }
  3. }

Dependencies

core:

  1. implementation("com.chrynan.chords:chords-core:VERSION")

compose:

  1. implementation("com.chrynan.chords:chords-compose:VERSION")

Usage

There are a few main components to using the library:

  • ChordWidget is the ChordView implementation that displays the chord.
  • ChordChart is a class that represents information about the chord chart that will be displayed.
  • Chord is a class that represents the markers on a chord that will be displayed.

Creating an instance of ChordWidget

Android:

  1. <!-- Specify an exact size (MATCH_PARENT, MATCH_CONSTRAINTS, DP value). -->
  2. <com.chrynan.chords.widget.ChordWidget
  3. android:id="@+id/chordWidget"
  4. android:layout_width="match_parent"
  5. android:layout_height="wrap_content"></com.chrynan.chords.widget.ChordWidget>

Kotlin JS:

  1. val widget = ChordWidget(htmlCanvas)

Jetpack Compose:

  1. ChordWidget(
  2. modifier = Modifier.size(width = 200.dp, height = 200.dp),
  3. chord = chord,
  4. chart = chart
  5. )

Assigning a ChordChart to a ChordWidget

  1. chordWidget?.chart = ChordChart(
  2. fretStart = FretNumber(1),
  3. fretEnd = FretNumber(2),
  4. stringCount = 6,
  5. stringLabels = setOf(
  6. StringLabel(string = StringNumber(1), label = "e"),
  7. StringLabel(string = StringNumber(2), label = "B"),
  8. StringLabel(string = StringNumber(3), label = "G"),
  9. StringLabel(string = StringNumber(4), label = "D"),
  10. StringLabel(string = StringNumber(5), label = "A"),
  11. StringLabel(string = StringNumber(6), label = "E")
  12. )
  13. )

Creating a Chord using the DSL

  1. val chord = chord("G") {
  2. +ChordMarker.Note(
  3. fret = FretNumber(3),
  4. finger = Finger.MIDDLE,
  5. string = StringNumber(6)
  6. )
  7. +ChordMarker.Note(
  8. fret = FretNumber(2),
  9. finger = Finger.INDEX,
  10. string = StringNumber(5)
  11. )
  12. +ChordMarker.Open(string = StringNumber(4))
  13. +ChordMarker.Open(string = StringNumber(3))
  14. +ChordMarker.Note(
  15. fret = FretNumber(3),
  16. finger = Finger.RING,
  17. string = StringNumber(2)
  18. )
  19. +ChordMarker.Note(
  20. fret = FretNumber(3),
  21. finger = Finger.PINKY,
  22. string = StringNumber(1)
  23. )
  24. }

Assigning a Chord to a ChordWidget

  1. 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.

  1. val chordDiagram = """
  2. C
  3. e |-----0------|
  4. B |-----1------|
  5. G |-----0------|
  6. D |-----2------|
  7. A |-----3------|
  8. E |------------|
  9. """.trimIndent()
  10. val parser = AsciiChordParser()
  11. launch {
  12. // parse() is a suspending function and needs to be called from another suspending
  13. // function or a coroutine
  14. val result = parser.parse(chordDiagram)
  15. val chord: Chord = result.chord
  16. val stringLabels: Set<StringLabel> = result.stringLabels
  17. val baseFret: FretNumber? = result.baseFret
  18. }

ChordProParser:

ChordProParser parses a String input of a Chord Pro (Chord or Define) Directive and outputs a ChordParseResult
containing a Chord.

  1. val chordDiagram = "{define: Bes base-fret 1 frets 1 1 3 3 3 1 fingers 1 1 2 3 4 1}"
  2. val parser = ChordProParser()
  3. launch {
  4. // parse() is a suspending function and needs to be called from another suspending
  5. // function or a coroutine
  6. val result = parser.parse(chordDiagram)
  7. val chord: Chord = result.chord
  8. val stringLabels: Set<StringLabel> = result.stringLabels
  9. val baseFret: FretNumber? = result.baseFret
  10. }

Customizing the appearance

ChordWidget implements the ChordView interface which contains properties to alter the appearance of the view.

ChordView:

  1. interface ChordView {
  2. ...
  3. var fitToHeight: Boolean
  4. var showFretNumbers: Boolean
  5. var showFingerNumbers: Boolean
  6. var stringLabelState: StringLabelState
  7. var mutedStringText: String
  8. var openStringText: String
  9. var fretColor: ColorInt
  10. var fretLabelTextColor: ColorInt
  11. var stringColor: ColorInt
  12. var stringLabelTextColor: ColorInt
  13. var noteColor: ColorInt
  14. var noteLabelTextColor: ColorInt
  15. ...
  16. }

Updating properties directly on ChordWidget:

  1. chordWidget?.noteColor = Color.BLUE
  2. 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:

  1. val binder = ChordViewBinder(chordWidget)
  2. val viewModel = ChordViewModel(
  3. fretColor = Color.BLACK,
  4. fretLabelTextColor = Color.WHITE,
  5. stringLabelTextColor = Color.BLACK,
  6. stringColor = Color.BLACK,
  7. noteColor = Color.BLACK,
  8. noteLabelTextColor = Color.WHITE
  9. )
  10. 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:

  1. <com.chrynan.chords.widget.ChordWidget
  2. android:id="@+id/chordWidget"
  3. android:layout_width="200dp"
  4. android:layout_height="300dp"
  5. app:stringLabelState="label"
  6. app:showFretNumbers="false"></com.chrynan.chords.widget.ChordWidget>

Available Android XML Attributes:

  1. <attr name="fretColor" format="color"></attr>
  2. <attr name="fretLabelTextColor" format="color"></attr>
  3. <attr name="stringColor" format="color"></attr>
  4. <attr name="stringLabelTextColor" format="color"></attr>
  5. <attr name="noteColor" format="color"></attr>
  6. <attr name="noteLabelTextColor" format="color"></attr>
  7. <attr name="mutedStringText" format="string"></attr>
  8. <attr name="openStringText" format="string"></attr>
  9. <attr name="showFingerNumbers" format="boolean"></attr>
  10. <attr name="showFretNumbers" format="boolean"></attr>
  11. <attr name="stringLabelState" format="enum">
  12. <enum name="number" value="0"></enum>
  13. <enum name="label" value="1"></enum>
  14. <enum name="hide" value="2"></enum>
  15. </attr>
  16. <attr name="fitToHeight" format="boolean"></attr>
  17. <attr name="fretLabelTypeface" format="reference"></attr>
  18. <attr name="noteLabelTypeface" format="reference"></attr>
  19. <attr name="stringLabelTypeface" format="reference"></attr>

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:

  1. val text = SpannableString("G")
  2. val span = ChordSpan(chord, this) // "this" refers to the listener
  3. text.setSpan(span, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
  4. textView?.text = text
  5. // Need to specify LinkTouchMovementMethod as the movement method for clicks to work
  6. textView?.movementMethod = LinkTouchMovementMethod()

Using DSL functions to add a ChordSpan to a TextView:

  1. val textBuilder = buildSpannableString {
  2. +chordSpan(chord, listener)
  3. +chordSpan("G", chord) {
  4. // Handle click event
  5. }
  6. +styledChordSpan(chord, listener) {
  7. this.backgroundColor = Color.BLUE
  8. }
  9. }
  10. textView?.text = textBuilder
  11. // Need to specify LinkTouchMovementMethod as the movement method for clicks to work
  12. textView?.movementMethod = LinkTouchMovementMethod()

Listening to Chord selected events:

  1. class MainActivity : AppCompatActivity(),
  2. ChordSpan.ChordSelectedListener {
  3. override fun onChordSpanSelected(chord: Chord) {
  4. // Perform action with chord
  5. }
  6. }

Customizing the ChordSpan appearance:

ChordSpan extends from TouchableSpan which inherits from TouchableSpanView and has the following customizable
properties:

  1. var backgroundColor = Color.TRANSPARENT
  2. var selectedBackgroundColor = Color.TRANSPARENT
  3. var textColor = Color.BLUE
  4. var selectedTextColor = Color.BLUE
  5. var isUnderlined = false
  6. var isUnderlinedWhenSelected = false
  7. var textTypeface = Typeface.DEFAULT
  8. var selectedTextTypeface = Typeface.DEFAULT

These properties can be changed on the span:

  1. span.textColor = Color.RED

Passing Chords between Android components

The model classes are not Parcelable because they are in a Kotlin multi-platform module and don’t have access to
Android Framework classes. But the Android library module does have wrapper classes that handle the serialization and
de-serialization of the Chord and ChordChart models.

These classes are ParcelableChordWrapper and ParcelableChartWrapper. To pass Chord and ChordChart between
components, such as, in a Bundle, just wrap them with their respective wrapper models.

  1. arguments =
  2. Bundle().apply {
  3. putParcelable(KEY_CHORD, ParcelableChordWrapper(chord))
  4. putParcelable(KEY_CHART, ParcelableChartWrapper(chart))
  5. }

Then retrieve the wrappers just as you would with any other Parcelable object.

  1. arguments?.getParcelable<ParcelableChordWrapper>(KEY_CHORD)
  2. arguments?.getParcelable<ParcelableChartWrapper>(KEY_CHART)

For convenience, there are extension functions on the Bundle and Intent objects which handle the wrapping and
unwrapping of the Chord and ChordChart objects.

Bundle:

  1. arguments =
  2. Bundle().apply {
  3. putChord(KEY_CHORD, chord)
  4. putChordChart(KEY_CHART, chart)
  5. }
  6. val chord = arguments?.getChord(KEY_CHORD)
  7. val chart = arguments?.getChordChart(KEY_CHART)

Intent:

  1. intent.putChord(KEY_CHORD, chord)
  2. intent.putChordChart(KEY_CHART, chart)
  3. val chord = intent.getChordExtra(KEY_CHORD)
  4. val chart = intent.getChordChartExtra(KEY_CHART)

There is also a convenience ChordAndChart class (with similar Parcelable extension functions) for when both a Chord
and a ChordChart need to be passed between Android components together.

  1. val chordAndChart = ChordAndChart(chord = chord, chart = chordChart)
  2. // Bundle
  3. arguments = bundle.putChordAndChart(KEY_CHORD_AND_CHART, chordAndChart)
  4. arguments?.getChordAndChart(KEY_CHORD_AND_CHART)
  5. // Intent
  6. intent.putChordAndChartExtra(KEY_CHORD_AND_CHART, chordAndChart)
  7. intent.getChordAndChartExtra(KEY_CHORD_AND_CHART)

Samples

Checkout the sample modules for examples on using the library.

License

  1. Copyright 2020 chRyNaN
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.