Android 사용자 정의 View 의 심 플 한 가사 컨트롤 실전 안내

머리말
최근 에는 이전 음악 재생 기 를 재 구성 해 가사,다운로드 기능 등 다양한 기능 을 추가 했다.이 글 은 가사 컨트롤 의 실현 에 대해 이야기 하고 효과 도 를 먼저 올 리 며 불가사리 가 느껴 지면 계속 보 자!

여 기 를 보면 이 컨트롤 에 관심 이 있 을 거 예요.그럼 이 가사 컨트롤 을 실현 하기 위해 무엇 을 해 야 하 는 지 봅 시다!
가사
우선,우 리 는 정상 적 인 가사 형식 이 어떤 지 알 아야 한다.대략 이렇게 생 겼 다.
 1[ti:좋아해요]
 2[ar:.]
 3[al:]
 4[by:]
 5[offset:0]
 6[00:0.10]널 좋아해-G.E.M.덩 쯔 기(젬 탕)
 망아지
 8[00:0.030]곡:망아지
 편곡:Lupo Groinig
10[00:00.50]
11[00:12.65]가랑비 가 바람 을 타고 황혼의 거 리 를 적 셨 다.
12[00:18.61]빗물 을 지우 고 두 눈 을 고향 없 이 쳐다보다
13[00:24.04]외 로 운 저녁 등불 을 바라보다
14[00:26.91]
15[00:27.44]그 슬 픈 기억
16[00:30.52]
17[00:34.12]다시 마음속 에 무수 한 그리움 이 떠오르다
18[00:39.28]
19[00:40.10]잠시 동안 의 웃음 이 여전히 얼굴 에 걸 려 있다.
20[00:45.49]지금 알 수 있 기 를 바 랍 니 다.
21[00:48.23]
22[00:48.95]진심으로 말씀 드 리 겠 습 니 다.
23[00:53.06]
24[00:54.35]너의 그 두 눈 이 마음 에 들 어.
25[00:59.35]
26[01:0.10]웃음소리 가 더 매력적이다
27[01:02.37]
28[01:03.15]더 위로 해 드 리 겠 습 니 다.
29[01:08.56]
30[01:0935]그 귀여운 얼굴
31[01:12.40]손 잡 고 잠꼬대 하기
32[01:14.78]
33[01:15.48]어제 처럼 너 와 나
34[01:20.84]
35[01:26.32]이상 적 인 내 가 얼마나 충동 적 이 었 는 지
36[01:32.45]그녀 와 사랑 하 는 것 이 자 유 롭 지 못 하 다 고 여러 번 원망 한다.
37[01:37.82]지금 이라도 알 수 있 기 를 바 랍 니 다.
38[01:40.40]
39[01:41.25]진심으로 말씀 드 리 겠 습 니 다.
40[01:44.81]
41[01:46.39]너의 그 두 눈 이 마음 에 들 어.
42[01:51.72]
43[01:52.42]웃음소리 가 더 매력적이다
44[01:54.75]
45[01:55.48]조금 만 더 위로 해 주 셨 으 면 좋 겠 습 니 다.
46[02:00.93]
47[02:1.68]그 귀여운 얼굴
48[02:03.99]
49[02:04.73]손 잡 고 잠꼬대 하기
50[02:07.13]
51[02:07.82]어제 처럼 너 와 나
52[02:14.53]
53[02:25.54]밤마다 혼자 다 녀 요.
54[02:29.30]여기저기 서 얼마나 차 가운 지.
55[02:35.40]
56[02:37.83]예전 에는 자기 자신 을 위해 발 버 둥 쳤 다.
57[02:41.62]그녀의 고통 을 몰 랐 다.
58[02:52.02]
59[02:54.11]너의 그 두 눈 이 마음 에 들 어.
60[03:00.13]웃음소리 가 더 매력적이다
61[03:02.38]
62[03:03.14]조금 만 더 위로 해 주 셨 으 면 좋 겠 습 니 다.
63[03:08.77]
64[03:09.33]그 귀여운 얼굴
65[03:11.71]
66[03:12.41]손 잡 고 잠꼬대 하기
67[03:14.61]
68[03:15.45]어제 처럼 너 와 나
위 에서 보 듯 이 이 격식 은 앞 이 시작 시간 이 고 왼쪽 에서 오른쪽으로 일일이 대응 분,초,밀리초,뒤 가 가사 다.그래서 우 리 는 모든 문장의 가사 정 보 를 저장 하기 위해 실체 클래스 를 만들어 야 한다.
1.가사 실체 류 LrcBean

 1public class LrcBean {
 2    private String lrc;//  
 3    private long start;//    
 4    private long end;//    
 5
 6    public String getLrc() {
 7        return lrc;
 8    }
 9
10    public void setLrc(String lrc) {
11        this.lrc = lrc;
12    }
13
14    public long getStart() {
15        return start;
16    }
17
18    public void setStart(long start) {
19        this.start = start;
20    }
21
22    public long getEnd() {
23        return end;
24    }
25
26    public void setEnd(long end) {
27        this.end = end;
28    }
29}
모든 가사,우 리 는 시작 시간,끝 시간 과 가사 라 는 메 시 지 를 필요 로 한다.그러면 의문 이 생 길 까?위 에서 언급 한 가사 양식 은 가사 시작 시간 밖 에 없 는 것 같은 데 끝 날 시간 을 어떻게 알 아 요?사실 매우 간단 하 다.이 가사의 시작 시간 은 바로 이전 가사의 끝 시간 이다.가사 실체 류 가 있 으 면 우 리 는 가 사 를 해석 해 야 한다!
2.가사 해석 도구 류 LrcUtil

 1public class LrcUtil {
 2
 3    /**
 4     *     ,         LrcBean   
 5     * @param lrcStr       ,        ,   
 6     * [ti:   ]
 7     * [ar:.]
 8     * [al:]
 9     * [by:]
10     * [offset:0]
11     * [00:00.10]    - G.E.M.     (Gem Tang)
12     * [00:00.20] :   
13     * [00:00.30] :   
14     * [00:00.40]  :Lupo Groinig
15     * @return     
16     */
17    public static List<LrcBean> parseStr2List(String lrcStr){
18        List<LrcBean> res = new ArrayList<>();
19        //              
20        String[] subLrc = lrcStr.split("
"); 21 // , , 22 for (int i = 5; i < subLrc.length; i++) { 23 String lineLrc = subLrc[i]; 24 //[00:00.10] - G.E.M. (Gem Tang) 25 String min = lineLrc.substring(lineLrc.indexOf("[")+1,lineLrc.indexOf("[")+3); 26 String sec = lineLrc.substring(lineLrc.indexOf(":")+1,lineLrc.indexOf(":")+3); 27 String mills = lineLrc.substring(lineLrc.indexOf(".")+1,lineLrc.indexOf(".")+3); 28 // , 29 long startTime = getTime(min,sec,mills); 30 // 31 String lrcText = lineLrc.substring(lineLrc.indexOf("]")+1); 32 // , 33 if(lrcText.equals("")) continue; 34 // , , 35 // (《 (Passengers)》 ) - G.E.M. (Gem Tang) 36 if (i == 5) { 37 int lineIndex = lrcText.indexOf("-"); 38 int first = lrcText.indexOf("("); 39 if(first<lineIndex&&first!=-1){ 40 lrcText = lrcText.substring(0,first)+lrcText.substring(lineIndex); 41 } 42 LrcBean lrcBean = new LrcBean(); 43 lrcBean.setStart(startTime); 44 lrcBean.setLrc(lrcText); 45 res.add(lrcBean); 46 continue; 47 } 48 // 49 LrcBean lrcBean = new LrcBean(); 50 lrcBean.setStart(startTime); 51 lrcBean.setLrc(lrcText); 52 res.add(lrcBean); 53 // , , 100s 54 if(i == subLrc.length-1){ 55 res.get(res.size()-1).setEnd(startTime+100000); 56 }else if(res.size()>1){ 57 // 1 , 58 res.get(res.size()-2).setEnd(startTime); 59 } 60 61 } 62 return res; 63 } 64 65 /** 66 * 67 * @param min 68 * @param sec 69 * @param mills 70 * @return 71 */ 72 private static long getTime(String min,String sec,String mills){ 73 return Long.valueOf(min)*60*1000+Long.valueOf(sec)*1000+Long.valueOf(mills); 74 } 75}
위의 코드 와 주석 이 이 가 사 를 잘 해석 했다 고 믿 습 니 다.주의해 야 할 것 은 위 에서 i=5,즉 가사 가 진정 으로 시 작 된 첫 번 째 문장 에 대해 특수 처 리 를 한 것 입 니 다.i=5 라 는 문장 은 매우 길 수 있 기 때 문 입 니 다.i=5 는'광년 밖'이 라 고 가정 합 니 다.
(《우주 여행객(Passengers)》영화 중국 구 주제 곡)-G.E.M.덩 쯔 기(Gem)
탕)'이 가 사 는 우리 가 특별한 처 리 를 하지 않 고 뒤에서 그 릴 때 이 가사 가 화면 크기 를 초과 하여 미관 에 영향 을 미 치 는 것 을 발견 할 수 있 기 때문에 우 리 는 노래 제목 과 가수 만 캡 처 하고 어떤 설명 은 직접 생략 했다.가 사 를 해석 해 봤 습 니 다.다음은 앙코르 입 니 다.-가사 그리 기!
가사
가 사 를 그 리 는 것 은 사용자 정의 View 지식 과 관련 되 기 때문에 사용자 정의 View 를 접 하지 못 한 파트너 는 먼저 자체 보기 의 기초 지식 을 살 펴 봐 야 한다.가사 그리 기의 주요 작업 은 주로 아래 몇 부분 으로 구성 된다.
4.567917.가사 컨트롤 에 사용자 정의 속성 을 설정 하고 구조 방법 에서 사용자 정의 속성 을 가 져 오고 설정 하 는 기본 값4.567917.두 개의 화필 을 초기 화 합 니 다.가 사 는 보통 붓 이 고 가 사 는 하 이 라이트 붓 이다현재 재생 중인 가사의 위 치 를 가 져 옵 니 다4.567917.가 사 를 그리고 현재 가 사 를 재생 하 는 위치 에 따라 어떤 그림 으로 획 을 그 릴 지 결정 한다.
가사
다시 그립 니 다.
1.자체 보기 속성 을 설정 하고 코드 에 기본 값 을 설정 합 니 다.
res 파일 의 values 에 attrs.xml 파일 을 새로 만 든 다음 가사의 사용자 정의 View 속성 을 정의 합 니 다.

1<?xml version="1.0" encoding="utf-8"?>
2<resources>
3    <declare-styleable name="LrcView">
4        <attr name="highLineTextColor" format="color|reference|integer"/>
5        <attr name="lrcTextColor" format="color|reference|integer"/>
6        <attr name="lineSpacing" format="dimension"/>
7        <attr name="textSize" format="dimension"/>
8    </declare-styleable>
9</resources>
가사 색상,가사 하 이 라이트 색상,가사 크기,가사 줄 간격 속성 만 사용자 정의 하여 필요 에 따라 스스로 추가 할 수 있 습 니 다.
그리고 자바 코드 에 기본 값 을 설정 합 니 다.

 1    private int lrcTextColor;//    
 2    private int highLineTextColor;//      
 3    private int width, height;//    
 4    private int lineSpacing;//   
 5    private int textSize;//    
 6
 7    public LrcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
 8        super(context, attrs, defStyleAttr);
 9        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LrcView);
10        lrcTextColor = ta.getColor(R.styleable.LrcView_lrcTextColor, Color.GRAY);
11        highLineTextColor = ta.getColor(R.styleable.LrcView_highLineTextColor, Color.BLUE);
12        float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
13        float scale = context.getResources().getDisplayMetrics().density;
14        //       16sp
15        textSize = ta.getDimensionPixelSize(R.styleable.LrcView_textSize, (int) (16 * fontScale));
16        //      30dp
17        lineSpacing = ta.getDimensionPixelSize(R.styleable.LrcView_lineSpacing, (int) (30 * scale));
18        //  
19        ta.recycle();
20    }
2.붓 두 자루 초기 화

 1    private void init() {
 2        //       
 3        dPaint = new Paint();
 4        dPaint.setStyle(Paint.Style.FILL);//  
 5        dPaint.setAntiAlias(true);//   
 6        dPaint.setColor(lrcTextColor);//    
 7        dPaint.setTextSize(textSize);//    
 8        dPaint.setTextAlign(Paint.Align.CENTER);//    
 9
10        //         
11        hPaint = new Paint();
12        hPaint.setStyle(Paint.Style.FILL);
13        hPaint.setAntiAlias(true);
14        hPaint.setColor(highLineTextColor);
15        hPaint.setTextSize(textSize);
16        hPaint.setTextAlign(Paint.Align.CENTER);
17    }
우 리 는 초기 화 하 는 방법 을 구조 방법 에 넣 었 다.그러면 다시 그 릴 때 다시 초기 화 하 는 것 을 피 할 수 있다.또한 우 리 는 init 방법 을 세 번 째 구조 방법 에 만 넣 었 기 때문에 위의 두 가지 구조 방법 은 슈퍼 를 this 로 바 꾸 어야 한다.그러면 어떤 구조 방법 이 든 init 방법 을 실행 할 수 있 도록 보장 할 수 있다.

 1    public LrcView(Context context) {
 2        this(context, null);
 3    }
 4
 5    public LrcView(Context context, @Nullable AttributeSet attrs) {
 6        this(context, attrs, 0);
 7    }
 8
 9    public LrcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
10        super(context, attrs, defStyleAttr);
11        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LrcView);
12        ......
13        //  
14        ta.recycle();
15        init();
16    }
3.onDraw 방법 반복
뒤의 절 차 는 모두 onDraw 방법 에서 실행 되 기 때문에 우 리 는 먼저 onDraw 방법 중의 코드 를 붙 입 니 다.

 1    @Override
 2    protected void onDraw(Canvas canvas) {
 3        super.onDraw(canvas);
 4
 5        getMeasuredWidthAndHeight();//        
 6        getCurrentPosition();//         
 7        drawLrc(canvas);//   
 8        scrollLrc();//    
 9        postInvalidateDelayed(100);//  0.1s  
10    }
1.컨트롤의 측정 너비 획득

