יום חמישי, דצמבר 9

- מגירה נפתחת Sliding Drawer

מגירת ה-notification הנשלפת מוכרת לכולם קרוב לודאי. הפוסט הזה ידגים יצירת מגירה נשלפת = Sliding Drawer מהסוג הזה. את המגירה שלנו אפשר לפתוח לאורך או לרוחב. ומדובר במנגנון פשוט, אבל בכל זאת נאלצתי להגיר מעט זיעה: משום מה המגירה גרמה להסתרת התצוגה שמתחתיה (הפיתרון: פריסת ה-layout באופן שיודגם כאן). נעבור לדוגמא.

מה עושה האפליקציה?
בעזרת שני כפתורים אפשר להוסיף טאבים או למחוק את כל הטאבים.
הכפתורים נמצאים על ה- Sliding Drawer כך שאפשר להחביא אותם.

הטאבים שנוצרים מכילים (כולם) שעון אנלוגי.
הסבר על טאבים  אפשר למצוא בפוסט הזה.
הנה  מספר תמונות מסך להתרשמות:

תמונה 1: המגירה פתוחה, 4 טאבים כבר נוצרו (כפתורי המגירה מסתירים אותם)




תמונה 2: המגירה סגורה, הטאבים  לא מוסתרים - הידית של המגירה בולטת בתחתית התמונה.




הקבצים

נשתמש בשני קבצים להגדרת layout:
  • קובץ ה-layout הראשי - main.xml.
  • קובץ layout עבור המגירה -  draw,xml.
הקוד עצמו ממומש כ-class יחיד. 
העובדה המעניינת והמיוחדת כאן היא שמדובר למעשה בשתי תצוגות, ובכל זאת הן שייכות לאותה Activity.
נציג את תוכן שלושת הקבצים הנ"ל, נתחיל עם קבצי ה-layout/

1. קובץ ה-layout של המגירה:
drawer.xml

  1. <?xml version="1.0" encoding="utf-8" ?>
  2.              <FrameLayout
  3.               android:layout_width="fill_parent"
  4.               android:layout_height="fill_parent"
  5.               xmlns:android="http://schemas.android.com/apk/res/android"
  6.               android:id="@+id/frameLayout">
  7.               <SlidingDrawer
  8.                   android:layout_height="wrap_content"
  9.                 android:handle="@+id/handle"
  10.                 android:content="@+id/content"
  11.                  android:id="@+id/slide"
  12.                  android:orientation="vertical"
  13.                 android:layout_width="fill_parent">
  14.                  <ImageView
  15.                      android:layout_width="wrap_content"
  16.                     android:layout_height="wrap_content"
  17.                     android:id="@id/handle"
  18.                     android:src="@drawable/tray_handle_normal" />
  19.                     <LinearLayout android:layout_width="fill_parent"
  20.                         android:layout_height="wrap_content"
  21.                         android:orientation="vertical"
  22.                         android:id="@id/content">
  23.                         <Button android:text="Add Tab"
  24.                             android:id="@+id/button_add_tab"
  25.                             android:layout_width="fill_parent"
  26.                             android:layout_height="wrap_content" />
  27.                             <Button android:text="Clear Tabs"
  28.                                 android:id="@+id/button_clear_tabs"
  29.                                 android:layout_width="fill_parent"
  30.                                 android:layout_height="wrap_content"/>
  31.                     </LinearLayout>
  32.                 </SlidingDrawer>
  33.                </FrameLayout>


ה-layout  כולל את האלמנטים הבאים:
  • FrameLayout (שורה 2)- זהו קונטיינר שמחזיק את כל האלמנטים האחרים מתחתיו - האב של כולם. היה ניתן להשתמש גם ב-RelativeLayout וגם ב-LinearLayout ללא כל הבדל.
  • מתחתיו בן יחיד: SlidingDrawer (שורה 7), שמגדיר כמובן את אובייקט ה-drawer.
  • ל-SlidingDrawer שני בנים:
    • ImageView (שורה 14) שמגדיר את הידית של המגירה. ה-id שלו חייב להיות בשם handle, והוא כולל את ה-icon של "ידית המגירה", במקרה הנ"ל התמונה נמצאת בקובץ tray_handle_normal.png (שורה 18).
    • LinearLayout (שורה 19) שמכיל את כל הווידגטים שבתוך המגירה. במקרה הנ"ל, שני הכפתורים.
2. קובץ ה-layout הראשי:
main.xml
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <TabHost xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:id="@+id/tabhost"
  4.     android:layout_width="fill_parent"
  5.     android:layout_height="fill_parent">
  6.         <TabWidget android:id="@android:id/tabs"
  7.             android:layout_width="fill_parent"
  8.             android:layout_height="wrap_content"
  9.         />
  10.         <FrameLayout android:id="@android:id/tabcontent"
  11.             android:layout_width="fill_parent"
  12.             android:layout_height="fill_parent">
  13.                 </FrameLayout>
  14. </TabHost>
זה ה-layout של התצוגה הראשית -"מתחת" למגירה.
הקובץ נלקח מהדוגמא השניה שהוצגה בפוסט של הטאבים. (הרעיון הרי הוא להוסיף את המגירה כשכבה מעל).
יש כאן שלושה קונטיינרים:
TabHost (שורה 2) שעוטף הכל: את הכפתורים ואת תוכנם, כך שיש לו שני בנים:
TabWidget (שורה 6) האחראי על תצוגת הכפתורים
FrameLayout (שורה 10) המכיל את התוכן של הטאבים. (זה הקונטיינר המתאים, היות שהוא מחזיק את האלמנטים שמתחתיו אחד על גבי השני).
לא מוגדר כאן שום ווידג'ט - הם יווצרו באופן דינאמי.

