יום ראשון, דצמבר 12

Gallery גלרית תמונות

ה-Gallery הוא view שמציג תמונות בגלילה אופקית.
אמנם מדובר ב"עוד view" ואכן כבר ראינו כמה וכמה, אבל זו הזדמנות להכיר אלמנטים חדשים, די מעניינים, שניתקל בהם בעתיד גם בנסיבות אחרות. מדובר בבניית Adapters. ניתקלנו ב-Adapters כבר מספר פעמים בעבר, בין השאר בהקשר של ListView: זהו ה-class המחבר בין הנתונים לבין ה-view. החידוש כאן הוא שבעוד בפעמים הקודמות יכולנו להשתמש ב-Adapter מוכן, הפעם נצטרך לבנות אותו בעצמנו. וזה יהיה ה"בשר" של דוגמא הזאת.

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

כמנהגנו בד"כ, נתחיל בסוף ונראה את ה-UI. לאחר מכן נכנס לקוד.

תמונה מס 1: Gallery
בתמונה הנ"ל שני אובייקטים של view: ה-Gallery ומתחתיו TextView.

נצלול פנימה:
נתחיל עם קובץ ה-layout:
שם הקובץ: main.xml והנה תוכנו:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:orientation="vertical"
  4.     android:layout_width="fill_parent"
  5.     android:layout_height="fill_parent"
  6.     >
  7. <Gallery
  8.     android:id="@+id/gallery"
  9.     android:layout_width="fill_parent"
  10.     android:layout_height="wrap_content"
  11. />
  12. <TextView
  13.     android:id="@+id/text_view_image"
  14.     android:layout_width="wrap_content"
  15.     android:layout_height="wrap_content"
  16.     android:textSize="30sp"
  17.     android:paddingLeft="50dp"
  18.     android:paddingTop="40dp"
  19.     >
  20.     </TextView>
  21.     </LinearLayout>
   

כפי שניתן היה לצפות על סמך תמונת ה-UI, יש כאן בסה"כ שני אלמנטים מסודרים בתוך LinearLayout: הראשון  מסוג Gallery והשני TextView, להכנסת כותרת בתחתית התמונה.
אגב, שימו לב לפרמטר ה- textSize בשורה 16. הוא משתמש ביחידות sp -- Scale Independent Pixels, שגודלה תלוי בצפיפות הפיקסלים ובגודל ה-פונט (גופן?) שנבחר.
ה-layout אכן טריוויאלי, אז נעבור מיד לג'אווה. נציג תחילה את  ה-class בשלמותו:



  1. public class MyGallery extends Activity {
  2.     private Integer[] mImageIds = {
  3.             R.drawable.p1,
  4.             R.drawable.p2,
  5.             R.drawable.p3,
  6.             R.drawable.p4,
  7.             R.drawable.p5,
  8.             R.drawable.p6,
  9.             R.drawable.p7,
  10.             R.drawable.p11,
  11.             R.drawable.p12,
  12.             R.drawable.p13,
  13.             R.drawable.p14,
  14.             R.drawable.p15,
  15.             R.drawable.p16,
  16.             R.drawable.p17,
  17.             R.drawable.p18       
  18.     };
  19.     TextView textViewImage;
  20.     /** Called when the activity is first created. */
  21.       @Override
  22.     public void onCreate(Bundle savedInstanceState) {
  23.            super.onCreate(savedInstanceState);
  24.             setContentView(R.layout.main);
  25.             textViewImage = (TextView)findViewById(R.id.text_view_image);
  26.             Gallery g = (Gallery) findViewById(R.id.gallery);
  27.             g.setAdapter(new ImageAdapter(this));
  28.             g.setOnItemClickListener(new OnItemClickListener() {
  29.                 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
  30.                    textViewImage.setText("Picture No "+position );
  31.                 }
  32.             });
  33.    
  34.         }    
  35.  
  36.     public class ImageAdapter extends BaseAdapter {
  37.         int mGalleryItemBackground;
  38.         private Context mContext;
  39.         public ImageAdapter(Context c) {
  40.             mContext = c;
  41.             TypedArray a = obtainStyledAttributes(R.styleable.HelloGallery);
  42.             mGalleryItemBackground = a.getResourceId(
  43.                     R.styleable.HelloGallery_android_galleryItemBackground, 0);
  44.             a.recycle();
  45.         }
  46.         public int getCount() {
  47.             return mImageIds.length;
  48.         }
  49.         public Object getItem(int position) {
  50.             return position;
  51.         }
  52.         public long getItemId(int position) {
  53.             return position;
  54.         }
  55.         public View getView(int position, View convertView, ViewGroup parent) {
  56.             ImageView i = new ImageView(mContext);
  57.             i.setImageResource(mImageIds[position]);
  58.             i.setLayoutParams(new Gallery.LayoutParams(150, 100));
  59.             i.setScaleType(ImageView.ScaleType.FIT_XY);
  60.             i.setBackgroundResource(mGalleryItemBackground);
  61.             return i;
  62.         }
  63.     }


