Android manual write smooth scrolling lyrics control, android Control

Source: Internet
Author: User
Tags drawtext

Android manual write smooth scrolling lyrics control, android Control
I am about to graduate. I have been busy with my graduation design and graduation thesis some time ago (egg pain is linked to chrysanthemum pain). I am working on an android music player, today, I specially extracted a feature to share this blog-the display of the lyrics.
Look at QQ music, And the lyrics are displayed slightly. Unfortunately, our LRC files cannot synchronize words, but only the rows can be synchronized. So, let's go back and find out that the space for today's lyrics is just a synchronization line, so what functions does he have? The synchronization of the lyrics won't be said. I added the sliding effect because I watched the switching of a row too hard.
Enter the topic.

1. First, let's take a look at how to use the control. The use of the control is very simple and can be configured and used in xml:

<org.loader.liteplayer.ui.LrcView        xmlns:lrc="http://schemas.android.com/apk/res/org.loader.liteplayer"        android:id="@+id/play_first_lrc_2"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginTop="5dp"        android:layout_marginBottom="5dp"        lrc:textSize="18sp"        lrc:normalTextColor="@android:color/white"        lrc:currentTextColor="@color/main"        lrc:dividerHeight="20dp"        lrc:rows="9" />

Here we will look at several configuration items using lrc as the namespace.
TextSize should be needless to say. It must be the text size. normalTextColor is the color of common text. Because the lyrics are divided into common rows and current highlighted lines, currentTextColor must be the color of highlighted lines; dividerHeight indicates the line spacing, rows indicates the number of lines of lyrics displayed, and 9 lines of lyrics are displayed in this configuration file. After configuration, we need to use it in activity or fragment.

...mLrcViewOnSecondPage = (LrcView) lrcView.findViewById(R.id.play_first_lrc_2);...mLrcViewOnSecondPage.setLrcPath(lrcPath);...@Overridepublic void onPublish(int progress) {if(mLrcViewOnSecondPage.hasLrc()) mLrcViewOnSecondPage.changeCurrent(progress);}


The first line of code gets the control, and then calls setLrcPath to load the lyrics to the memory. In the onPushlish method, changeCurrent is continuously called to update the lyrics. Where does changeCurrent parameter come from? This is the progress of the music playback callback. At this point, there may be some questions. Will this constantly update the lyrics control? Even if the current lyrics are not switched back to update? Here, I will give you an answer: Of course not. We have made a judgment in the changeCurrent method, so despite the call, you can call it with confidence!


Next, let's start with today's topic: LrcView.


Before entering the code, let's take a look at my design ideas:
When we upload the path of an lrc file, first read the file according to the row, and store the time and lyrics separately using the regular expression. After the lyrics are set, we constantly call changeCurrent () to switch the lyrics. What is changeCurrent responsible? In changeCurrent, first determine whether the start time of the next row is later than the current input time. If yes, return directly. Otherwise, traverse all the time, find the key of the previous line greater than the current time, find the lyrics again through the key, click it, and it will be OK.


Look code:

