משאבים ותמונות


פורסם ב 18/06/2010 ע"י האקדמיה לפיתוח לאנדרואיד

אנדי שוב שלום לכל המצטרפים החדשים והקבועים לאקדמיה ללימודי תיכנות באנדרואיד!

השבוע אנחנו הולכים ללמוד מה הם משאבים (Resources), ונשתמש בהם כדי להחיות באופן משמעותי את משחק הפינג פונג שלנו, על ידי שילוב של גראפיקה משופרת.

משאבים (Resources)

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

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

אנדיתודה פרופסור.
כרגיל, מרחיב את אופקינו

אה, ועוד דבר מעניין ששכחתי לציין - כל קבצי ה- resource הלא בינריים נשמרים בצורה של קבצי XML. בזמן שאנו מקמפלים את התוכנה שלנו, רכיב בשם Resource Compiler דואג לכווץ לנו בצורה יעילה את כל המשאבים וליצור לנו מחלקה מיוחד (בשם R) אשר בעזרתה אנו יכולים להגיע למשאבים האלה.

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

איפה נמצאים המשאבים?

היכנסו ל Eclipse ותוך חלון ה Package Explorer בצד ימין, שימו לב שיש ספריה בשם res

מיקום המשאבים (Resource) בתוך ה Package Explorer

בתוך סיפריה זאת אנו מרכזים את כל המשאבים שלנו, כאשר אנו מחלקים אותם בצורה הגיונית בתוך תתי ספריות.
לדוגמא, את התמונות שלנו אנו שמים בתוך ספרית drawable, כאשר אם יש לנו סוגי תמונות שונים לרזולוציות שונות, אנו מחלקים אותם לתוך ספריות שונות כגון drawable-hdpi (high DPI) המחזיק תמונות באיכות גבוהה, drawable-mdpi (medium, DPI( המחזיק תמונות באיכות בינונית וכדומה.

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

איך מוסיפים משאבים?

בואו ונוסיף קצת משאבים למשחק הפינג פונג שלנו שיחיו אותו קצת.
אנו נוסיף 2 תמונות אשר ישמשו אותנו במשחק שלנו ויהפכו את הפינג פונג שלנו ל"פינג פונג חלל"!

תמונת רקע

תמונת רקע של "היקום"

ותמונת רקע

את התמונה פשוט הורידו ותגררו לתוך הספריה res/drawable בתוך האקליפס.
למה קראתי לתמונה universe1 ? בגלל שאולי בעתיד נרצה להוסיף תמונות נוספות. אולי לא. תלוי בכם.

תמונה "אסטרואיד" שתחליף את הכדור המשעמם שלנו

תמונת האסטרואיד שתחליף את הכדור

באותה צורה, פשוט תיגררו את התמונה לתוך res/drawable.

עכשיו ה Package Explorer שלכם צריך להיראות כך:

שימו לב! חשוב לתת את השמות rock.png לסלע ו universe1.jpg לתמונת הרקע כדי שקטעי הקוד הכתובים בשיעור זה יתאימו לקוד שלכם.

שילוב המשאבים בתוך המשחק

כפי שציינתי מקודם, ה- Resource Compiler דואג לכווץ לנו את המשאבים וליצור לנו, אוטומטית, מחלקה בשם R בה אנחנו יכולים להשתמש כדי לשלב את המשאבים שלנו. בואו נראה איך זה עובד

שלב א' – החלפת הכדור

היכנסו לתוך מחלקת BallView, והחליפו את הקוד בתוך המתודה onDraw לקוד הבא:

[sourcecode language="java"]
//do the actual drawing itself
@Override
public void onDraw(Canvas canvas)
{
Bitmap ballBitmap;
ballBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rock);

canvas.drawBitmap(ballBitmap, theBall.getX(), theBall.getY(), null);
}
[/sourcecode]

הסבר על הקוד:
שורה 5: הגדרנו משתנה חדש בשם ballBitmap מסוג Bitmap, אשר בתוכו נחזיק את תמונת הכדור שלנו.
שורה 6: קראנו למתודה decodeResource במחלקת BitmapFactory על מנת שתבצע "פיענוח" של התמונה שלנו מתוך המאשב שלנו לתוך ballBitmap.
המתודה decodeResource מקבלת 2 פרמטרים. הראשון הוא "חבילת המשאבים" שלנו, והשני הוא קוד המשאב בו אנחנו רוצים להשתמש.
מכיוון שה Resource Compiler יוצר לנו אוטומטית מחלקה בשם R המכילה את הקודים לכל המשאבים שלנו, נוכל להשתמש בה כדי להצביע על תמונת האסטרואיד שלנו.
מכאן שהפרמטרים שהעברנו למתודה decodeResource הם:
1. getResources, שזאת בעצם מתודה נוספת המחזירה לנו את "חבילת המשאבים" שלנו.
2. R.drawable.rock, המצביע למשאב ה rock שהוספנו. שימו לב שבכך שיצרנו ספריה בשם drawable והוספנו משאב בשם rock, הדבר מתבטה באופן ישיר בתוך מחלקת R.

שורה 8: קריאה למתודה drawBitmap במחלקה canvas כדי לצייר את התמונה שלנו. בפרמטרים אנו מעבירים את ballBitmap בתור התמונה שיש לצייר. שימו לב שאין לנו יותר צורך להעביר את הפרמטר האחרון (Paint) ולכן פשוט החלפנו אותו בnull (כלומר, ב"כלום").

הריצו את המשחק וזה מה שאתם אמורים לקבל:

שהכדור זז, כמובן 🙂

שלב ב' – הוספת תמונת הרקע

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

במחלקת PingPongView נבצע את הפעולות הבאות:

1. נגדיר במחלקת PingPongView משתנה פרטי מסוג Bitmap בשם backgroundBitmap.
2. בבנאי המחלקה, נוסיף קריאה ל decodeResource ונכניס את התמונה ה"מפוענחת" ל backgroundBitmap.
3. במתודה onDraw, נצייר את תמונת הרקע לפני שאנחנו מציירים את הכדור והמחבט.

להלן קטע הקוד המעודכן של מחלקת PingPongView:

[sourcecode language="java"]
package iAndroid.pingPong;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.view.View;

public class PingPongView extends View
{
private BallView ballView; //ball view
private PaddleView paddleView; // paddle view

private Bitmap backgroundBitmap;

//initialize our view
public PingPongView(Context context)
{
super(context);

backgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.universe1);
}

//bind views
public void setViews(BallView ballView,PaddleView paddleView)
{
this.ballView = ballView;
this.paddleView = paddleView;
}

//draw the views
@Override
public void onDraw(Canvas canvas)
{
canvas.drawBitmap(this.backgroundBitmap, 1, 1, null);

// Drawing the ball
this.ballView.onDraw(canvas);

// Drawing the paddle
this.paddleView.onDraw(canvas);
}
}
[/sourcecode]

