כשצריך להציג תמונות סטטיות באפליקציה, אפשר להשתמש במחלקה Drawable
ובמחלקות המשנה שלה כדי לשרטט צורות
תמונות. Drawable
הוא הפשטה כללית של
משהו שאפשר לצייר. מחלקות המשנה השונות עוזרות עם תמונה ספציפית
ותוכלו להרחיב אותם כך שיגדירו אובייקטים שניתנים להזזה.
שמתנהגות בדרכים ייחודיות.
יש שתי דרכים להגדיר וליצור Drawable
מלבד השימוש ב-constructor של המחלקות:
- מתנפחים משאב של תמונה (קובץ מפת סיביות (bitmap)) שנשמר בפרויקט.
- מתנפח משאב XML שמגדיר את המאפיינים שניתנים להזזה.
הערה: יכול להיות שבמקום זאת תעדיפו להשתמש בקובץ גרפי וקטורי שניתן להזזה, שמגדיר תמונה עם נקודות, קווים ועקומות, יחד עם מידע על הצבעים שמשויכים אליהם. ההגדרה הזו מאפשרת להשתמש בפריטי גרפיקה וקטוריים כך שיתאימו לגדלים שונים בלי לגרוע מהאיכות. אפשר למצוא מידע נוסף במאמר וקטורים סקירה כללית של פריטים שניתנים להזזה.
יצירת פריטים שניתנים להזזה מתמונות של משאבים
אפשר להוסיף גרפיקה לאפליקציה על ידי הפניה לקובץ תמונה מה במשאבי הפרויקט. סוגי הקבצים הנתמכים הם PNG (מועדף), JPG (קביל), ו-GIF (לא מומלץ). סמלי אפליקציות, סמלי לוגו ופריטים גרפיים אחרים, כמו אלה שנעשה בהם שימוש מאוד מתאימים לטכניקה הזו.
כדי להשתמש במשאב של תמונות, צריך להוסיף את הקובץ אל res/drawable/
של הפרויקט. בתוך הפרויקט, תוכלו להפנות אל התמונה
מהקוד או מפריסת ה-XML. בכל מקרה, זה נקרא שימוש
מזהה משאב, שהוא שם הקובץ ללא הסיומת של סוג הקובץ. עבור
לדוגמה, התייחסו אל my_image.png
בתור my_image
.
הערה: משאבי תמונות שנמצאים ב
ניתן לבצע אופטימיזציה אוטומטית של הספרייה res/drawable/
באמצעות
דחיסת תמונות ללא אובדן מידע על ידי הכלי aapt
במהלך ה-build
תהליך האימות. לדוגמה, קובץ PNG בצבע אמיתי שלא דורש יותר מ-256 צבעים
ניתן להמיר לקובץ PNG בגודל 8 סיביות עם לוח צבעים. התוצאה היא תמונה
באיכות זהה אך דורש פחות זיכרון. כתוצאה מכך, הקובץ הבינארי של התמונה
בספרייה הזו יכולים להשתנות בזמן ה-build. אם אתם מתכננים לקרוא
התמונה בתור קטע סיביות (bitstream) כדי להמיר אותה למפת סיביות (bitmap),
במקום זאת, תיקייה אחת (res/raw/
), שבה הכלי aapt
לשנות אותם.
קטע הקוד הבא מדגים איך לפתח ImageView
שמשתמשת
תמונה שנוצרה ממשאב שניתן להזזה ומוסיפה אותו לפריסה:
Kotlin
private lateinit var constraintLayout: ConstraintLayout override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Instantiate an ImageView and define its properties val i = ImageView(this).apply { setImageResource(R.drawable.my_image) contentDescription = resources.getString(R.string.my_image_desc) // set the ImageView bounds to match the Drawable's dimensions adjustViewBounds = true layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) } // Create a ConstraintLayout in which to add the ImageView constraintLayout = ConstraintLayout(this).apply { // Add the ImageView to the layout. addView(i) } // Set the layout as the content view. setContentView(constraintLayout) }
Java
ConstraintLayout constraintLayout; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create a ConstraintLayout in which to add the ImageView constraintLayout = new ConstraintLayout(this); // Instantiate an ImageView and define its properties ImageView i = new ImageView(this); i.setImageResource(R.drawable.my_image); i.setContentDescription(getResources().getString(R.string.my_image_desc)); // set the ImageView bounds to match the Drawable's dimensions i.setAdjustViewBounds(true); i.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); // Add the ImageView to the layout and set the layout as the content view. constraintLayout.addView(i); setContentView(constraintLayout); }
במקרים אחרים, כדאי לטפל במשאב התמונה כאובייקט Drawable
, כפי שמוצג באופן הבא:
דוגמה:
Kotlin
val myImage: Drawable = ResourcesCompat.getDrawable(context.resources, R.drawable.my_image, null)
Java
Resources res = context.getResources(); Drawable myImage = ResourcesCompat.getDrawable(res, R.drawable.my_image, null);
אזהרה:כל משאב ייחודי בפרויקט
יכולים לשמור רק מצב אחד, לא משנה כמה אובייקטים שונים
ליצור אותו. לדוגמה, אם יוצרים שני אובייקטים של Drawable
מאותו משאב תמונה ו
משנה מאפיין (כמו אלפא) של אובייקט אחד, ואז הוא משפיע גם
את השני. כשמדובר במופעים מרובים של משאב תמונה, במקום זאת
של טרנספורמציה ישירה של האובייקט Drawable
, צריך לבצע
טרום-עשרה
אנימציה.
קטע קוד ה-XML הבא מראה איך להוסיף משאב שניתן להזזה ל-ImageView
בפריסת XML:
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/my_image" android:contentDescription="@string/my_image_desc" />
מידע נוסף על השימוש במשאבי פרויקט זמין במאמר משאבים ונכסים.
הערה: כשמשתמשים במשאבי תמונות כמקור לחומרים שניתנים להזזה, לוודא שהתמונות בגודל המתאים לדחיסות פיקסלים שונה. אם תמונות שגויות, הן יותאמו לגודל המרבי שלהן, דבר שעלול לגרום לשיבושים בפריטים הנפתחים שלך. אפשר לקרוא מידע נוסף במאמר תמיכה ברמות שונות, דחיסות פיקסלים.
יצירת פריטים שניתנים להזזה ממשאבי XML
אם יש Drawable
שתרצו ליצור, שלא תלוי בהתחלה במשתנים שהגדרתם
או אינטראקציה של המשתמשים, ואז הגדרת Drawable
ב-XML היא אפשרות טובה. באופן שווה
אם ציפית שהמאפיינים של Drawable
ישתנו במהלך האינטראקציה של המשתמש
באפליקציה שלכם, כדאי להגדיר את האובייקט ב-XML, כי אפשר לשנות מאפיינים אחרי
האובייקט נוצר.
אחרי שמגדירים את Drawable
ב-XML, שומרים את הקובץ ב
ספריית res/drawable/
של הפרויקט. הדוגמה הבאה מציגה את קוד ה-XML
מגדיר
TransitionDrawable
במשאב שיורש מ-Drawable
:
<!-- res/drawable/expand_collapse.xml --> <transition xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/image_expand"/> <item android:drawable="@drawable/image_collapse"/> </transition>
לאחר מכן, מאחזרים את האובייקט ויוצרים אותו באמצעות קריאה
Resources#getDrawable()
ולהעביר את מזהה המשאב של קובץ ה-XML. כלשהו
תת-מחלקה אחת (Drawable
)
שתומך ב-method inflate()
, ניתן להגדיר ב-XML וליצור
על ידי האפליקציה.
כל מחלקה ניתנת להזזה שתומכת באינפלציה של XML משתמשת במאפייני XML ספציפיים
שעוזרים להגדיר את מאפייני האובייקט. הקוד הבא מייצר את
TransitionDrawable
ומגדיר אותו כתוכן של
אובייקט ImageView
:
Kotlin
val transition= ResourcesCompat.getDrawable( context.resources, R.drawable.expand_collapse, null ) as TransitionDrawable val image: ImageView = findViewById(R.id.toggle_image) image.setImageDrawable(transition) // Description of the initial state that the drawable represents. image.contentDescription = resources.getString(R.string.collapsed) // Then you can call the TransitionDrawable object's methods. transition.startTransition(1000) // After the transition is complete, change the image's content description // to reflect the new state.
Java
Resources res = context.getResources(); TransitionDrawable transition = (TransitionDrawable) ResourcesCompat.getDrawable(res, R.drawable.expand_collapse, null); ImageView image = (ImageView) findViewById(R.id.toggle_image); image.setImageDrawable(transition); // Description of the initial state that the drawable represents. image.setContentDescription(getResources().getString(R.string.collapsed)); // Then you can call the TransitionDrawable object's methods. transition.startTransition(1000); // After the transition is complete, change the image's content description // to reflect the new state.
למידע נוסף על מאפייני ה-XML הנתמכים, ראו את המחלקות (מחלקות) שצוינו למעלה.
צורות ניתנות להזזה
אפשר להשתמש באובייקט ShapeDrawable
.
כשרוצים לצייר באופן דינמי גרפיקה דו ממדית. אפשר
שרטוט צורות פרימיטיביות באופן פרוגרמטי על אובייקט ShapeDrawable
ולהחיל את הסגנונות שדרושים לאפליקציה.
ShapeDrawable
היא מחלקה משנית של Drawable
. לכן אפשר להשתמש
ShapeDrawable
בכל מקום שבו צפוי Drawable
. עבור
לדוגמה, אפשר להשתמש באובייקט ShapeDrawable
כדי להגדיר את הרקע.
של צפייה על ידי העברתה לשיטה setBackgroundDrawable()
של התצוגה. אפשר גם לשרטט את הצורה
תצוגה מותאמת אישית והוספתה לפריסה באפליקציה.
ל-ShapeDrawable
יש שיטת draw()
משלה, לכן אפשר ליצור
המחלקה המשנית של View
שמושכת את ShapeDrawable
במהלך האירוע onDraw()
, כפי שמוצג
דוגמת הקוד הבאה:
Kotlin
class CustomDrawableView(context: Context) : View(context) { private val drawable: ShapeDrawable = run { val x = 10 val y = 10 val width = 300 val height = 50 contentDescription = context.resources.getString(R.string.my_view_desc) ShapeDrawable(OvalShape()).apply { // If the color isn't set, the shape uses black as the default. paint.color = 0xff74AC23.toInt() // If the bounds aren't set, the shape can't be drawn. setBounds(x, y, x + width, y + height) } } override fun onDraw(canvas: Canvas) { drawable.draw(canvas) } }
Java
public class CustomDrawableView extends View { private ShapeDrawable drawable; public CustomDrawableView(Context context) { super(context); int x = 10; int y = 10; int width = 300; int height = 50; setContentDescription(context.getResources().getString( R.string.my_view_desc)); drawable = new ShapeDrawable(new OvalShape()); // If the color isn't set, the shape uses black as the default. drawable.getPaint().setColor(0xff74AC23); // If the bounds aren't set, the shape can't be drawn. drawable.setBounds(x, y, x + width, y + height); } protected void onDraw(Canvas canvas) { drawable.draw(canvas); } }
אפשר להשתמש במחלקה CustomDrawableView
בדוגמת הקוד
למעלה כמו בכל תצוגה מותאמת אישית אחרת. לדוגמה, אפשר:
להוסיף אותו באופן פרוגרמטי לפעילות באפליקציה, כמו בדוגמה הבאה
דוגמה:
Kotlin
private lateinit var customDrawableView: CustomDrawableView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) customDrawableView = CustomDrawableView(this) setContentView(customDrawableView) }
Java
CustomDrawableView customDrawableView; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); customDrawableView = new CustomDrawableView(this); setContentView(customDrawableView); }
אם רוצים להשתמש במקום זאת בתצוגה מותאמת אישית בפריסת XML, צריך להשתמש
המחלקה CustomDrawableView
חייבת לשנות את ה-constructor של View(Context, AttributeSet)
, שמופעל כשהמחלקה היא
התנפחות מ-XML. הדוגמה הבאה מראה איך להצהיר (declare) על
CustomDrawableView
בפריסת ה-XML:
<com.example.shapedrawable.CustomDrawableView android:layout_width="fill_parent" android:layout_height="wrap_content" />
הכיתה ShapeDrawable
, כמו הרבה אחרים
בחבילה android.graphics.drawable
, מאפשרים לכם
מגדירים מאפיינים שונים של האובייקט באמצעות שיטות ציבוריות. דוגמה
מאפיינים שאולי תרצה לשנות כוללים שקיפות אלפא, פילטר צבע,
מיזוג, שקיפות וצבע.
אפשר גם להגדיר צורות פרימיטיביות שניתנות להזזה באמצעות משאבי XML. לקבלת מידע נוסף מידע נוסף, ראה צורה ניתנת להזזה ב סוגי משאבים שניתנים לשרטוט.
פריטים שנמשכים על ידי NinePatch
גרפיקה של NinePatchDrawable
היא
תמונה של מפת סיביות ניתנת למתיחה שניתן להשתמש בה כרקע של תצוגה. במכשירי Android
משנה אוטומטית את גודל הגרפיקה כדי להתאים לתוכן של התצוגה.
שימוש לדוגמה בתמונת NinePatch הוא הרקע שמשמש את מערכת Android הרגילה
לחצנים — הלחצנים חייבים להימתח כדי להתאים למחרוזות באורכים שונים. א'
גרפיקה של NinePatch היא תמונה סטנדרטית בפורמט PNG עם גבול נוסף של פיקסל אחד.
צריך לשמור אותה עם התוסף 9.png
ב-
ספריית res/drawable/
של הפרויקט.
השתמשו בשוליים כדי להגדיר את האזורים שניתנים למתיחה והסטטי בתמונה. כדי לציין קטע שניתן למתוח, משרטטים שרטוט של פיקסל אחד (או יותר) ברוחב של פיקסל אחד. קווים שחורים בחלק השמאלי והעליונה של הגבול (פיקסלים אחרים לגבול) צריך להיות שקוף לגמרי או לבן). אפשר למתוח כמה שיותר קטעים כרצונך. הגודל היחסי של הקטעים שניתן למתוח נשאר ללא שינוי, כך החלק הגדול ביותר תמיד נשאר הגדול ביותר.
ניתן גם להגדיר קטע אופציונלי של התמונה (למעשה,
על ידי ציור קו בצד ימין וקו למטה. אם
האובייקט View
מגדיר את הגרפיקה של NinePatch כרקע שלו
ואז מציין את הטקסט של התצוגה, הוא נמתח כך שכל הטקסט
מכסה רק את האזור שצוין באמצעות השורות הימניות והתחתונות (אם כלול).
אם שורות המרווח הפנימי לא כלולות, Android משתמש בשורה השמאלית ובשורה העליונה כדי
להגדיר את האזור הניתן לשרטוט.
כדי להבהיר את ההבדל בין הקווים, הקווים השמאליים והשורה העליונה מגדירים אילו פיקסלים של התמונה מורשים לשכפל כדי למתוח תמונה. הקווים התחתונים והימניים מגדירים את האזור היחסי בתוך התמונה מותר לשמור את התוכן של התצוגה.
איור 1 מציג דוגמה לגרפיקה של NinePatch שמשמשת להגדרת לחצן:
הגרפיקה הזו של NinePatch מגדירה אזור אחד למתיחה עם הצד השמאלי ובחלק העליון ואת האזור שניתן לשרטוט עם הקווים התחתון והימני. בתמונה העליונה, הקווים האפורים מזהים את האזורים בתמונה המשוכפלים כדי למתוח את התמונה. המלבן הוורוד בתמונה התחתונה מזהה האזור שבו מותר להציג את התוכן. אם התוכן לא שמתאימות לאזור הזה, התמונה נמתחת כדי להתאים אותן.
הכלי Draw 9-patch מאפשר דרך שימושית במיוחד ליצירת תמונות NinePatch באמצעות גרפיקה של WYSIWYG עם הרשאת עריכה. היא אפילו מציגה אזהרות אם האזור שהגדרתם למתיחה ייתכן שהאזור עלול להיווצר כתוצאה מפעולת פיקסל רפליקציה.
קטע ה-XML לדוגמה של הפריסה הבא מדגים איך להוסיף גרפיקה של NinePatch
לכמה לחצנים. תמונת NinePatch נשמרה ב
res/drawable/my_button_background.9.png
<Button android:id="@+id/tiny" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerInParent="true" android:text="Tiny" android:textSize="8sp" android:background="@drawable/my_button_background"/> <Button android:id="@+id/big" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerInParent="true" android:text="Biiiiiiig text!" android:textSize="30sp" android:background="@drawable/my_button_background"/>
חשוב לשים לב שהערכים layout_width
וlayout_height
מוגדרים ל-wrap_content
כדי שהלחצן יתאים בדיוק
סביב הטקסט.
איור 2 מציג את שני הלחצנים שעברו רינדור מהתמונה XML ו-NinePatch שמוצג למעלה. שימו לב שהרוחב והגובה של הלחצן משתנים בהתאם לטקסט. ותמונת הרקע נמתחת כדי להתאים לה.
פריטים שניתנים להזזה בהתאמה אישית
כדי ליצור שרטוטים בהתאמה אישית, צריך להרחיב את המחלקה Drawable
(או כל אחת ממחלקות המשנה שלה).
השיטה החשובה ביותר להטמעה היא draw(Canvas)
כי זה מספק את האובייקט Canvas
שצריך להשתמש בו כדי לספק
את הוראות השרטוט.
הקוד הבא מציג מחלקה פשוטה של Drawable
שמשרטטת מעגל:
Kotlin
class MyDrawable : Drawable() { private val redPaint: Paint = Paint().apply { setARGB(255, 255, 0, 0) } override fun draw(canvas: Canvas) { // Get the drawable's bounds val width: Int = bounds.width() val height: Int = bounds.height() val radius: Float = Math.min(width, height).toFloat() / 2f // Draw a red circle in the center canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, redPaint) } override fun setAlpha(alpha: Int) { // This method is required } override fun setColorFilter(colorFilter: ColorFilter?) { // This method is required } override fun getOpacity(): Int = // Must be PixelFormat.UNKNOWN, TRANSLUCENT, TRANSPARENT, or OPAQUE PixelFormat.OPAQUE }
Java
public class MyDrawable extends Drawable { private final Paint redPaint; public MyDrawable() { // Set up color and text size redPaint = new Paint(); redPaint.setARGB(255, 255, 0, 0); } @Override public void draw(Canvas canvas) { // Get the drawable's bounds int width = getBounds().width(); int height = getBounds().height(); float radius = Math.min(width, height) / 2; // Draw a red circle in the center canvas.drawCircle(width/2, height/2, radius, redPaint); } @Override public void setAlpha(int alpha) { // This method is required } @Override public void setColorFilter(ColorFilter colorFilter) { // This method is required } @Override public int getOpacity() { // Must be PixelFormat.UNKNOWN, TRANSLUCENT, TRANSPARENT, or OPAQUE return PixelFormat.OPAQUE; } }
לאחר מכן תוכלו להוסיף פריט גרפי שנוח לכם בכל מקום, למשל
ImageView
כפי שמוצג כאן:
Kotlin
val myDrawing = MyDrawable() val image: ImageView = findViewById(R.id.imageView) image.setImageDrawable(myDrawing) image.contentDescription = resources.getString(R.string.my_image_desc)
Java
MyDrawable mydrawing = new MyDrawable(); ImageView image = findViewById(R.id.imageView); image.setImageDrawable(mydrawing); image.setContentDescription(getResources().getString(R.string.my_image_desc));
ב-Android 7.0 (רמת API 24) ואילך אפשר גם להגדיר מופעים של פריט גרפי בהתאמה אישית עם XML בדרכים הבאות:
- שימוש בשם המחלקה המוגדר במלואו כשם רכיב ה-XML. בגישה הזאת,
מחלקה ניתנת להזזה חייבת להיות מחלקה ציבורית ברמה עליונה:
<com.myapp.MyDrawable xmlns:android="http://schemas.android.com/apk/res/android" android:color="#ffff0000" />
- שימוש ב-
drawable
כשם תג ה-XML וציון המחלקה המלאה מהמאפיין class. ניתן להשתמש בגישה הזו גם לכיתות ציבוריות ברמה עליונה וגם מחלקות פנימיות סטטיות ציבוריות:<drawable xmlns:android="http://schemas.android.com/apk/res/android" class="com.myapp.MyTopLevelClass$MyDrawable" android:color="#ffff0000" />
הוספת גוון לפריטי גרפיקה
ב-Android 5.0 (רמת API 21) ואילך, אפשר לבצע גוון של מפות סיביות ותשעה תיקונים שמוגדרים כך
מסכות אלפא. אפשר לשנות את הגוון שלהם באמצעות משאבי צבע או מאפייני עיצוב שמתאימים לצבע
משאבים (לדוגמה, ?android:attr/colorPrimary
). בדרך כלל יוצרים את הנכסים האלה
רק פעם אחת ולצבע אותן באופן אוטומטי כך שיתאימו לעיצוב שלך.
אפשר להחיל גוון על BitmapDrawable
, NinePatchDrawable
או VectorDrawable
אובייקטים עם השיטה setTint()
. אפשר
להגדיר גם את גוון הצבע והמצב של הפריסות באמצעות android:tint
וגם
android:tintMode
.
חילוץ צבעים בולטים מהתמונה
ספריית התמיכה של Android כוללת את המחלקה Palette
, שמאפשרת לחלץ צבעים בולטים מהתמונה.
יש לך אפשרות לטעון את הפריטים הנעים בתור Bitmap
ולהעביר אותם אל Palette
כדי לקבל גישה לצבעים שלהם.
למידע נוסף, אפשר לקרוא את המאמר בחירת צבעים עם
Palette API.