Skip to content

Commit

Permalink
v1.3 release, reusable styles
Browse files Browse the repository at this point in the history
  • Loading branch information
2dxgujun committed Jan 19, 2018
1 parent 7b63a54 commit 284ce82
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 47 deletions.
51 changes: 39 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ val text = span {
You can grab it via Gradle:

```
implementation 'me.gujun.android:span:1.2'
implementation 'me.gujun.android:span:1.3'
```

### Usage
Expand All @@ -79,11 +79,16 @@ val text = span {
paddingBottom = dp(5)
onClick = {
}
addSpan(TextAppearanceSpan(context, R.style.TextAppearance))

span("Text") {
// text = "Text"
span {
text = "Hi"
}

span("Hi")

+"Hi"

link("http://google.com", "Text")

quote(Color.RED, "Text")
Expand All @@ -93,9 +98,6 @@ val text = span {
superscript("Text")

image(getDrawable(R.drawable.ic_fun))

// Add custom span
style(TextAppearanceSpan(context, R.style.TextAppearance))
}
```

Expand Down Expand Up @@ -132,16 +134,41 @@ val text = span {
}
```

#### Global styles
#### Reusable style

You can change global styles by add spans to the companion object `Span.globalStyles`.
You can create reusable styles and reuse them in multiple spans.

For example, if your project using a custom typeface, you can construct a `CustomTypefaceSpan`
and add it to the `Span.globalStyles` then you will not specified typeface each time you using spans.
```kotlin
val headerStyle = style {
textColor = Color.BLACK
textStyle = "bold"
textSize = dp(20)
verticalPadding = dp(3)
}
val content = span {
span("Header 1") {
style = headerStyle
}
span {
text = "\n...\n"
}
span("Header 2") {
style = headerStyle
}
}
```

#### Global style

You can set global style by create a *style* and pass it to the companion object `Span.globalStyle`.

For example, if your project using a custom typeface, you can apply styles with custom typeface,
then you will no need to specify typeface each time you using spans.

```kotlin
Span.globalStyles.add(AbsoluteSizeSpan(dp(14)))
Span.globalStyle = style {
typeface = getFont(R.font.pacifico)
}
```

### Reference
Expand Down Expand Up @@ -189,7 +216,7 @@ Span.globalStyles.add(AbsoluteSizeSpan(dp(14)))

- image: ImageSpan, include "bottom" and "baseline" alignment

- style: Specify custom spans
- addSpan: Add custom span


### License
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import android.os.Bundle
import android.support.v4.content.res.ResourcesCompat
import android.support.v7.app.AppCompatActivity
import android.text.method.LinkMovementMethod
import android.text.style.AbsoluteSizeSpan
import android.text.style.TextAppearanceSpan
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_sample.text
import me.gujun.android.span.Span
import me.gujun.android.span.addSpan
import me.gujun.android.span.image
import me.gujun.android.span.link
import me.gujun.android.span.quote
Expand All @@ -25,7 +25,9 @@ class SampleActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sample)

Span.globalStyles.add(AbsoluteSizeSpan(dp(14)))
Span.globalStyle = style {
textSize = dp(14)
}

// You must set this movement method to make clickable span work
text.movementMethod = LinkMovementMethod.getInstance()
Expand Down Expand Up @@ -78,8 +80,8 @@ class SampleActivity : AppCompatActivity() {
}
}
+"\n"
span("CustomStyle") {
style(TextAppearanceSpan(this@SampleActivity,
span("CustomSpannable") {
addSpan(TextAppearanceSpan(this@SampleActivity,
R.style.TextAppearance_Sample))
}
+"\n"
Expand Down Expand Up @@ -145,7 +147,21 @@ class SampleActivity : AppCompatActivity() {
+"?"
}
}
+"\n"
val reusableStyle = style {
textColor = Color.BLACK
verticalPadding = dp(3)
backgroundColor = Color.LTGRAY
}
span("Reuse styles") {
style = reusableStyle
}
+"\n"
span("Reuse styles") {
style = reusableStyle
}
}

}

private fun dp(dp: Int): Int = (dp * resources.displayMetrics.density + .5f).toInt()
Expand Down
2 changes: 1 addition & 1 deletion span/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dependencies {

def GROUP = 'me.gujun.android'
def ARTIFACTID = 'span'
def VERSION = '1.2'
def VERSION = '1.3'

group = GROUP
version = VERSION
Expand Down
105 changes: 75 additions & 30 deletions span/src/main/kotlin/me/gujun/android/span/span.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ import me.gujun.android.span.style.LineSpacingSpan
import me.gujun.android.span.style.TextDecorationLineSpan
import me.gujun.android.span.style.VerticalPaddingSpan

// TODO Save styles
class Span(val parent: Span? = null) : SpannableStringBuilder() {

companion object {
val UNSPECIFIED = -1
const val UNSPECIFIED = -1

val globalStyles: ArrayList<Any> = arrayListOf()
val EMPTY_STYLE = Span()

var globalStyle: Span = EMPTY_STYLE
}

var text: CharSequence = ""
Expand All @@ -43,15 +44,15 @@ class Span(val parent: Span? = null) : SpannableStringBuilder() {

@Dimension(unit = Dimension.PX) var textSize: Int = parent?.textSize ?: UNSPECIFIED

var fontFamily: String = parent?.fontFamily ?: ""
var fontFamily: String? = parent?.fontFamily

var typeface: Typeface? = parent?.typeface

var textStyle: String = parent?.textStyle ?: ""
var textStyle: String? = parent?.textStyle

var alignment: String = parent?.alignment ?: ""
var alignment: String? = parent?.alignment

var textDecorationLine: String = parent?.textDecorationLine ?: ""
var textDecorationLine: String? = parent?.textDecorationLine

@Dimension(unit = Dimension.PX) var lineSpacing: Int = UNSPECIFIED

Expand All @@ -63,7 +64,9 @@ class Span(val parent: Span? = null) : SpannableStringBuilder() {

var onClick: (() -> Unit)? = null

var styles: ArrayList<Any> = arrayListOf()
var spans: ArrayList<Any> = ArrayList()

var style: Span = EMPTY_STYLE

private fun buildCharacterStyle(builder: ArrayList<Any>) {
if (textColor != UNSPECIFIED) {
Expand Down Expand Up @@ -97,7 +100,7 @@ class Span(val parent: Span? = null) : SpannableStringBuilder() {
}

if (!TextUtils.isEmpty(textDecorationLine)) {
builder.add(TextDecorationLineSpan(textDecorationLine))
builder.add(TextDecorationLineSpan(textDecorationLine!!))
}

if (onClick != null) {
Expand Down Expand Up @@ -138,8 +141,13 @@ class Span(val parent: Span? = null) : SpannableStringBuilder() {
}
}

private fun prebuild() {
override(style)
}

fun build(): Span {
val spans = arrayListOf<Any>()
prebuild()
val builder = ArrayList<Any>()
if (!TextUtils.isEmpty(text)) {
var p = this.parent
while (p != null) {
Expand All @@ -149,25 +157,58 @@ class Span(val parent: Span? = null) : SpannableStringBuilder() {
p = p.parent
}
append(text)

buildCharacterStyle(spans)
buildParagraphStyle(spans) // AlignmentSpan
} else {
buildParagraphStyle(spans)
}
// Add custom styles
spans.addAll(styles)
buildCharacterStyle(builder)
buildParagraphStyle(builder)

spans.forEach {
builder.addAll(spans)
builder.forEach {
setSpan(it, 0, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
return this
}

fun setGlobalStyles() {
globalStyles.forEach {
setSpan(it, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE)
fun override(style: Span) {
if (textColor == UNSPECIFIED) {
textColor = style.textColor
}
if (backgroundColor == UNSPECIFIED) {
backgroundColor = style.backgroundColor
}
if (textSize == UNSPECIFIED) {
textSize = style.textSize
}
if (fontFamily == null) {
fontFamily = style.fontFamily
}
if (typeface == null) {
typeface = style.typeface
}
if (textStyle == null) {
textStyle = style.textStyle
}
if (alignment == null) {
alignment = style.alignment
}
if (textDecorationLine == null) {
textDecorationLine = style.textDecorationLine
}
if (lineSpacing == UNSPECIFIED) {
lineSpacing = style.lineSpacing
}
if (paddingTop == UNSPECIFIED) {
paddingTop = style.paddingTop
}
if (paddingBottom == UNSPECIFIED) {
paddingBottom = style.paddingBottom
}
if (verticalPadding == UNSPECIFIED) {
verticalPadding = style.verticalPadding
}
if (onClick != null) {
onClick = style.onClick
}
spans.addAll(style.spans)
}

operator fun CharSequence.unaryPlus(): CharSequence {
Expand All @@ -186,18 +227,22 @@ class Span(val parent: Span? = null) : SpannableStringBuilder() {
}

fun span(init: Span.() -> Unit): Span = Span().apply {
setGlobalStyles()
override(Span.globalStyle)
init()
build()
}

fun span(text: CharSequence, init: Span.() -> Unit): Span = Span().apply {
setGlobalStyles()
override(Span.globalStyle)
this.text = text
init()
build()
}

fun style(init: Span.() -> Unit): Span = Span().apply {
init()
}

fun Span.span(init: Span.() -> Unit = {}): Span = run {
append(Span(parent = this).apply {
init()
Expand All @@ -219,7 +264,7 @@ fun Span.link(url: String, text: CharSequence = "",
init: Span.() -> Unit = {}): Span = run {
append(Span(parent = this).apply {
this.text = text
this.styles.add(URLSpan(url))
this.spans.add(URLSpan(url))
init()
build()
})
Expand All @@ -230,7 +275,7 @@ fun Span.quote(@ColorInt color: Int, text: CharSequence = "",
init: Span.() -> Unit = {}): Span = run {
append(Span(parent = this).apply {
this.text = text
this.styles.add(QuoteSpan(color))
this.spans.add(QuoteSpan(color))
init()
build()
})
Expand All @@ -240,7 +285,7 @@ fun Span.quote(@ColorInt color: Int, text: CharSequence = "",
fun Span.superscript(text: CharSequence = "", init: Span.() -> Unit = {}): Span = run {
append(Span(parent = this).apply {
this.text = text
this.styles.add(SuperscriptSpan())
this.spans.add(SuperscriptSpan())
init()
build()
})
Expand All @@ -250,7 +295,7 @@ fun Span.superscript(text: CharSequence = "", init: Span.() -> Unit = {}): Span
fun Span.subscript(text: CharSequence = "", init: Span.() -> Unit = {}): Span = run {
append(Span(parent = this).apply {
this.text = text
this.styles.add(SubscriptSpan())
this.spans.add(SubscriptSpan())
init()
build()
})
Expand All @@ -262,7 +307,7 @@ fun Span.image(drawable: Drawable, alignment: String = "bottom",
drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
append(Span(parent = this).apply {
this.text = " "
this.styles.add(ImageSpan(drawable, when (alignment) {
this.spans.add(ImageSpan(drawable, when (alignment) {
"bottom" -> ImageSpan.ALIGN_BOTTOM
"baseline" -> ImageSpan.ALIGN_BASELINE
else -> throw RuntimeException("Unknown image alignment")
Expand All @@ -273,6 +318,6 @@ fun Span.image(drawable: Drawable, alignment: String = "bottom",
this
}

fun Span.style(what: Any) = run {
this.styles.add(what)
fun Span.addSpan(what: Any) = run {
this.spans.add(what)
}

0 comments on commit 284ce82

Please sign in to comment.