جت‌پک کامپوز در اندروید: استایل‌دهی پیشرفته متن با AnnotatedString

جت‌پک کامپوز در اندروید: استایل‌دهی پیشرفته متن با AnnotatedString

بررسی AnnotatedString

گاهی برای نمایش یک متن داخل Text در کامپوز نیاز داریم قسمت های مختلف متن رو به صورت جداگونه استایل‌دهی کنیم، میتونیم برای هر قسمت یک Text تعریف کنیم اما این کار به صرفه نیست؛ برای این کار از تابع buildAnnotatedString استفاده میکنیم که یک نمونه از AnnotatedString رو بر میگردونه و میتونیم به عنوان پارامتر به تابع Text پاس بدیم.

فرم کلی

buildAnnotatedString(builder: AnnotatedString.Builder.() -> Unit): AnnotatedString
                

تابع buildAnnotatedString یک نمونه از AnnotatedString رو بر می‌گردونه.

پارامتر builder یک تابع لفظی است که به صورت اکستنشن AnnotatedString.Builder تعریف شده و داخلش میتونیم توابع کلاس AnnotatedString.Builder رو صدا بزنیم و متن رو استایل‌دهی کنیم؛

استفاده از AnnotatedString در تابع Text

تابع Text در کامپوز یک تابع Overload شدس یک بار تابع با پارامتر String تعریف شده و یک بار تابع با پارامتر AnnotatedString؛ ابتدا یک نمونه از AnnotatedString باید ایجاد کنیم و سپس نمونه ی ایجاد شده رو به عنوان پارامتر تابع Text پاس بدیم.

val annotatedString: AnnotatedString = buildAnnotatedString{
    //Append and style text here
}

@Composable
fun MyText(annotatedString: AnnotatedString, modifier: Modifier = Modifier){
    Text(
        annotatedString = annotatedString
        modifier = Modifier.padding(16.dp),
    )
}

@Composable
@Preview
fun MyTextPreview(){
    MyText(annotatedString)
}

            

افزودن و استایل‌دهی متن در AnnotatedString

ابتدا باید یک نمونه از AnnotatedString ایجاد کنیم.

val annotatedString: AnnotatedString = buildAnnotatedString{
    //Append and style text here
}
               

سپس داخل تابع لفظی ایجاد شده میتونیم متن رو اضافه و قسمت های مختلف متن رو استایل‌‌دهی کنیم؛ همانطور که گفتیم این تابع یک تابع اکستنشن کلاس AnnotatedString.Builder است.

تابع append

هربار که بخوایم به AnnotatedString، یک String اضافه کنیم تابع append رو صدا میزنیم.

val annotatedString: AnnotatedString = buildAnnotatedString{
    append("Hello")
    append("Android")
}
                    

تابع withStyle

از این تابع برای استایل‌دهی متن استفاده می‌کنیم؛ تابع withStyle یک تابع Overload شدس و دو نسخه ازش وجود داره، یک نسخه با پارامتر SpanStyle و یک نسخه دیگه با پارامتر ParagraphStyle و در هردو نسخه یک پارامتر از نوع تابع لفظی وجود داره.

در زیر هردو نسخه از این تابع اورده شده است.

withStyle(style: SpanStyle, block: AnnotatedString.Builder.() -> R)
withStyle(style: ParagraphStyle, block: AnnotatedString.Builder.() -> R)
                    

کلاس SpanSyle

با استفاده از کلاس SpanStyle میتونیم ویژگی های ظاهری متن مثل رنگ، فونت و... رو مقدار دهی کنیم؛ پارامتر های کلاس SpanStyle با پارامتر های تابع Text مشترکه میتونید این پارامتر ها رو در اونجا مطالعه کنید.

در مثال زیر میخوایم کلمه Hello Android رو به رنگ فیروزه ای و کلمه Android رو به رنگ پیشفرض (مشکی) در بیاریم.

val annotatedString: AnnotatedString = buildAnnotatedString{
    withStyle(SpanStyle(Color(0xFF00695C))){
        append("Hello")
    }
    append("Android")
}
                        

کلاس ParagraphStyle

با استفاده از کلاس ParagraphStyle میتونیم ویژگی های مکانی و نحوه نمایش متن در صفحه رو استایل‌دهی کنیم، پارامتر های این کلاس نیز با پارامتر های تابع Text مشترک است.

در مثال زیر میخوایم متن From KodeDevel رو در وسط صفحه نمایش بدیم.