ננתח את הקוד הנ"ל בחלקים:

תחילה מערך המצביעים על התמונות: סדרת התמונות של ה-Gallery הושמה בספריית drawable. שורות 2-18 מחזיקות את מערך ה-id  שמצביעים על התמונות:
  1.   private Integer[] mImageIds = {
  2.             R.drawable.p1,
  3.             R.drawable.p2,
  4.             R.drawable.p3,
  5.             R.drawable.p4,
  6.             R.drawable.p5,
  7.             R.drawable.p6,
  8.             R.drawable.p7,
  9.             R.drawable.p11,
  10.             R.drawable.p12,
  11.             R.drawable.p13,
  12.             R.drawable.p14,
  13.             R.drawable.p15,
  14.             R.drawable.p16,
  15.             R.drawable.p17,
  16.             R.drawable.p18       
  17.     };

המתודה הראשונה  onCreate:

  1.     public void onCreate(Bundle savedInstanceState) {
  2.            super.onCreate(savedInstanceState);
  3.             setContentView(R.layout.main);
  4.             textViewImage = (TextView)findViewById(R.id.text_view_image);
  5.             Gallery g = (Gallery) findViewById(R.id.gallery);
  6.             g.setAdapter(new ImageAdapter(this));
  7.             g.setOnItemClickListener(new OnItemClickListener() {
  8.                 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
  9.                    textViewImage.setText("Picture No "+position );
  10.                 }
  11.             });
  12.    
  13.         }   




שורות 2-5 סטנדרטיות לגמרי. 
בשורה 6, בניית אובייקט מסוג Gallery, תוך שליפתו מה-layout.
שורה 7 מתחילה להיות מעניינת:

g.setAdapter(new ImageAdapter(this));

כפי שציינתי כבר למעלה, מדובר בפעולה שגרתית של הכנסת Adapter שמחבר בין הנתונים לבין ה-view. עד עתה השתמשנו ב-Adapter מוכן, בד"כ זה היה  - ArrayAdapter. הפעם נצטרך לבנות את האובייקט -Adapter.
האובייקט ImageAdapter  שהוא subclass של BaseAdapter  מופיע בקוד למעלה החל משורה 36.הנה הוא:
  1.   public class ImageAdapter extends BaseAdapter {
  2.         int mGalleryItemBackground;
  3.         private Context mContext;
  4.         public ImageAdapter(Context c) {
  5.             mContext = c;
  6.             TypedArray a = obtainStyledAttributes(R.styleable.HelloGallery);
  7.             mGalleryItemBackground = a.getResourceId(
  8.                     R.styleable.HelloGallery_android_galleryItemBackground, 0);
  9.             a.recycle();
  10.         }
  11.         public int getCount() {
  12.             return mImageIds.length;
  13.         }
  14.         public Object getItem(int position) {
  15.             return position;
  16.         }
  17.         public long getItemId(int position) {
  18.             return position;
  19.         }
  20.         public View getView(int position, View convertView, ViewGroup parent) {
  21.             ImageView i = new ImageView(mContext);
  22.             i.setImageResource(mImageIds[position]);
  23.             i.setLayoutParams(new Gallery.LayoutParams(150, 100));
  24.             i.setScaleType(ImageView.ScaleType.FIT_XY);
  25.             i.setBackgroundResource(mGalleryItemBackground);
  26.             return i;
  27.         }
  28.     }