1     private int width, height;//    
2    private void getMeasuredWidthAndHeight(){
3        if (width == 0 || height == 0) {
4            width = getMeasuredWidth();
5            height = getMeasuredHeight();
6        }
7    }
왜 컨트롤 의 너비 와 높이 를 얻 으 려 고 합 니까?아래 에서 우 리 는 가 사 를 그 려 야 하기 때문에 가 사 를 그 릴 때 그 려 야 할 위 치 는 컨트롤 의 너비 와 높이 를 사용 해 야 한다.
2.현재 가사의 위 치 를 얻는다

 1     private List<LrcBean> lrcBeanList;//    
 2    private int currentPosition;//       
 3    private MediaPlayer player;//      
 4
 5
 6    private void getCurrentPosition() {
 7        int curTime = player.getCurrentPosition();
 8        //         10  ,       ,        0
 9        if (curTime < lrcBeanList.get(0).getStart()||curTime>10*60*1000) {
10            currentPosition = 0;
11            return;
12        } else if (curTime > lrcBeanList.get(lrcBeanList.size() - 1).getStart()) {
13            currentPosition = lrcBeanList.size() - 1;
14            return;
15        }
16        for (int i = 0; i < lrcBeanList.size(); i++) {
17            if (curTime >= lrcBeanList.get(i).getStart() && curTime <= lrcBeanList.get(i).getEnd()) {
18                currentPosition = i;
19            }
20        }
21    }
우 리 는 현재 재생 되 고 있 는 노래 시간 에 따라 가 사 를 옮 겨 다 니 며 집합 하여 현재 재생 되 고 있 는 가사의 위 치 를 판단 한다.세심 한 당신 은 current Position=0 에 curtime>10601000 의 판단 이 있 는 것 을 발견 할 수 있 습 니 다.이 는 실제 사용 에서 플레이어 가 아직 재생 되 지 않 았 을 때 얻 는 curTime 이 매우 크기 때문에 이 판단 이 있 습 니 다(정상 적 인 노래 는 10 분 을 넘 지 않 기 때 문 입 니 다).
이 방법 에서 우 리 는 가사 집합 과 재생 기 를 발견 할 수 있 습 니 다.당신 은 곤 혹 스 러 울 수 있 습 니 다.이것 은 아직 값 을 부여 하지 않 았 지 않 습 니까?곤 혹 스 러 운 것 이 옳 기 때문에 우 리 는 외부 에 가사 컨트롤 가사 집합 과 재생 기 를 전달 하 는 외부 방법 을 제공 해 야 한다.

 1    //             View 
 2    public LrcView setLrc(String lrc) {
 3        lrcBeanList = LrcUtil.parseStr2List(lrc);
 4        return this;
 5    }
 6
 7    //  mediaPlayer    View 
 8    public LrcView setPlayer(MediaPlayer player) {
 9        this.player = player;
10        return this;
11    }
외부 방법 에서 setLrc 의 인 자 는 앞에서 언급 한 표준 가사 형식의 문자열 형식 이 어야 합 니 다.그러면 우 리 는 위의 해석 도구 류 인 LrcUtil 의 해석 방법 을 이용 하여 문자열 을 가사 집합 으로 해석 할 수 있 습 니 다.
3.가 사 를 그린다

