Custom Drawing

本文深入探讨了自定义视图中至关重要的onDraw()方法的使用,包括如何创建绘图对象、处理布局事件以及实现高效的绘制流程。通过实例分析,详细解释了如何在自定义视图中正确绘制形状、文本以及复杂图形,同时优化布局以适应不同屏幕尺寸和密度,提升用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Override onDraw()


The most important step in drawing a custom view is to override the onDraw() method. The parameter to onDraw() is a Canvasobject that the view can use to draw itself. The Canvas class defines methods for drawing text, lines, bitmaps, and many other graphics primitives. You can use these methods in onDraw() to create your custom user interface (UI).

Before you can call any drawing methods, though, it's necessary to create a Paint object. The next section discusses Paint in more detail.

Create Drawing Objects


The android.graphics framework divides drawing into two areas:

  • What to draw, handled by Canvas
  • How to draw, handled by Paint.

For instance, Canvas provides a method to draw a line, while Paint provides methods to define that line's color.Canvas has a method to draw a rectangle, while Paint defines whether to fill that rectangle with a color or leave it empty. Simply put, Canvas defines shapes that you can draw on the screen, while Paint defines the color, style, font, and so forth of each shape you draw.

So, before you draw anything, you need to create one or more Paint objects. The PieChart example does this in a method called init, which is called from the constructor:

private void init() {
   mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   mTextPaint.setColor(mTextColor);
   if (mTextHeight == 0) {
       mTextHeight = mTextPaint.getTextSize();
   } else {
       mTextPaint.setTextSize(mTextHeight);
   }

   mPiePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   mPiePaint.setStyle(Paint.Style.FILL);
   mPiePaint.setTextSize(mTextHeight);

   mShadowPaint = new Paint(0);
   mShadowPaint.setColor(0xff101010);
   mShadowPaint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL));

   ...

Creating objects ahead of time is an important optimization. Views are redrawn very frequently, and many drawing objects require expensive initialization. Creating drawing objects within your onDraw() method significantly reduces performance and can make your UI appear sluggish.

Handle Layout Events


In order to properly draw your custom view, you need to know what size it is. Complex custom views often need to perform multiple layout calculations depending on the size and shape of their area on screen. You should never make assumptions about the size of your view on the screen. Even if only one app uses your view, that app needs to handle different screen sizes, multiple screen densities, and various aspect ratios in both portrait and landscape mode.

Although View has many methods for handling measurement, most of them do not need to be overridden. If your view doesn't need special control over its size, you only need to override one method: onSizeChanged().

onSizeChanged() is called when your view is first assigned a size, and again if the size of your view changes for any reason. Calculate positions, dimensions, and any other values related to your view's size in onSizeChanged(), instead of recalculating them every time you draw. In the PieChart example, onSizeChanged() is where thePieChart view calculates the bounding rectangle of the pie chart and the relative position of the text label and other visual elements.

When your view is assigned a size, the layout manager assumes that the size includes all of the view's padding. You must handle the padding values when you calculate your view's size. Here's a snippet fromPieChart.onSizeChanged() that shows how to do this:

       // Account for padding
       float xpad = (float)(getPaddingLeft() + getPaddingRight());
       float ypad = (float)(getPaddingTop() + getPaddingBottom());

       // Account for the label
       if (mShowText) xpad += mTextWidth;

       float ww = (float)w - xpad;
       float hh = (float)h - ypad;

       // Figure out how big we can make the pie.
       float diameter = Math.min(ww, hh);

If you need finer control over your view's layout parameters, implement onMeasure(). This method's parameters are View.MeasureSpec values that tell you how big your view's parent wants your view to be, and whether that size is a hard maximum or just a suggestion. As an optimization, these values are stored as packed integers, and you use the static methods of View.MeasureSpec to unpack the information stored in each integer.

Here's an example implementation of onMeasure(). In this implementation, PieChart attempts to make its area big enough to make the pie as big as its label:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   // Try for a width based on our minimum
   int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
   int w = resolveSizeAndState(minw, widthMeasureSpec, 1);

   // Whatever the width ends up being, ask for a height that would let the pie
   // get as big as it can
   int minh = MeasureSpec.getSize(w) - (int)mTextWidth + getPaddingBottom() + getPaddingTop();
   int h = resolveSizeAndState(MeasureSpec.getSize(w) - (int)mTextWidth, heightMeasureSpec, 0);

   setMeasuredDimension(w, h);
}

There are three important things to note in this code:

  • The calculations take into account the view's padding. As mentioned earlier, this is the view's responsibility.
  • The helper method resolveSizeAndState() is used to create the final width and height values. This helper returns an appropriate View.MeasureSpec value by comparing the view's desired size to the spec passed intoonMeasure().
  • onMeasure() has no return value. Instead, the method communicates its results by callingsetMeasuredDimension(). Calling this method is mandatory. If you omit this call, the View class throws a runtime exception.

Draw!


Once you have your object creation and measuring code defined, you can implement onDraw(). Every view implements onDraw() differently, but there are some common operations that most views share:

For example, here's the code that draws PieChart. It uses a mix of text, lines, and shapes.