בבניית ה-class הנ"ל חייבים לממש 4 מתודות, בנוסף ל-constructor.
נתחיל עם ה-constructor:
  1. public ImageAdapter(Context c) {
  2.             mContext = c;
  3.             TypedArray a = obtainStyledAttributes(R.styleable.HelloGallery);
  4.             mGalleryItemBackground = a.getResourceId( R.styleable.HelloGallery_android_galleryItemBackground, 0);
  5.             a.recycle();
  6.         }

ה-constructor מבצע שתי משימות:
שורה 2: שמירת ה-context לשימושים עתידיים באובייקט.
שורות 3-6: שליפת הפרמטר המאפיין את הרקע של ה-Gallery. הסבר: אנו מתכוננים לקבוע מספר פרמטרים המאפיינים את ה-Gallery view. בין השאר, נקבע את מאפיין הרקע.  המאפיין הזה (או במילים אחרות  ה-Attribute הזה) נשלף מתוך הקובץ attrs.xml שנמצא ב-res/values. הנה תוכנו:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3.     <declare-styleable name="HelloGallery">
  4.         <attr name="android:galleryItemBackground" />
  5.     </declare-styleable>
  6. </resources>
בקובץ ה-attrs.xml הנ"ל ישנו אלמנט style בלבד - HelloGallery ומוגדר לו המאפיין galleryItemBackground. המאפיין הזה הוא "מאפיין מדף" שמסופק ע"י אנדרואיד וקובע את רקע התמונה.

חזרה ל-constructor בג'אווה:
נבצע שליפת מאפיין ה-background מתוך ה-xml בשלוש שורות:
שורה 3 משתמשת במתודה obtainStyledAttributes ושולפת את כל מאפייני ה-style מתוך ה-attr.xml.
 שורה 4 שולפת מתוך האובייקט הנ"ל את מאפיין ה-background בעזרת המתודה getResourceId. הפרמטר השני במתודה זו, הוא הערך שיש להחזיר אם ה-Attrivute  לא נמצא.
שורה 5 מחזירה את אובייקט ה StyleAttribute לשימוש חוזר.

 זה היה ה-constructor.  ה-background בידינו.



המתודה הבאה ב-class ה-ImageAdapter עליה נתעכב היא ה-getView - שורה 20-27 למעלה ושוב כאן למען הנוחות:

  1.         public View getView(int position, View convertView, ViewGroup parent) {
  2.             ImageView i = new ImageView(mContext);
  3.             i.setImageResource(mImageIds[position]);
  4.             i.setLayoutParams(new Gallery.LayoutParams(150, 100));
  5.             i.setScaleType(ImageView.ScaleType.FIT_XY);
  6.             i.setBackgroundResource(mGalleryItemBackground);
  7.             return i;
  8.         }

המתודה תופעל עבור כל תמונה שתוצג ב-Gallery. היא מגדירה את ה-view בעזרת פקודות ג'אווה (ולא עם xml כמו שאנו רגילים ומעדיפים בד"כ).
בשורה 6, מוגדירים את ה-background של ה-view בעזרת  הערך ששלפנו ב-constructor. אגב, אפשר לוותר על הכנסת ה-background. התצוגה תהיה פשוט פחות מוצלחת.

סיימנו עם ה-class ImageAdapter.

 נחזור לרגע ל-onCreate של ה-class הראשי. דילגנו שם על קטע קטן: ה-callback של ה-Gallery שמופעל עם לחיצה עליו. הנה הקטע:

  1.      g.setOnItemClickListener(new OnItemClickListener() {
  2.                 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
  3.                    textViewImage.setText("Picture No "+position );
  4.                 }
  5.             });

את הפרוצדורה של הכנסת ה-listener אנו מכירים היטב.  חתיכת הבשר היחידה כאן נמצאת בשורה 3: הדפסת טקסט עם מספר תמונה בהתאם לתמונה שהוקלקה.












3 תגובות:

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

    השבמחק
  2. אני לא מצליח להבין מתי ואיך מתבצעת הקריאה לפונקציה
    GetView
    אשמח לקבל הסבר
    תודה

    השבמחק
  3. הפונקציה getView נקראת אוטומטית ביצירת ה - ADAPTER ב MAINACTIVITY

    השבמחק