1     private void drawLrc(Canvas canvas) {
2        for (int i = 0; i < lrcBeanList.size(); i++) {
3            if (currentPosition == i) {//                
4                canvas.drawText(lrcBeanList.get(i).getLrc(), width / 2, height / 2 + i * lineSpacing, hPaint);
5            } else {
6                canvas.drawText(lrcBeanList.get(i).getLrc(), width / 2, height / 2 + i * lineSpacing, dPaint);
7            }
8        }
9    }
현재 가사의 위 치 를 알 게 되면 가 사 를 그리 기 쉽다.가 사 를 옮 겨 다 니 며 집합 하고 현재 가사 라면 하 이 라이트 펜 으로 그리고 다른 가 사 는 보통 펜 으로 그린다.여기 서 주의해 야 할 것 은 두 획 의 위치 공식 이 모두 같 고 좌표 위 치 는 x=너비 의 절반,y=높이 의 절반+현재 위치*줄 간격 이다.현재 위치 에 따라 상하 문 가 사 를 그 릴 수 있다.그래서 사실 그 려 진 후에 가 사 는 컨트롤 의 정 중앙 에서 그 려 진 것 을 발견 할 수 있 습 니 다.이것 은 아래 가사 와 동기 화 된 미끄럼 기능 의 조 화 를 편리 하 게 하기 위해 서 입 니 다.
4.가사 동시 슬라이딩

 1     //    
 2    private void scrollLrc() {
 3        //          
 4        long startTime = lrcBeanList.get(currentPosition).getStart();
 5        long currentTime = player.getCurrentPosition();
 6
 7        //      , 0.5     ,       
 8        float y = (currentTime - startTime) > 500 ? currentPosition * lineSpacing : lastPosition * lineSpacing + (currentPosition - lastPosition) * lineSpacing * ((currentTime - startTime) / 500f);
 9        scrollTo(0,(int)y);
10        if (getScrollY() == currentPosition * lineSpacing) {
11            lastPosition = currentPosition;
12        }
13    }
신축성 슬라이딩 이 이 루어 지지 않 는 다 면 현재 재생 곡 의 시간 이 현재 위치 가사의 종료 시간 보다 큰 지 판단 한 다음 scrollTo(0,(int)current Position*lineSpacing)슬라이딩 을 하면 됩 니 다.그러나 탄력 적 인 미끄럼 을 실현 하기 위해 우 리 는 한 번 의 미끄럼 을 몇 번 의 작은 미끄럼 으로 나 누 어 한 시간 안에 완성 해 야 한다.그래서 우 리 는 Y 의 값 을 동적 으로 설정 하고 계속 다시 그리 기 때문에 0.5 초 안에 View 의 미끄럼 을 완성 할 수 있다.그러면 가사 가 동시에 탄력 적 으로 미 끄 러 질 수 있다.
500 은 사실 0.5s 입 니 다.여기 서 currentTime 과 startTime 의 단 위 는 모두 ms 이기 때 문 입 니 다.