קטעי הקוד שנוספו הם:
שורה 14: הגדרה של backgroundBitmap.
שורה 21: ביצוע ה"פיענוח" של התמונה והכנסתה ל backgroundBitmap.
שורה 35: ציור תמונת הרקע לפני הקריאה ל onDraw האחרים.

והתוצאה?

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

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

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

להורדת קוד המשחק המלא ליחצו כאן -> Lesson 8 PingPong

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

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

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

אנדיטוב, כנראה שפיספסת פה איזה אסטרואיד במהלך השיעור
(או לרוע המזל, האסטרואיד פיספס אותך…).

זה הכל להיום, נתראה בשיעור הבא!

Share

האקדמיה לפיתוח לאנדרואיד

בואו ללמוד לפתח לאנדרואיד! אתם בטח זוכרים אותי בתור Andy מהבלוג הישן!

7 Comments

  1. גיבור
    19/06/2010 בשעה 14:51

    אתם בסדר אתם (:

  2. AndRoyD
    24/06/2010 בשעה 06:03

    חחחח אתם בסדר גמור אתם 😛
    מעולה מעולה אהבתי את השיעורים, מצפה לעוד!:)
    אנדי אתה תותח

  3. שמךnexus
    30/06/2010 בשעה 10:41

    הסברים מעולים תודה רבה לכם,אפשר ללמוד הרבה
    מצפים להמשך

  4. שיעור כ’ – הייי, מישהו שומע אותי? - האקדמיה ללימודי פיתוח באנדרואיד
    11/07/2010 בשעה 08:21

    […] של האפליקציה נשמור בתוך אזור המשאבים שלנו (עליו דיברנו בשיעור ט’). תחת הענף res, ניצור ספריה חדשה בשם raw (חשוב להקפיד על […]

  5. מבוא לXML ושימוש ברכיבים גרפיים - האקדמיה ללימודי פיתוח באנדרואיד
    06/08/2010 בשעה 23:29

    […] שנוצרו בעת יצירת הפרוייקט. הקובץ נחשב למשאב (ראו שיעור משאבים ותמונות), ולכן אנו נמצא אותו תחת ספריית reslayout בפרוייקט שלנו. […]

  6. אורן
    29/11/2010 בשעה 23:47

    בקריאה לציור תמונת הסלע בשביל הBallView נפלה טעות:
    drawBitmap מקבלת ערכי left top כעוגן לציור ובשיעור הקריאה היא עם ערכי x y של הכדור לפרמטרים אלו.
    זה מוביל לכך שהכדור מצוייר נמוך ממה שהוא "נמצא" לפי המשחק ולכן נראה כאילו הכדור נכנס לתוך המחבט.

  7. עופר שפירא
    17/03/2011 בשעה 14:07

    עוד דרך למימוש (כך אני עשיתי) היא לקרוא את הBITMAPים מהRESOURCE בקוד של PingPongGame ולהעביר את התמונה למחלקות BALL וPADDLE בקונסטרקטור. כך הBALL למשל יכול לחשב את הMAXWIDHT שלו על ידי החישובים:
    maxWidth = screenWidth – ballBitmap.getWidth();
    maxHeight = screenHeight – ballBitmap.getHeight();

השאר תגובה