פריסות בתצוגות

רוצה לנסות את שיטת הכתיבה?
'Jetpack פיתוח נייטיב' היא ערכת הכלים המומלצת לממשק המשתמש ל-Android. הסבר איך עובדים עם פריסות ב'כתיבה'.

פריסה מגדירה את המבנה של ממשק משתמש באפליקציה, למשל ה פעילות. כל הרכיבים ב נבנו לפי היררכיה View והקבוצה ViewGroup אובייקטים. View בדרך כלל משרטט משהו שהמשתמש יכול לראות ליצור איתם אינטראקציה. ViewGroup הוא קונטיינר בלתי נראה שמגדיר מבנה הפריסה של View ושל ViewGroup אחרים של האובייקטים, כפי שמוצג באיור 1.

איור 1. איור של היררכיית תצוגה שמגדירה פריסה של ממשק המשתמש.

לרוב האובייקטים View נקראים ווידג'טים והם יכולים להיות הרבה תתי-מחלקות, Button או TextView ViewGroup אובייקטים נקראים בדרך כלל פריסות ויכולים להיות אחד סוגים רבים שמספקים מבנה פריסה שונה, כמו LinearLayout או ConstraintLayout.

יש שתי דרכים להצהיר על פריסה:

  • להצהיר על רכיבים בממשק המשתמש ב-XML. ב-Android יש קובץ XML פשוט שמתאים לכיתות ולכיתות המשנה של View, כמו אלו שמיועדות לווידג'טים ולפריסות. אפשר גם להשתמש עורך הפריסה לבניית ה-XML באמצעות ממשק גרירה ושחרור.

  • יצירת רכיבי פריסה מיידיים בזמן ריצה האפליקציה שלך יכולה ליצור אובייקטים של View ו-ViewGroup וביצוע שינויים בהם באופן פרוגרמטי.

הצהרה על ממשק המשתמש ב-XML מאפשרת להפריד את הצגת האפליקציה בקוד ששולט בהתנהגות שלו. השימוש בקובצי XML מקל גם על לספק פריסות שונות למסכים בגדלים שונים ולכיוונים שונים. הדבר לדיון נוסף בנושא תמיכה במסך אחר גדלים .

מסגרת Android נותנת לך את הגמישות להשתמש באחד או בשניהם השיטות האלה כדי לבנות את ממשק המשתמש של האפליקציה. לדוגמה, אתם יכולים להצהיר על זכויות היוצרים של האפליקציה שלכם פריסות ברירת המחדל ב-XML, ולאחר מכן לשנות את הפריסה בזמן הריצה.

כתיבת ה-XML

בעזרת אוצר המילים של XML ב-Android, אפשר לעצב במהירות פריסות של ממשק המשתמש שהם מכילים, באותו אופן שבו יוצרים דפי אינטרנט ב-HTML באמצעות סדרה של אלמנטים שהוצבו בו.

כל קובץ פריסה חייב להכיל רק רכיב בסיס אחד, חייב להיות אובייקט View או ViewGroup. אחרי שמגדירים את השורש ניתן להוסיף עוד אובייקטי פריסה או ווידג'טים כרכיבי צאצא ליצור בהדרגה היררכיית View שמגדירה את הפריסה שלך. עבור הנה פריסת XML שמשתמשת ב-LinearLayout אנכי כדי שומרים את TextView ואת Button:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, I am a Button" />
</LinearLayout>

אחרי שמצהירים על הפריסה ב-XML, שומרים את הקובץ עם תוסף אחד (.xml) בres/layout/ של פרויקט Android שלך כך שהוא יוכל הידור כראוי.

למידע נוסף על התחביר של קובץ XML לפריסה: משאב פריסה.

צריך לטעון את משאב ה-XML

כשיוצרים את האפליקציה, כל קובץ פריסת XML עובר הידור משאב View. צריך לטעון את משאב הפריסה באפליקציה Activity.onCreate() הטמעת קריאה חוזרת (callback). כדי לעשות זאת, צריך להתקשר setContentView(), ומעבירים לו את ההפניה למשאב הפריסה בתבנית: R.layout.layout_file_name. לדוגמה, אם קובץ ה-XML הפריסה נשמרה בשם main_layout.xml, ניתן לטעון אותה Activity באופן הזה:

