Building a Speedometer widget using Jetpack Compose Canvas API

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello Android developers my name is Mosen and after a short delay caused by my health issue I'm back with a new episode of Android developer tips in this video I have a special guest with me and together we will be creating a custom widget using jetpack compos canvas so without further Ado let's [Music] start s thanks for joining me hello hey hi thank you for having me pleasure to be here cool uh tell us some information about yourself when did you start Android yeah so I started Android in 2012 actually in my final year in my University as a part of my uh final year project we were uh I was using an arino board so to program drones like some autonomous uh drone detection like drones multiple drones flying and like being able to detect each other so we had a an Aro we had a processor on the arino which which we were programming with using C and then we had a companion mobile app on which you can see the feed coming in from the camera and like uh try to control the or try to control the drones a bit like the arino was mounted on top of these drones so you could get their life feed and try to maneuver the the drones using your uh the Android app so it was a part of my final year project basic like only a six-month thing but then and then I graduated got a job in a consulting firm and they there I actually started off with Windows phone so 2013 they they needed somebody who could build uh Windows Phone app of actually a Windows PC app and then also Windows Phone companion app and then a surface app as well like Microsoft Surface was just starting off on Microsoft yeah so that's where I started mobile and then slowly you know Windows wanted work out so I pivoted more into Android and yeah been doing Android for now close to what 10 years yeah and where are you working right now right now I'm working at X formly Twitter based out of Singapore and I've been in Singapore ever since I started my working career I actually studied here so been in Singapore for about 14 years and running cool cool yeah I know the time difference was a bit challenging for us but thanks for joining me okay whenever you're ready a stage is yours you can share your screen and we can start watching so today we'll be building this speedometer which I had written about in October last year and we'll try to record this live I just have this blog post open here for just to show what we're trying to do rather but we'll try not to use it as a reference and we'll see what are the things we need to keep in mind when we're building something like this right so this is what we're trying to build a speedometer Arc which has a progress indicator or a speed indicator and a bunch of markers and yeah the speed speed text as well as what speed you are in right so actually the thing behind this is it's a it's a very not a funny but a very random story I was in a long taxi ride uh coming back from the airport coming back home and I just was just looking at the speedometer in the taxi just seeing how fast the taxi was going I was sitting at the back seat and it just popped in my head that you know how would one build something like this and I thought yeah why not give it a try building it in compose it's just like it just the most absolute random thing just sitting at the back of a taxi SC this hey let's try go home and let's try to build it so well few weeks later went by and at one weekend I thought no why not why let's try building it so let's try to replicate that that where we try to build this and see how far we can go all right so I'll push this into presentation mode so it's more legible all right so first up we have okay let's see let's have a composable function which is just our speedometers spe Speeders okay so this will be a speedometer screen and we can have our preview up front so we are able to see preview show background and show system you [Music] just got the speed all right so yeah there's something in the preview just just an empty all right so let's look at back our speedometer that we have so we have an arc here so you want to First up like like if you try to break this down into smaller components you you have an outer AR you have a bunch of markers and you have them in in different colors so this is just a replica of what a speedometer looks like you know like these are your 020 40 like your major speed indicators and you have these minor ones which like I like to call them minor just to I a way to differentiate in my code so I would like to call these as major markers because they are text associated with them and then you have minor markers which is like your 10 30s 50s and then you just have more indicators I don't know what what to call them but just further more indicators which represent each unit right so if you're trying to fit 0 to 20 so you have 10 20 30 40 so on so forth and you have this speed indicator right at the center which topics your current speed so first up let's try to draw the arc so the arc is oh wait where is all right so we we'll begin with the canvas so let let's have an empty canvas and we could pass in a modifier all these stuff like how you wanted to look on the screen what's your modifier and all Etc it's something we'll look into later like the best practices like such as you should pass a modifier y yada we we uh not really worry about that for the purpose of this this video and this demo right so first up let's draw an arc a handy plugin in Android studio called fill function like m in case people are wondering you could just open uh download this plugin install it in your uh Android studio and then this helps to autocomplete whatever uh parameters this function is accepting so just do fill function just like to get us started and right okay so let's say we use let's use color. red right we have a red color start end angle we'll get into we don't need to worry about top left size is just going to enclose the size what we have Alpha get rid of it blend Moree no need style uh okay not for the time being Let's see we can work with the default style so let's look at back to this so if you see this one thing to know about the canvas draw R API is that it starts drawing from 3° uh from 3:00 in a clockwise Direction so if you look at the definition of draw Arc it says it draws an arc to fit the given rectangle it's starts from start angle degrees around the oval up to start angle plus sweep angle right and another important point is that if you see here this is what used to stump me earlier like the first time I was doing this I clearly remember I could not figure out how to get the arc perfectly because the starting angle is from represents 3:00 so it's it's like this so when I'm sweeping sweep it like this right so but I want to start it bit bit more at the bottom because if you see here it doesn't really start at three it starts below three which is like somewhere around 30° right so this is what we'll do we'll try to say okay you start off from 30 right and you draw me something which is all the way till 270 right so you have 270 here so this is what the the angle that you're looking at this is the entire sweep that you're looking at but not too 2 70 sorry not is it 70 it's actually I think it's actually less so this would be about my 30 this would be about 180 it's actually 240 270 would be somewhere here which is not what we want we want about something around 240 so let's change it to 240 and we are sweeping it in uh anticlockwise not clockwise right we doing anticlockwise so we give it a Nega then let's give it a style so that it's let's give it a stroke um with off let's Give It 2 DP good enough and then we don't have anything yes okay uh we don't have anything yet probably because we don't have uh the size the size that is needed on the on the screen right so how much size would this take so let's say 360 DP yeah and now you start to see disappear right uh let's just give it some padding so it looks so it's it's more legible in the in the video let's say we get a let just TDP right okay it pushes us a bit into the center of the screen right I think it should be visible now so now you have you have your Arc here so one tip I would have here which I generally use myself for debugging purposes is where's the center so we know in comp uh in Android like your top left is actually 0 comma 0 but where's the center of the circle the center of the circle would be somewhere here right so let's draw draw a line just for debugging purposes or so that you know later what you're looking at let's just say we have not for looking purposes but more like you have a context on how your subsequent code flows because it flows with respect to what the center looks like so let's say we do this and then we save you start at offset um 0f and 0 F and you end at center right and see so now you know this is your Center so this is the center we working working with we will remove this later once your speedometer are done but this is a good way to gauge okay this is my Center I see my Center this was my uh 3:00 and now I've drawn an angle from 30 to minus 240 right giving me the entire uh the entire Arc that I'm after right so it's a 270 this Center this Center is coordinat of the canvas Center corre and since we didn't set any Center to draw Arc it it fits or sits in in the center so it uses so this essentially now becomes the center of your circle or of this AR which is a part of a circle right so if I so here if you actually say okay let's draw zero to 360 you would see it's the entire circle and this is the center of okay I was expecting something more hard hardcore to find the center but any that's great okay all uh it has a use center field but that's slightly different use Center is another way you could use to figure out what is the center mhm is oh we set it to false it's already said yes if you set it to true you will see it closes itself at the center uh so that's another way to verify what is the center of the of this Arc that we're talking about looks looks more like the Pac-Man but like this is becomes the center right but I for the purpose of this I was keeping this as false because we don't want it to be closed and with the nonclosed one you can see how the speedometer is evolving as we go along MH MH right so next uh let's try to draw the markers that we have the indidual markers right so for the individual marker what we need is the marker appears every Point here right at every Point here so now with this being your center of the circle right so if if you remember high school math you'll see if this is your center of the circle right so your circle is still here like let me complete the circle again to get some idea to try to explain this one because this one was tricky for me as well mhm you have your circle here now if you draw a line from here to here right so this is and then you draw a line here you get this angle here mhm right if it doesn't really make sense I have a Graphic ready let me pull that graphic out so if you see this graphic here you can see this is what I'm trying to talk about that you have your Center here this is 0° this is 330 like this is the angle so if like let's go from here this is zero this angle here is 90 right mhh this angle from this point to this point this angle is 60 right this point to this point your angle is 30 so essentially if you look at this speedometer we essentially are from 300 to 60 right so my markers have to be drawn at an angle of 300 all the way up to 180 150 120 990 down to 60 so that's how I'm looking to draw my markers starting from 300 all the way down to 60 and then if you look at the core speedometer we are actually drawing 2 4 6 8 10 12 14 16 18 20 so we're drawing it in increments of two that's just what I chose you could choose to draw it in increments of five whatever like so for this purposes let's continue with the fact that we going to draw it in increments of two so we'll let let's set up a loop then for angle in uh 300 down to down to 60 uh step two all right so now we have our the angle so now this is 300 as we looked and this is 60 right but speed at 300 is actually zero right that's where the speed is so your speed is actually angle minus angle minus 300 so this is my speed that I need to the speed that I need to draw now I I want to draw a line here it's a it's a line which will be like this right so for to draw a line as we saw here we need a start point and an end point right so what would be the start Point here on this circle here it's it's it's we go back to trigonometry from high school that if you know if you if you've given a circle right and you know the radius of the circle and you know the angle at which you're supposed to draw that uh that if you need an angle if you need the point on the circle and you know the angle you can use trigonometry to get that point on the on the arc where that angle meets right so that function is I have it written down so we won't write it iared with this it's somewhere in my here so I have this so this is the function that we will use which is point on the circle which is essentially given the Theta de like the angle here given that angle and the radius of the circle and the starting point the center offset coordinates because the center is not 0 comma 0 right the center is something else given the center points your X point is actually CX Plus radius of sin Theta and Y is c y + radius into cos Theta so we will use this helper function to actually get the point on the arc where we are supposed to draw the marker so that will give me the start point on the circle right so this is my so then I have my start Point start offset would be point on the circle uh right point on the circle which would be this is my angle to double okay then my radius is so my radius is size size do height / two right and my this is my center. X and this is my y right so this is my start start Point like this is my this point where I want to draw and what would be my end point my end point would essentially be just a bit off from here something inside like if you look back at the diagram here you can see are you able to see the the block post again yes right if you see my start point is this and my end point is the length of this right M so let's say my let's let's define the length of these markers like my uh the longer marker or the major marker is slightly taller than the minor markers right so let's give them some some arbitrary length that we want to have so let's say my major indicator length would be like an 18 DP right so my so then my end point where is it uh my end offset end offset would be the same point on Circle Theta and degrees is essentially the same angle do two double right radius is size do height / 2 minus because now we want a smaller radius right which would be my major indicator length. 2px right so this is my uh this thing and we left with what else we left with CX is the same which is center. X and Cy is Center do y right so now we have our start point and end point we could use this to draw a line let's say where I'm using color is equal to color of black start offset start isal to start offset n isal to end offset and now we have these these lines being drawn right right we've not gone and like made them better yet now we have drawn all of them right at the same like because this this function is drawing all of them the same way we we tweak it a bit to check if it's a major or a minor but this gives you an idea one optimization we can make is if you look at the speedometer generally your your start point is not really overlapping with The Arc which in our case is overlapping right now if we zoom in you could see this is not what we want we want a slide gap between them right so we can change our start offset where we say oh our start offset actually has an initial offset right so let's add an initial offset we say okay you have an initial offset of 5 DP and we subtract that from my start point just like we did there and now everything shifts a bit in right right and now we can uh start doing the the major and minor stuff right we can say oh if my speed is actually major which means it's a Modelo of 20 right I then only draw this and now I have my major markers 0 20 40 60 uh we could add some styling to my uh to this we could give it a some kind of a we can give it a stroke width of let's say 4 DP so it's a bit Bolder looks quite bold go it to okay we can give it a 2dp of making it a bit bold then we can go ahead and say else if my speed marker modulo 10 right is equal to zero then then we want some we it's a similar stuff but we tweak it I mean of course we can later abstract all uh extract all of this out into helper functions but not for the purpose of this and then we if that's the case then let me have a minor indicator marker right which is which is not 18 which is like 14 let's say we can subtract it from this and let's give it a different color like blue right and now we have your minor indicators in the in the middle coming up right which is slightly even smaller and otherwi else if my oh you don't need a check because you're already stepping by two right so you're stepping by two stepping down here by two else if I have this then same length uh same thing same blue just that there's no stroke withd to summarize up to this point so we have a convas we use ARC to draw a a circle which is not complete and using the angle of that Circle we draw lines with some help per function to calculate the the start and end of the line and then based on the angle and if it's it's divided by 20 or 10 or other other angles we just draw different lights that that looks really cool go on all right great so now uh let's try to get the text on board like where do we place like we want the text to signify these markers 0 20 40 60 so on so forth right so the idea would be the same right because we're trying to draw text a we'll try to draw the text only at here which is your major marker we do not need it inside the minor markers right so you want to place text here which is again to draw text inside a canvas you need the text layout result as it's mentioned here right and wait yeah where we do the fill function okay so we need text layout result Alpha is optional this is optional this is optional this is optional we'll get to the optional values later uh we can do a text color here we have a color option as well so we'll just give the text color let's just call it uh let's just give black I think I think it was black or whatever we can tweak it later right so we need the text layout result we can get via a textt measure so let us uh Define our text measure here so thenos text measure okay so we have this and uh using the text measure I can get the the text layout result text layout text measure measure so I want to show the speed so I'll say speed which is which I already have and I want to have a style so let's just give it textile do default default should work for us right and oh yeah just Str okay right so now I have my text layout results so I say okay let's uh I give my text layout result here dra it so now we have the text drawn but all of them are just drawn on top of each other right so now the trick here is by using using translate right what you could do is you could move your uh canvas to a different coordinate space right and then then subsequent instructions we'll use that new coordinate space to draw so this is what we want to do for next we want to move to a different coordinate space which is essentially at these points and start drawing at these points so I was wondering we can say to text to draw in a specific place but can ask text to draw in a separate place maybe I I actually did not try that offsets the text from the top left point of the current coordinate system H we can try I did not try top left last time but if but if you look at the top left the top left is set to zero right which is essentially this zero so now what we want we want the top left to become essentially somewhere here right so if I don't translate and I say this is my top left H get there okay right but the speed is showing it negative I think uh it should be 300 minus angle angle is smaller okay right okay so we could do without translating right because the top offset does this for me but here we see that we have a slight issue and now we'll try to debug this in real time because this is not what I used so let's let's let's let's try we can get back to what I did the issue that we are seeing is that it's a bit offset it's a bit not at the right position it's a bit shifted sometimes down sometimes a bit to the right and I feel that's because uh I'm top left of the text and then corre the top left of this text what we want top left of the text to idly be the center of the text MH exactly right so what we could do is using the text layout result I actually have the height and width of my m so what I could do is that I could say okay my text height is actually text layout result dot uh size do and my text width is actually the width so then instead of saying that I want to use end offset I create a new text offset and I say my text offset is the point on the circle essentially let's just copy this it's essentially this but my my radius is now the radius still here which is essentially this guy minus my because I want it to be a bit further ahead to the center right so text withd divided by two right and I use this here so I want yeah now we getting there it's a bit text with two right uh and and then I think set up Center yeah I think you'll have to move the center a bit also your Center is essentially Center dot h did Center really work here cuz if I move this this gives us the text offset but then we need to uh uh you know divide the width and height to two and yeah we moving this a bit up and this will also need to yeah exactly yeah and I think missing something oh we only doing major indicator. length oh we missing the the offset here so we've only translated by this much we've not included this Gap here which was uh indicator initial offset oh two pixel right okay you actually don't need to translate you can you can achieve that same functionality with the with the top left of moving right and now I think this looks pretty good yeah that's very nice it it it looks all aligned if if people if you're not happy with the gaps here you can uh reduce the indicator offset or the major indicator length depending on your own requirements right if you're like oh there should be let there you can just tweak this values but yeah the text looks nicely well spread around and now we are left with our final the speed indicator right MH so now we know this is 0 comma 0 this line we had earlier we can get rid of it so now we had our this is our Center and the logic is essentially the same right you have this and now you want to draw your speed which let's say let's say the current indicator is pointing at 60 right so you can say we can essentially accept the current speed as a mhm uh input current speed as a float right and we can use this but uh we should specify that our current speed is actually limited right so let's use the flat range which is annotation we can say oh my float range this value should be between 0 to 0 uh to 240 right mhm mhm okay this is my current speed and let's just give my current speed here as 60 right okay so I have my current speed and where is it where does this end it ends here so given my current speed essentially the same thing right start offset oh you don't need the start offset the start is zero we just need the end you just do let's just take this offset is equal to current speed radius is you don't need this radius is this guy right and then you just do a draw draw line color is equal [Music] to what color start is uh Center X and Center no just Center that's an offset and N is equal to current speed offset then you it do really point in the right way let's wait stroke width is equal to 4. DP okay uh wait it's not it's pointing the wrong way what are we doing wrong here uh current speed [Music] offset uh because our zero is not the the real zero zero is not the real zero you said it's 3 o' so you have to correct yes and uh okay we probably need to reduce the radius a bit uh because we need to point it I don't know point it at least till here all right so we can do is Major indicator l. 2px so it moves back a bit right um I don't know another four now I'm just ad hoc putting in values again we could abstract that out into right uh you have this and I think another tweak yeah I think this is this looks good enough I think we could make slight changes if we want to like I remember I did a few stuff to make it it look a bit better you have a cap which is a default cap I wanted it to be rounded because if you see uh it's actually right it's or it's actually round right yeah because the speedometer right round and I think one more thing could potentially try to do I don't know if I did there yeah oh I had an uh is that you wanted to you want the speed to be shown right yeah transparency yeah you can just use an alpha channel here for the color oh it has an alpha right so we can add an alpha here we can say Okay 0.7 right yeah now you can see you went for okay beautiful okay great that's that looks very nice thanks yeah I think one thing extra I did in the in the demo was I add a slider here so and you could the slider would essentially just control the speed that's it effect right if you're like driving the speedometer actually moves but I think that's that's quite straightforward at this point where you can take your this speedometer value you can pass it as a as a state right mhh you can have a slider and you can take the value from the slider and keep on calling the speedometer screen to render itself like to recompose right and and because only the current speed will change it will only recompose the section yeah interesting yeah there are some room to Improvement but we got the whole this that's that's what I this is not production code in anyways yeah yeah for sure for sure but uh okay I learned a lot up to this point so I have never tried conversing compos before which is really nice to know that it has a center it has some some coordinate already ready to use which is really nice and yeah drawing arcs lines and yeah texting texting canvas is also interesting I had no idea uh about that text measure um cool cool I already learned lot than I learned something live right like the last time I did not use top left I remember absolutely that I translate use translate to move the canvas to that point and then draw over there and then like every time I would translate draw there and then do a restore so I can get back to my original state again in the next Loop to a translate again right but yeah this is this is quite nice thanks Sora for for joining me it's it was really nice uh tutorial um yeah as you said sometimes you see something and you think can I do it or not and you challenge yourself and it was s such an interesting challenge I already learned a lot from it uh anything else you want to add at the end no nothing else just thank you for having me it was it was great fun trying to build this live after so many months yeah thank you so much uh and uh thanks for joining me uh how can people find you on social network um I work at X or nor Twitter right so that is definitely the best way to find me my handle is uh I sort of Aurora uh just like my my name just suffix it with with I yeah I I will put the link to the article in the video description uh if you're interested you can go and take a second look and see how all the details that sorup already wrote here and thanks for joining us if you're interested in such content this is what I do in this channel so subscribe and hit the Bell button to get notified whenever there's a new new video and leave a comment how did you find this tutorial and if You' like to see more thank you so much have a nice day thank you [Music] bye [Music] a
Info
Channel: Android Developer Tips
Views: 714
Rating: undefined out of 5
Keywords: Android, Development, Android Studio, Mobile, Application, Software, mohsenoid, Tips and Tricks, Developer, Saurabh Arora, Jetpack, Compose, UI, UX, Custom Widget, Speedometer, Canvas, Draw Line, Draw Arc, Draw Text
Id: gSDoOOalX_o
Channel Id: undefined
Length: 41min 15sec (2475 seconds)
Published: Mon Apr 01 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.