每天写个APP_第2天——升级版音乐播放器

每天写个APP_第2天——升级版音乐播放器

android 音乐播放器 SeekBar Runnable



前面的话

:有同学说我写的不够详细,我以后会注意和完善。又说能否有源码,我一般会在文章里贴出所有源码,另外我今天会吧代码传到github上提供下载。

回顾:上篇文章实现各一个简单的播放器,但是仍然不是很酷。今天,我们对它做个改进。还记得网易云音乐APP中播放界面有个CD吗?当播放音乐时,中间的CD就会转动,是不是很酷?今天我们也把中间的CD转起来!然后,我们还要添加歌曲进度条,还可以用手指拖动进度条实现歌曲的指定播放位置。

改进点:

  • 实现CD唱片的转动!
  • 添加歌曲进度条

编程关键词

  • SeekBar类
  • Runnable类
  • Animation类
  • xml:rotate

界面

转动吧!CD

Android 平台提供了两类动画,一类是 Tween 动画,即通过对场景里的对象不断做图像变换(平移、缩放、旋转)产生动画效果;第二类是 Frame 动画,即顺序播放事先做好的图像,跟电影类似。这里我们使用 Tween动画的rotate实现旋转效果。

rotate.xml

在res文件夹新建anim文件夹,在里面新建rotae.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:fromDegrees="0"
        android:toDegrees="359"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="-1"
        android:duration="4500"
        />
</set>

解析:从0到359度开始循环旋转,0-359(若设置成360在停止时会出现停顿现象)度旋转所用时间为500ms,旋转中心距离view的左顶点为50%距离,距离view的上边缘为50%距离,即正中心,具体每个含义见下面的具体属性介绍。

android:fromDegrees 起始的角度度数

android:toDegrees 结束的角度度数,负数表示逆时针,正数表示顺时针。如10圈则比android:fromDegrees大3600即可

android:pivotX 旋转中心的X坐标

浮点数或是百分比。浮点数表示相对于Object的左边缘,如5; 百分比表示相对于Object的左边缘,如5%; 另一种百分比表示相对于父容器的左边缘,如5%p; 一般设置为50%表示在Object中心

android:pivotY 旋转中心的Y坐标

浮点数或是百分比。浮点数表示相对于Object的上边缘,如5; 百分比表示相对于Object的上边缘,如5%; 另一种百分比表示相对于父容器的上边缘,如5%p; 一般设置为50%表示在Object中心

android:duration 表示从android:fromDegrees转动到android:toDegrees所花费的时间,单位为毫秒。可以用来计算速度。

android:interpolator表示变化率,但不是运行速度。一个插补属性,可以将动画效果设置为加速,减速,反复,反弹等。默认为开始和结束慢中间快,

android:startOffset 在调用start函数之后等待开始运行的时间,单位为毫秒,若为10,表示10ms后开始运行

android:repeatCount 重复的次数,默认为0,必须是int,可以为-1表示不停止

android:repeatMode 重复的模式,默认为restart,即重头开始重新运行,可以为

reverse即从结束开始向前重新运行。在android:repeatCount大于0或为infinite时生效

android:detachWallpaper 表示是否在壁纸上运行

android:zAdjustment 表示被animated的内容在运行时在z轴上的位置,默认为

normal。

normal保持内容当前的z轴顺序

top运行时在最顶层显示

bottom运行时在最底层显示

具体参考我转载的文章:点击进入

哪里用的到:没见过刷新转圈的动画吗

在java里调用

animation=AnimationUtils.loadAnimation(this, R.anim.rotate);
        //设置匀速
LinearInterpolator linearInterpolator=new LinearInterpolator();
animation.setInterpolator(linearInterpolator);

SeekBar!

音乐播放是MediaPlayer这个类控制的,进度条SeekBar可以用来显示播放进度,用户也可以利用SeekBar的滑块来控制音乐的播放。两个按钮一个用来播放歌曲启动线程,另一个取消线程和停止播放歌曲。

SeekBar:

设置滑块的位置方法为:setProgress(int
value)
;

设置最大值的方法为:setMax(int
value);

MusicPlayer:

播放音乐方法为:start();

停止音乐播放的方法为:stop();

控制音乐播放位置为:seekTo(int
value);

获得音乐长度为:getDuration();

获得现在播放的位置:getCurrentPosition();

Handler:

启动线程:post(Runnable
runnable);

取消线程:removeCallbacks(Runnable
runnable);

延迟启动线程(time 单位为毫秒):postDelayed(Runnable,
int time);