protected void onDraw(Canvas canvas) {
   super.onDraw(canvas);

   // Draw the shadow
   canvas.drawOval(
           mShadowBounds,
           mShadowPaint
   );

   // Draw the label text
   canvas.drawText(mData.get(mCurrentItem).mLabel, mTextX, mTextY, mTextPaint);

   // Draw the pie slices
   for (int i = 0; i < mData.size(); ++i) {
       Item it = mData.get(i);
       mPiePaint.setShader(it.mShader);
       canvas.drawArc(mBounds,
               360 - it.mEndAngle,
               it.mEndAngle - it.mStartAngle,
               true, mPiePaint);
   }

   // Draw the pointer
   canvas.drawLine(mTextX, mPointerY, mPointerX, mPointerY, mTextPaint);
   canvas.drawCircle(mPointerX, mPointerY, mPointerSize, mTextPaint);
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; // 保存当前播放状态和进度 NSIndexPath * indexPath = [self metalienKK_getCurrentIndexPath]; MetalienKKDynamicModel * currentModel = self.dataSource[indexPath.row]; CGFloat playTime = currentModel.currentPlaybackTime; BOOL wasPlaying = currentModel.isPlaying; BOOL willBeFullScreen = (size.width > size.height); currentModel.isFullScreen = willBeFullScreen; // 更新控件可见性 self.metalienKK_statusImageView.hidden = willBeFullScreen; self.backButton.hidden = willBeFullScreen; self.searchButton.hidden = willBeFullScreen; self.shareButton.hidden = willBeFullScreen; self.portraitButton.hidden = !willBeFullScreen; // 暂停当前播放但不重置 MetalienKKShortVideoItemCollectionViewCell * currentCell = [self.collectionView cellForItemAtIndexPath:indexPath]; [currentCell.metalienKK_playerView metalienKK_pauseShortVideoWithoutReset]; // 更新约束 [self metalienKK_updateContainerFrameForSize:size isFullScreen:willBeFullScreen atIndexPath:indexPath]; // 更新布局而不重载数据 [coordinator animateAlongsideTransition:^(id <UIViewControllerTransitionCoordinatorContext> context) { // 立即刷新布局避免闪烁 [UIView performWithoutAnimation:^{ [self.collectionView.collectionViewLayout invalidateLayout]; [self.collectionView layoutIfNeeded]; [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO]; }]; // 更新旋转状态 [currentCell.metalienKK_playerView metalienKK_shortVideoPlayerViewRotate:willBeFullScreen]; } completion:^(id <UIViewControllerTransitionCoordinatorContext> context) { self.collectionView.scrollEnabled = !currentModel.isFullScreen; // 恢复播放状态 if (wasPlaying) { [currentCell.metalienKK_playerView metalienKK_startPlayShortVideo]; [currentCell.metalienKK_playerView metalienKK_seekToTime:playTime]; } else { [currentCell.metalienKK_playerView metalienKK_pauseShortVideo]; } // 执行等待中的动作 if (self.interfaceOrientationBlock) { self.interfaceOrientationBlock(); self.interfaceOrientationBlock = nil; } }]; } #import "MetalienKKShortVideoPlayerView.h" #define MetalienKK_kMiniPlayerViewHeight (265.0) #define MetalienKK_kBottomViewHeight (55.0 + MetalienKK_SafeAreaBottom) #define MetalienKK_kProgressViewSize CGSizeMake(MetalienKK_ScreenWidth - 15*2.0, 18.0) #define MetalienKK_kFullScreenDefaultSize CGSizeMake(110, 30) #define MetalienKK_FullScreenOpenButtonFont MetalienKK_RegularFont(12) @implementation MetalienKKShortVideoPlayerView /// 加载子视图 - (void)metalienKK_loadSubviews { // 新增视图 [self addSubview:self.metalienKK_playerView]; [self addSubview:self.metalienKK_maskView]; [self addSubview:self.metalienKK_bottomView]; [self addSubview:self.metalienKK_durationLabel]; [self addSubview:self.metalienKK_contentView]; [self addSubview:self.metalienKK_userView]; [self addSubview:self.metalienKK_progressView]; [self addSubview:self.metalienKK_popupView]; [self addSubview:self.metalienKK_loadingView]; [self addSubview:self.metalienKK_fullScreenButton]; [self addSubview:self.metalienKK_playImageView]; [self.metalienKK_playerView addSubview:self.metalienKK_videoEngine.playerView]; [self.metalienKK_bottomView addSubview:self.metalienKK_likeButton]; [self.metalienKK_bottomView addSubview:self.metalienKK_bottomLineView_1]; [self.metalienKK_bottomView addSubview:self.metalienKK_bottomLineView_2]; [self.metalienKK_bottomView addSubview:self.metalienKK_commentButton]; [self.metalienKK_bottomView addSubview:self.metalienKK_wowButton]; // 设置约束 [self.metalienKK_bottomView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.bottom.right.equalTo(self); make.height.mas_offset(MetalienKK_kBottomViewHeight); }]; [self.metalienKK_likeButton mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.metalienKK_bottomView); make.top.equalTo(self.metalienKK_bottomView); make.width.equalTo(self.metalienKK_bottomView.mas_width).multipliedBy(1/3.0); make.height.mas_offset(55.0); }]; [self.metalienKK_bottomLineView_1 mas_makeConstraints:^(MASConstraintMaker *make) { make.centerY.equalTo(self.metalienKK_likeButton); make.right.equalTo(self.metalienKK_likeButton.mas_left); make.size.mas_offset(CGSizeMake(1, 20)); }]; [self.metalienKK_bottomLineView_2 mas_makeConstraints:^(MASConstraintMaker *make) { make.centerY.equalTo(self.metalienKK_likeButton); make.left.equalTo(self.metalienKK_likeButton.mas_right); make.size.mas_equalTo(self.metalienKK_bottomLineView_1); }]; [self.metalienKK_commentButton mas_makeConstraints:^(MASConstraintMaker *make) { make.top.left.equalTo(self.metalienKK_bottomView); make.right.equalTo(self.metalienKK_bottomLineView_1.mas_left); make.height.mas_equalTo(self.metalienKK_likeButton); }]; [self.metalienKK_wowButton mas_makeConstraints:^(MASConstraintMaker *make) { make.top.right.equalTo(self.metalienKK_bottomView); make.left.equalTo(self.metalienKK_bottomLineView_2.mas_right); make.height.mas_equalTo(self.metalienKK_likeButton); }]; [self.metalienKK_playerView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.left.right.equalTo(self); make.bottom.equalTo(self).offset(-MetalienKK_kBottomViewHeight); }]; [self.metalienKK_videoEngine.playerView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.metalienKK_playerView); }]; [self.metalienKK_popupView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.mas_bottom); // 初始位置在底部之外 make.left.right.equalTo(self); make.height.mas_equalTo(MetalienKK_ScreenHeight - MetalienKK_StatusBarHeight - MetalienKK_kMiniPlayerViewHeight); }]; [self.metalienKK_maskView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.metalienKK_playerView); }]; [self.metalienKK_playImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.metalienKK_playerView); make.size.mas_offset(CGSizeMake(40, 40)); }]; [self.metalienKK_progressView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self); make.bottom.equalTo(self.metalienKK_bottomView.mas_top); make.size.mas_offset(MetalienKK_kProgressViewSize); }]; [self.metalienKK_durationLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.leading.equalTo(self.metalienKK_progressView); make.bottom.equalTo(self.metalienKK_progressView.mas_top); }]; [self.metalienKK_contentView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.equalTo(self); make.bottom.equalTo(self.metalienKK_progressView.mas_top); make.height.mas_offset(20.0); }]; [self.metalienKK_userView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.equalTo(self); make.bottom.equalTo(self.metalienKK_contentView.mas_top).offset(-12); make.height.mas_offset(36.0); }]; [self.metalienKK_fullScreenButton mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.metalienKK_playerView); make.size.mas_equalTo(MetalienKK_kFullScreenDefaultSize); }]; [self.metalienKK_loadingView mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.metalienKK_playerView); make.left.right.equalTo(self).inset(15.0); make.height.mas_equalTo(60); }]; // 按钮布局 [self.metalienKK_commentButton metalienKK_layoutWithStyle:MetalienKKButtonEdgeInsetsStyleImageLeft andSpace:4.0]; [self.metalienKK_likeButton metalienKK_layoutWithStyle:MetalienKKButtonEdgeInsetsStyleImageLeft andSpace:4.0]; [self.metalienKK_wowButton metalienKK_layoutWithStyle:MetalienKKButtonEdgeInsetsStyleImageLeft andSpace:4.0]; // 创建通知 [self metalienKK_setupNotifications]; } /// 创建通知 - (void)metalienKK_setupNotifications { // 应用进入后台时暂停 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActiveNotification) name:UIApplicationWillResignActiveNotification object:nil]; // 应用返回前台时恢复 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomeActiveNotification) name:UIApplicationDidBecomeActiveNotification object:nil]; } /// 显示&隐藏控件 - (void)metalienKK_showAndHiddenControls { self.metalienKK_fullScreenButton.hidden = self.responseData.isFullScreen ?:(!self.responseData.isHorizontalResolution); self.metalienKK_bottomView.hidden = self.responseData.isFullScreen; self.metalienKK_contentView.hidden = self.responseData.isFullScreen ?:!((self.responseData.isShowTitle || self.responseData.isShowContent)); self.metalienKK_durationLabel.hidden = self.responseData.isFullScreen ? self.responseData.isPlaying:YES; self.metalienKK_userView.hidden = self.responseData.isFullScreen ? self.responseData.isPlaying:NO; self.metalienKK_progressView.hidden = self.responseData.isFullScreen ? self.responseData.isPlaying:NO; } /// 重置约束 - (void)metalienKK_remarkConstraints { // 视频播放器 [self.metalienKK_playerView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self); make.left.right.equalTo(self).inset(self.responseData.isFullScreen ? 76.0:0.0); make.bottom.equalTo(self).offset(self.responseData.isFullScreen ? 0.0:(-MetalienKK_kBottomViewHeight)); }]; [self.metalienKK_videoEngine.playerView mas_remakeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.metalienKK_playerView); }]; // 用户信息视图 [self.metalienKK_userView mas_remakeConstraints:^(MASConstraintMaker *make) { if (self.responseData.isFullScreen) { make.top.equalTo(self).offset(20); make.left.right.equalTo(self).inset(94); } else { make.left.right.equalTo(self); if (!self.metalienKK_contentView.hidden) { make.bottom.equalTo(self.metalienKK_contentView.mas_top).offset(-12); } else { make.bottom.equalTo(self.metalienKK_progressView.mas_top); } } make.height.mas_offset(36.0); }]; // 进度条 [self.metalienKK_progressView mas_remakeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self); if (self.responseData.isFullScreen) { make.left.right.equalTo(self).inset(50); make.bottom.equalTo(self).offset(-39); make.height.mas_equalTo(MetalienKK_kProgressViewSize.height); } else { make.bottom.equalTo(self.metalienKK_bottomView.mas_top); make.size.mas_offset(MetalienKK_kProgressViewSize); } }]; // 内容 if (!self.metalienKK_contentView.hidden) { [self.metalienKK_contentView mas_remakeConstraints:^(MASConstraintMaker *make) { CGFloat contentHeight = [self.metalienKK_contentView metalienKK_getContentViewHeight]; make.left.right.equalTo(self); make.bottom.equalTo(self.metalienKK_progressView.mas_top); make.height.mas_offset(contentHeight); }]; } // 全屏播放按钮 if (!self.metalienKK_fullScreenButton.hidden) { [self.metalienKK_fullScreenButton mas_remakeConstraints:^(MASConstraintMaker *make) { CGSize tempSize = [self.metalienKK_fullScreenButton.currentTitle metalienKK_calculateMultipleLinesTextSizeWithFont:MetalienKK_FullScreenOpenButtonFont andMaxSize:CGSizeMake(CGFLOAT_MAX, MetalienKK_kFullScreenDefaultSize.height)]; CGFloat tempWidth = 2*12.0 + 20.0 + 8.0 + tempSize.width; if (tempWidth < MetalienKK_kFullScreenDefaultSize.width) { tempWidth = MetalienKK_kFullScreenDefaultSize.width; } CGFloat tempVideoHeight = (self.responseData.videoHeight/(self.responseData.videoWidth*1.0)) * MetalienKK_ScreenWidth; make.centerX.equalTo(self.metalienKK_playerView); make.centerY.equalTo(self.metalienKK_playerView).offset(tempVideoHeight/2.0 + 12.0 + MetalienKK_kFullScreenDefaultSize.height/2.0); make.size.mas_equalTo(CGSizeMake(tempWidth, MetalienKK_kFullScreenDefaultSize.height)); }]; } // 立即刷新 [self layoutIfNeeded]; } # pragma mark - Public /// 视图复用 - (void)metalienKK_shortVideoPlayerViewReuse { // 暂停播放 [self metalienKK_pauseShortVideo]; // 重置播放器 [self.metalienKK_videoEngine resetPlayerVideoProcessor]; } /// 视图旋转 - (void)metalienKK_shortVideoPlayerViewRotate:(BOOL)isFullScreen { // 标记 self.responseData.isFullScreen = isFullScreen; // 显示&隐藏控件 [self metalienKK_showAndHiddenControls]; // 重置约束 [self metalienKK_remarkConstraints]; } /// 刷新数据 - (void)metalienKK_reloadVideoPlayerViewWithData:(MetalienKKDynamicModel *)responseData { // 缓存 self.responseData = responseData; // 显示&隐藏控件 self.metalienKK_loadingView.hidden = responseData.isReadyDisplay; self.metalienKK_playImageView.hidden = YES; self.metalienKK_fullScreenButton.hidden = !responseData.isHorizontalResolution; // 刷新数据 [self.metalienKK_userView metalienKK_refreshShortVideoUser:responseData]; [self.metalienKK_popupView metalienKK_reloadShortVideoViewData:responseData]; // 判断是否显示文本 BOOL isShowContent = (responseData.isShowTitle || responseData.isShowContent); self.metalienKK_contentView.hidden = !isShowContent; if (isShowContent) { [self.metalienKK_contentView metalienKK_refreshShortVideoContent:responseData]; } // 视频进度 [self.metalienKK_progressView metalienKK_shortVideoSetProgress:responseData.currentPlaybackProgress]; // 设置视频资源 [self metalienKK_setupVideoEngineSource]; // 显示&隐藏控件 [self metalienKK_showAndHiddenControls]; // 重置约束 [self metalienKK_remarkConstraints]; } /// 设置视频资源 - (void)metalienKK_setupVideoEngineSource { if (self.responseData.metalienKK_mediaID.length > 0 && self.responseData.metalienKK_mediaToken.length > 0) { // 设置视频资源(使用 Video ID) [self metalienKK_setShortVideoSourceWithVideoID:self.responseData.metalienKK_mediaID andToken:self.responseData.metalienKK_mediaToken]; } else if (self.responseData.metalienKK_href.length > 0) { // 设置视频资源(使用 HTTP URL) [self metalienKK_setShortVideoSourceWithURL:self.responseData.metalienKK_href]; } } /// 预加载方法 - (void)metalienKK_preloadVideo { // 设置视频资源 [self metalienKK_setupVideoEngineSource]; // 准备播放 [self.metalienKK_videoEngine prepareToPlay]; } /// 开始播放 - (void)metalienKK_startPlayShortVideo { // 标识 self.responseData.isPlaying = YES; // 隐藏播放按钮 self.metalienKK_playImageView.hidden = YES; // 弱引用 MetalienKK_WeakSelf(self); // 播放进度 [self.metalienKK_videoEngine addPeriodicTimeObserverForInterval:0.5 queue:dispatch_get_main_queue() usingBlock:^{ // 强引用 MetalienKK_StrongSelf(self); // 刷新视频播放进度 [self metalienKK_refreshShortVideoPlaybackProgress]; }]; // 指定视频播放开始时间 [self.metalienKK_videoEngine setOptionForKey:VEKKeyPlayerStartTime_CGFloat value:@(self.responseData.currentPlaybackTime)]; // 播放视频 [self.metalienKK_videoEngine play]; } /// 暂停播放 - (void)metalienKK_pauseShortVideo { // 显示播放按钮 self.metalienKK_playImageView.hidden = NO; // 手动标记 self.responseData.isPlaying = NO; // 暂停播放 [self.metalienKK_videoEngine pause:YES]; // 移除监听 [self.metalienKK_videoEngine removeTimeObserver]; } /// 重置播放器 - (void)metalienKK_resetPlayer { // 显示播放按钮 self.metalienKK_playImageView.hidden = NO; // 手动标记 self.responseData.isPlaying = NO; // // 视频进度 // [self.metalienKK_progressView metalienKK_shortVideoSetProgress:0]; // 移除监听 [self.metalienKK_videoEngine removeTimeObserver]; // 重置播放器 [self.metalienKK_videoEngine pause:YES]; // [self.metalienKK_videoEngine resetPlayerVideoProcessor]; } // 无重置的暂停 - (void)metalienKK_pauseShortVideoWithoutReset { [self.metalienKK_videoEngine pause]; self.responseData.isPlaying = NO; } // 跳转到指定时间 - (void)metalienKK_seekToTime:(CGFloat)time { MetalienKK_WeakSelf(self); [self.metalienKK_videoEngine setCurrentPlaybackTime:time complete:^(BOOL success) { if (success) { [weak_self metalienKK_refreshShortVideoPlaybackProgress]; } }]; } # pragma mark - Notification Event /// App 进入后台 - (void)appWillResignActiveNotification { if (self.responseData.isPlaying) { [self metalienKK_pauseShortVideo]; } } /// App 即将进入前台 - (void)appDidBecomeActiveNotification { if (self.responseData.isPlaying) { [self metalienKK_startPlayShortVideo]; } } # pragma mark - Event /// 设置视频资源(使用 Video ID) - (void)metalienKK_setShortVideoSourceWithVideoID:(NSString *)videoID andToken:(NSString *)token { TTVideoEngineVidSource * videoSource = [[TTVideoEngineVidSource alloc] initWithVid:videoID playAuthToken:token resolution:TTVideoEngineResolutionTypeAuto]; [self.metalienKK_videoEngine setVideoEngineVideoSource:videoSource]; } /// 设置视频资源(使用 HTTP URL) - (void)metalienKK_setShortVideoSourceWithURL:(NSString *)videoURL { NSString * cacheKey = [videoURL metalienKK_coverToMD5String]; TTVideoEngineUrlSource * urlSource = [[TTVideoEngineUrlSource alloc] initWithUrl:videoURL cacheKey:cacheKey]; [self.metalienKK_videoEngine setVideoEngineVideoSource:urlSource]; } /// 刷新视频播放进度 - (void)metalienKK_refreshShortVideoPlaybackProgress { CGFloat playTime = self.metalienKK_videoEngine.currentPlaybackTime; CGFloat duration = self.metalienKK_videoEngine.duration; CGFloat progress = (playTime/duration); [self.metalienKK_progressView metalienKK_shortVideoSetProgress:progress]; // 视频播放进度 NSString * playTimeString = [NSString metalienKK_secondConvertToFormat:playTime]; NSString * durationString = [NSString metalienKK_secondConvertToFormat:duration]; NSString * content = [NSString stringWithFormat:@"%@ / %@", playTimeString, durationString]; NSRange playTimeRange = [content rangeOfString:playTimeString]; NSMutableAttributedString * attributedString = [[NSMutableAttributedString alloc] initWithString:content]; [attributedString addAttribute:NSForegroundColorAttributeName value:MetalienKK_ColorHexAlpha(@"#A8B0B9", 0.7) range:attributedString.yy_rangeOfAll]; [attributedString addAttribute:NSForegroundColorAttributeName value:UIColor.whiteColor range:playTimeRange]; self.metalienKK_durationLabel.attributedText = attributedString; // 缓存 self.responseData.currentPlaybackProgress = progress; self.responseData.currentPlaybackTime = playTime; } /// 弹出视图 - (void)metalienKK_showPopupViewAtIndex:(int)index { // 选中处理 if (index >= 0) { [self.metalienKK_popupView metalienKK_popupViewDidSelectIndex:index]; } // 控件显示&隐藏 self.metalienKK_fullScreenButton.hidden = YES; self.metalienKK_popupView.hidden = NO; // 标记是否弹出视图 [self metalienKK_markPopupViewIsShow:YES]; // 重置约束 [self.metalienKK_playerView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.left.right.equalTo(self); make.height.mas_equalTo(MetalienKK_kMiniPlayerViewHeight); }]; [self.metalienKK_popupView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.metalienKK_playerView.mas_bottom); make.left.right.equalTo(self); make.height.mas_equalTo(MetalienKK_ScreenHeight - MetalienKK_StatusBarHeight - MetalienKK_kMiniPlayerViewHeight); }]; // 创建弹簧动画器 UISpringTimingParameters * springParams = [[UISpringTimingParameters alloc] initWithDampingRatio:0.75 initialVelocity:CGVectorMake(0.5, 0.5)]; UIViewPropertyAnimator * animator = [[UIViewPropertyAnimator alloc] initWithDuration:0.4 timingParameters:springParams]; [animator addAnimations:^{ // 强制立即刷新布局 [self layoutIfNeeded]; }]; [animator addCompletion:^(UIViewAnimatingPosition finalPosition) { [self metalienKK_disableCollectionViewScroll:YES]; }]; [animator startAnimation]; } /// 关闭视图 - (void)metalienKK_closePopupView { // 标记是否弹出视图 [self metalienKK_markPopupViewIsShow:NO]; // 重置约束 [self.metalienKK_playerView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.left.right.equalTo(self); make.bottom.equalTo(self).offset(-MetalienKK_kBottomViewHeight); }]; [self.metalienKK_popupView mas_remakeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.mas_bottom); make.left.right.equalTo(self); make.height.mas_equalTo(MetalienKK_ScreenHeight - MetalienKK_StatusBarHeight - MetalienKK_kMiniPlayerViewHeight); }]; // 使用相同的动画参数 UISpringTimingParameters * springParams = [[UISpringTimingParameters alloc] initWithDampingRatio:0.8 initialVelocity:CGVectorMake(0, 0)]; UIViewPropertyAnimator * animator = [[UIViewPropertyAnimator alloc] initWithDuration:0.35 timingParameters:springParams]; [animator addAnimations:^{ // 强制立即刷新布局 [self layoutIfNeeded]; }]; [animator addCompletion:^(UIViewAnimatingPosition finalPosition) { self.metalienKK_fullScreenButton.hidden = !self.responseData.isHorizontalResolution; self.metalienKK_popupView.hidden = YES; [self metalienKK_disableCollectionViewScroll:NO]; }]; [animator startAnimation]; } /// 标记是否弹出视图 - (void)metalienKK_markPopupViewIsShow:(BOOL)isShow { // 缓存 self.responseData.isShowingPopupView = isShow; // 弹出&收起视图 if ([self.delegate respondsToSelector:@selector(metalienKK_collectionViewCellDidShowPopupView:)]) { [self.delegate metalienKK_collectionViewCellDidShowPopupView:isShow]; } } /// 禁止滚动 - (void)metalienKK_disableCollectionViewScroll:(BOOL)isDisable { // 移除手势 [self.metalienKK_maskView removeGestureRecognizer:self.metalienKK_maskViewBlockPanGesture]; // 判断是否禁止 if (isDisable) { [self.metalienKK_maskView addGestureRecognizer:self.metalienKK_maskViewBlockPanGesture]; } } # pragma mark - Gesture /// 点击视频 - (void)metalienKK_maskViewTapGesture:(UITapGestureRecognizer *)gesture { // 判断是否正在弹出视图 if (self.responseData.isShowingPopupView) { [self metalienKK_closePopupView]; } else { if (self.metalienKK_videoEngine.playbackState == TTVideoEnginePlaybackStatePlaying) { [self metalienKK_pauseShortVideo]; } else { [self metalienKK_startPlayShortVideo]; } } } /// 点击内容视图 - (void)metalienKK_contentViewTapGesture:(UITapGestureRecognizer *)gesture { [self metalienKK_showPopupViewAtIndex:0]; } /// 视频窗口滑动手势 - (void)metalienKK_maskViewPanGesture:(UIPanGestureRecognizer *)gesture { // Nothing ... } /// 预览视图滑动手势 - (void)metalienKK_popupViewPanGesture:(UIPanGestureRecognizer *)gesture { static CGPoint originalCenter; static CGRect originalFrame; static CGFloat initialTranslationY; CGPoint translation = [gesture translationInView:self]; CGPoint velocity = [gesture velocityInView:self]; switch (gesture.state) { case UIGestureRecognizerStateBegan: // 记录初始位置 originalCenter = self.metalienKK_popupView.center; originalFrame = self.metalienKK_popupView.frame; initialTranslationY = translation.y; break; case UIGestureRecognizerStateChanged: { // 计算新的Y位置(限制在屏幕范围内) CGFloat newY = originalFrame.origin.y + translation.y; CGFloat minY = MetalienKK_kMiniPlayerViewHeight; CGFloat maxY = MetalienKK_ScreenHeight; // 限制拖拽范围(不能超过屏幕顶部/底部) newY = MIN(MAX(newY, minY), maxY); // 应用新位置 CGRect newFrame = originalFrame; newFrame.origin.y = newY; self.metalienKK_popupView.frame = newFrame; // 计算背景视图的高度变化(跟随拖拽) CGFloat backgroundHeight = MetalienKK_kMiniPlayerViewHeight + (newY - minY); [self.metalienKK_playerView mas_updateConstraints:^(MASConstraintMaker *make) { make.height.mas_equalTo(backgroundHeight); }]; [self layoutIfNeeded]; break; } case UIGestureRecognizerStateEnded: case UIGestureRecognizerStateCancelled: { CGFloat currentY = self.metalienKK_popupView.frame.origin.y; CGFloat threshold = MetalienKK_ScreenHeight * 0.5; // 阈值设为屏幕高度的50% BOOL shouldClose = NO; // 根据速度或位置决定操作 if (velocity.y > 800) { // 快速下滑则关闭 shouldClose = YES; } else if (velocity.y < -800) { // 快速上滑则展开 shouldClose = NO; } else { // 根据位置决定 shouldClose = (currentY > threshold); } if (shouldClose) { [self metalienKK_closePopupView]; } else { [self metalienKK_showPopupViewAtIndex:-999]; } break; } default: break; } } #pragma mark - Action /// 全屏播放 - (void)metalienKK_fullScreenButtonAction:(UIButton *)sender { // 确保使用主线程执行旋转 dispatch_async(dispatch_get_main_queue(), ^{ if ([self.delegate respondsToSelector:@selector(metalienKK_fullScreenOpenButtonDidClick:)]) { [self.delegate metalienKK_fullScreenOpenButtonDidClick:sender]; } }); } /// 评论 - (void)metalienKK_commentButtonAction:(UIButton *)sender { [self metalienKK_showPopupViewAtIndex:1]; } /// 赞 & 取消赞 - (void)metalienKK_likeButtonAction:(UIButton *)sender { } /// 棒 & 取消棒 - (void)metalienKK_wowButtonAction:(UIButton *)sender { } # pragma mark - TTVideoEngineDelegate /// 播放停止回调 - (void)videoEngineUserStopped:(TTVideoEngine *)videoEngine { MetalienKK_Log(@"videoEngineUserStopped:"); } /// 播放结束回调 - (void)videoEngineDidFinish:(TTVideoEngine *)videoEngine error:(nullable NSError *)error { MetalienKK_Log(@"videoEngineDidFinish:error:"); } /// 异常播放结束回调 - (void)videoEngineDidFinish:(TTVideoEngine *)videoEngine videoStatusException:(NSInteger)status { MetalienKK_Log(@"videoEngineDidFinish:videoStatusException:"); } /// 播放器实例销毁回调 - (void)videoEngineCloseAysncFinish:(TTVideoEngine *)videoEngine { MetalienKK_Log(@"videoEngineCloseAysncFinish:"); } /// 播放状态改变回调 - (void)videoEngine:(TTVideoEngine *)videoEngine playbackStateDidChanged:(TTVideoEnginePlaybackState)playbackState { MetalienKK_Log(@"videoEngine:playbackStateDidChanged:"); self.responseData.isPlaying = (playbackState == TTVideoEnginePlaybackStatePlaying); self.metalienKK_progressView.isPlaying = self.responseData.isPlaying; // 显示&隐藏控件 [self metalienKK_showAndHiddenControls]; } /// 加载状态改变回调 - (void)videoEngine:(TTVideoEngine *)videoEngine loadStateDidChanged:(TTVideoEngineLoadState)loadState { MetalienKK_Log(@"videoEngine:loadStateDidChanged:"); if (loadState == TTVideoEngineLoadStatePlayable) { self.responseData.isLoading = NO; [self.metalienKK_progressView metalienKK_shortVideoStopLoadingAnimation]; } else { self.responseData.isLoading = YES; [self.metalienKK_progressView metalienKK_shortVideoStartLoadingAnimation]; } } /// 播放器各模块准备完成、可以播放回调 - (void)videoEnginePrepared:(TTVideoEngine *)videoEngine { MetalienKK_Log(@"videoEnginePrepared:"); self.metalienKK_progressView.duration = videoEngine.duration; } /// 视频加载完成、开始播放回调 - (void)videoEngineReadyToPlay:(TTVideoEngine *)videoEngine { MetalienKK_Log(@"videoEngineReadyToPlay:"); } /// 显示视频首帧回调 - (void)videoEngineReadyToDisPlay:(TTVideoEngine *)videoEngine { MetalienKK_Log(@"videoEngineReadyToDisPlay:(width:%ld, height:%ld)", self.responseData.videoWidth, self.responseData.videoHeight); self.responseData.isReadyDisplay = YES; self.metalienKK_loadingView.hidden = YES; } # pragma mark - TTVideoEngineResolutionDelegate /// 视频分辨率发生变化回调 - (void)videoSizeDidChange:(TTVideoEngine *)videoEngine videoWidth:(NSInteger)videoWidth videoHeight:(NSInteger)videoHeight { MetalienKK_Log(@"videoSizeDidChange:videoWidth:%ld videoHeight:%ld", (long)videoWidth, (long)videoHeight); // 缓存信息 self.responseData.videoWidth = videoWidth; self.responseData.videoHeight = videoHeight; // 按钮显示状态 self.responseData.isHorizontalResolution = (self.responseData.videoWidth > self.responseData.videoHeight); self.metalienKK_fullScreenButton.hidden = !self.responseData.isHorizontalResolution; // 重置约束 [self metalienKK_remarkConstraints]; } # pragma mark - Override - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if (self.responseData.isShowingPopupView) { CGPoint commentPoint = [self.metalienKK_popupView convertPoint:point fromView:self]; if ([self.metalienKK_popupView pointInside:commentPoint withEvent:event]) { return [self.metalienKK_popupView hitTest:commentPoint withEvent:event]; } CGPoint maskPoint = [self.metalienKK_maskView convertPoint:point fromView:self]; if ([self.metalienKK_maskView pointInside:maskPoint withEvent:event]) { return self.metalienKK_maskView; } return self; } return [super hitTest:point withEvent:event]; } //# pragma mark - Helper // ///// 判断视图是否存在 //- (BOOL)metalienKK_isVideoEnginePlayerViewExist { // return [self.metalienKK_videoEngine.playerView isDescendantOfView:self.metalienKK_playerView]; //} # pragma mark - Getter - (TTVideoEngine *)metalienKK_videoEngine { if (!_metalienKK_videoEngine) { _metalienKK_videoEngine = [[TTVideoEngine alloc] initWithOwnPlayer:YES]; _metalienKK_videoEngine.delegate = self; _metalienKK_videoEngine.resolutionDelegate = self; _metalienKK_videoEngine.looping = YES; [_metalienKK_videoEngine setOptionForKey:VEKKeyViewScaleMode_ENUM value:@(TTVideoEngineScalingModeAspectFit)]; [_metalienKK_videoEngine setOptionForKey:VEKKeyAudioChannelEffect_ENUM value:@(TTVideoEngineAudioChannelNormal)]; // 播放 DASH 视频 [_metalienKK_videoEngine setOptionForKey:VEKKeyPlayerBashEnabled_BOOL value:@(YES)]; [_metalienKK_videoEngine setOptionForKey:VEKKeyPlayerDashEnabled_BOOL value:@(YES)]; } return _metalienKK_videoEngine; } - (MetalienKKShortVideoProgressView *)metalienKK_progressView { if (!_metalienKK_progressView) { // 弱引用 MetalienKK_WeakSelf(self); // 初始化 _metalienKK_progressView = [[MetalienKKShortVideoProgressView alloc] initWithFrame:CGRectMake(0, 0, MetalienKK_kProgressViewSize.width, MetalienKK_kProgressViewSize.height)]; [_metalienKK_progressView setOnSeek:^(NSInteger time) { // 强引用 MetalienKK_StrongSelf(self); // 跳转到指定的时间位置 [self.metalienKK_videoEngine setCurrentPlaybackTime:time complete:^(BOOL success) { // Nothing ... }]; }]; [_metalienKK_progressView metalienKK_shortVideoStartLoadingAnimation]; } return _metalienKK_progressView; } - (MetalienKKShortVideoContentPreviewView *)metalienKK_contentView { if (!_metalienKK_contentView) { _metalienKK_contentView = [[MetalienKKShortVideoContentPreviewView alloc] init]; _metalienKK_contentView.userInteractionEnabled = YES; // 添加手势 UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(metalienKK_contentViewTapGesture:)]; [_metalienKK_contentView addGestureRecognizer:tap]; } return _metalienKK_contentView; } - (MetalienKKShortVideoUserView *)metalienKK_userView { if (!_metalienKK_userView) { _metalienKK_userView = [[MetalienKKShortVideoUserView alloc] init]; } return _metalienKK_userView; } - (UIView *)metalienKK_playerView { if (!_metalienKK_playerView) { _metalienKK_playerView = [[UIView alloc] init]; _metalienKK_playerView.userInteractionEnabled = YES; } return _metalienKK_playerView; } - (UIView *)metalienKK_bottomView { if (!_metalienKK_bottomView) { _metalienKK_bottomView = [[UIView alloc] init]; _metalienKK_bottomView.backgroundColor = MetalienKK_ColorHex(@"#11111D"); } return _metalienKK_bottomView; } - (UIButton *)metalienKK_commentButton { if (!_metalienKK_commentButton) { _metalienKK_commentButton = [UIButton buttonWithType:UIButtonTypeCustom]; [_metalienKK_commentButton.titleLabel setFont:MetalienKK_BoldFont(14)]; [_metalienKK_commentButton setTitle:MetalienKK_LocalizedString(@"评论") forState:UIControlStateNormal]; [_metalienKK_commentButton setTitleColor:MetalienKK_ColorHex(@"#E6EBF4") forState:UIControlStateNormal]; [_metalienKK_commentButton setImage:[UIImage imageNamed:@"metalienKKImage_shortVideo_comment"] forState:UIControlStateNormal]; [_metalienKK_commentButton addTarget:self action:@selector(metalienKK_commentButtonAction:) forControlEvents:UIControlEventTouchUpInside]; } return _metalienKK_commentButton; } - (UIButton *)metalienKK_likeButton { if (!_metalienKK_likeButton) { _metalienKK_likeButton = [UIButton buttonWithType:UIButtonTypeCustom]; [_metalienKK_likeButton.titleLabel setFont:MetalienKK_BoldFont(14)]; [_metalienKK_likeButton setTitle:MetalienKK_LocalizedString(@"赞") forState:UIControlStateNormal]; [_metalienKK_likeButton setTitleColor:MetalienKK_ColorHex(@"#E6EBF4") forState:UIControlStateNormal]; [_metalienKK_likeButton setImage:[UIImage imageNamed:@"metalienKKImage_shortVideo_like"] forState:UIControlStateNormal]; [_metalienKK_likeButton addTarget:self action:@selector(metalienKK_likeButtonAction:) forControlEvents:UIControlEventTouchUpInside]; } return _metalienKK_likeButton; } - (UIButton *)metalienKK_wowButton { if (!_metalienKK_wowButton) { _metalienKK_wowButton = [UIButton buttonWithType:UIButtonTypeCustom]; [_metalienKK_wowButton.titleLabel setFont:MetalienKK_BoldFont(14)]; [_metalienKK_wowButton setTitle:MetalienKK_LocalizedString(@"棒") forState:UIControlStateNormal]; [_metalienKK_wowButton setTitleColor:MetalienKK_ColorHex(@"#E6EBF4") forState:UIControlStateNormal]; [_metalienKK_wowButton setImage:[UIImage imageNamed:@"metalienKKImage_shortVideo_WOW"] forState:UIControlStateNormal]; [_metalienKK_wowButton addTarget:self action:@selector(metalienKK_wowButtonAction:) forControlEvents:UIControlEventTouchUpInside]; } return _metalienKK_wowButton; } - (UIView *)metalienKK_bottomLineView_1 { if (!_metalienKK_bottomLineView_1) { _metalienKK_bottomLineView_1 = [[UIView alloc] init]; _metalienKK_bottomLineView_1.backgroundColor = MetalienKK_ColorHexAlpha(@"#848C96", 0.1); } return _metalienKK_bottomLineView_1; } - (UIView *)metalienKK_bottomLineView_2 { if (!_metalienKK_bottomLineView_2) { _metalienKK_bottomLineView_2 = [[UIView alloc] init]; _metalienKK_bottomLineView_2.backgroundColor = MetalienKK_ColorHexAlpha(@"#848C96", 0.1); } return _metalienKK_bottomLineView_2; } - (UIImageView *)metalienKK_playImageView { if (!_metalienKK_playImageView) { _metalienKK_playImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"metalienKKImage_shortVideoPlay"]]; _metalienKK_playImageView.hidden = YES; } return _metalienKK_playImageView; } - (UIView *)metalienKK_maskView { if (!_metalienKK_maskView) { _metalienKK_maskView = [[UIView alloc] init]; _metalienKK_maskView.backgroundColor = UIColor.clearColor; _metalienKK_maskView.userInteractionEnabled = YES; // 添加手势 UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(metalienKK_maskViewTapGesture:)]; [_metalienKK_maskView addGestureRecognizer:tap]; } return _metalienKK_maskView; } - (UIPanGestureRecognizer *)metalienKK_maskViewBlockPanGesture { if (!_metalienKK_maskViewBlockPanGesture) { _metalienKK_maskViewBlockPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(metalienKK_maskViewPanGesture:)]; } return _metalienKK_maskViewBlockPanGesture; } - (UIPanGestureRecognizer *)metalienKK_popupViewPanGesture { if (!_metalienKK_popupViewPanGesture) { _metalienKK_popupViewPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(metalienKK_popupViewPanGesture:)]; } return _metalienKK_popupViewPanGesture; } - (MetalienKKShortVideoPopupView *)metalienKK_popupView { if (!_metalienKK_popupView) { // 初始化视图 _metalienKK_popupView = [[MetalienKKShortVideoPopupView alloc] init]; _metalienKK_popupView.backgroundColor = [UIColor whiteColor]; _metalienKK_popupView.clipsToBounds = YES; _metalienKK_popupView.layer.cornerRadius = 12.0; _metalienKK_popupView.hidden = YES; _metalienKK_popupView.parentViewPanGestureRecognizer = self.metalienKK_popupViewPanGesture; // 添加拖拽手势 [_metalienKK_popupView addGestureRecognizer:self.metalienKK_popupViewPanGesture]; // 添加按钮事件 [_metalienKK_popupView.metalienKK_commentView.metalienKK_closeButton addTarget:self action:@selector(metalienKK_closePopupView) forControlEvents:UIControlEventTouchUpInside]; } return _metalienKK_popupView; } - (UIButton *)metalienKK_fullScreenButton { if (!_metalienKK_fullScreenButton) { _metalienKK_fullScreenButton = [UIButton buttonWithType:UIButtonTypeCustom]; [_metalienKK_fullScreenButton setFrame:CGRectMake(0, 0, MetalienKK_kFullScreenDefaultSize.width, MetalienKK_kFullScreenDefaultSize.height)]; [_metalienKK_fullScreenButton.titleLabel setFont:MetalienKK_FullScreenOpenButtonFont]; [_metalienKK_fullScreenButton setTitle:MetalienKK_LocalizedString(@"全屏播放") forState:UIControlStateNormal]; [_metalienKK_fullScreenButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; [_metalienKK_fullScreenButton setImage:[UIImage imageNamed:@"metalienKKImage_shortVideo_fullScreen_open"] forState:UIControlStateNormal]; [_metalienKK_fullScreenButton setClipsToBounds:YES]; [_metalienKK_fullScreenButton.layer setCornerRadius:MetalienKK_kFullScreenDefaultSize.height/2.0]; [_metalienKK_fullScreenButton.layer setBorderColor:MetalienKK_ColorHexAlpha(@"#FFFFFF", 0.15).CGColor]; [_metalienKK_fullScreenButton.layer setBorderWidth:1.0]; [_metalienKK_fullScreenButton addTarget:self action:@selector(metalienKK_fullScreenButtonAction:) forControlEvents:UIControlEventTouchUpInside]; [_metalienKK_fullScreenButton metalienKK_layoutWithStyle:MetalienKKButtonEdgeInsetsStyleImageLeft andSpace:8.0]; [_metalienKK_fullScreenButton setHidden:YES]; } return _metalienKK_fullScreenButton; } - (UILabel *)metalienKK_durationLabel { if (!_metalienKK_durationLabel) { _metalienKK_durationLabel = [[UILabel alloc] init]; _metalienKK_durationLabel.font = MetalienKK_RegularFont(14); _metalienKK_durationLabel.text = @"00:00 / 00:00"; _metalienKK_durationLabel.textColor = MetalienKK_ColorHexAlpha(@"#A8B0B9", 0.7); _metalienKK_durationLabel.hidden = YES; } return _metalienKK_durationLabel; } - (UIView *)metalienKK_loadingView { if (!_metalienKK_loadingView) { _metalienKK_loadingView = [[UIView alloc] init]; _metalienKK_loadingView.hidden = YES; // 添加图标 UIImageView * iconImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"metalienKKImage_metalienIconBig"]]; iconImageView.contentMode = UIViewContentModeScaleAspectFit; [_metalienKK_loadingView addSubview:iconImageView]; [iconImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.centerX.equalTo(_metalienKK_loadingView); make.size.mas_equalTo(CGSizeMake(40, 40)); }]; // 添加说明 UILabel * tipLabel = [[UILabel alloc] init]; tipLabel.font = MetalienKK_RegularFont(12); tipLabel.text = MetalienKK_LocalizedString(@"加载中..."); tipLabel.textColor = MetalienKK_ColorHexAlpha(@"#848C96", 0.6); [_metalienKK_loadingView addSubview:tipLabel]; [tipLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(_metalienKK_loadingView); make.bottom.equalTo(_metalienKK_loadingView); }]; } return _metalienKK_loadingView; } # pragma mark - Dealloc - (void)dealloc { // 移除通知 [[NSNotificationCenter defaultCenter] removeObserver:self]; // 销毁播放器 [self.metalienKK_videoEngine stop]; [self.metalienKK_videoEngine removeTimeObserver]; [self.metalienKK_videoEngine.playerView removeFromSuperview]; [self.metalienKK_videoEngine closeAysnc]; self.metalienKK_videoEngine = nil; } /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { // Drawing code } */ @end 旋转视图时,为什么播放器的视频会黑一下,再显示出来的?
最新发布
07-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值