1        float y = (currentTime - startTime) > 500 ? currentPosition * lineSpacing : lastPosition * lineSpacing + (currentPosition - lastPosition) * lineSpacing * ((currentTime - startTime) / 500f);
5.끊임없이 다시 그린다
끊임없이 다시 그 려 야만 가사 가 동시에 미 끄 러 지 는 것 을 실현 할 수 있 습 니 다.여 기 는 0.1s 간격 으로 다시 그 릴 수 있 습 니 다.

1postInvalidateDelayed(100);//  0.1s  
이게 끝 인 줄 알 아?사실은 아직 없습니다.답 은 다음 에 발표 하 겠 습 니 다!
사용
그리고 xml 에서 이 사용자 정의 View 를 인용 합 니 다.
LrcView 앞의 이름 은 이러한 종류의 전체 패키지 이름 을 만 들 수 있 습 니 다.

1    <com.example.library.view.LrcView
2        android:id="@+id/lrcView"
3        android:layout_width="match_parent"
4        android:layout_height="match_parent"
5        app:lineSpacing="40dp"
6        app:textSize="18sp"
7        app:lrcTextColor="@color/colorPrimary"
8        app:highLineTextColor="@color/highTextColor"
9        />
자바 코드 에서 이 사용자 정의 View 에 표준 가사 문자열 과 재생 기 를 입력 합 니 다.