MainActivity.java

本代码较于上篇的代码,针OOP思想做了些改善。注意SeekBar监听器的代码,你想写个老式卡带播放机子或是留声机吗?看看吧!

package com.example.playerofmusic;

import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.Window;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

import com.example.playerofmusic.R.anim;

public class MainActivity extends Activity {
    private ImageView songImage;
    private TextView songText;
    private Button songStart;
    private Button songStop;
    private Button songPause;
    private EditText editSongName;
    private MediaPlayer musicPlayer;
    //----------------以下是新添代码----------
    private Handler handler;
    private SeekBar songSeekBar;
    private Animation animation;//使用动画类
    private Runnable seekBarThread;
    //------------------------------------
    /*
     * 也可用Timer类和TimerTask类实现SeekBar的持续移动
     */
    /*private Timer mTimer;
    private TimerTask mTimerTask;
    private boolean isChanging;*/
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        /*
         * //设置为NoTitle,没有标题栏,上篇在清单文件中,添加的
         * theme属性。
         */
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        findView();//绑定控件
        setListener();
        SongImageAnim();
        musicPlayer= new MediaPlayer();//实例化MediaPlayer
        handleSongImageAnim();
        songSeekBarProgress();//开启线子程控制seekbar跟随歌曲的进度
    }

    private void songSeekBarProgress() {
        // TODO Auto-generated method stub
        seekBarThread=new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                //10毫秒执行以下线程,目的是持续改变SeekBar的位置
                songSeekBar.setProgress(musicPlayer.getCurrentPosition());
                handler.postDelayed(seekBarThread, 10);
            }
        };
    }

    private void handleSongImageAnim() {
        // TODO Auto-generated method stub
        //用handler来处理图片的旋转
        handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                // TODO Auto-generated method stub
                super.handleMessage(msg);
                if(msg.what==1){//姑且设what为1
                    if(animation!=null&&musicPlayer.isPlaying()){
                        songImage.startAnimation(animation);
                    }
                }else{
                    if(animation!=null){
                        songImage.clearAnimation();
                    }
                }
            }
        };

    }

    private void SongImageAnim() {
        // TODO Auto-generated method stub
        animation=AnimationUtils.loadAnimation(this, R.anim.rotate);
        //设置匀速
        LinearInterpolator linearInterpolator=new LinearInterpolator();
        animation.setInterpolator(linearInterpolator);
    }

    private void setListener() {
        // TODO Auto-generated method stub
        //为三个按钮设置监听器
        songPause.setOnClickListener(new ButtonListener());
        songStop.setOnClickListener(new ButtonListener());
        songStart.setOnClickListener(new ButtonListener());
        songSeekBar.setOnSeekBarChangeListener(new SongSeekBarListener());
    }

    private void findView() {
        // TODO Auto-generated method stub
        songImage=(ImageView)findViewById(R.id.image_songImage);
        songText=(TextView)findViewById(R.id.text_songName);
        editSongName=(EditText)findViewById(R.id.edit_songName);
        songSeekBar=(SeekBar) findViewById(R.id.seekBar_song);
        songStart=(Button)findViewById(R.id.button_start);
        songStop=(Button)findViewById(R.id.button_stop);
        songPause=(Button)findViewById(R.id.button_pause);

    }

    private class ButtonListener implements View.OnClickListener{
        @Override
        public void onClick(View view) {
            try{//这里需要处理异常
                switch (view.getId()){
                    case R.id.button_start:
                        //文件名
                        String fileName=editSongName.getText().toString();
                        songText.setText(fileName);
                        //恢复原始界面
                        if(songPause.getText().equals("继续")){
                            songPause.setText("暂停");
                        }
                        //重置
                        musicPlayer.reset();
                        //文件路径
                        musicPlayer.setDataSource("/sdcard/netease/cloudmusic/Music/"+fileName+".mp3");
                        musicPlayer.prepare();
                        musicPlayer.start();
                        handler.sendEmptyMessage(1);
                        songSeekBar.setMax(musicPlayer.getDuration());//设置进度条
                        handler.post(seekBarThread);
                        /*mTimer = new Timer();
                        mTimerTask = new TimerTask() {
                            @Override
                            public void run() {     

                                if(isChanging==true) {
                                    return;
                                }
                                songSeekBar.setProgress(musicPlayer.getCurrentPosition());
                            }
                        };
                        mTimer.schedule(mTimerTask, 0, 10); */
                        break;
                    case R.id.button_stop:
                        musicPlayer.stop();
                        songPause.setText("暂停");
                        handler.sendEmptyMessage(2);//
                        songSeekBar.setProgress(0);
                        handler.removeCallbacks(seekBarThread);
                        break;

                    case R.id.button_pause:
                        //注意界面的更改
                        if(musicPlayer.isPlaying()){
                            musicPlayer.pause();
                            ((Button)view).setText("继续");
                            handler.sendEmptyMessage(2);
                            handler.post(seekBarThread);
                        }else{
                            musicPlayer.start();
                            handler.sendEmptyMessage(1);
                            ((Button)view).setText("暂停");
                        }

                        break;
                }
            }catch (IOException e){
                e.printStackTrace();
            }

        }
    }
    class SongSeekBarListener implements OnSeekBarChangeListener{

        @Override
        public void onProgressChanged(android.widget.SeekBar arg0, int progress,
                boolean fromUser) {
            // TODO Auto-generated method stub
            if(fromUser==true){
                if(musicPlayer.isPlaying())
                musicPlayer.pause();
                musicPlayer.seekTo(progress);
                //如果在这里start(),效果就像老式卡带机或是留声机,不信你试试
                //musicPlayer.start();
            }
        }

        @Override
        public void onStartTrackingTouch(android.widget.SeekBar arg0) {
            // TODO Auto-generated method stub
            //isChanging=true;

        }

        @Override
        public void onStopTrackingTouch(android.widget.SeekBar arg0) {
            // TODO Auto-generated method stub
            //musicPlayer.seekTo(songSeekBar.getProgress());
            //isChanging=false;
            //当手指抬起时,开始从歌曲新的位置播放歌曲
            if(!musicPlayer.isPlaying())
                musicPlayer.start();

        }

    }

    @Override
    protected void onPause() {
        super.onPause();
        if(musicPlayer!=null){
            if(musicPlayer.isPlaying())
                musicPlayer.stop();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if(musicPlayer!=null){
            if(musicPlayer.isPlaying())
                musicPlayer.start();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(musicPlayer!=null){
            if(musicPlayer.isPlaying())
                musicPlayer.stop();
            musicPlayer.release();
        }
        if(animation!=null){
            songImage.clearAnimation();
        }
        handler.removeCallbacks(seekBarThread);
    }
}

界面xml

做了一些小改动。


<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ff646469"
    tools:context=".MainActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:weightSum="1">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:paddingTop="15dp"
            android:textSize="35sp"
            android:textColor="#fff"
            android:text="音乐播放器"/>
        <TextView
            android:layout_marginTop="22dp"
            android:id="@+id/text_songName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textColor="#fff"
            android:textSize="19sp"
            android:text="好妹妹"/>
        <ImageView
            android:layout_marginTop="18dp"
            android:id="@+id/image_songImage"
            android:layout_width="290dp"
            android:layout_height="290dp"
            android:layout_gravity="center"
            android:src="@drawable/ico2"/>
        <SeekBar
            android:layout_marginTop="20dp"
            android:id="@+id/seekBar_song"
            android:max="100"
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"/>

        <EditText
            android:layout_gravity="center"
            android:id="@+id/edit_songName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="输入歌曲名"/>
        <LinearLayout
            android:layout_marginTop="20dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            >
            <Button
                android:id="@+id/button_pause"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="3.333"
                android:textColor="#fff"
                android:text="暂停"/>
            <Button
                android:id="@+id/button_start"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="3.333"
                android:textColor="#fff"
                android:text="播放"/>
            <Button
                android:id="@+id/button_stop"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="3.333"
                android:textColor="#fff"
                android:text="停止"/>
        </LinearLayout>
    </LinearLayout>
</RelativeLayout>

明天的APP

明天继续完善这个音乐播放器,1.美化按钮2.播放网络上的音乐

时间: 2024-08-26 11:42:49

每天写个APP_第2天——升级版音乐播放器的相关文章

升级版音乐播放器

// //  secondViewController.m //  AudioPlayer // //  Created by apple on 14-7-24. //  Copyright (c) 2014年 苹果IOS软件开发者. All rights reserved. // #import "secondViewController.h" #import "ViewController.h" @interface secondViewController (

如何写一个正经的Android音乐播放器 一

以前写过很多次音乐播放器,但是总有一些问题出现,例如: 1,音乐长时间播放问题(即便是放在service中去播放,依然会被杀死): 2,音乐的播放进度如何掌握?(如何利用mediaplayer.getCurrentPosition()来有效的通知界面变更进度?): 3,在我以往的经验中,音乐播放完毕下一曲时候,经常出现当前音乐播放还差几秒钟的时候就下一曲了的情况. 从网上找到教程中,通常都是一个播放器的demo,简单的直接把MediaPlayer放在了一个Activity中去操作,稍有良心的教程

如何写一个正经的音乐播放器 四 意外情况

四,意外情况的控制. 在音频播放时候,容易遇到一些意外情况,这时候,我们就要处理这些意外情况,这时候,我们需要针对不同的意外情况进行处理.大概可以分成两种情况. 1,失去audio_focus的控制. 造成我们的播放器失去焦点的情况很多,主要是其他声音请求了焦点,例如说,其他音乐播放器开始播放音乐,突然来电,短息等. 以上的焦点失去,都可以用AudioManager.OnAudioFocusChangeListener中的回调来处理.先取得AudioManager AudioManager au

自己动手写的音乐播放器

其实很早就有这个想法了,不求界面多么华丽,只求功能实现,想来也不会很难,音乐播放器无非也就是些IO的操作,我写的山寨音乐播放器暂不支持在线播放,如果要播放的话,要将音乐文件下载到本地,通过IO操作读入程序中来,废话不多说了,界面如下(参考了传智播客蒋坤老师的相关课程): 一些变量的定义及含义: string temp1 = null; string temp2 = null; //设置添加曲目的路径 string strSearchPath; //定时退出的时间 int iSecond; boo

如何写一个正经的Android音乐播放器 三

实现音乐的播放. 为了快速实现音乐播放,我们使用MediaPlayer而不用SoundPool,据说SoundPool比MediaPlayer的自由度更大.不过,根据我的了解,soundpool并不支持音频的解码,得自己去解码,而MeidaPlayer已经内置了一些解码方案,我看到的是,音频除了ape无损格式,都能播放. 你需要阅读: MediaPlayer API: http://developer.android.com/reference/android/media/MediaPlayer

如何写一个正经的Android音乐播放器 二

这一篇讲如何 与音乐播放Service交互 稍有经验的同学都知道,将长时间的操作放在Service中进行,如何做到界面和音乐播放Service的有效沟通呢,在这一章中给出我的答案,同样希望大神们给出指点. 希望你阅读(自行翻墙): Service的API Guide:http://developer.android.com/guide/components/services.html Service的API Guide中的有关bound service中的部分: http://developer

自己写的音乐播放器Demo开源给需要学习的人,大家编程路上一直进步,

本项目使用到的技术 1:网络请求以及图片加载用的是volley框架 2:播放音乐使用MediaPlayer类 3:数据来源使用JSON解析 4:登录收藏功能使用的SQLite实现 5:Android 6.0动态权限申请 下面是运行时图片 图就这么多,音乐的api接口调用的是易源数据的QQ音乐接口, 关于播放进度时间的,我记得我写了,但是没找到,这里把思路说一下,具体思路是MediaPlayer播放音乐可以得到播放音乐的总大小,已经播放的位置,使用一个定时器,实时获取MediaPlayer的播放位

用 C 语言和 nkCEngine 写了一个基于命令的迷你脚本编译器与脚本播放器

首先,博主感到非常高兴,就在很久以前,博主就想写一个属于自己的脚本引擎,哪怕功能再简单也好,只要能跑起来就行了,直到今天,博主终于向这个梦想迈出了第一步,将脚本引擎的雏形给做了出来,因为这个脚本系统属于 nkCEngine 的一部分,所以博主将其命名为 nkScript,nkScript 是一门基于命令的语言,没有复杂的函数调用,没有复杂的循环语句,它拥有的,是简单的数值运算语句.条件跳转语句.无条件跳转语句.命令语句,等等:nkScript 的开发目标主要是用于制作简单的文字冒险游戏,就是那种

用WPF写了一个弹幕播放器

看弹幕视频的时候,如果不发弹幕,一个本地的弹幕播放器往往能带来更好的体验.目前已经有一些实现了,最初用过一个MukioPlayer, 后来又用过一个用C++写的BiliLocal,这个程序能自动下载弹幕,播放格式也非常宽泛,功能和性能基本上都没有什么值得挑剔的地方,感觉主要的一个Bug就是进度跳转的时候弹幕要消失一阵子(包括显示/隐藏弹幕的时候). 前段时间为学习WPF的动画的时候顺便做了一个本地的弹幕播放器练手,后来发现效果还不错,便将其完善了下,现在已经能基本可用了.虽然BiliLocal已