התאמות אישיות של ממשק המשתמש

ב-Media3 יש PlayerView שמוגדר כברירת מחדל ומספק כמה אפשרויות להתאמה אישית.

שינוי רכיבים גרפיים

‫PlayerView משתמש ב-PlayerControlView כדי להציג את רכיבי ה-UI להפעלה ואת סרגל ההתקדמות. אפשר לשנות את רכיבי ה-drawable שבהם נעשה שימוש ב-PlayerControlView באמצעות רכיבי drawable עם אותם שמות שמוגדרים באפליקציה. במסמכי התיעוד של PlayerControlView מפורטת רשימה של רכיבי drawable של פקדים שאפשר לשנות.

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

שיטות מומלצות

כשמטמיעים ממשק משתמש של מדיה שמתחבר ל-Media3 Player (לדוגמה, ExoPlayer,‏ MediaController או הטמעה מותאמת אישית של Player), מומלץ לפעול לפי השיטות המומלצות הבאות כדי ליהנות מחוויית ממשק המשתמש הטובה ביותר.

כפתור הפעלה והשהיה

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

כדי לפשט את ההטמעה, Media3 מספקת שיטות שירות שמאפשרות להחליט איזה כפתור להציג (Util.shouldShowPlayButton) ולטפל בלחיצות על כפתורים (Util.handlePlayPauseButtonAction):

Kotlin

val shouldShowPlayButton: Boolean = Util.shouldShowPlayButton(player)
playPauseButton.setImageDrawable(if (shouldShowPlayButton) playDrawable else pauseDrawable)
playPauseButton.setOnClickListener { Util.handlePlayPauseButtonAction(player) }

Java

boolean shouldShowPlayButton = Util.shouldShowPlayButton(player);
playPauseButton.setImageDrawable(shouldShowPlayButton ? playDrawable : pauseDrawable);
playPauseButton.setOnClickListener(view -> Util.handlePlayPauseButtonAction(player));

האזנה לעדכונים על המצב

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

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

Kotlin

player.addListener(
  object : Player.Listener {
    override fun onEvents(player: Player, events: Player.Events) {
      if (
        events.containsAny(
          Player.EVENT_PLAY_WHEN_READY_CHANGED,
          Player.EVENT_PLAYBACK_STATE_CHANGED,
          Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED,
        )
      ) {
        updatePlayPauseButton()
      }
      if (events.containsAny(Player.EVENT_REPEAT_MODE_CHANGED)) {
        updateRepeatModeButton()
      }
    }
  }
)

Java

player.addListener(
    new Player.Listener() {
      @Override
      public void onEvents(Player player, Player.Events events) {
        if (events.containsAny(
            Player.EVENT_PLAY_WHEN_READY_CHANGED,
            Player.EVENT_PLAYBACK_STATE_CHANGED,
            Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED)) {
          updatePlayPauseButton();
        }
        if (events.containsAny(Player.EVENT_REPEAT_MODE_CHANGED)) {
          updateRepeatModeButton();
        }
      }
    });

טיפול בפקודות זמינות

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

Kotlin

nextButton.isEnabled = player.isCommandAvailable(COMMAND_SEEK_TO_NEXT)

Java

nextButton.setEnabled(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT));

תריס הפריים הראשון ותצוגת התמונה

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

דפוס נפוץ לטיפול בעדכונים האלה הוא להאזין ל-Player.Listener.onEvents() כדי לזהות שינויים בטראקים שנבחרו (EVENT_TRACKS_CHANGED) וכשהפריים הראשון של הסרטון עבר עיבוד (EVENT_RENDERED_FIRST_FRAME), וגם ל-ImageOutput.onImageAvailable() כדי לזהות מתי תמונה חדשה זמינה:

Kotlin

override fun onEvents(player: Player, events: Player.Events) {
  if (events.contains(Player.EVENT_TRACKS_CHANGED)) {
    // If no video or image track: show shutter, hide image view.
    // Otherwise: do nothing to wait for first frame or image.
  }
  if (events.contains(Player.EVENT_RENDERED_FIRST_FRAME)) {
    // Hide shutter, hide image view.
  }
}

override fun onImageAvailable(presentationTimeUs: Long, bitmap: Bitmap) {
  // Show shutter, set image and show image view.
}

Java

@Override
public void onEvents(Player player, Player.Events events) {
  if (events.contains(Player.EVENT_TRACKS_CHANGED)) {
    // If no video or image track: show shutter, hide image view.
    // Otherwise: do nothing to wait for first frame or image.
  }
  if (events.contains(Player.EVENT_RENDERED_FIRST_FRAME)) {
    // Hide shutter, hide image view.
  }
}

@Override
public void onImageAvailable(long presentationTimeUs, Bitmap bitmap) {
  // Show shutter, set image and show image view.
}