val annotatedString: AnnotatedString = buildAnnotatedString{
    withStyle(ParagraphStyle(textAlign = TextAlign.Center)){
        append("From")
        append("Kode")
        append("Devel")
    }
}
                    

تابع pushStyle و pop

با صدا زدن تابع pushStyle میتونیم متن هایی که با append اضافه می‌کنیم رو استایل‌دهی کنیم.

دو نسخه از pushStyle وجود داره یکی با پارمتر SpanStyle و یکی با پارامتر ParagraphStyle.

pushStyle(style: SpanStyle): Int
pushStyle(style: ParagraphStyle): Int
                    

با هربار صدا زدن pushStyle یک استایل به استک اضافه میشه و تا زمانی که با تابع pop استایل رو حذف نکرده باشیم متن های بعدش تحت تاثیر قرار می‌گیرن.

در مثال زیر میخوایم From رو قرمز کنیم Kode مشکی بمونه و Devel رو آبی کنیم و هردو کلمه Kode Devel بولد باشه.

val annotatedString: AnnotatedString = buildAnnotatedString{
    withStyle(ParagraphStyle(textAlign = TextAlign.Center)){
        pushStyle(SpanStyle(Color(0xFFC62828)))
        append("From ")
        pop()
        pushStyle(SpanStyle(fontWeight = FontWeight.Bold))
        append("Kode")
        pushStyle(SpanStyle(Color(0xFF0D47A1)))
        append("Devel")
    }
}
                    

فرض کنید میخوایم یک بخش دیگه به متن اضافه کنیم به نام Website، در مثال قبل دو استایل به متن اضافه کردیم اگه بخوایم هردو استایل روی Website تاثیر نداشته باشن، باید هردو استایل رو حذف کنیم.

val annotatedString: AnnotatedString = buildAnnotatedString{
    withStyle(ParagraphStyle(textAlign = TextAlign.Center)){
        pushStyle(SpanStyle(Color(0xFFC62828)))
        append("From ")
        pop()
        pushStyle(SpanStyle(fontWeight = FontWeight.Bold))
        append("Kode")
        pushStyle(SpanStyle(Color(0xFF0D47A1)))
        append("Devel")
        pop()  //Remove pushStyle(SpanStyle(Color(0xFF0D47A1)))
        pop() //Remove pushStyle(SpanStyle(fontWeight = FontWeight.Bold))
        append("Website") //Append Website without any Style

    }
}
                    

تابع withLink

با استفاده از تابع withLink میتونیم یک بخشی از متن رو تبدیل به لینک یا قابل کلیک کنیم؛ فرم کلی تابع withLink به صورت زیر است:

withLink(link: LinkAnnotation,block: AnnotatedString.Builder.() -> R): R
                    

LinkAnnotation یک کلاس ابستراکت است و باید یکی از کلاس های داخلی Clickable یا Url رو به صورت LinkAnnotation.Url و LinkAnnotation.Clickable صدا بزنیم.

کلاس LinkAnnotation.Url

این کلاس سه پارامتر داره

val url = LinkAnnotation.Url(url: String, styles: TextLinkStyles, linkInteractionListener: LinkInteractionListener)
                        

با پارامتر url میتونیم url مورد نظر رو به صورت String مقداردهی کنیم، به styles میتونیم یک نمونه از TextLinkStyles پاس بدیم؛ و در پارامتر LinkInteractionListener با کلیک های کاربر روی قسمت مشخص شده تعامل ایجاد میکنیم.

کلاس LinkAnnotation.Clickable

این کلاس بجای اینکه یک url رو باز کنه با کلیک روی متن تعامل ایجاد میکنه.

val clickable = LinkAnnotation.Clickable(tag: String, styles: TextLinkStyles, linkInteractionListener: LinkInteractionListener)
                        

در این کلاس پارامتر url وجود نداره و تنها کار کلاس تعامل با کلیک کاربر روی قسمت مشخص شده از متن است و با استفاده از tag میتونیم متن مورد نظر رو در برنامه برچسب گذاری کنیم تا در صورت لزوم صداش کنیم.

کلاس TextLinkStyles

از کلاس TextLinkStyles برای استایل‌‌دهی LinkAnnotation استفاده میکنیم و به صورت مقدار پارامتر styles به کلاس های LinkAnnotation.Url یا LinkAnnotation.Clickable پاس میدیم.

val textLinkStyles: TextLinkStyles = TextLinkStyles(style: SpanStyle, pressedStyle: SpanStyle, focusedStyle: SpanStyle, hoveredStyle: SpanStyle)                                
                            