Kotlin

fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_layout)
}

Java

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}

ה-framework של Android מפעיל את שיטת הקריאה החוזרת onCreate() ב- שלך ב-Activity כשנשיק את Activity. לקבלת מידע נוסף מידע על מחזורי חיים של פעילות, מבוא ל פעילויות נוספות.

מאפיינים

כל אובייקט View ו-ViewGroup תומך משלו מגוון של מאפייני XML. חלק מהמאפיינים ספציפיים לView לאובייקט. לדוגמה, הפקודה TextView תומכת בפונקציה textSize . עם זאת, המאפיינים האלה עוברים בירושה גם לכל View אובייקטים שמרחיבים את המחלקה הזו. חלקם משותפים לכל View כי הם עוברים בירושה ממחלקת השורש View, כמו המאפיין id. מאפיינים אחרים נחשבים לפריסה , שהם מאפיינים שמתארים כיוונים מסוימים של הפריסה של האובייקט View, כפי שהוגדר על ידי ההורה של האובייקט אובייקט ViewGroup.

מזהה

לכל אובייקט View יכול להיות מזהה מספר שלם שמשויך אליו לזהות באופן ייחודי את View בעץ. כשהאפליקציה שעברו הידור, למזהה הזה יש הפניה למספר שלם, אבל בדרך כלל המזהה מוקצה בקובץ ה-XML של הפריסה, כמחרוזת במאפיין id. זהו מאפיין XML המשותף לכל האובייקטים View, והוא מוגדר באמצעות כיתה אחת (View). אתם משתמשים בה לעיתים קרובות. התחביר של מזהה בתוך תג XML הוא התג הבא:

android:id="@+id/my_button"

הסמל at (@) בתחילת המחרוזת מציין מנתח ה-XML מנתח ומרחיב את שאר מחרוזת המזהה ומזהה אותה בתור משאב מזהה. סמל הפלוס (+) מציין שזה שם של משאב חדש שצריך ליצור ולהוסיף למשאבים שלכם בR.java חדש.

במסגרת Android יש הרבה משאבי זיהוי אחרים. כאשר מפנים מזהה משאב של Android, אין צורך בסמל הפלוס, אבל חייבים להוסיף את מרחב שמות של חבילה android באופן הבא:

android:id="@android:id/empty"

מרחב השמות של החבילה android מציין שהתייחסת אליו מזהה ממחלקת המשאבים android.R ולא מהמחלקה המקומית במחלקת המשאבים.

כדי ליצור תצוגות ולהפנות אליהן מהאפליקציה, אפשר להשתמש דפוס:

  1. מגדירים תצוגה בקובץ הפריסה ומקצים לה מזהה ייחודי, כמו בדוגמה הבאה:
    <Button android:id="@+id/my_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/my_button_text"/>
    
  2. יוצרים מופע של אובייקט ה-View ומצלמים אותו מהפריסה, בדרך כלל onCreate() כפי שאפשר לראות בדוגמה הבאה:

    Kotlin

    val myButton: Button = findViewById(R.id.my_button)
    

    Java

    Button myButton = (Button) findViewById(R.id.my_button);
    

חשוב להגדיר מזהים לאובייקטים של תצוגה מפורטת כשיוצרים RelativeLayout בפריסה יחסית, תצוגות אחות יכולות להגדיר את הפריסה שלהן ביחס לאחרת לתצוגה אחות, שאליה מפנה המזהה הייחודי.

המזהה לא חייב להיות ייחודי לאורך כל העץ, אבל הוא צריך להיות ייחודיות בחלק של העץ שאתם מחפשים. בדרך כלל מדובר לכן מומלץ להפוך אותו לייחודי ככל האפשר.

פרמטרים של פריסה

מגדירים מאפייני פריסת XML בשם layout_something פרמטרים של פריסה עבור View שמתאימים ל- ViewGroup המכשיר נמצא.

