UI কাস্টমাইজেশন

Media3 একটি ডিফল্ট PlayerView প্রদান করে যা কিছু কাস্টমাইজেশন বিকল্প প্রদান করে।

ড্রয়েবল ওভাররাইড করুন

PlayerView প্লেব্যাক কন্ট্রোল ও প্রোগ্রেস বার দেখানোর জন্য PlayerControlView ব্যবহার করে। PlayerControlView-এর ব্যবহার করা ড্রয়েবল আপনার অ্যাপ্লিকেশনে একই নামে সংজ্ঞায়িত ড্রয়েবল দ্বারা ওভাররাইড করা যেতে পারে। ওভাররাইড করা যেতে পারে এমন কন্ট্রোল ড্রয়েবলের তালিকার জন্য PlayerControlView ডকুমেন্টেশন দেখুন।

আরও কাস্টমাইজেশনের জন্য, অ্যাপ ডেভেলপারদের নিজস্ব UI কম্পোনেন্ট প্রয়োগ করতে হবে। তবে, শুরু করার জন্য এখানে কিছু পেশাদার পদ্ধতি দেওয়া হল।

পেশাদার পদ্ধতি

Media3 Player-এর সাথে কানেক্ট করে এমন মিডিয়া UI প্রয়োগ করার সময় (যেমন ExoPlayer, MediaController বা কাস্টম Player প্রয়োগ), অ্যাপকে সেরা UI অভিজ্ঞতা পেতে এইসব পেশাদার পদ্ধতি অনুসরণ করার পরামর্শ দেওয়া হয়।

চালানোর / পজ করার বোতাম

চালানো বা পজ করার বোতামটি সরাসরি কোনও একটি প্লেয়ারের অবস্থার সাথে সম্পর্কিত নয়। যেমন, প্লেব্যাক শেষ বা ব্যর্থ হওয়ার পরে ব্যবহারকারীকে সেটি আবার চালু করার সুবিধা দিতে হবে, এমনকি প্লেয়ার পজ করা না থাকলেও।

প্রয়োগ করার পদ্ধতি সহজ করতে, Media3 কোন বোতাম দেখাতে হবে (Util.shouldShowPlayButton) এবং বোতাম প্রেস করা কীভাবে ম্যানেজ করতে হবে (Util.handlePlayPauseButtonAction) তা নির্ধারণ করার জন্য util পদ্ধতি প্রদান করে:

Kotlin

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

জাভা

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

স্টেট আপডেট শুনুন

UI কম্পোনেন্টে Player.Listener যোগ করতে হবে যাতে স্ট্যাটাস পরিবর্তন সম্পর্কে জানানো যায় এবং সেই অনুযায়ী UI আপডেট করা যায়। বিস্তারিত জানতে প্লেব্যাক ইভেন্ট শুনুন দেখুন।

UI রিফ্রেশ করা ব্যয়বহুল হতে পারে এবং একাধিক প্লেয়ার ইভেন্ট প্রায়ই একসাথে আসে। অল্প সময়ের মধ্যে UI-কে ঘন ঘন রিফ্রেশ করা এড়াতে, সাধারণত শুধু onEvents-এ পরিবর্তন লক্ষ্য করা এবং সেখান থেকে UI আপডেট ট্রিগার করা ভালো:

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()
      }
    }
  }
)

জাভা

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();
        }
      }
    });

উপলভ্য কমান্ড ম্যানেজ করা

সাধারণ উদ্দেশ্যে ব্যবহৃত UI কম্পোনেন্ট যা বিভিন্ন Player-এর সাথে কাজ করতে পারে সেটিকে উপলভ্য প্লেয়ার কমান্ড চেক করতে হবে যাতে বোতাম দেখানো বা লুকানো যায় এবং কাজ করে না এমন পদ্ধতি কল করা এড়ানো যায়:

Kotlin

nextButton.isEnabled = player.isCommandAvailable(COMMAND_SEEK_TO_NEXT)

জাভা

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

প্রথম ফ্রেমের শাটার ও ছবি ডিসপ্লে

কোনও UI কম্পোনেন্ট ভিডিও বা ছবি দেখালে, আসল প্রথম ফ্রেম বা ছবি উপলভ্য না হওয়া পর্যন্ত এটি সাধারণত একটি প্লেসহোল্ডার শাটার ভিউ ব্যবহার করে। এছাড়াও, মিক্সড ভিডিও ও ছবি প্লেব্যাকের জন্য সঠিক সময়ে ছবির ভিউ লুকানো ও দেখানোর প্রয়োজন হয়।

এইসব আপডেট ম্যানেজ করার সাধারণ প্যাটার্ন হল, বেছে নেওয়া ট্র্যাকে কোনও পরিবর্তন হলে 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.
}

জাভা

@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.
}

স্ক্রাবার ও সিক বার কন্ট্রোল

ExoPlayer ইনস্ট্যান্সের সাথে কানেক্ট করা কাস্টম সিক বার বা স্ক্রাবার প্রয়োগ করার সময়, ব্যবহারকারী স্ক্রাবার থাম্ব টেনে আনা শুরু করলে ExoPlayer.setScrubbingModeEnabled-এর মাধ্যমে স্ক্রাবিং মোড চালু করুন, টেনে আনার সময় Player.seekTo আপডেট পাঠান এবং জেসচার শেষ বা বাতিল হয়ে গেলে স্ক্রাবিং মোড বন্ধ করে দিন। সাধারণ প্লেব্যাক বা একবার খোঁজার সময় স্ক্রাবিং মোড চালু রাখবেন না, কারণ এটি প্লেব্যাক বন্ধ করে দেয় এবং ডিকোডার রিসোর্সের ব্যবহার বাড়িয়ে দেয়।

এছাড়াও, আমরা সাজেস্ট করি যে আপনি dp-ভিত্তিক ফ্র্যাকশনাল সিক টলারেন্স কনফিগার করুন। এটি আপনার UI স্ক্রাবার প্রস্থ থেকে ScrubbingModeParameters.Builder.setFractionalSeekTolerance ব্যবহার করে করা হয় যাতে ExoPlayer স্ক্রাবারের টাচ প্রিসিশনের মধ্যে কাছাকাছি থাকা কীফ্রেমে স্ন্যাপ করতে পারে। কোডের উদাহরণ ও বিবরণের জন্য, স্ক্রাবিং মোড দেখুন।