مفاهیم و پیادهسازی Jetpack Compose
یک منبع رشتهای، رشتههای متنی را برای برنامه شما با استایل و قالببندی متن اختیاری فراهم میکند. سه نوع منبع وجود دارد که میتوانند رشتهها را در اختیار برنامه شما قرار دهند:
- رشته
- منبع XML که یک رشته واحد ارائه میدهد.
- آرایه رشتهای
- منبع XML که آرایهای از رشتهها را ارائه میدهد.
- رشتههای مقداری (جمع)
- منبع XML که رشتههای مختلفی را برای جمعبندی حمل میکند.
همه رشتهها قابلیت اعمال برخی از آرگومانهای نشانهگذاری و قالببندی استایل را دارند. برای اطلاعات بیشتر در مورد استایلبندی و قالببندی رشتهها، به بخش مربوط به قالببندی و استایلبندی مراجعه کنید.
رشته
یک رشته واحد که میتواند از برنامه یا از سایر فایلهای منبع (مانند طرحبندی XML) ارجاع داده شود.
- محل فایل:
-
res/values/ filename .xml
نام فایل دلخواه است.nameعنصر<string>به عنوان شناسه منبع استفاده میشود. - نوع داده منبع کامپایل شده:
- اشارهگر منبع به یک
String. - مرجع منابع:
- در جاوا:
R.string. string_name
در XML:@string/ string_name - نحو:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="string_name" >text_string</string> </resources>
- عناصر:
- مثال:
- فایل XML ذخیره شده در
res/values/strings.xml:<?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">Hello!</string> </resources>
این طرحبندی XML یک رشته را به یک نما اعمال میکند:
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" />
این کد برنامه یک رشته را بازیابی میکند:
شما میتوانید از
getString(int)یاgetText(int)برای بازیابی یک رشته استفاده کنید.getText(int)هرگونه استایل متن غنی اعمال شده روی رشته را حفظ میکند.
آرایه رشتهای
آرایهای از رشتهها که میتوان از طریق برنامه به آنها ارجاع داد.
- محل فایل:
-
res/values/ filename .xml
نام فایل دلخواه است.nameعنصر<string-array>به عنوان شناسه منبع استفاده میشود. - نوع داده منبع کامپایل شده:
- اشارهگر منبع به آرایهای از
String. - مرجع منابع:
- در جاوا:
R.array. string_array_name
در XML:@[ package :]array/ string_array_name - نحو:
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="string_array_name"> <item >text_string</item> </string-array> </resources>
- عناصر:
- مثال:
- فایل XML ذخیره شده در
res/values/strings.xml: این کد برنامه یک آرایه رشتهای را بازیابی میکند:<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="planets_array"> <item>Mercury</item> <item>Venus</item> <item>Earth</item> <item>Mars</item> </string-array> </resources>
کاتلین
val array: Array<String> =
resources.getStringArray(R.array.planets_array)جاوا
Resources res =
getResources(); String[] planets = res.getStringArray(R.array.planets_array);
رشتههای مقداری (جمع)
زبانهای مختلف، قوانین متفاوتی برای تطابق دستوری با کمیت دارند. برای مثال، در انگلیسی، کمیت ۱ یک مورد خاص است. ما مینویسیم "۱ کتاب"، اما برای هر کمیت دیگری مینویسیم " n کتاب". این تمایز بین مفرد و جمع بسیار رایج است، اما زبانهای دیگر تمایزات دقیقتری قائل میشوند. مجموعه کامل پشتیبانی شده توسط اندروید شامل zero ، one ، two ، few ، many و other است.
قوانین مربوط به تصمیمگیری در مورد اینکه کدام حالت برای یک زبان و مقدار مشخص استفاده شود، میتواند بسیار پیچیده باشد، بنابراین اندروید متدهایی مانند getQuantityString() را برای انتخاب منبع مناسب در اختیار شما قرار میدهد.
در API 24+ میتوانید به جای آن از کلاس بسیار قدرتمندتر ICU MessageFormat استفاده کنید.
- محل فایل:
-
res/values/ filename .xml
نام فایل دلخواه است.nameعنصر<plurals>به عنوان شناسه منبع استفاده میشود. - مرجع منابع:
- در جاوا:
R.plurals. plural_name - نحو:
<?xml version="1.0" encoding="utf-8"?> <resources> <plurals name="plural_name"> <item quantity=["zero" | "one" | "two" | "few" | "many" | "other"] >text_string</item> </plurals> </resources>
- عناصر:
- مثال:
- فایل XML ذخیره شده در
res/values/strings.xml:<?xml version="1.0" encoding="utf-8"?> <resources> <plurals name="numberOfSongsAvailable"> <!-- As a developer, you should always supply "one" and "other" strings. Your translators will know which strings are actually needed for their language. Always include %d in "one" because translators will need to use %d for languages where "one" doesn't mean 1 (as explained above). --> <item quantity="one">%d song found.</item> <item quantity="other">%d songs found.</item> </plurals> </resources>
فایل XML که در
res/values-pl/strings.xmlذخیره شده است:<?xml version="1.0" encoding="utf-8"?> <resources> <plurals name="numberOfSongsAvailable"> <item quantity="one">Znaleziono %d piosenkę.</item> <item quantity="few">Znaleziono %d piosenki.</item> <item quantity="other">Znaleziono %d piosenek.</item> </plurals> </resources>
کاربرد:
کاتلین
val count = getNumberOfSongsAvailable() val songsFound = resources.
getQuantityString(R.plurals.numberOfSongsAvailable, count, count)جاوا
int count = getNumberOfSongsAvailable(); Resources res =
getResources(); String songsFound = res.getQuantityString(R.plurals.numberOfSongsAvailable, count, count);هنگام استفاده از متد
getQuantityString()، اگر رشته شما شامل قالببندی رشتهای باشد، باید مقدارcountرا دو بار ارسال کنید. برای مثال، برای رشته%d songs found، پارامترcountاول، رشته جمع مناسب را انتخاب میکند و پارامترcountدوم در متغیر%dقرار میگیرد. اگر رشتههای جمع شما شامل قالببندی رشتهای نباشند، نیازی به ارسال پارامتر سوم بهgetQuantityStringنیست.
قالب و سبک
در اینجا چند نکته مهم وجود دارد که باید در مورد نحوه قالببندی و استایلدهی صحیح منابع رشتهای خود بدانید.
قالببندی رشتهها
اگر نیاز به قالببندی رشتههای خود دارید، میتوانید این کار را با قرار دادن آرگومانهای قالببندی خود در منبع رشته انجام دهید، همانطور که در مثال منبع زیر نشان داده شده است.
<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>
در این مثال، رشتهی فرمت دو آرگومان دارد: %1$s یک رشته و %2$d یک عدد اعشاری است. سپس، رشته را با فراخوانی getString(int, Object...) فرمت کنید. برای مثال:
کاتلین
var text = getString(R.string.welcome_messages, username, mailCount)
جاوا
String text = getString(R.string.welcome_messages, username, mailCount);
استایلدهی با استفاده از نشانهگذاری HTML
شما میتوانید با استفاده از نشانهگذاری HTML به رشتههای خود استایل بدهید. برای مثال:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="welcome">Welcome to <b>Android</b>!</string> </resources>
اگر قالببندی را اعمال نمیکنید، میتوانید متن TextView را مستقیماً با فراخوانی setText(java.lang.CharSequence) تنظیم کنید. با این حال، در برخی موارد، ممکن است بخواهید یک منبع متن استایلبندی شده ایجاد کنید که به عنوان یک رشته فرمت نیز استفاده شود. معمولاً این کار جواب نمیدهد زیرا متدهای format(String, Object...) و getString(int, Object...) تمام اطلاعات استایل را از رشته حذف میکنند. راه حل این مشکل، نوشتن تگهای HTML با موجودیتهای escape شده است که پس از انجام قالببندی، با fromHtml(String) بازیابی میشوند. به عنوان مثال:
- منبع متنی استایلدهی شده خود را به عنوان یک رشته HTML-escaped ذخیره کنید:
<resources> <string name="welcome_messages">Hello, %1$s! You have <b>%2$d new messages</b>.</string> </resources>
در این رشتهی قالببندیشده، یک عنصر
<b>اضافه شده است. توجه داشته باشید که براکت آغازین با استفاده از نمادگذاری<از نوع HTML-escaped است. - سپس رشته را طبق معمول قالببندی کنید، اما همچنین
fromHtml(String)را برای تبدیل متن HTML به متن استایلدار فراخوانی کنید:کاتلین
val text: String = getString(R.string.welcome_messages, username, mailCount) val styledText: Spanned = Html.fromHtml(text, FROM_HTML_MODE_LEGACY)
جاوا
String text = getString(R.string.welcome_messages, username, mailCount); Spanned styledText = Html.fromHtml(text, FROM_HTML_MODE_LEGACY);
از آنجا که متد fromHtml(String) تمام موجودیتهای HTML را قالببندی میکند، حتماً با استفاده از htmlEncode(String) هر کاراکتر HTML ممکن را در رشتههایی که با متن قالببندی شده استفاده میکنید، escape کنید. برای مثال، اگر در حال قالببندی رشتهای هستید که حاوی کاراکترهایی مانند "<" یا "&" است، باید قبل از قالببندی escape شوند، به طوری که وقتی رشته قالببندی شده از fromHtml(String) عبور داده میشود، کاراکترها به همان شکلی که در ابتدا نوشته شدهاند، ظاهر شوند. برای مثال:
کاتلین
val escapedUsername: String = TextUtils.htmlEncode(username)
val text: String = getString(R.string.welcome_messages, escapedUsername, mailCount)
val styledText: Spanned = Html.fromHtml(text, FROM_HTML_MODE_LEGACY)جاوا
String escapedUsername = TextUtils.htmlEncode(username);
String text = getString(R.string.welcome_messages, escapedUsername, mailCount);
Spanned styledText = Html.fromHtml(text);استایل دادن با اسپانبلها
یک Spannable یک شیء متنی است که میتوانید آن را با ویژگیهای typeface مانند رنگ و وزن فونت، استایلدهی کنید. شما SpannableStringBuilder برای ساخت متن خود استفاده میکنید و سپس استایلهای تعریف شده در پکیج android.text.style را به متن اعمال میکنید.
شما میتوانید از متدهای کمکی زیر برای تنظیم بخش عمدهای از کار ایجاد متن spannable استفاده کنید:
کاتلین
/** * Returns a CharSequence that concatenates the specified array of CharSequence * objects and then applies a list of zero or more tags to the entire range. * * @param content an array of character sequences to apply a style to * @param tags the styled span objects to apply to the content * such as android.text.style.StyleSpan */ private fun apply(content: Array<out CharSequence>, vararg tags: Any): CharSequence { return SpannableStringBuilder().apply { openTags(tags) content.forEach { charSequence -> append(charSequence) } closeTags(tags) } } /** * Iterates over an array of tags and applies them to the beginning of the specified * Spannable object so that future text appended to the text will have the styling * applied to it. Do not call this method directly. */ private fun Spannable.openTags(tags: Array<out Any>) { tags.forEach { tag -> setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK) } } /** * "Closes" the specified tags on a Spannable by updating the spans to be * endpoint-exclusive so that future text appended to the end will not take * on the same styling. Do not call this method directly. */ private fun Spannable.closeTags(tags: Array<out Any>) { tags.forEach { tag -> if (length > 0) { setSpan(tag, 0, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } else { removeSpan(tag) } } }
جاوا
/** * Returns a CharSequence that concatenates the specified array of CharSequence * objects and then applies a list of zero or more tags to the entire range. * * @param content an array of character sequences to apply a style to * @param tags the styled span objects to apply to the content * such as android.text.style.StyleSpan * */ private static CharSequence applyStyles(CharSequence[] content, Object[] tags) { SpannableStringBuilder text = new SpannableStringBuilder(); openTags(text, tags); for (CharSequence item : content) { text.append(item); } closeTags(text, tags); return text; } /** * Iterates over an array of tags and applies them to the beginning of the specified * Spannable object so that future text appended to the text will have the styling * applied to it. Do not call this method directly. */ private static void openTags(Spannable text, Object[] tags) { for (Object tag : tags) { text.setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK); } } /** * "Closes" the specified tags on a Spannable by updating the spans to be * endpoint-exclusive so that future text appended to the end will not take * on the same styling. Do not call this method directly. */ private static void closeTags(Spannable text, Object[] tags) { int len = text.length(); for (Object tag : tags) { if (len > 0) { text.setSpan(tag, 0, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } else { text.removeSpan(tag); } } }
متدهای bold ، italic و color زیر، متدهای کمکی بالا را در بر میگیرند و مثالهای خاصی از اعمال استایلهای تعریف شده در پکیج android.text.style را نشان میدهند. میتوانید متدهای مشابهی برای انجام انواع دیگر استایلبندی متن ایجاد کنید.
کاتلین
/** * Returns a CharSequence that applies boldface to the concatenation * of the specified CharSequence objects. */ fun bold(vararg content: CharSequence): CharSequence = apply(content, StyleSpan(Typeface.BOLD)) /** * Returns a CharSequence that applies italics to the concatenation * of the specified CharSequence objects. */ fun italic(vararg content: CharSequence): CharSequence = apply(content, StyleSpan(Typeface.ITALIC)) /** * Returns a CharSequence that applies a foreground color to the * concatenation of the specified CharSequence objects. */ fun color(color: Int, vararg content: CharSequence): CharSequence = apply(content, ForegroundColorSpan(color))
جاوا
/** * Returns a CharSequence that applies boldface to the concatenation * of the specified CharSequence objects. */ public static CharSequence bold(CharSequence... content) { return apply(content, new StyleSpan(Typeface.BOLD)); } /** * Returns a CharSequence that applies italics to the concatenation * of the specified CharSequence objects. */ public static CharSequence italic(CharSequence... content) { return apply(content, new StyleSpan(Typeface.ITALIC)); } /** * Returns a CharSequence that applies a foreground color to the * concatenation of the specified CharSequence objects. */ public static CharSequence color(int color, CharSequence... content) { return apply(content, new ForegroundColorSpan(color)); }
در اینجا مثالی از نحوهی ترکیب این متدها برای اعمال سبکهای مختلف به کلمات منفرد در یک عبارت آورده شده است:
کاتلین
// Create an italic "hello, " a red "world", // and bold the entire sequence. val text: CharSequence = bold(italic(getString(R.string.hello)), color(Color.RED, getString(R.string.world)))
جاوا
// Create an italic "hello, " a red "world", // and bold the entire sequence. CharSequence text = bold(italic(getString(R.string.hello)), color(Color.RED, getString(R.string.world)));
ماژول core-ktx کاتلین همچنین شامل توابع افزونهای است که کار با spanها را آسانتر میکند. برای کسب اطلاعات بیشتر میتوانید مستندات بسته android.text را در گیتهاب بررسی کنید.
برای اطلاعات بیشتر در مورد کار با اسپنها، به لینکهای زیر مراجعه کنید:
استایلدهی با حاشیهنویسی
شما میتوانید با استفاده از کلاس Annotation به همراه تگ <annotation> در فایلهای منبع strings.xml خود، استایلبندی پیچیده یا سفارشی اعمال کنید. تگ annotation به شما این امکان را میدهد که با تعریف جفتهای کلید-مقدار سفارشی در XML که فریمورک سپس به Annotation spans تبدیل میکند، بخشهایی از رشته را برای استایلبندی سفارشی علامتگذاری کنید. سپس میتوانید این annotationها را بازیابی کرده و از کلید و مقدار برای اعمال استایلبندی استفاده کنید.
هنگام ایجاد حاشیهنویسیها، مطمئن شوید که برچسب <annotation> را به تمام ترجمههای رشته در هر فایل strings.xml اضافه میکنید.

اعمال یک فونت سفارشی به کلمه "text" در همه زبانها
مثال - اضافه کردن یک فونت سفارشی
تگ
<annotation>را اضافه کنید و جفت کلید-مقدار را تعریف کنید. در این مورد، کلید font و مقدار، نوع فونتی است که میخواهیم استفاده کنیم: title_emphasis// values/strings.xml <string name="title">Best practices for <annotation font="title_emphasis">text</annotation> on Android</string> // values-es/strings.xml <string name="title"><annotation font="title_emphasis">Texto</annotation> en Android: mejores prácticas</string>
منبع رشته را بارگذاری کنید و حاشیهنویسیها را با کلید فونت پیدا کنید. سپس یک span سفارشی ایجاد کنید و span موجود را جایگزین کنید.
کاتلین
// get the text as SpannedString so we can get the spans attached to the text val titleText = getText(R.string.title) as SpannedString // get all the annotation spans from the text val annotations = titleText.getSpans(0, titleText.length, Annotation::class.java) // create a copy of the title text as a SpannableString. // the constructor copies both the text and the spans. so we can add and remove spans val spannableString = SpannableString(titleText) // iterate through all the annotation spans for (annotation in annotations) { // look for the span with the key font if (annotation.key == "font") { val fontName = annotation.value // check the value associated to the annotation key if (fontName == "title_emphasis") { // create the typeface val typeface = getFontCompat(R.font.permanent_marker) // set the span at the same indices as the annotation spannableString.setSpan(CustomTypefaceSpan(typeface), titleText.getSpanStart(annotation), titleText.getSpanEnd(annotation), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) } } } // now, the spannableString contains both the annotation spans and the CustomTypefaceSpan styledText.text = spannableString
جاوا
// get the text as SpannedString so we can get the spans attached to the text SpannedString titleText = (SpannedString) getText(R.string.title); // get all the annotation spans from the text Annotation[] annotations = titleText.getSpans(0, titleText.length(), Annotation.class); // create a copy of the title text as a SpannableString. // the constructor copies both the text and the spans. so we can add and remove spans SpannableString spannableString = new SpannableString(titleText); // iterate through all the annotation spans for (Annotation annotation: annotations) { // look for the span with the key font if (annotation.getKey().equals("font")) { String fontName = annotation.getValue(); // check the value associated to the annotation key if (fontName.equals("title_emphasis")) { // create the typeface Typeface typeface = ResourcesCompat.getFont(this, R.font.roboto_mono); // set the span at the same indices as the annotation spannableString.setSpan(new CustomTypefaceSpan(typeface), titleText.getSpanStart(annotation), titleText.getSpanEnd(annotation), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } // now, the spannableString contains both the annotation spans and the CustomTypefaceSpan styledText.text = spannableString;
اگر چندین بار از یک متن استفاده میکنید، باید یک بار شیء SpannableString را بسازید و در صورت نیاز دوباره از آن استفاده کنید تا از مشکلات احتمالی عملکرد و حافظه جلوگیری شود.
برای مثالهای بیشتر از کاربرد حاشیهنویسی، به «طراحی متن بینالمللیشده در اندروید» مراجعه کنید.
حاشیهنویسی و دستهبندی متن
از آنجا که Annotation spans نیز ParcelableSpans هستند، جفتهای کلید-مقدار به صورت parceled و unparceled هستند. تا زمانی که گیرندهی parcel بداند چگونه annotationها را تفسیر کند، میتوانید Annotation spans برای اعمال استایلبندی سفارشی به متن parceled استفاده کنید.
برای حفظ استایل سفارشی خود هنگام ارسال متن به یک Intent Bundle، ابتدا باید Annotation spans را به متن خود اضافه کنید. میتوانید این کار را در منابع XML از طریق تگ <annotation> انجام دهید، همانطور که در مثال بالا نشان داده شده است، یا در کد با ایجاد یک Annotation جدید و تنظیم آن به عنوان span، همانطور که در زیر نشان داده شده است:
کاتلین
val spannableString = SpannableString("My spantastic text") val annotation = Annotation("font", "title_emphasis") spannableString.setSpan(annotation, 3, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) // start Activity with text with spans val intent = Intent(this, MainActivity::class.java) intent.putExtra(TEXT_EXTRA, spannableString) startActivity(intent)
جاوا
SpannableString spannableString = new SpannableString("My spantastic text"); Annotation annotation = new Annotation("font", "title_emphasis"); spannableString.setSpan(annotation, 3, 7, 33); // start Activity with text with spans Intent intent = new Intent(this, MainActivity.class); intent.putExtra(TEXT_EXTRA, spannableString); this.startActivity(intent);
متن را از Bundle به عنوان SpannableString بازیابی کنید و سپس حاشیهنویسیهای پیوست شده را تجزیه کنید، همانطور که در مثال بالا نشان داده شده است.
کاتلین
// read text with Spans val intentCharSequence = intent.getCharSequenceExtra(TEXT_EXTRA) as SpannableString
جاوا
// read text with Spans SpannableString intentCharSequence = (SpannableString)intent.getCharSequenceExtra(TEXT_EXTRA);
برای اطلاعات بیشتر در مورد سبک متن، به لینکهای زیر مراجعه کنید: