我正在尝试在我的Exo媒体播放器中在顶部实现一个带有选框动画的TextView标题。 据我所知,我已经设置了所有必要的属性,它确实有效,但随着文本滚动,我得到一个奇怪的滞后/动画停止/重新启动。我怀疑它与焦点有关? 你怎么想,并且知道如何解决这个问题? 这是我的代码:
玩家活动:
public class MediaPlayerActivity extends Activity implements View.OnClickListener,
PlaybackControlView.VisibilityListener,
ExoPlayer.EventListener,
AdEvent.AdEventListener,
AdErrorEvent.AdErrorListener {
public static final String ACTION_VIEW = "cambium.com.il.sport5.action.VIEW";
public static final String EXTENSION_EXTRA = "extension";
public static final String ACTION_VIEW_LIST = "cambium.com.il.sport5.action.VIEW_LIST";
public static final String URI_LIST_EXTRA = "uri_list";
public static final String EXTENSION_LIST_EXTRA = "extension_list";
private static final String LOG_TAG = "MediaPlayer-ImaAds";
/**
* Constants
*/
private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
private static final CookieManager DEFAULT_COOKIE_MANAGER;
static {
DEFAULT_COOKIE_MANAGER = new CookieManager();
DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
}
/**
* General Obj
*/
private GeneralUtils mGeneralUtils;
private SimpleExoPlayerView simpleExoPlayerView;
private Handler mainHandler;
private DataSource.Factory mediaDataSourceFactory;
private SimpleExoPlayer player;
private DefaultTrackSelector trackSelector;
private TrackSelectionHelper trackSelectionHelper;
private Timeline.Window window;
private boolean isTimelineStatic;
private boolean playerNeedsSource;
private long playerPosition;
private int playerWindow;
private boolean shouldAutoPlay;
/**
* UI
*/
private View rootView;
private PlaybackControlView controlsRootView;
private Button retryButton;
private RelativeLayout mediaPlayerLoader;
private LinearLayout mTopBar;
private CustomTextView mVideoTitle;
private ImageButton mVideoBackBtn;
/**
* IMA ads
*/
/* Factory class for creating SDK objects */
private ImaSdkFactory mSdkFactory;
/* The AdsLoader instance exposes the requestAds method */
private AdsLoader mAdsLoader;
/* AdsManager exposes methods to control ad playback and listen to ad events */
private AdsManager mAdsManager;
/* Whether an ad is displayed */
private boolean mIsAdDisplayed;
/**
* =================
* Lifecycle methods
* =================
*/
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initActivity();
setContentView(R.layout.activity_media_player);
getViews();
setListeners();
createAdsLoader();
}
@Override
public void onNewIntent(Intent intent) {
releasePlayer();
isTimelineStatic = false;
setIntent(intent);
}
@Override
public void onResume() {
if (mAdsManager != null && mIsAdDisplayed) {
mAdsManager.resume();
} else if (player != null && player.getPlaybackState() == ExoPlayer.STATE_READY) {
player.setPlayWhenReady(true);
}
super.onResume();
}
@Override
public void onPause() {
if (mAdsManager != null && mIsAdDisplayed) {
mAdsManager.pause();
}
if (player != null)
player.setPlayWhenReady(false);
super.onPause();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
@Override
protected void onStop() {
releasePlayer();
window = null;
mediaDataSourceFactory = null;
mainHandler = null;
mSdkFactory = null;
mAdsLoader = null;
mAdsLoader = null;
isTimelineStatic = false;
finish();
super.onStop();
}
@Override
public void onBackPressed() {
super.onBackPressed();
}
/**
* =================
* Helper methods
* =================
*/
private void initActivity() {
mGeneralUtils = GeneralUtils.getInstance();
mediaDataSourceFactory = buildDataSourceFactory(true);
mainHandler = new Handler();
window = new Timeline.Window();
if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) {
CookieHandler.setDefault(DEFAULT_COOKIE_MANAGER);
}
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
private void getViews() {
rootView = findViewById(R.id.root);
controlsRootView = (PlaybackControlView) findViewById(R.id.controls_root);
retryButton = (Button) findViewById(R.id.retry_button);
simpleExoPlayerView = (SimpleExoPlayerView) findViewById(R.id.player_view);
mediaPlayerLoader = (RelativeLayout) findViewById(R.id.mediaPlayerLoader);
mTopBar = (LinearLayout) findViewById(R.id.topBar);
mVideoBackBtn = (ImageButton) findViewById(R.id.videoBackBtn);
mVideoTitle = (CustomTextView) findViewById(R.id.videoPlayerTitle);
mVideoTitle.setSelected(true);
mVideoTitle.setFocusable(true);
mVideoTitle.setFocusableInTouchMode(true);
}
private void setListeners() {
rootView.setOnClickListener(this);
simpleExoPlayerView = (SimpleExoPlayerView) findViewById(R.id.player_view);
simpleExoPlayerView.setControllerVisibilityListener(this);
simpleExoPlayerView.requestFocus();
retryButton.setOnClickListener(this);
mVideoBackBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onBackPressed();
}
});
}
private void showToast(int messageId) {
showToast(getString(messageId));
}
private void showToast(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
/**
* =================
* Exo player
* =================
*/
private void initPlayer() {
Intent intent = getIntent();
String action = intent.getAction();
if (player == null) {
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveVideoTrackSelection.Factory(BANDWIDTH_METER);
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
trackSelectionHelper = new TrackSelectionHelper(trackSelector, videoTrackSelectionFactory);
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, new DefaultLoadControl());
player.addListener(this);
simpleExoPlayerView.setPlayer(player);
if (isTimelineStatic) {
if (playerPosition == C.TIME_UNSET) {
player.seekToDefaultPosition(playerWindow);
} else {
player.seekTo(playerWindow, playerPosition);
}
}
player.setPlayWhenReady(shouldAutoPlay);
playerNeedsSource = true;
if (playerNeedsSource) {
Uri[] uris;
String[] extensions;
if (ACTION_VIEW.equals(action)) {
uris = new Uri[]{intent.getData()};
extensions = new String[]{intent.getStringExtra(EXTENSION_EXTRA)};
} else if (ACTION_VIEW_LIST.equals(action)) {
String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA);
uris = new Uri[uriStrings.length];
for (int i = 0; i < uriStrings.length; i++) {
uris[i] = Uri.parse(uriStrings[i]);
}
extensions = intent.getStringArrayExtra(EXTENSION_LIST_EXTRA);
if (extensions == null) {
extensions = new String[uriStrings.length];
}
} else {
showToast(getString(R.string.unexpected_intent_action, action));
return;
}
MediaSource[] mediaSources = new MediaSource[uris.length];
for (int i = 0; i < uris.length; i++) {
mediaSources[i] = buildMediaSource(uris[i], extensions[i]);
}
MediaSource mediaSource = mediaSources.length == 1 ? mediaSources[0]
: new ConcatenatingMediaSource(mediaSources);
player.prepare(mediaSource, !isTimelineStatic, !isTimelineStatic);
playerNeedsSource = false;
mGeneralUtils.zoomOutAnimation(mediaPlayerLoader);
updateButtonVisibilities();
}
}
}
private void releasePlayer() {
if (player != null) {
playerWindow = player.getCurrentWindowIndex();
playerPosition = C.TIME_UNSET;
Timeline timeline = player.getCurrentTimeline();
if (!timeline.isEmpty() && timeline.getWindow(playerWindow, window).isSeekable) {
playerPosition = player.getCurrentPosition();
}
player.release();
player = null;
trackSelector = null;
trackSelectionHelper = null;
}
}
private void updateButtonVisibilities() {
controlsRootView.removeAllViews();
retryButton.setVisibility(playerNeedsSource ? View.VISIBLE : View.GONE);
controlsRootView.addView(retryButton);
if (player == null) {
return;
}
}
private void showControls() {
controlsRootView.setVisibility(View.VISIBLE);
}
private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) {
return ((Sport5App) getApplication())
.buildDataSourceFactory(useBandwidthMeter ? BANDWIDTH_METER : null);
}
private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
: uri.getLastPathSegment());
switch (type) {
case C.TYPE_SS:
return new SsMediaSource(uri, buildDataSourceFactory(false),
new DefaultSsChunkSource.Factory(mediaDataSourceFactory), mainHandler, null);
case C.TYPE_DASH:
return new DashMediaSource(uri, buildDataSourceFactory(false),
new DefaultDashChunkSource.Factory(mediaDataSourceFactory), mainHandler, null);
case C.TYPE_HLS:
return new HlsMediaSource(uri, mediaDataSourceFactory, mainHandler, null);
case C.TYPE_OTHER:
return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(),
mainHandler, null);
default: {
throw new IllegalStateException("Unsupported type: " + type);
}
}
}
/**
* =================
* Listener methods
* =================
*/
@Override /* View.OnClickListener */
public void onClick(View view) {
if (view == retryButton) {
shouldAutoPlay = true;
initPlayer();
} else if (view.getParent() == controlsRootView) {
MappingTrackSelector.MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
if (mappedTrackInfo != null) {
trackSelectionHelper.showSelectionDialog(this, ((Button) view).getText(),
trackSelector.getCurrentMappedTrackInfo(), (int) view.getTag());
}
}
}
@Override /* PlaybackControlView.VisibilityListener */
public void onVisibilityChange(int visibility) {
controlsRootView.setVisibility(visibility);
mTopBar.setVisibility(visibility);
}
@Override /* ExoPlayer.EventListener */
public void onTimelineChanged(Timeline timeline, Object manifest) {
isTimelineStatic = !timeline.isEmpty()
&& !timeline.getWindow(timeline.getWindowCount() - 1, window).isDynamic;
}
@Override /* ExoPlayer.EventListener */
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
updateButtonVisibilities();
MappingTrackSelector.MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
if (mappedTrackInfo != null) {
if (mappedTrackInfo.getTrackTypeRendererSupport(C.TRACK_TYPE_VIDEO)
== MappingTrackSelector.MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
showToast(R.string.error_unsupported_video);
}
if (mappedTrackInfo.getTrackTypeRendererSupport(C.TRACK_TYPE_AUDIO)
== MappingTrackSelector.MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
showToast(R.string.error_unsupported_audio);
}
}
}
@Override /* ExoPlayer.EventListener */
public void onLoadingChanged(boolean isLoading) {
}
@Override /* ExoPlayer.EventListener */
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == ExoPlayer.STATE_ENDED) {
// Handle completed event for playing post-rolls.
if (mAdsLoader != null) {
mAdsLoader.contentComplete();
}
showControls();
finish();
}
if (playbackState == ExoPlayer.STATE_BUFFERING) {
// Handle completed event for playing post-rolls.
/*Toast.makeText(this, "STATE_BUFFERING", Toast.LENGTH_SHORT).show();*/
}
if (playbackState == ExoPlayer.STATE_IDLE) {
// Handle completed event for playing post-rolls.
/*Toast.makeText(this, "STATE_IDLE", Toast.LENGTH_SHORT).show();*/
}
if (playbackState == ExoPlayer.STATE_READY) {
// Handle completed event for playing post-rolls.
/*Toast.makeText(this, "STATE_READY", Toast.LENGTH_SHORT).show();*/
}
updateButtonVisibilities();
}
@Override /* ExoPlayer.EventListener */
public void onPlayerError(ExoPlaybackException e) {
String errorString = null;
if (e.type == ExoPlaybackException.TYPE_RENDERER) {
Exception cause = e.getRendererException();
if (cause instanceof MediaCodecRenderer.DecoderInitializationException) {
// Special case for decoder initialization failures.
MediaCodecRenderer.DecoderInitializationException decoderInitializationException =
(MediaCodecRenderer.DecoderInitializationException) cause;
if (decoderInitializationException.decoderName == null) {
if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) {
errorString = getString(R.string.error_querying_decoders);
} else if (decoderInitializationException.secureDecoderRequired) {
errorString = getString(R.string.error_no_secure_decoder,
decoderInitializationException.mimeType);
} else {
errorString = getString(R.string.error_no_decoder,
decoderInitializationException.mimeType);
}
} else {
errorString = getString(R.string.error_instantiating_decoder,
decoderInitializationException.decoderName);
}
}
}
if (errorString != null) {
showToast(errorString);
}
playerNeedsSource = true;
updateButtonVisibilities();
showControls();
}
@Override /* ExoPlayer.EventListener */
public void onPositionDiscontinuity() {
}
@Override /* AdErrorEvent.AdErrorListener */
public void onAdError(AdErrorEvent adErrorEvent) {
Log.e(LOG_TAG, "Ad Error: " + adErrorEvent.getError().getMessage());
if (player != null)
player.setPlayWhenReady(true);
else {
shouldAutoPlay = true;
initPlayer();
}
}
@Override /* AdEvent.AdEventListener */
public void onAdEvent(AdEvent adEvent) {
Log.i(LOG_TAG, "Event: " + adEvent.getType());
// These are the suggested event types to handle. For full list of all ad event
// types, see the documentation for AdEvent.AdEventType.
switch (adEvent.getType()) {
case LOADED:
// AdEventType.LOADED will be fired when ads are ready to be played.
// AdsManager.start() begins ad playback. This method is ignored for VMAP or
// ad rules playlists, as the SDK will automatically start executing the
// playlist.
mGeneralUtils.zoomOutAnimation(mediaPlayerLoader);
mAdsManager.start();
break;
case CONTENT_PAUSE_REQUESTED:
// AdEventType.CONTENT_PAUSE_REQUESTED is fired immediately before a video
// ad is played.
mIsAdDisplayed = true;
player.setPlayWhenReady(false);
break;
case CONTENT_RESUME_REQUESTED:
// AdEventType.CONTENT_RESUME_REQUESTED is fired when the ad is completed
// and you should start playing your content.
mIsAdDisplayed = false;
if (player != null)
player.setPlayWhenReady(true);
else {
shouldAutoPlay = true;
initPlayer();
}
break;
case ALL_ADS_COMPLETED:
if (mAdsManager != null) {
mAdsManager.destroy();
mAdsManager = null;
}
break;
default:
break;
}
}
/**
* ================
* IMA ADS
* ================
*/
private void createAdsLoader() {
String adUrl = DataUtils.getInstance().getAdsAdmin().getAdInPlayerUrl();
adUrl = null;
// Create an AdsLoader.
mSdkFactory = ImaSdkFactory.getInstance();
mAdsLoader = mSdkFactory.createAdsLoader(this);
// Add listeners for when ads are loaded and for errors.
mAdsLoader.addAdErrorListener(this);
mAdsLoader.addAdsLoadedListener(new AdsLoader.AdsLoadedListener() {
@Override
public void onAdsManagerLoaded(AdsManagerLoadedEvent adsManagerLoadedEvent) {
// Ads were successfully loaded, so get the AdsManager instance. AdsManager has
// events for ad playback and errors.
mAdsManager = adsManagerLoadedEvent.getAdsManager();
// Attach event and error event listeners.
mAdsManager.addAdErrorListener(MediaPlayerActivity.this);
mAdsManager.addAdEventListener(MediaPlayerActivity.this);
mAdsManager.init();
shouldAutoPlay = false;
initPlayer();
}
});
if (adUrl == null || adUrl.equals("")) {
shouldAutoPlay = true;
initPlayer();
} else {
shouldAutoPlay = false;
requestAds(adUrl);
}
// When Play is clicked, request ads and hide the button.
/*mPlayButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mVideoPlayer.setVideoPath(getString(R.string.content_url));
requestAds(getString(R.string.ad_tag_url));
view.setVisibility(View.GONE);
}
});*/
}
private void requestAds(String adTagUrl) {
AdDisplayContainer adDisplayContainer = mSdkFactory.createAdDisplayContainer();
adDisplayContainer.setAdContainer(simpleExoPlayerView);
// Create the ads request.
AdsRequest request = mSdkFactory.createAdsRequest();
request.setAdTagUrl(adTagUrl);
request.setAdDisplayContainer(adDisplayContainer);
request.setContentProgressProvider(new ContentProgressProvider() {
@Override
public VideoProgressUpdate getContentProgress() {
if (mIsAdDisplayed || player == null || player.getDuration() <= 0) {
return VideoProgressUpdate.VIDEO_TIME_NOT_READY;
}
return new VideoProgressUpdate(player.getCurrentPosition(),
player.getDuration());
}
});
// Request the ad. After the ad is loaded, onAdsManagerLoaded() will be called.
mAdsLoader.requestAds(request);
}
}
我的布局xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:keepScreenOn="true"
android:orientation="vertical">
<!-- Player view -->
<com.google.android.exoplayer2.ui.SimpleExoPlayerView
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" />
<!-- Loader container -->
<RelativeLayout
android:id="@+id/mediaPlayerLoader"
android:layout_width="124dp"
android:layout_height="124dp"
android:layout_centerInParent="true">
<com.mikhaellopez.circularimageview.CircularImageView
android:layout_width="121dp"
android:layout_height="121dp"
android:layout_centerInParent="true"
android:src="@drawable/ic_sport5_loader"
app:civ_border="true" />
<!-- Loader-->
<fr.castorflex.android.circularprogressbar.CircularProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:indeterminate="true"
app:cpb_color="@color/colorPrimary"
app:cpb_max_sweep_angle="300"
app:cpb_min_sweep_angle="10"
app:cpb_stroke_width="4dp" />
</RelativeLayout>
<!-- Top bar -->
<LinearLayout
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="#88000000"
android:orientation="horizontal" >
<ImageButton
android:id="@+id/videoBackBtn"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_margin="16dp"
android:layout_gravity="center_vertical"
android:background="@null"
android:src="@drawable/hplib_ic_back" />
<cambium.com.il.sport5.views.CustomTextView
android:id="@+id/videoPlayerTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center|right"
android:layout_gravity="center_vertical"
android:text="אתר ערוץ הספורט - חדשות הספורט, תוצאות, תקצירים ושידורים - Sport5.co.il"
android:textColor="#FFF"
android:textStyle="italic"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:focusable="true"
android:focusableInTouchMode="true"/>
<ImageView
android:id="@+id/sport5Ic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="16dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_sport5" />
</LinearLayout>
<!-- New player controls -->
<com.google.android.exoplayer2.ui.PlaybackControlView
android:id="@+id/controls_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#88000000">
<Button
android:id="@+id/retry_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Retry"
android:visibility="gone"/>
</com.google.android.exoplayer2.ui.PlaybackControlView>
<!-- Old controls -->
<LinearLayout
android:id="@+id/controls"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom"
android:background="#88000000"
android:orientation="horizontal"
android:visibility="gone">
<ImageButton
android:id="@+id/btn_lock"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="20dp"
android:layout_weight="0"
android:background="@null"
android:src="@drawable/hplib_ic_lock" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:layout_weight="1" />
<ImageButton
android:id="@+id/btn_settings"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:layout_marginRight="20dp"
android:layout_weight="0"
android:background="@null"
android:src="@drawable/hplib_ic_settings"
android:text="Settings" />
</LinearLayout>
最后是显示问题的简短视频的共享链接:
https://1drv.ms/v/s!AkkKHWeBCU1WhZwAmz3YW1rlhyYTHA
谢谢
答案 0 :(得分:0)
问题原来是设置宽度为0dp,weightSum为1。 刚刚将文本视图包装在RelativeLayout而不是LinearLayout中,一切都按预期工作。