Public class LrcView extends View {private static final int SCROLL_TIME = 500; private static final String DEFAULT_TEXT = "no lyrics "; private List <String> mLrcs = new ArrayList <String> (); // store the lyrics private List <Long> mTimes = new ArrayList <Long> (); // storage time private long mNextTime = 0l; // Save the start time of the next sentence private int mViewWidth; // view width private int mLrcHeight; // height of the lrc interface private int mRows; // number of rows private int mCurrentLine = 0; // The current row private int mOffsetY; // The offset private int mMaxScroll on y; // maximum sliding distance = height of a line of lyrics + pitch of the lyrics private float mTextSize; // font private float mDividerHeight; // line spacing private Rect mTextBounds; private Paint mNormalPaint; // General font private Paint mCurrentPaint; // size of the current lyrics private Bitmap mBackground; private Scroller mScroller; public LrcView (Context context, AttributeSet attrs) {this (context, attrs, 0);} public LrcView (Context context, AttributeSet attrs, int defStyleAttr) {super (context, attrs, defStyleAttr); mScroller = new Scroller (context, new LinearInterpolator ()); inflateAttributes (attrs );}...}

So many variables! What is it! Is it just for B? NO. We need to define it. Let's explain it one by one.
The constant SCROLL_TIME defines the sliding time when the lyrics are switched. The time here is 500 ms.
The constant DEFAULT_TEXT defines the default text displayed when no lyrics exist.
There are two arraylists. mLrcs stores the lyrics of a row, and mTimes stores the corresponding time of the lyrics.
MNextTime indicates the start time of the next line.
For some other variables, you can check the comments in the Code. They are different here.


Let's take a look at the constructor method. Apart from initializing Scroller, we call inflateAttributes (). Then we follow up with inflateAttributes ():

// Initialize private void inflateAttributes (AttributeSet attrs) {// <begin> // parse custom attribute TypedArray ta = getContext (). obtainStyledAttributes (attrs, R. styleable. lrc); mTextSize = ta. getDimension (R. styleable. lrc_textSize, 501_f); mRows = ta. getInteger (R. styleable. lrc_rows, 5); mDividerHeight = ta. getDimension (R. styleable. lrc_dividerHeight, 0.0f); int normalTextColor = ta. getColor (R. styleable. lrc_normalTextColor, 0 xffffffff); int currentTextColor = ta. getColor (R. styleable. lrc_currentTextColor, 0xff00ffde); ta. recycle (); // </end> // calculate the height of the lrc panel mLrcHeight = (int) (mTextSize + mDividerHeight) * mRows + 5; mNormalPaint = new Paint (); mCurrentPaint = new Paint (); // initialize paintmNormalPaint. setTextSize (mTextSize); mNormalPaint. setColor (normalTextColor); mNormalPaint. setAntiAlias (true); mCurrentPaint. setTextSize (mTextSize); mCurrentPaint. setColor (currentTextColor); mCurrentPaint. setAntiAlias (true); mTextBounds = new Rect (); mCurrentPaint. getTextBounds (DEFAULT_TEXT, 0, DEFAULT_TEXT.length (), mTextBounds); mMaxScroll = (int) (mTextBounds. height () + mDividerHeight );}

5 ~ 12 rows: parse the attribute values. There is nothing to say. It is nothing more than getting the color of the user configuration, the font size, and the number of rows.
The 16 rows show how many Heights the Lrc can display based on the obtained attributes.
Then, the next series of actions are to initialize two pasters and obtain the maximum rolling distance of Scroller. Why do we need to calculate this? Because we need to know how long the lyrics are to be rolled each time. (Nonsense !)
After Initialization is complete, it is the measurement. Our measurement is also very simple.

@ Overrideprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {super. onMeasure (widthMeasureSpec, heightMeasureSpec); // reset the int measuredHeightSpec = MeasureSpec. makeMeasureSpec (mLrcHeight, MeasureSpec. AT_MOST); super. onMeasure (widthMeasureSpec, measuredHeightSpec);} @ Overrideprotected void onSizeChanged (int w, int h, int oldw, int oldh) {super. onSizeChanged (w, h, oldw, oldh); // get the view width mViewWidth = getMeasuredWidth ();}

Measurement, we just redefined the height, and then obtained the width of the view in onSizeChanged.


According to the progress, we should look at draw next, but now we should look at the setLrcPath method instead of onDraw.

// Method provided externally // set the lrc path public void setLrcPath (String path) {reset (); File file = new File (path); if (! File. exists () {postInvalidate (); return;} BufferedReader reader = null; try {reader = new BufferedReader (new InputStreamReader (new FileInputStream (file ))); string line = ""; String [] arr; while (null! = (Line = reader. readLine () {arr = parseLine (line); if (arr = null) continue; // if it is parsed, there is only one if (arr. length = 1) {String last = mLrcs. remove (mLrcs. size ()-1); mLrcs. add (last + arr [0]); continue;} mTimes. add (Long. parseLong (arr [0]); mLrcs. add (arr [1]) ;}} catch (Exception e) {e. printStackTrace ();} finally {if (reader! = Null) {try {reader. close ();} catch (IOException e) {e. printStackTrace ();}}}}

Although it is a little longer, it is basically java io. Read files by row and then perform regular matching.
Note that 22 ~ 26 rows. here we need to explain that we just matched the form as follows:

[. 59] I am from heaven

Such lyrics. Continue with the parseLine () method. Match the lyrics with regular expressions.

// Parse each line private String [] parseLine (String line) {Matcher matcher = Pattern. compile ("\ [\ d. + \]. + "). matcher (line); // if there is nothing behind [xxx], return NULL if (! Matcher. matches () {System. out. println ("throws" + line); return null;} line = line. replaceAll ("\ [", ""); String [] result = line. split ("\]"); result [0] = String. valueOf (parseTime (result [0]); return result ;}

It's just a simple regular expression. If you don't understand it, you can fill in the regular expression. Here we only match [starting with a number]. If it's not a number, for example, [title: unfortunately not if it's not], in this way, we discard it directly. In this method, each line of lyrics is saved, but the time still needs to be processed by calling the parseTime () method. Continue with parseTime ().

// Resolution time private Long parseTime (String time) {// 0:02. 12 String [] min = time. split (":"); String [] sec = min [1]. split ("\\. "); long minInt = Long. parseLong (min [0]. replaceAll ("\ D + ",""). replaceAll ("\ r ",""). replaceAll ("\ n ",""). trim (); long secInt = Long. parseLong (sec [0]. replaceAll ("\ D + ",""). replaceAll ("\ r ",""). replaceAll ("\ n ",""). trim (); long milInt = Long. parseLong (sec [1]. replaceAll ("\ D + ",""). replaceAll ("\ r ",""). replaceAll ("\ n ",""). trim (); return minInt * 60*1000 + secInt * 1000 + milInt * 10 ;}

It is also very simple, by dividing the time in the form of ". 12", and returning in milliseconds at the end.


So far, all the time corresponding to the lyrics have been saved. Next, you need to call the changeCurrent () method to switch the lyrics.

// Method provided externally // pass in the current playback time public synchronized void changeCurrent (long time) {// if the current time is earlier than the start time of the next sentence // returnif (mNextTime> time) {return ;} // The storage time for each incoming request is traversed (int I = 0; I <mTimes. size (); I ++) {// It is found that this time is later than the passed in time. // now, the corresponding line before this time should be displayed again. // do you need to judge whether: if (mTimes. get (I)> time) {mNextTime = mTimes. get (I); mScroller. abortAnimation (); mScroller. startScroll (I, 0, 0, mMaxScroll, SCROLL_TIME); // mNex TTime = mTimes. get (I); // mCurrentLine = I <= 1? 0: I-1; postInvalidate (); return ;}}}

6 ~ Check whether the input time is later than the time of the next row. If not, return directly to avoid over-repainting.
Next, traverse all the time. If we find that the time is later than the passed in time, it proves that the last line is displayed, save the time, and start a Scroller. The startScroll method parameters are set in this way. The value of x is useless in scroll, So we store the current key and set its degree of change to 0. The value of y ranges from 0 to mMaxScroll.
Next, let's take a look at how computeScroll () works.

@Overridepublic void computeScroll() {if(mScroller.computeScrollOffset()) {mOffsetY = mScroller.getCurrY();if(mScroller.isFinished()) {int cur = mScroller.getCurrX();mCurrentLine = cur <= 1 ? 0 : cur - 1;mOffsetY = 0;}postInvalidate();}}

The variation value of y is used as the moving offset, and x is of course the current row.


Next, we will begin to enter the onDraw method.

@ Overrideprotected void onDraw (Canvas canvas) {// float centerY = (getMeasuredHeight () + mTextBounds. height ()-mDividerHeight)/2; float centerY = (getMeasuredHeight () + mTextBounds. height ()/2; if (mLrcs. isEmpty () | mTimes. isEmpty () {canvas. drawText (DEFAULT_TEXT, (mViewWidth-mCurrentPaint. measureText (DEFAULT_TEXT)/2, centerY, mCurrentPaint); return;} String currentLrc = mLrcs. get (mCurrentLin E); float currentX = (mViewWidth-mCurrentPaint. measureText (currentLrc)/2; // draw the current row canvas. drawText (currentLrc, currentX, centerY-mOffsetY, mCurrentPaint); float offsetY = mTextBounds. height () + mDividerHeight; int firstLine = mCurrentLine-mRows/2; firstLine = firstLine <= 0? 0: firstLine; int lastLine = mCurrentLine + mRows/2 + 2; lastLine = lastLine> = mLrcs. size ()-1? MLrcs. size ()-1: lastLine; // draw the for above the current row (int I = mCurrentLine-1, j = 1; I> = firstLine; I --, j ++) {String lrc = mLrcs. get (I); float x = (mViewWidth-mNormalPaint. measureText (lrc)/2; canvas. drawText (lrc, x, centerY-j * offsetY-mOffsetY, mNormalPaint);} // draw the for (int I = mCurrentLine + 1, j = 1; I <= lastLine; I ++, j ++) {String lrc = mLrcs. get (I); float x = (mViewWidth-mNormalPaint. measureText (lrc)/2; canvas. drawText (lrc, x, centerY + j * offsetY-mOffsetY, mNormalPaint );}}

First, we calculated the middle position of the view, because our lyrics were drawn from the middle to both sides.
5 ~ 10 lines. If the lyrics are empty, the default text "no lyrics" is displayed.
12 ~ The 15 rows are used to draw the currently singing rows. The third parameter of drawText is mOffetY, and the effect is a sliding process.


After drawing the current row, we need to draw the above and below of the current row.
17 rows are the height occupied by each row.
Rows 18 and 19 get the first line to be displayed (not the first line of the lyrics ).
Rows 20 and 21 get the last line to be displayed.
24 ~ Line 28: Draw the lyrics to be displayed on the current line. The third parameter of drawText needs to be noted. We offset the position of the row in the middle.
31 ~ The 35 rows are drawn under the current row. The principle is the same as that above.
In this way, a lyrics control with smooth scrolling effect is complete.


Finally, let's take a look at the final effect:


Finally, it is about the code. Some people say that my blog does not have a demo download. I will pay attention to it later. This code will be completed by the end of this month, it will make the music player open-source.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.