Let's continue pur story about creating our own media player.
You can find a demo of the resulting videoPlayer on this page: demo of the videoPlayer - part 3.
What will we do in our third part ? :
- Add a control bar to the videoPlayer
- Display the progess of the video in this bar
- Add a play/pause button in this bar
- Enjoy ourselves!
Add a control bar to the videoPlayer
The goal is to display a control bar when the mouse is hover the video or when the media is paused.
The control bar will look like this :
First we create the gray rectangle which is behind the meta-information and the two differents text. We will add them to a group to ease the use of trigger to show/hide them.
/** ########################################### * The control bar * ########################################### */ var controlBarBackGroundRect : Rectangle = Rectangle{ x:x y: vidHeight + y - 40 width:vidWith height: 40 opacity:0.5 fill:Color.DARKGRAY } var playPauseGroupWidth = 20 ; var pauseGroup : Group = Group{content:[]}; var playGroup : Group = Group{content:[]}; var progressNode = Group{content:[]}; var groupControlBar: Group = Group{ content: [controlBarBackGroundRect,progressNode, pauseGroup, playGroup] cursor: Cursor.DEFAULT opacity: 0.0 }
Don't forget to add it in the returned Group of our component:
return Group{ content: [mediaView,groupVidMETA, groupControlBar] }
What you can notice is that we have already prepare the differents things which will be putted in : the playButton, the pauseButton, the progressNode and the grayRectangle which is the bacground ofthe controlbar.
Display the progess of the video in this bar
To display the progress of the media we will create a Progress's node. To create this progress Node we will uses the james weaver's how-to which is on this page : Progress Indicator: Creating a JavaFX Custom Node and Binding to a Model but we will customize it a little to fits our specifications.
Here is a summary of the changes :
- No more polygon for the progression but a good old Rectangle,
- Add left, right and center text.
And here is the code :
/* * ProgressNode.fx - * A custom node that functions as a progress bar * TODO: Add the ability to have an "infinite progress" look as well * * Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com) * to demonstrate how to create custom nodes in JavaFX * * Used and changed 2008 by Jonathan ANTOINE to use in his videoPlayer */ package fr.antoinej.tools; import javafx.scene.CustomNode; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.paint.Color; import javafx.scene.paint.LinearGradient; import javafx.scene.paint.Paint; import javafx.scene.paint.Stop; import javafx.scene.shape.Rectangle; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; import javafx.scene.text.TextOrigin; public class ProgressNode extends CustomNode { /* * A number from 0.0 to 1.0 that indicates the amount of progress */ public var progress:Number; /* * The fill of the progress part of the progress bar. Because * this is of type Paint, a Color or gradient may be used. */ public-init var progressFill:Paint = LinearGradient { startX: 0.0 startY: 0.0 endX: 0.0 endY: 1.0 stops: [ Stop { offset: 0.0 color: Color.rgb(0, 192, 255) }, Stop { offset: 0.20 color: Color.rgb(0, 172, 234) }, Stop { offset: 1.0 color: Color.rgb(0, 112, 174) }, ] }; /* * The fill of the bar part of the progress bar. Because * this is of type Paint, a Color or gradient may be used. */ public-init var barFill:Paint = LinearGradient { startX: 0.0 startY: 0.0 endX: 0.0 endY: 1.0 stops: [ Stop { offset: 0.0 color: Color.rgb(112, 112, 112) }, Stop { offset: 1.0 color: Color.rgb(88, 88, 88) }, ] }; /* * The color of the progress percent text on the progress bar */ public-init var progressPercentColor:Color = Color.rgb(191, 223, 239); /* * The color of the progress text on the right side of the progress bar */ public-init var progressTextColor:Color = Color.WHITE; /* * Determines the width, in pixels, of the progress bar */ public-init var width:Integer = 200; public var leftStr : String = ""; public var rightStr : String = ""; public var centerStr : String = ""; /* * Determines the height, in pixels, of the progress bar */ public-init var height:Integer = 20; /** * Create the Node */ public override function create():Node { var textRef:Text; var progBarFont =Font.font("Verdana", FontWeight.EXTRA_BOLD, 10); var leftAndRightFont =Font.font("Sans serif", FontWeight.EXTRA_BOLD, 10); return Group{ content: [ // The entire progress bar Rectangle { x:0 y:0 width: bind width height: bind height fill: bind barFill }, Rectangle { x:0 y:0 width: bind progress * width height: height fill: bind progressFill } , // The percent complete displayed on the progress bar textRef = Text { translateX: width / 2 - 20 translateY: 5 textOrigin: TextOrigin.TOP font: progBarFont fill: bind progressPercentColor content: bind "{centerStr}({progress * 100 as Integer}%)" }, Text { translateX: bind width - rightStr.length() * 6 translateY: 5 textOrigin: TextOrigin.TOP font: leftAndRightFont fill: bind progressPercentColor content: bind "{rightStr}" }, Text { translateX: 4 translateY: 5 textOrigin: TextOrigin.TOP font: leftAndRightFont fill: bind progressPercentColor content: bind "{leftStr}" } ] } } }
Now we gonna add it to our videoPlayer changing the precedent progressNode declaration by this one :
var progressNode: ProgressNode = ProgressNode{ translateX:controlBarBackGroundRect.x + 10 + playPauseGroupWidth translateY:controlBarBackGroundRect.y + 10 progress: bind mediaPlayer.currentTime.toMillis() / media.duration.toMillis() centerStr: bind "{%tM mediaPlayer.currentTime.toDate()}:{%tS mediaPlayer.currentTime.toDate()}" height:20 width: vidWith - 20 - playPauseGroupWidth leftStr:"0:00${media.duration}" rightStr: bind "{%tM media.duration.toDate()}:{%tS media.duration.toDate()}" };
Note that we bind the rightStr to fit the sun usages given on this page.
Add a play/pause button in this bar
Now we gonna add a play/pause button. To do this we are going to create two groups, a play group which will be visible when the video is paused and a pause group visible when the video is played.
To create this buttons we are using polygons whe re some may prefers image: that's our choice !
Here is the code :
var pauseGroup : Group = Group{ var rect1 =Rectangle { x: controlBarBackGroundRect.x + 7 y: controlBarBackGroundRect.y + 13 width: 4 height: 13 fill: Color.WHITE } ; var rect2 = Rectangle { x: controlBarBackGroundRect.x + 14 y: controlBarBackGroundRect.y + 13 width: 4 height: 13 fill: Color.WHITE }; content: [rect1,rect2 , Rectangle { x: controlBarBackGroundRect.x + 4 y: controlBarBackGroundRect.y + 11 width: 17 height: 16 fill: Color.TRANSPARENT } ] cursor:Cursor.HAND //On left click, play or pause the video onMouseClicked: function(me: MouseEvent){ //Only a left click play/pause the video if(me.button == MouseButton.PRIMARY) { mediaPlayer.pause(); } } onMouseEntered: function(me: MouseEvent){ Timeline { keyFrames: [ at(0s) { rect1.fill => rect1.fill tween Interpolator.LINEAR; rect2.fill => rect1.fill tween Interpolator.LINEAR; } at(800ms) { rect1.fill => Color.RED tween Interpolator.LINEAR; rect2.fill => Color.RED tween Interpolator.LINEAR; } ] }.play(); } onMouseExited: function(me: MouseEvent){ Timeline { keyFrames: [ at(0s) { rect1.fill => rect1.fill tween Interpolator.LINEAR; rect2.fill => rect1.fill tween Interpolator.LINEAR; } at(800ms) { rect1.fill => Color.WHITE tween Interpolator.LINEAR; rect2.fill => Color.WHITE tween Interpolator.LINEAR; } ] }.play(); } visible: bind (mediaPlayer.status == mediaPlayer.PLAYING) } var playGroup : Group = Group{ var polygon = Polygon { points: [ controlBarBackGroundRect.x + 7,controlBarBackGroundRect.y + 14 , controlBarBackGroundRect.x + 7,controlBarBackGroundRect.y + 27, controlBarBackGroundRect.x + 20,controlBarBackGroundRect.y + 20 ] fill: Color.WHITE }; content: [polygon , Rectangle { x: controlBarBackGroundRect.x + 4 y: controlBarBackGroundRect.y + 11 width: 17 height: 16 fill: Color.TRANSPARENT } ] cursor:Cursor.HAND //On left click, play or pause the video onMouseClicked: function(me: MouseEvent){ //Only a left click play/pause the video if(me.button == MouseButton.PRIMARY) { mediaPlayer.play(); } } onMouseEntered: function(me: MouseEvent){ Timeline { keyFrames: [ at(0s) { polygon.fill => polygon.fill tween Interpolator.LINEAR; } at(800ms) { polygon.fill => Color.RED tween Interpolator.LINEAR; } ] }.play(); } onMouseExited: function(me: MouseEvent){ Timeline { keyFrames: [ at(0s) { polygon.fill => polygon.fill tween Interpolator.LINEAR; } at(800ms) { polygon.fill => Color.WHITE tween Interpolator.LINEAR; } ] }.play(); } visible: bind (mediaPlayer.status == mediaPlayer.PAUSED) }
You can find our component source in this post and view a demo on this page: demo of the videoPlayer - part 3.