כל מחלקה ViewGroup מטמיעה מחלקה פנימית שמתרחבת ViewGroup.LayoutParams. תת-המחלקה הזו מכילה סוגי נכסים שמגדירים את הגודל והמיקום של כל אחד מהם. תצוגת צאצא, בהתאם לקבוצת התצוגה המפורטת. כפי שמוצג באיור 2, הורה לקבוצת התצוגה הזו מוגדרים פרמטרים של פריסה לכל תצוגת צאצא, כולל הצאצא. קבוצת תצוגה מפורטת.

איור 2. תצוגה של היררכיית תצוגות עם פריסה שמשויכים לכל תצוגה מפורטת.

לכל תת-מחלקה ב-LayoutParams יש תחביר משלה להגדרה ערכים. כל רכיב צאצא צריך להגדיר LayoutParams שמתאים להורה שלו, למרות שהוא עשוי גם להגדיר LayoutParams לילדים שלו.

כל קבוצות התצוגות המפורטות כוללות רוחב וגובה, באמצעות המאפיין layout_width ו-layout_height, וכל תצוגה מפורטת נדרשת כדי להגדיר אותם. הרבה LayoutParams כוללים שוליים וגבולות אופציונליים.

אפשר לציין רוחב וגובה במידות מדויקות, אבל יכול להיות שלא כדאי לעשות זאת לעיתים קרובות. לעיתים קרובות יותר משתמשים באחד מהקבועים האלה כדי להגדיר רוחב או גובה:

  • wrap_content: מנחה את התצוגה להתאים את הגודל שלו שנדרשים על ידי התוכן שלו.
  • match_parent: הגדרה של גודל החשיפה של ההורה לאפשר לקבוצת צפיות.

באופן כללי, לא מומלץ לציין רוחב וגובה של הפריסה באמצעות יחידות מוחלטות כמו פיקסלים. גישה טובה יותר היא להשתמש במדידות יחסיות, כמו יחידות פיקסלים שלא תלויות בדחיסות (dp), wrap_content או match_parent, מפני שהיא עוזרת להציג את האפליקציה בצורה תקינה מגוון גדלים של מסכים במכשירים. סוגי המדידה הקבילים מוגדרים ב: משאב פריסה.

מיקום הפריסה

בתצוגה יש גיאומטריה מלבנית. יש לו מיקום, מבוטא בזוג קואורדינטות left ו-top (ראש הדף) ושני מאפיינים, שמבוטאים רוחב וגובה. היחידה של המיקום והמידות היא הפיקסל.

כדי לאחזר מיקום של תצוגה, צריך להפעיל את השיטות getLeft() וגם getTop(). הראשונה מחזירה את הקואורדינטה שמאלית (x) של המלבן שמייצג את התצוגה. השנייה מחזירה את הקואורדינטה העליונה (y) של המלבן שמייצגים את התצוגה. שיטות אלה מחזירות את מיקום התצוגה ביחס ההורה שלו. לדוגמה, אם getLeft() מחזירה 20, המשמעות היא התצוגה ממוקמת 20 פיקסלים מימין לקצה השמאלי של הקצה הורה.

בנוסף, יש שיטות נוחות למניעת חישובים מיותרים: כלומר getRight() וגם getBottom(). השיטות האלה מחזירות את הקואורדינטות של הקצה הימני והתחתון של שמייצג את התצוגה. לדוגמה, השיחה אל getRight() היא בדומה לחישוב הבא: getLeft() + getWidth().

גודל, מרווח פנימי ושוליים

גודל התצוגה מצוין באמצעות רוחב וגובה. לתצוגה מפורטת יש שני צמדים של ערכי רוחב וגובה.

הצמד הראשון נקרא רוחב נמדד הגובה שנמדד. המאפיינים האלה מגדירים את הגודל הרצוי של התצוגה בתוך ההורה שלו. כדי לקבל את המידות שנמדדו, צריך לשלוח קריאה getMeasuredWidth() וגם getMeasuredHeight().

הצמד השני נקרא width ו-height, או לפעמים רוחב שרטוט וגובה שרטוט. המאפיינים האלה מגדירים את הגודל בפועל של התצוגה על המסך, בזמן הציור ואחרי הפריסה. האלה עשויים להיות שונים מהרוחב והגובה שנמדדו, אבל לא חייבים להיות בהם. שלך יכול לקבל את הרוחב והגובה באמצעות קריאה getWidth() וגם getHeight().