1lrcView.setLrc(lrc).setPlayer(player);
클릭 하여 실행 하고 자신의 성 과 를 기대 합 니 다.그 다음 에 당신 은 멍 한 얼굴 을 할 것 입 니 다.what?왜 공백 이 야,아무것도 없어!사실 이때 위 가사 가 그 려 진 절 차 를 다시 정리 하면 문제점 을 발견 할 수 있다.우선 사용자 정의 View 컨트롤 이 레이아웃 에 인 용 될 때 onDraw 방법 을 먼저 실행 합 니 다.따라서 setLrc 와 setPlayer 방법 을 호출 하면 onDraw 방법 을 다시 호출 하지 않 습 니 다.가사 문자열 과 재생 기 를 입력 하지 않 은 것 과 같 기 때문에 당연히 공백 이 표 시 됩 니 다.
해결 방법:방금 사용자 정의 View 가사 컨트롤 에 onDraw 를 호출 하 는 외부 방법 을 추가 하 였 습 니 다.마침 이 invalidate()는 onDraw 방법 을 다시 호출 할 수 있 습 니 다.

1    public LrcView draw() {
2        currentPosition = 0;
3        lastPosition = 0;
4        invalidate();
5        return this;
6    }
그리고 우 리 는 메 인 코드 에서 setLrc 와 setPlayer 를 호출 한 후에 draw 방법 을 호출 해 야 합 니 다.

1lrcView.setLrc(lrc).setPlayer(player).draw();
이렇게 해서 우리 의 절약 풍 가사 컨트롤 이 큰 성 과 를 거 두 었 다.
총결산
안 드 로 이 드 사용자 정의 View 의 심 플 한 가사 컨트롤 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 안 드 로 이 드 심 플 한 가사 컨트롤 내용 은 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 부탁드립니다!

좋은 웹페이지 즐겨찾기