تمام پارامتر های این کلاس از نوع SpanStyle است و پارامتر style برای استایل‌دهی زمانیه که هیچ تعاملی با متن توسط کاربر برقرار نشده، پارامتر pressedStyle برای استایل‌دهی زمان کلیک کاربر رو متن است، پارامتر focusedStyle برای استایل‌دهی متن در زمانیه که روی متن focus وجود داره و پارامتر hoveredStyle برای استایل‌‌دهی در زمانیه که با موس میریم روی قسمت مشخص شده.

با LinkAnnotation لینک تعریف می‌کنیم و داخل تابع لفظی block، متن مربوط به لینک رو با append اضافه میکنیم.

در مثال زیر میخوایم یک لینک به متن قبلی اضافه کنیم.

val annotatedString: AnnotatedString = buildAnnotatedString{

    withLink(
        link = LinkAnnotation.Url(
            url = "https://kodedevel.ir",
            styles = TextLinkStyles(
                style = SpanStyle(Color(0xFF0D47A1), fontWeight = FontWeight.Bold, textDecoration = TextDecoration.Underline),
                pressedStyle = SpanStyle(color = Color(0xFFC62828)),
                hoveredStyle = SpanStyle(color = Color(0xFFC62828))
            )
        )
    ){
        pushStyle(ParagraphStyle(textAlign = TextAlign.Center))
        append("Visit the Site")
    }
}

                    

مورد مطالعه (مثال) ها

در این قسمت میخوایم تمام چیزایی که گفته شد رو با یک AnnotatedString تعریف کنیم و در Text نمایش بدیم.

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.LinkAnnotation
import androidx.compose.ui.text.ParagraphStyle
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withLink
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import ir.kodedevel.helloworld.ui.theme.HelloWorldTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                MyText(annotatedString, modifier = Modifier.padding(innerPadding))
            }
        }
    }
}

val annotatedString = buildAnnotatedString {

    withStyle(ParagraphStyle(TextAlign.Center)) {
        withStyle(
            SpanStyle(Color(0xFF00695C))
        ) {
            append("Hello")
        }

        append("Android!")
    }

    withStyle(ParagraphStyle(textAlign = TextAlign.Center)){
        pushStyle(SpanStyle(Color(0xFFC62828)))
        append("From")
        append(" ")
        pop()
        pushStyle(SpanStyle(fontWeight = FontWeight.Bold))
        append("Kode")
        pushStyle(SpanStyle(Color(0xFF0D47A1)))
        append("Devel\n")
        pop()
        pop()
        append("Website")
    }

    withLink(
        link = LinkAnnotation.Url(
            url = "https://kodedevel.ir",
            styles = TextLinkStyles(
                style = SpanStyle(Color(0xFF0D47A1), fontWeight = FontWeight.Bold, textDecoration = TextDecoration.Underline),
                pressedStyle = SpanStyle(color = Color(0xFFC62828)),
                hoveredStyle = SpanStyle(color = Color(0xFFC62828))
            )
        )
    ){
        pushStyle(ParagraphStyle(textAlign = TextAlign.Center))
        append("Visit the Site")
    }
}

@Composable
fun MyText(content: AnnotatedString, modifier: Modifier = Modifier){
    Text(
        text = content,
        fontSize = 50.sp,
        lineHeight = 60.sp,
        modifier = modifier.padding(16.dp)
    )
}

@Preview
@Composable
fun MyTextPreview(){
    MyText(annotatedString)
}
                

شکل نهایی باید مشابه زیر باشه.

تصویر نهایی استایل‌دهی متن با AnnotatedString
تصویر نهایی استایل‌دهی متن با AnnotatedString

خلاصه

  • با AnnotatedString میتونیم بخش های مختلف یک متن رو به صورت جدا جدا استایل‌دهی کنیم.
  • با تابع append میتونیم به AnnotatedString به صورت String متن اضافه کنیم.
  • با تابع withStyle میتونیم بخشی از متن رو استایل‌دهی کنیم.
  • با withLink میتونیم یک بخشی از متن رو قابل کلیک یا تبدیل به لینک کنیم.
  • در حین اضافه کردن متن به AnnotatedString میتونیم بخش هایی رو با تابع pushStyle، استایل‌دهی کنیم.
  • برای رفع اثر استایل های اضافه شده با pushStyle، باید با تابع pop استایل رو از استک حذف کنیم.

بازخورد و دیدگاه‌ ها

اولین نفری باشید که در این صفحه نظر می‌دهید

notifications برای اطلاع از جدیدترین مطالب و پست های مرتبط، عضو کانال تلگرام ما بشید.

arrow_drop_up
کپی شد!