כדי למדוד את מידות המודעה, המרווח הפנימי של התצוגה נלקח בחשבון. המרווח הפנימי מבוטאת בפיקסלים בחלקים השמאליים, העליונים, הימניים והתחתונים של התצוגה. ניתן להשתמש במרווח פנימי כדי לקזז את תוכן התצוגה במספר ספציפי של פיקסלים. לדוגמה, מרווח פנימי שמאלי של שני פיקסלים דוחף את תוכן התצוגה בשני פיקסלים. מימין לקצה השמאלי. אפשר להגדיר מרווח פנימי באמצעות setPadding(int, int, int, int) ולהרצת שאילתה על ידי getPaddingLeft(), getPaddingTop(), getPaddingRight(), וגם getPaddingBottom()

למרות שבתצוגה אפשר להגדיר מרווח פנימי, היא לא תומכת בשוליים. אבל, לפעמים כן תומכות בשולי רווח. צפייה ViewGroup והקבוצה ViewGroup.MarginLayoutParams אפשר לקבל מידע נוסף.

למידע נוסף על מאפיינים, אפשר לעיין במאמר מאפיין.

מלבד הגדרה של שוליים ומרווח פנימי באופן פרוגרמטי, אפשר גם להגדיר אותם בפריסות ה-XML, כמו שאפשר לראות בדוגמה הבאה:

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical" >
      <TextView android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="16dp"
                android:padding="8dp"
                android:text="Hello, I am a TextView" />
      <Button android:id="@+id/button"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginTop="16dp"
              android:paddingBottom="4dp"
              android:paddingEnd="8dp"
              android:paddingStart="8dp"
              android:paddingTop="4dp"
              android:text="Hello, I am a Button" />
  </LinearLayout>
  

בדוגמה שלמעלה רואים שוליים ומרווח פנימי שהוחל. ב-TextView יש שוליים ומרווח פנימי אחידים מסביב, וגם Button מראה איך ניתן ליישם אותם בנפרד הקצוות.

פריסות נפוצות

כל תת-מחלקה במחלקה ViewGroup מספקת דרך ייחודית להציג את התצוגות שהוצבו בו. סוג הפריסה הגמישה ביותר שמספק את הכלים הטובים ביותר לשמירה על היררכיית פריסה נמוכה, ConstraintLayout

הנה כמה מסוגי הפריסה הנפוצים שמובְנים במכשיר ה-Android הפלטפורמה.

יצירת פריסה לינארית

מארגנת את הצאצאים בשורה אופקית או אנכית אחת ויוצרת סרגל גלילה אם אורך החלון חורג מאורך המסך.

יצירת רשימות דינמיות

אם התוכן לפריסה דינמי או לא קבוע מראש, אפשר להשתמש RecyclerView או תת-מחלקה של AdapterView. בדרך כלל עדיף להשתמש ב-RecyclerView כי הוא משתמש בזיכרון בצורה יעילה יותר מאשר AdapterView.

פריסות נפוצות שאפשר להשתמש בהן בעזרת RecyclerView וגם AdapterView כוללות:

רשימה

מציגה רשימה נגללת של עמודות בודדות.

רשת

הצגת רשת גלילה של עמודות ושורות.

RecyclerView מציע עוד אפשרויות ו האפשרות ליצור התאמה אישית מנהל הפריסה.

מילוי תצוגת מתאם בנתונים

אפשר לאכלס AdapterView כמו ListView או GridView על ידי הפונקציה מקשרת את המופע של AdapterView Adapter, שמאחזר נתונים ממקור חיצוני ויוצר View שמייצג כל רשומת נתונים.

ב-Android יש כמה מחלקות משנה של Adapter שימושיות לאחזור סוגים שונים של נתונים וליצירת תצוגות AdapterView. שני המתאמים הנפוצים ביותר הם:

ArrayAdapter
יש להשתמש במתאם הזה כאשר מקור הנתונים הוא מערך. כברירת מחדל, הפונקציה ArrayAdapter יוצרת תצוגה לכל פריט במערך באמצעות קריאה toString() על כל פריט ומציבים את התוכן בTextView.