3. קובץ הג'אווה 
MySlidingDrawer.java





  1. public class MySlidingDrawer extends Activity {
  2.     TabHost tabs;
  3.     public void onCreate(Bundle savedInstanceState) {
  4.         super.onCreate(savedInstanceState);
  5.         setContentView(R.layout.main);
  6.         final TabHost tabs=(TabHost)findViewById(R.id.tabhost);
  7.         tabs.setup();
  8.      
  9.         View inflatedSlidingDrawerLayout = getLayoutInflater().inflate(R.layout.drawer, null);
  10.         WindowManager.LayoutParams params = getWindow().getAttributes();
  11.         getWindow().addContentView(inflatedSlidingDrawerLayout, params);
  12.    
  13.         Button buttonAddTAb=(Button)findViewById(R.id.button_add_tab);
  14.         Button buttonClearTabs=(Button)findViewById(R.id.button_clear_tabs);
  15.          buttonAddTAb.setOnClickListener(new View.OnClickListener() {
  16.             public void onClick(View view) {
  17.                 TabHost.TabSpec spec=tabs.newTabSpec("tag1");
  18.                
  19.                 spec.setContent(new TabHost.TabContentFactory() {
  20.                     public View createTabContent(String tag) {
  21.                         return(new AnalogClock(MySlidingDrawer.this));
  22.                     }
  23.                 });
  24.                 spec.setIndicator("Clock");
  25.                 tabs.addTab(spec);
  26.             }
  27.         });
  28.         buttonClearTabs.setOnClickListener(new View.OnClickListener() {
  29.             public void onClick(View view) {
  30.                 tabs.clearAllTabs();
  31.             }
  32.         });
  33.   }
  34. }

התוכנית בנויה משלושה חלקים:
חלק 1 (שורות 3-8), מטפל בבניית התצוגה של המסך הראשי. קטע זה מבוסס על הדוגמא שהוצגה בפוסט של הטאבים.
לא נדון בו כאן מעבר לכך.
חלק 2 (שורות 10-12) מטפל ביצירת התצוגה של ה-Drawer.
נעתיק אותו לכאן:

  1.         View inflatedSlidingDrawerLayout = getLayoutInflater().inflate(R.layout.drawer, null);
  2.         WindowManager.LayoutParams params = getWindow().getAttributes();
  3.         getWindow().addContentView(inflatedSlidingDrawerLayout, params);

קטע קוד זה פותח לתצוגה (inflate) את הקובץ drawer.xml אותו הצגנו למעלה.
התהליך נעשה בשלושה שלבים \ שלוש שורות:
שורה 1. שליפת ה-layout מתוך קובץ ה-layout של ה-drawer.
שורה 2: שליפת הפרמטרים של מסך התצוגה הראשי- אורך ורוחב.
שורה 3: הוספת ה-content view של המגירה ל-Activity, עם הפרמטרים של המסך הראשי.


חלק 3 (שורות 14-33) מטפל ביצירת ומחיקת הטאבים. התהליכים הללו מופעלים ע"י שני הכפתורים, כך שהם ממומשים במסגרת ה-callback של שני הכפתורים: כפתור ה-add וכפתור ה-clear.
בשורות 14 ו-15 מיוצרים האובייקטים מסוג Button, ואחכ" נעשית בניית ה-callbacks, תחילה זה של כפתור ה-  buttonAddTAb. נעתיק את הקטע לכאן:



  1.    buttonAddTAb.setOnClickListener(new View.OnClickListener() {
  2.             public void onClick(View view) {
  3.                 TabHost.TabSpec spec=tabs.newTabSpec("tag1");
  4.                
  5.                 spec.setContent(new TabHost.TabContentFactory() {
  6.                     public View createTabContent(String tag) {
  7.                         return(new AnalogClock(MySlidingDrawer.this));
  8.                     }
  9.                 });
  10.                 spec.setIndicator("Clock");
  11.                 tabs.addTab(spec);
  12.             }
  13.         });


שורה 1: התהליך המוכר של יצירת ה-interface מסוג OnClickListener באופן אנונימי.
שורה 2: המתודה onClick.
בתוך ה-onClick מיוצר טאב מסוג AnalogClock, באופן שתואר באותו פוסט מפורסם:
שורה 3: יצירת אובייקט ה-spec שדרכו מקנפגים את הפרמטרים של הטאב.
שורה 5: המתודה setContent באמצאותה  מגדירים את תוכן הטאב. 
היות שתוכנו לא מוגדר ב-xml משתמשים  ב-TabContentFactory בו אנו מגדירים את המתודה createTabContent.
בתוך מתודה (שורה 7) זו יוצרים את תוכן הטאב שהוא השעון האנלוגי.
שורות 10: מוסיפה את הכותרת של הטאב.
שורה 11: מוסיפה טאב חדש, כשהפרמטר של הפונקציה הוא spec - שמכיל את הפרמטרים הדרושים.

ה-callback של הכפתור השני, די דומה אך קצר יותר: (שורות 29-33 בקטע הקוד המקורי):

  1.   buttonClearTabs.setOnClickListener(new View.OnClickListener() {
  2.             public void onClick(View view) {
  3.                 tabs.clearAllTabs();
  4.             }
  5.         });
בתוך ה-onClick של הכפתור מופעלת המתודה clearAllTabs.





תגובה 1:

  1. משום מה לא ניתן להריץ, זה כותב שחסר Shared Library.

    השבמחק