לדוגמה, אם יש מערך של מחרוזות שאתם רוצים להציג ListView, צריך לאתחל ArrayAdapter חדש באמצעות כדי לציין את הפריסה לכל מחרוזת ומערך המחרוזת:

Kotlin

    val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)
    

Java

    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_list_item_1, myStringArray);
    

הארגומנטים של ה-constructor האלה הם:

  • האפליקציה שלך Context
  • הפריסה שמכילה TextView לכל מחרוזת ב- מערך
  • מערך המחרוזת

ואז להתקשר setAdapter() ב-ListView שלך:

Kotlin

    val listView: ListView = findViewById(R.id.listview)
    listView.adapter = adapter
    

Java

    ListView listView = (ListView) findViewById(R.id.listview);
    listView.setAdapter(adapter);
    

כדי להתאים אישית את המראה של כל פריט אפשר לשנות את המאפיין של toString() לאובייקטים במערך. או כדי ליצור תצוגה של כל פריט שהוא לא TextView - לדוגמה, אם רוצים ImageView לכל פריט במערך – מרחיבים את המחלקה ArrayAdapter לשנות getView() כדי להחזיר את סוג התצוגה הרצויה לכל פריט.

SimpleCursorAdapter
יש להשתמש במתאם הזה כאשר הנתונים מגיעים Cursor. כשמשתמשים ב-SimpleCursorAdapter, צריך לציין את הפריסה שתשמש כל שורה ב-Cursor ואילו עמודות Cursor שברצונך להוסיף לתצוגות של הפריסה הרצויה. לדוגמה, אם אתה רוצה ליצור רשימה של שמות ומספרי טלפון של אנשים אפשר להריץ שאילתה שמחזירה Cursor שכוללות שורה לכל אדם ועמודות עם השמות והמספרים. שלך ליצור מערך מחרוזת שמציין אילו עמודות הערך הרצוי של Cursor בפריסה של כל תוצאה ומספר שלם מערך שמציין את התצוגות המתאימות שצריכות להיות לכל עמודה ממוקם:

Kotlin

    val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME,
                              ContactsContract.CommonDataKinds.Phone.NUMBER)
    val toViews = intArrayOf(R.id.display_name, R.id.phone_number)
    

Java

    String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
                            ContactsContract.CommonDataKinds.Phone.NUMBER};
    int[] toViews = {R.id.display_name, R.id.phone_number};
    

כשיוצרים את SimpleCursorAdapter, מעבירים את לשימוש בכל תוצאה, ה-Cursor שמכיל את ושני המערכים האלה:

Kotlin

    val adapter = SimpleCursorAdapter(this,
            R.layout.person_name_and_number, cursor, fromColumns, toViews, 0)
    val listView = getListView()
    listView.adapter = adapter
    

Java

    SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
            R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
    ListView listView = getListView();
    listView.setAdapter(adapter);
    

לאחר מכן הפרמטר SimpleCursorAdapter יוצר תצוגה מפורטת לכל שורה Cursor באמצעות הפריסה שסופקה על ידי הוספת כל אחד מהם פריט אחד (fromColumns) ב-toViews המתאים צפייה.

אם במהלך חיי האפליקציה משנים את הנתונים הבסיסיים נקרא על ידי המתאם, notifyDataSetChanged() פעולה זו מיידעת את התצוגה המצורפת שהנתונים השתנו והם מתרעננים עצמו.

טיפול באירועי קליקים

אפשר להגיב לאירועים מסוג קליק על כל פריט ב-AdapterView באמצעות יישום של AdapterView.OnItemClickListener גרפי. לדוגמה:

Kotlin

listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
    // Do something in response to the click.
}

Java

// Create a message handling object as an anonymous class.
private OnItemClickListener messageClickedHandler = new OnItemClickListener() {
    public void onItemClick(AdapterView parent, View v, int position, long id) {
        // Do something in response to the click.
    }
};

listView.setOnItemClickListener(messageClickedHandler);

מקורות מידע נוספים

איך משתמשים בפריסות חמנית אפליקציית הדגמה ב-GitHub.