首页 > 其他分享 >音乐播放器的实现,框架的基本搭建,资源

音乐播放器的实现,框架的基本搭建,资源

时间:2023-09-12 14:01:33浏览次数:45  
标签:播放器 框架 int private new import android public 搭建

效果图

音乐播放器的实现,框架的基本搭建,资源_音乐播放器

你们肯定会说没图我怎么做...那好吧 我压缩一下藏在百度云把是整体的所有图片哦 有的可能用不到


链接:http://pan.baidu.com/s/1mhOXk4g 密码:7t92

有的人肯定比我还懒  我就想要个apk 或者我就想要个项目 好吧 你赢了 我给你地址 我用的开发工具是androidStudio

链接:http://pan.baidu.com/s/1pLbIjMV 密码:55in



他是从vbFragmen里面跳进去的 所以 得从Main说起 我把完整的项目结构拷贝进来了 然后就实现音乐

我把重要的类写出来  如果想看全部的话 可以下载上面有地址

MainActivity

播放音乐的界面

MusicPlayerActivity

package com.example.liuan.phonevideo.activity;
 
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.graphics.Color;
import android.graphics.drawable.AnimationDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
 
import com.example.liuan.phonevideo.R;
import com.example.liuan.phonevideo.bean.MusicItem;
import com.example.liuan.phonevideo.lyic.LyncView;
import com.example.liuan.phonevideo.service.MusicPlayerService;
import com.example.liuan.phonevideo.utils.Utils;
 
import java.io.File;
 
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
 
public class MusicPlayerActivity extends AppCompatActivity {
 
 
    @Bind(R.id.iv_back)
    ImageView ivBack;
 
 
    @Bind(R.id.tv_title)
    TextView tvTitle;
    @Bind(R.id.tv_artist)
    TextView tvArtist;
    @Bind(R.id.rl_top)
    RelativeLayout rlTop;
    @Bind(R.id.iv_wave)
    ImageView ivWave;
    @Bind(R.id.tv_time)
    TextView tvTime;
    @Bind(R.id.sb_progress)
    SeekBar sbProgress;
    @Bind(R.id.iv_playmode)
    ImageView ivPlaymode;
    @Bind(R.id.iv_pre)
    ImageView ivPre;
    @Bind(R.id.iv_play_pause)
    ImageView ivPlayPause;
    @Bind(R.id.iv_next)
    ImageView ivNext;
    @Bind(R.id.iv_list)
    ImageView ivList;
    @Bind(R.id.lic_view)
    LyncView mLyricView;
    private MusicPlayerService.MusicController music;
    private String totalTime;
    private MyServiceConnection serviceConnection;
    private AnimationDrawable animation;
    private MyReceiver receiver;
 
 
    private static final int UPDATA_PLAYED_TIME = 1;
    private static final int UPDATE_LYRIC = 2;
    public Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATA_PLAYED_TIME:
                    updataPlayedTime();
                    break;
                case UPDATE_LYRIC:
                    updatalyric();
                    break;
 
 
            }
 
 
        }
    };
 
    private void updatalyric() {
        mLyricView.updateLyrics(music.getCurrentPosition(), music.getDuration());
        handler.sendEmptyMessageDelayed(UPDATE_LYRIC, 100);
    }
 
    private void updataPlayedTime() {
        //获取当前播放的位置
        int currentPosition = music.getCurrentPosition();
        String time = Utils.formatPlayTime(currentPosition);
        tvTime.setText(time + "/" + totalTime);
        //跟新进度条进度
        sbProgress.setProgress(currentPosition);
        //通过hanller
        //半秒后 再次执行这个方法
        handler.sendEmptyMessageDelayed(UPDATA_PLAYED_TIME, 500);
 
 
    }
 
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //沉浸状态栏
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = getWindow();
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
                    | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.TRANSPARENT);
//            window.setNavigationBarColor(Color.TRANSPARENT);
        }
 
        setContentView(R.layout.activity_music_player);
        ButterKnife.bind(this);
 
        initService();
        initView();
 
        receiver = new MyReceiver();
        IntentFilter filter = new IntentFilter("com.example.startPlay");
        filter.addAction("com.example.stopPlay");
        registerReceiver(receiver, filter);
 
 
    }
 
    private void initService() {
        Intent intent = getIntent();
        intent.setClass(getApplicationContext(), MusicPlayerService.class);
        startService(intent);//会执行 onCreate onStatrtCommand*(会多次执行)
        //混合方式开启服务
        serviceConnection = new MyServiceConnection();
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);
 
 
    }
 
    private void initView() {
        /**
         * 获取状态栏高度
         */
 
        int statusBarHeight1 = -1;
//获取status_bar_height资源的ID
        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            //根据资源ID获取响应的尺寸值
            statusBarHeight1 = getResources().getDimensionPixelSize(resourceId);
        }
        //获取状态栏高度 设置到顶部标题栏的顶部外边距
        LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) rlTop.getLayoutParams();
        layoutParams.topMargin = statusBarHeight1;
        rlTop.setLayoutParams(layoutParams);
        //开启动画
        animation = (AnimationDrawable) ivWave.getDrawable();
        sbProgress.setOnSeekBarChangeListener(new MyOnSeekBarChangeListener());
    }
 
 
    @OnClick({R.id.iv_back, R.id.iv_playmode, R.id.iv_pre, R.id.iv_play_pause, R.id.iv_next, R.id.iv_list})
    public void onClick(View view) {
        switch (view.getId()) {
 
            case R.id.iv_playmode:
                MusicPlayerService.currenMode = ++MusicPlayerService.currenMode % 3;
                updataPlayModeIcon();
                getSharedPreferences("music_config", MODE_PRIVATE).edit().putInt("playmode", MusicPlayerService.currenMode).commit();
                break;
            case R.id.iv_pre:
                music.preNext(MusicPlayerService.PLAY_PRE);
                break;
            case R.id.iv_play_pause:
                music.palyPause();
                updatePlayPanseIcon();
                break;
            case R.id.iv_next:
                music.preNext(MusicPlayerService.PLAY_NEXT);
                break;
            case R.id.iv_list:
                break;
            case R.id.iv_back:
                finish();
                break;
        }
    }
 
    private void updataPlayModeIcon() {
        switch (MusicPlayerService.currenMode) {
            case MusicPlayerService.PLAY_MODE_LIST:
                ivPlaymode.setImageResource(R.drawable.selector_playmode_list);
 
                break;
            case MusicPlayerService.PLAY_MODE_SINGLE:
                ivPlaymode.setImageResource(R.drawable.selector_playmode_single);
                break;
            case MusicPlayerService.PLAY_MODE_SHUFFLE:
                ivPlaymode.setImageResource(R.drawable.selector_playmode_shuffle);
                break;
 
        }
    }
 
    private class MyOnSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
 
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            //用户操作
            if (fromUser) {
                music.seekTo(progress);
            }
 
        }
 
        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            //开始操作进度条走这个方法
 
 
        }
 
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            //停止操作进度条走这个方法
        }
    }
 
    private class MyServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //bindService开启 如果onBinde方法有返回值 就会执行这个方法
            //第二个参数 IBinder service就是onBind返回值
            music = (MusicPlayerService.MusicController) service;
 
        }
 
        @Override
        public void onServiceDisconnected(ComponentName name) {
 
        }
    }
 
    @Override
    protected void onStart() {
        super.onStart();
        if (music != null) {
            handler.sendEmptyMessage(UPDATA_PLAYED_TIME);
        }
    }
 
    @Override
    protected void onStop() {
        super.onStop();
        handler.removeCallbacksAndMessages(null);
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //bindService开启服务需要在activity 销毁 解锁绑定
        unbindService(serviceConnection);
        unregisterReceiver(receiver);
 
 
    }
 
    private class MyReceiver extends BroadcastReceiver {
 
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if ("com.example.startPlay".equals(action)) {
                initPlayerUI();
                updataPlayedTime();
                updatePlayPanseIcon();
                //加载歌词 解析
                //tianhou.MP3
                String fileName = music.getCurrentMusic().displayName.split("\\.")[0];
                File file = new File(Environment.getExternalStorageDirectory() + "/hh", fileName + ".txt");
             mLyricView.loadLyrice(file);
 
                //更新歌词
                updatalyric();
 
 
            } else if ("com.example.stopPlay".equals(action)) {
                handler.removeMessages(UPDATA_PLAYED_TIME);
                //移除所有的消息
                handler.removeCallbacksAndMessages(null);
            }
 
 
        }
 
 
    }
 
 
    /**
     * 初始化播放界面
     */
    private void initPlayerUI() {
        MusicItem currentMusic = music.getCurrentMusic();
        tvTitle.setText(currentMusic.title);
        tvArtist.setText(currentMusic.artist);
        //更新播放的图标
        updatePlayPanseIcon();
        //更新播放的总时长
        totalTime = Utils.formatPlayTime(currentMusic.duration);
        tvTime.setText("00:00/" + totalTime);
        //初始化 seekbar的总时长
        sbProgress.setMax(currentMusic.duration);
 
    }
 
    private void updatePlayPanseIcon() {
        if (music.isPlaying()) {
            ivPlayPause.setImageResource(R.drawable.selector_play);
            animation.start();
        } else {
            ivPlayPause.setImageResource(R.drawable.selector_pause);
            animation.stop();
        }
    }
 
 
}

初始化listView数据的adapter继承了cursoradapterMusicAdapter

package com.example.liuan.phonevideo.adapter;
 
import android.content.Context;
import android.database.Cursor;
import android.provider.MediaStore;
import android.support.v4.widget.CursorAdapter;
import android.text.format.Formatter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
 
import com.example.liuan.phonevideo.R;
 
/**
 * Name: MusicAdapter
 * Action:
 * Author: liuan
 * creatTime:2017-01-15 11:39
 */
 
public class MusicAdapter extends CursorAdapter {
 
 
 
 
    //不推荐第三个参数是boolean 如果第三个传入true当Currsor发生改变 会自动调用requery刷新界面
    public MusicAdapter(Context context, Cursor c) {
        //参3 注册内容观察者来处理游标变化的操作
        super(context, c, FLAG_REGISTER_CONTENT_OBSERVER);
    }
 
    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        //创建新的布局
        View inflate = LayoutInflater.from(context).inflate(R.layout.item_music, parent, false);
        ViewHolder viewHolder = new ViewHolder(inflate);
        inflate.setTag(viewHolder);
        return inflate;
    }
 
    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        //给条目设置数据 通过这个bindVIew
        ViewHolder holder= (ViewHolder) view.getTag();
        int title;
        title = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
        String titleString = cursor.getString(title);
 
        holder.tv_title.setText(titleString);
        holder.tv_artist.setText(cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)));
        long size = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE));
        String sizeString = Formatter.formatFileSize(context, size);
 
        if(titleString.contains("-")){
            holder.tv_title.setText(titleString.split("-")[0]);
            holder.tv_artist.setText(titleString.split("-")[1]);
        }
        holder.tv_size.setText(sizeString);
 
    }
    class ViewHolder{
        TextView tv_title;
        TextView tv_artist;
        TextView tv_size;
 
        public ViewHolder(View view) {
          tv_title = (TextView) view.findViewById(R.id.tv_title);
            tv_artist = (TextView) view.findViewById(R.id.tv_artist);
            tv_size = (TextView) view.findViewById(R.id.tv_size);
        }
    }
}

超级简单的Fragment的封装BaseFragment一般有集合的地方 就有数据bean歌词的数据beanLyric

package com.example.liuan.phonevideo.bean;
 
/**
 * Name: Lyric
 * Action:
 * Author: liuan
 * creatTime:2017-01-16 10:51
 */
 
public class Lyric implements Comparable<Lyric> {
    /***
     * 一行歌词的文字
     */
    public String text;
    /**
     * 当前行歌词开始演唱的时刻
     */
    public int time;
 
    public Lyric(String text, int time) {
        this.text = text;
        this.time = time;
    }
 
    @Override
    public int compareTo(Lyric o) {
        return this.time-o.time;
    }
}

音乐条目的数据

beanMusicItem

package com.example.liuan.phonevideo.bean;
 
import android.database.Cursor;
import android.provider.MediaStore;
 
import java.io.Serializable;
 
/**
 * Name: MusicItem
 * Action:
 * Author: liuan
 * creatTime:2017-01-15 14:53
 */
 
public class MusicItem implements Serializable{
    public String data;
    public String title;
    public String artist;
    public String displayName;
    public long size;
    public int duration;
 
    public static MusicItem getMusicFromCursor(Cursor cursor){
        if(cursor==null){
            return null;
        }
        MusicItem musicItem = new MusicItem();
        musicItem.data=cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
        musicItem.title=cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
        musicItem.artist=cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
        musicItem.size=cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE));
        musicItem.duration=cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));
 
        if(musicItem.title.contains("-")){
            musicItem.artist= musicItem.title.split("-")[0].trim();
            musicItem.title= musicItem.title.split("-")[1].trim();
 
        }
        musicItem.displayName=cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME));
        return musicItem;
    }
}

如果音乐列表太大的话 会阻塞主线程 一般不会用的是数据库 解决方案MyAsyncQueryHandler

package com.example.liuan.phonevideo.db;
 
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.database.Cursor;
 
import com.example.liuan.phonevideo.adapter.MusicAdapter;
 
/**
 * Name: MyAsyncQueryHandler
 * Action:
 * Author: liuan
 * creatTime:2017-01-15 14:45
 */
 
public class MyAsyncQueryHandler extends AsyncQueryHandler {
    public MyAsyncQueryHandler(ContentResolver cr) {
        super(cr);
    }
 
    @Override
    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
        super.onQueryComplete(token, cookie, cursor);
        MusicAdapter adapter = (MusicAdapter) cookie;
        //替换旧的游标 如果旧的游标不为空 会关闭
        adapter.changeCursor(cursor);
        //替换旧的游标 把旧的游标作为返回值 返回 不会关闭
        adapter.swapCursor(cursor);
    }
}

音乐列表的实现

VbangFragment

package com.example.liuan.phonevideo.fragment;
 
import android.Manifest;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore.Audio.Media;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.PermissionChecker;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
 
import com.example.liuan.phonevideo.R;
import com.example.liuan.phonevideo.activity.MusicPlayerActivity;
import com.example.liuan.phonevideo.adapter.MusicAdapter;
import com.example.liuan.phonevideo.base.BaseFragment;
import com.example.liuan.phonevideo.bean.MusicItem;
import com.example.liuan.phonevideo.db.MyAsyncQueryHandler;
 
import java.util.ArrayList;
 
import butterknife.Bind;
 
/**
 * Name: MainFragment
 * Action:
 * Author: liuan
 * creatTime:2017-01-15 10:25
 */
 
public class VbangFragment extends BaseFragment {
    @Bind(R.id.lv_vbang)
    ListView lvVbang;
    private MusicAdapter adapter;
 
    @Override
    protected int getLayoutId() {
        return R.layout.fragment_vbang;
 
    }
 
    @Override
    protected void initView(View view) {
        adapter = new MusicAdapter(getContext(), null);
        lvVbang.setAdapter(adapter);
        lvVbang.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 
                ArrayList<MusicItem> musics=new ArrayList<MusicItem>();
                Cursor cursor = adapter.getCursor();
                if(cursor!=null){
                    cursor.moveToPosition(-1);
                    while (cursor.moveToNext()){
                        musics.add(MusicItem.getMusicFromCursor(cursor));
 
                    }
                    Intent intent = new Intent(getContext(), MusicPlayerActivity.class);
                    intent.putExtra("musics",musics);
 
                    intent.putExtra("position",position);
                    System.out.println("zou "+musics.get(position));
                    startActivity(intent);
                    System.out.println("zou ");
                }
 
            }
        });
        //如果版本》23
        if (Build.VERSION.SDK_INT >= 23) {
            //参1 检测当前应用是否有特定权限
            //参2 需要检测权限 维系那权限 才需要动态申请
            // 危险权限 跟用户隐私相关的权限 sd卡 短信 通话记录 打带你话 位置 摄像头 录音机
            if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) == PermissionChecker.PERMISSION_DENIED) {
                ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
                return;
            }
 
        }
 
        initData();
 
 
    }
 
    public void initData() {
//        AsyncQueryHandler asyncQueryHandler
        //内容解析者
        ContentResolver contentResolver = getActivity().getContentResolver();
        Uri uri = Media.EXTERNAL_CONTENT_URI;
//        Cursor cursor = contentResolver.query(uri, new String[]{
//                        Media._ID,
//                        Media.DATA,//文件路径
//                        Media.DURATION,//时长
//                        Media.SIZE,
//                        Media.TITLE, Media.ARTIST
//
//
//        },null, null, null);
 
        //       Utils.printCUrrsor(cursor);
        MyAsyncQueryHandler queryHandler = new MyAsyncQueryHandler(contentResolver);
        //参1 异步查询 如果有不同查询 用来区分不同查询
        //第二参数 cookie可以传任意对象给onQUeCO
        queryHandler.startQuery(1,adapter,uri, new String[]{
                Media._ID,
                Media.DATA,//文件路径
                Media.DURATION,//时长
                Media.SIZE,
                Media.TITLE,
                Media.ARTIST,
                Media.DISPLAY_NAME
 
 
        },null, null, null);
    }
 
 
}

自定义歌词view继承TextView不用写onMeasure方法LyncView


package com.example.liuan.phonevideo.lyic;
 
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Shader;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.widget.TextView;
 
import com.example.liuan.phonevideo.R;
import com.example.liuan.phonevideo.bean.Lyric;
 
import java.io.File;
import java.util.ArrayList;
 
/**
 * Name: LyncView
 * Action:
 * Author: liuan
 * creatTime:2017-01-16 10:16
 */
 
public class LyncView extends TextView {
 
 
    private int viewHeight;
    private int viewWidth;
    //正在播放的歌词文字大小
    private float bigFongSize;
    //普通歌词文字大小
    private float normalFongSize;
    //行高 每一行的行高
    private float lineHeight;
 
    private ArrayList<Lyric> lyrices=new ArrayList<>();
    //正在演唱的歌词 在歌词集合索引中
    private int currentIndex = 5;
 
    private Paint paint;
    private Rect bounds;
    private int heightLightColor;
    private int currentTime;
    private float passedPercent;
    private int centerY;
    private int duration;
 
    public LyncView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
 
    public LyncView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initParams();
 
 
    }
 
 
 
    private void initParams() {
 
        bigFongSize = getResources().getDimension(R.dimen.bigFontSize);
        normalFongSize = getResources().getDimension(R.dimen.normalFongSize);
        lineHeight = getResources().getDimension(R.dimen.lineHeight);
        heightLightColor = getResources().getColor(R.color.colorMusicProgress);
 
        paint = new Paint();
        paint.setAntiAlias(true);
//矩形对象 用来测量文字边界
        bounds = new Rect();
    }
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public LyncView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (lyrices != null && lyrices.size() > 0) {
            canvas.translate(0, -getTransY());
            drawAllText(canvas);
 
        }
 
 
    }
 
 
 
    private float getTransY() {
        if (currentIndex == lyrices.size() - 1) {
            //最后一句
            //当前歌词开始演唱的时刻
            int startTime = lyrices.get(currentIndex).time;
            //这一行应该演唱的总时长 用歌曲的总时长-最后一行歌词开始唱
            int totalTime = duration - startTime;
            //这一行歌词已经演唱了多久
            int passedTime = currentTime - startTime;
            //当时时刻应该移动的距离
            return lineHeight * passedTime / totalTime;
        }
        //当歌词开始演唱的时刻
        int startTime = lyrices.get(currentIndex).time;
        //这一行应该演唱的总时长
        int totalTime = lyrices.get(currentIndex + 1).time - startTime;
        //这一行歌词已经演唱了多久
        int passedTime=currentTime-startTime;
        passedPercent = passedTime / (float) totalTime;
        //当时时刻应该移动的距离
        return lineHeight * passedTime / totalTime;
 
 
    }
 
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //获取到view的宽度和高度
        viewHeight = h;
        viewWidth = w;
 
        String text="我是一行歌词歌词";
        paint.setTextSize(bigFongSize);
        paint.getTextBounds(text,0,text.length(),bounds);
        //确定屏幕中间的y坐标的位置
        centerY = viewHeight / 2 + bounds.height() / 2;
 
 
    }
 
    /**
     * 根据当前歌词的索引 以及正在演唱的歌词索引currentPosition 来绘制一行歌词
     * @param index 当前歌词在集合中的索引
     * @param canvas
     */
 
    private void drawSingleLyric(int index, Canvas canvas) {
        //通过索引获取到这一行歌词
        String text = lyrices.get(index).text;
        //判断当前的索引和正在演唱的歌词索引 是否相同
 
        if (index == currentIndex) {
            //正在演唱的歌词
            paint.setTextSize(bigFongSize);
            paint.setColor(heightLightColor);
            paint.getTextBounds(text,0,text.length(),bounds);
            int textWidth = bounds.width();
            float x=viewWidth/2-textWidth/2;
            paint.setShader(new LinearGradient(x,centerY,x+textWidth,centerY,
                    new int[]{heightLightColor, Color.WHITE},
                    new float[]{passedPercent,passedPercent+0.01f},
                    Shader.TileMode.CLAMP));
        } else {
            paint.setTextSize(normalFongSize);
            paint.setColor(Color.WHITE);
            paint.setShader(null);
        }
        //测量文字的边界
        paint.getTextBounds(text,0,text.length(),bounds);
        int textWidth=bounds.width();
        float x=viewWidth/2-textWidth/2;
        // y坐标 中间位置+当前行和正在唱的行号差距*行高
        float y=centerY+(index-currentIndex)*lineHeight;
        canvas.drawText(text,x,y,paint);
    }
    /*
    遍历结合 绘制集合中的所有歌词
     */
    private void drawAllText(Canvas canvas) {
        for (int i = 0; i < lyrices.size(); i++) {
            drawSingleLyric(i, canvas);
 
        }
    }
    /**
     * 更新正在演唱的时刻 更新正在唱的歌词索引
     *
     * @param currentTime
     */
    public void updateCurrentIndex(int currentTime) {
        for (int i = 0; i < lyrices.size(); i++) {
            if (i == lyrices.size() - 1) {
                currentIndex = i;
                return;
            }
            //当前行的时刻小于正在播放的时刻 并且下一行的时刻 大于正在播放的时刻
            if (lyrices.get(i).time < currentTime && lyrices.get(i + 1).time > currentTime) {
                //这行歌词正在演唱
                currentIndex = i;
                return;
            }
 
        }
    }
    public void updateLyrics(int currentTime,int duration){
        this.currentTime=currentTime;
        this.duration=duration;
        //更新正在唱的歌词索引
        updateCurrentIndex(currentTime);
        //重新绘制界面
        invalidate();
    }
 
    public void loadLyrice(File file) {
//      解析歌词保存到lyrics这个集合
        lyrices=LyricsParser.parserFromFile(file);
 
    }
}

歌词的解析类

LyricsParser


package com.example.liuan.phonevideo.lyic;
 
import com.example.liuan.phonevideo.bean.Lyric;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
 
/**
 * Name: LyricsParser
 * Action:
 * Author: liuan
 * creatTime:2017-01-16 20:26
 */
 
public class LyricsParser  {
 
    /**
     * 从歌词文件解析出歌词数据列表
     */
    public static ArrayList<Lyric> parserFromFile(File lyricsFile) {
        ArrayList<Lyric> lyricsList = new ArrayList<>();
        // 数据可用性检查
        if (lyricsFile == null || !lyricsFile.exists()) {
            lyricsList.add(new Lyric("没有找到歌词文件。",0));
            return lyricsList;
        }
 
        // 按行解析歌词
        try {
 
            BufferedReader buffer = new BufferedReader(new InputStreamReader(new FileInputStream(lyricsFile),"GBK"));
           String line  = buffer.readLine();
 
            while (line!=null){
 
                List<Lyric> lineList = parserLine(line);
 
                lyricsList.addAll(lineList);
                line = buffer.readLine();
            }
 
 
 
 
 
 
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        // 歌词排序  如果歌词是这种格式的[01:45.51][02:58.62]整理好心情再出发
        //如果解析之后不排序 歌词的顺序是乱的 sort方法 需要javabean 实现comparable接口 重写compareTo方法
        //返回负数排在前面
        Collections.sort(lyricsList);
        return lyricsList;
 
    }
 
 
//
 
 
    private static List<Lyric> parserLine(String line) {
        ArrayList<Lyric> lineList = new ArrayList<>();
//        [01:45.51][02:58.62 整理好心情再出发
        String[] arr = line.split("]");
        String content = arr[arr.length - 1];
        //[01:45.51][02:58.62
        for (int i = 0; i < arr.length - 1; i++) {
            int startPoint = parserStartPoint(arr[i]);
            lineList.add(new Lyric(content, startPoint));
 
        }
        return lineList;
 
    }
 
    private static int parserStartPoint(String startPoint) {
 
        int time = 0;
 
      String[] arr = startPoint.split(":");
       String minStr = arr[0].substring(1);
       arr = arr[1].split("\\.");
        String secStr = arr[0];
        String mSecStr = arr[1];
        time = Integer.parseInt(minStr) * 60 * 1000
               +Integer.parseInt(secStr) * 1000
                +Integer.parseInt(mSecStr) * 10;
        return time;
 
    }
}

音乐就算返回了也可以播放 所以要写在服务中

MusicPlayerService

package com.example.liuan.phonevideo.service;
 
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.widget.RemoteViews;
 
import com.example.liuan.phonevideo.R;
import com.example.liuan.phonevideo.activity.MusicPlayerActivity;
import com.example.liuan.phonevideo.bean.MusicItem;
 
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
 
/**
 * Name: MusicPlayerService
 * Action:
 * Author: liuan
 * creatTime:2017-01-15 15:53
 */
 
public class MusicPlayerService extends Service {
 
    private static final int OPEM_ACTIVITY = 3;
    private static final int CANCEL_NOTIFICATION =4 ;
    private static final int PLAY_PAUSE =5 ;
    private MediaPlayer mediaPlayer;
 
    private ArrayList<MusicItem> musics;
    private int currentPosition = -1;
    public static final int PLAY_MODE_LIST = 0;
    public static final int PLAY_MODE_SINGLE = 1;
    public static final int PLAY_MODE_SHUFFLE = 2;
    public static final int PLAY_PRE = 38;
    public static final int PLAY_NEXT = 836;
    public static int currenMode = 0;
 
 
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        // 只执行一次
        return new MusicController();
    }
 
    public class MusicController extends Binder {
        /**
         * 根据当前的播放状态控制音乐的播放和暂停
         */
        public void palyPause() {
 
            MusicPlayerService.this.playPause();
 
        }
 
        /**
         * 获取当前正在播放音乐的JavaBean* s
         *
         * @return
         */
        public MusicItem getCurrentMusic() {
            return musics.get(currentPosition);
        }
 
        /**
         * 获取播放的状态
         *
         * @return
         */
        public boolean isPlaying() {
            return mediaPlayer.isPlaying();
        }
 
        /**
         * * \获取当前播放了多久
         *
         * @return 已经播放时长的毫秒值
         */
        public int getCurrentPosition() {
            return mediaPlayer.getCurrentPosition();
        }
 
        public void seekTo(int position) {
            mediaPlayer.seekTo(position);
        }
 
        public void preNext(int mode) {
            MusicPlayerService.this.preNext(mode);
 
        }
 
        public int getDuration() {
            return mediaPlayer.getDuration();
        }
 
 
    }
    private void stopUpdateUI() {
        sendBroadcast(new Intent("com.example.stopPlay"));
    }
    /**
     * 根据当前的播放状态控制音乐的播放和暂停
     */
    private void playPause() {
        //如果处于播放状态就暂停
        if (mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
            stopUpdateUI();
        } else {
            //如果处于暂停状态 就开始播放
            mediaPlayer.start();
            notifyPlayerUpdateUI();
        }
 
    }
 
    private void preNext(int mode) {
        //就是根据当前的播放模式来修改 currentposition
        //就是正在播放的歌曲在列表中的索引
        switch (currenMode) {
            case PLAY_MODE_LIST:
                if (mode == PLAY_PRE) {
                    //如果是第一首 移动到列表的最后一首继续播
                    currentPosition = currentPosition == 0 ? musics.size() - 1 : --currentPosition;
 
                } else if (mode == PLAY_NEXT) {
                    currentPosition = (++currentPosition) % musics.size();
 
                }
                break;
            case PLAY_MODE_SINGLE:
                //单曲循环不需要修该播放的索引
                break;
            //随机
            case PLAY_MODE_SHUFFLE:
                Random random = new Random();
                int temp = random.nextInt(musics.size());
                while (temp == currentPosition) {
                    temp = random.nextInt(musics.size());
                }
                currentPosition = temp;
 
                break;
        }
        //重新播放音乐
        startPlay();
    }
 
 
 
    @Override
    public void onCreate() {
 
        super.onCreate();
        currenMode = getSharedPreferences("music_config", MODE_PRIVATE).getInt("playmode", 0);
 
    }
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //区分这个意图是从通知中来还是从上一个activity中来
        boolean fromNotification = intent.getBooleanExtra("fromNotification", false);
        if (fromNotification) {
            int operation = intent.getIntExtra("operation", 0);
            switch (operation) {
                case PLAY_NEXT:
                    preNext(PLAY_NEXT);
                    break;
                case PLAY_PRE:
                    preNext(PLAY_PRE);
                    break;
                case OPEM_ACTIVITY:
                 notifyPlayerUpdateUI();
                    break;
                case CANCEL_NOTIFICATION:
           NotificationManager manager= (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                    //消除通知
                    manager.cancel(1);
                    break;
                case PLAY_PAUSE:
                    playPause();
                  //更新通知
                    sendCustomNotification();
                    break;
            }
 
        }else {
            //获取所有音乐
            musics = (ArrayList<MusicItem>) intent.getSerializableExtra("musics");
            //获取到了点击当前条目位置
            int temp = intent.getIntExtra("position", 0);
            if (temp == currentPosition) {
                //如果点击的条目 跟当前的音乐是同一首  不做音乐播放处理
                //通知更新界面
                notifyPlayerUpdateUI();
            } else {
                //如果点击的条目 跟当前的音乐不是一首  再重置处理
 
                currentPosition = temp;
 
                //开始准备mediaPlayer
                startPlay();
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }
 
    private void startPlay() {
        if (mediaPlayer == null) {
            mediaPlayer = new MediaPlayer();
            mediaPlayer.setOnPreparedListener(new MyOnPreparedListerer());
            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                                                    @Override
                                                    public void onCompletion(MediaPlayer mp) {
                                                        //音乐播放结束 自动播放下一首
                                                        preNext(PLAY_NEXT);
 
                                                    }
                                                }
            );
            mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
                @Override
                public boolean one rror(MediaPlayer mp, int what, int extra) {
                    return false;
                }
            });
 
        } else {
            //切换歌曲的操作
            //停止UI更新
            stopUpdateUI();
           //重置mediaplayer
            mediaPlayer.reset();
        }
 
        //设置要播放的音乐路径
        try {
            mediaPlayer.setDataSource(musics.get(currentPosition).data);
            //异步准备 准备好了 之后会走setONPreparedListerer
            //MyONpreparedListener的onPrepared
            mediaPlayer.prepareAsync();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
 
    private class MyOnPreparedListerer implements MediaPlayer.OnPreparedListener {
        @Override
        public void onPrepared(MediaPlayer mp) {
            //准备好 之后以后音乐就开始了
            mediaPlayer.start();
            //通知activity更新界面
            notifyPlayerUpdateUI();
            sendNomalNotification();
 
        }
    }
 
    private void notifyPlayerUpdateUI() {
        //发送广播 通知activity一月已经开始播放了 可以更新UI
        sendBroadcast(new Intent("com.example.startPlay"));
    }
 
 
 
 
    private void sendNomalNotification() {
        //创建通知
        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
        builder.setSmallIcon(R.drawable.music_default_bg);
        builder.setContentTitle(musics.get(currentPosition).title);
        builder.setContentText(musics.get(currentPosition).artist);
        builder.setContentInfo(musics.get(currentPosition).displayName);
        //说明是正在进行的通知 不能让用户通过手动操作销毁通知
//        builder.setOngoing(true);
        //点击以后会触发一个PendingIntnetn点击会自动小时一般促销、新闻系哦啊系 一般是antoCancel
//        builder.setAutoCancel(true);
        Notification notification = builder.build();
 
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        //参1 id
        manager.notify(1, notification);
 
    }
 
    private void sendCustomNotification() {
        //创建通知
        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
        builder.setSmallIcon(R.drawable.music_default_bg);
        builder.setContent(getRemoteViews());
        //android4.1之后支持的
        if(Build.VERSION.SDK_INT>=16){
            builder.setCustomContentView(getBitRemoteViews());
        }
        Notification notification = builder.build();
        NotificationManager manager= (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.notify(1,notification);
    }
 
    private RemoteViews getBitRemoteViews() {
        //构造第一个参数 包名===第二个参数 布局的资源id
        RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.notification_big);
        remoteViews.setTextViewText(R.id.tv_notification_title,musics.get(currentPosition).title);
        remoteViews.setTextViewText(R.id.tv_notification_artist,musics.get(currentPosition).artist);
        if(mediaPlayer.isPlaying()){
            //给remoteView设置imageView的资源
            remoteViews.setImageViewResource(R.id.iv_notification_playPause,R.drawable.selector_play);
        }else{
            remoteViews.setImageViewResource(R.id.iv_notification_playPause,R.drawable.selector_pause);
 
        }
        //给remoteViews设置点击事件
        remoteViews.setOnClickPendingIntent(R.id.iv_notification_pre,getPrePendingIntent());
        remoteViews.setOnClickPendingIntent(R.id.iv_notification_next,getNextPendingIntent());
        remoteViews.setOnClickPendingIntent(R.id.rl_notification,getActivityPendingIntent());
        remoteViews.setOnClickPendingIntent(R.id.iv_notification_playPause,getPlayPausePendingIntent());
        remoteViews.setOnClickPendingIntent(R.id.iv_notification_cancel,getCancelPendingIntent());
        return remoteViews;
    }
 
 
 
    private PendingIntent getCancelPendingIntent() {
        Intent intent = new Intent(getApplicationContext(), MusicPlayerService.class);
 
        intent.putExtra("fromNotification",true);
        intent.putExtra("operation,",CANCEL_NOTIFICATION);
        //getService==stringService中的onStaritCOmmand中可以收到这个意图
       return PendingIntent.getService(getApplicationContext(),4,intent,PendingIntent.FLAG_UPDATE_CURRENT);
 
    }
 
    private PendingIntent getPlayPausePendingIntent() {
        Intent intent = new Intent(getApplicationContext(), MusicPlayerService.class);
 
        intent.putExtra("fromNotification",true);
        intent.putExtra("operation,",PLAY_PAUSE);
        //getService==stringService中的onStaritCOmmand中可以收到这个意图
        return PendingIntent.getService(getApplicationContext(),5,intent,PendingIntent.FLAG_UPDATE_CURRENT);
 
    }
 
 
    public RemoteViews getRemoteViews() {
        RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.notification_normal);
        remoteViews.setTextViewText(R.id.tv_notification_title,musics.get(currentPosition).title);
        remoteViews.setTextViewText(R.id.tv_notification_artist,musics.get(currentPosition).artist);
        remoteViews.setOnClickPendingIntent(R.id.iv_notification_pre,getPrePendingIntent());
        remoteViews.setOnClickPendingIntent(R.id.iv_notification_next,getNextPendingIntent());
        remoteViews.setOnClickPendingIntent(R.id.rl_notification,getActivityPendingIntent());
        return remoteViews;
    }
 
    private PendingIntent getActivityPendingIntent() {
        Intent intent = new Intent(getApplicationContext(), MusicPlayerActivity.class);
 
        intent.putExtra("fromNotification",true);
        intent.putExtra("operation,",OPEM_ACTIVITY);
        //getService==stringService中的onStaritCOmmand中可以收到这个意图
        PendingIntent activity = PendingIntent.getActivity(getApplicationContext(), 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//        activity.getBroadcast()
        return activity;
 
    }
 
    private PendingIntent getNextPendingIntent() {
        Intent intent = new Intent(getApplicationContext(), MusicPlayerService.class);
 
        intent.putExtra("fromNotification",true);
        intent.putExtra("operation,",PLAY_NEXT);
        //getService==stringService中的onStaritCOmmand中可以收到这个意图
        return PendingIntent.getService(getApplicationContext(),3,intent,PendingIntent.FLAG_UPDATE_CURRENT);
    }
 
    private PendingIntent getPrePendingIntent() {
        Intent intent = new Intent(getApplicationContext(), MusicPlayerService.class);
 
        intent.putExtra("fromNotification",true);
        intent.putExtra("operation,",PLAY_PRE);
        //getService==stringService中的onStaritCOmmand中可以收到这个意图
        return PendingIntent.getService(getApplicationContext(),2,intent,PendingIntent.FLAG_UPDATE_CURRENT);
    }
}

右下∠时间的格式化 还有看看是否能查询到的log打印Utils布局activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
android:orientation="vertical"
    tools:context="com.example.liuan.phonevideo.activity.MainActivity">
 
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
 >
 
    </android.support.v7.widget.Toolbar>
    <FrameLayout
        android:id="@+id/fl_container"
        android:layout_weight="1"
        android:layout_width="match_parent"
        android:layout_height="0dp"/>
    <com.roughike.bottombar.BottomBar
        android:id="@+id/bottomBar"
        android:background="@color/colorPrimary"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        app:bb_activeTabColor="@android:color/white"
        app:bb_inActiveTabColor="#55ffffff"
        app:bb_behavior="shifting"
        app:bb_tabXmlResource="@xml/bottombar_tabs"/>
 
 
 
</LinearLayout>

activity_music_player.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/music_bg"
    android:orientation="vertical">
 
    <RelativeLayout
        android:id="@+id/rl_top"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp">
 
        <ImageView
            android:id="@+id/iv_back"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_margin="5dp"
            android:src="@drawable/selector_back"
 
            />
 
        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="5dp"
            android:text="忘情水"
            android:textColor="@android:color/white"
            android:textSize="18sp" />
 
        <TextView
            android:id="@+id/tv_artist"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/tv_title"
            android:layout_centerHorizontal="true"
            android:text=""
            android:textColor="#cccccc" />
    </RelativeLayout>
 
    <ImageView
        android:id="@+id/iv_wave"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
 
        android:layout_gravity="center_horizontal"
        android:src="@drawable/animation_wave" />
 
    <com.example.liuan.phonevideo.lyic.LyncView
        android:id="@+id/lic_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
 
 
 
        android:textColor="@android:color/white"
        android:textSize="30dp" />
 
    <TextView
        android:id="@+id/tv_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:layout_margin="5dp"
        android:text="03:00/3:48"
        android:textColor="@android:color/white" />
 
    <SeekBar
        android:id="@+id/sb_progress"
        style="@android:style/Widget.SeekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:maxHeight="3dp"
        android:minHeight="3dp"
        android:progress="50"
        android:progressDrawable="@drawable/audio_seekbardrawable"
        android:thumb="@mipmap/audio_seek_thumb" />
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:padding="5dp">
 
        <ImageView
            android:id="@+id/iv_playmode"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:src="@drawable/selector_playmode_shuffle" />
        <ImageView
            android:id="@+id/iv_pre"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:src="@drawable/selector_pre" />
        <ImageView
            android:id="@+id/iv_play_pause"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:src="@drawable/selector_play" />
        <ImageView
            android:id="@+id/iv_next"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:src="@drawable/selector_next" />
        <ImageView
            android:id="@+id/iv_list"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:src="@drawable/selector_list" />
 
    </LinearLayout>
 
</LinearLayout>

fragment_vbang.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
<ListView
    android:id="@+id/lv_vbang"
    android:divider="@null"
    android:dividerHeight="0dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"></ListView>
</LinearLayout>

item_music.xml


<?xml version="1.0" encoding="utf-8"?>
 
 
    <android.support.v7.widget.CardView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
 
    app:cardUseCompatPadding="true"
        android:foreground="?attr/selectableItemBackground"
        app:cardElevation="4dp"
   >
 
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:padding="5dp">
 
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:src="@mipmap/music_default_bg" />
 
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginTop="5dp"
                android:layout_weight="1"
                android:orientation="vertical">
 
                <TextView
                    android:id="@+id/tv_title"
 
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="歌曲名字" />
 
                <TextView
                    android:id="@+id/tv_artist"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="艺术家"
                    android:textColor="#ffcccccc" />
 
            </LinearLayout>
 
            <TextView
                android:id="@+id/tv_size"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:text="3.0MB"
 
                android:textColor="#ffcccccc"
                android:textSize="16sp" />
 
        </LinearLayout>
    </android.support.v7.widget.CardView>

notification_big.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl_notification"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="5dp">
    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/music_default_bg"
        android:layout_margin="5dp"/>
    <TextView
        android:id="@+id/tv_notification_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:text="忘情水"
        android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
        android:layout_centerHorizontal="true"
        android:textSize="20sp"/>
    <TextView
        android:id="@+id/tv_notification_artist"
        android:layout_below="@id/tv_notification_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="刘德华"
        android:layout_centerHorizontal="true"
        android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent"
        android:textSize="18sp"/>
    <LinearLayout
        android:layout_toRightOf="@id/iv_icon"
        android:layout_below="@id/tv_notification_artist"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <ImageView
            android:id="@+id/iv_notification_pre"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="40dp"
            android:src="@drawable/selector_pre"
            android:layout_margin="5dp"/>
        <ImageView
            android:id="@+id/iv_notification_playPause"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="40dp"
            android:src="@drawable/selector_play"
            android:layout_margin="5dp"/>
        <ImageView
            android:id="@+id/iv_notification_next"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="40dp"
            android:src="@drawable/selector_next"
            android:layout_margin="5dp"/>
    </LinearLayout>
 
    <ImageView
        android:id="@+id/iv_notification_cancel"
        android:layout_alignParentRight="true"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:src="@drawable/audio_seek_thumb"/>
 
</RelativeLayout>

notification_normal.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl_notification"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="5dp">
    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/music_default_bg"
        android:layout_margin="5dp"/>
    <TextView
        android:id="@+id/tv_notification_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/iv_icon"
        android:layout_marginTop="5dp"
        android:text="忘情水"
        android:textColor="#000000"
        android:textSize="18sp"/>
    <TextView
        android:id="@+id/tv_notification_artist"
        android:layout_below="@id/tv_notification_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/iv_icon"
        android:text="刘德华"
        android:textColor="#99000000"
        android:textSize="16sp"/>
    <ImageView
        android:id="@+id/iv_notification_next"
        android:layout_alignParentRight="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/selector_next"
        android:layout_margin="5dp"/>
    <ImageView
        android:id="@+id/iv_notification_pre"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@id/iv_notification_next"
        android:src="@drawable/selector_pre"
        android:layout_margin="5dp"/>
</RelativeLayout>

放在新建文件夹layout-v21 用的是布局适配notification_big.xml


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl_notification"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="5dp">
    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/music_default_bg"
        android:layout_margin="5dp"/>
    <TextView
        android:id="@+id/tv_notification_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:text="忘情水"
        android:textAppearance="@android:style/TextAppearance.Material.Notification.Title"
        android:layout_centerHorizontal="true"
        android:textSize="20sp"/>
    <TextView
        android:id="@+id/tv_notification_artist"
        android:layout_below="@id/tv_notification_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="刘德华"
        android:layout_centerHorizontal="true"
        android:textAppearance="@android:style/TextAppearance.Material.Notification.Line2"
        android:textSize="18sp"/>
    <LinearLayout
        android:layout_toRightOf="@id/iv_icon"
        android:layout_below="@id/tv_notification_artist"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <ImageView
            android:id="@+id/iv_notification_pre"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="40dp"
            android:src="@drawable/selector_pre"
            android:layout_margin="5dp"/>
        <ImageView
            android:id="@+id/iv_notification_playPause"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="40dp"
            android:src="@drawable/selector_play"
            android:layout_margin="5dp"/>
        <ImageView
            android:id="@+id/iv_notification_next"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="40dp"
            android:src="@drawable/selector_next"
            android:layout_margin="5dp"/>
    </LinearLayout>
 
    <ImageView
        android:id="@+id/iv_notification_cancel"
        android:layout_alignParentRight="true"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:src="@drawable/audio_seek_thumb"/>
 
</RelativeLayout>

notification_normal.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl_notification"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="5dp">
    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/music_default_bg"
        android:layout_margin="5dp"/>
    <TextView
        android:id="@+id/tv_notification_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/iv_icon"
        android:layout_marginTop="5dp"
        android:text="忘情水"
        android:textAppearance="@android:style/TextAppearance.Material.Notification"
        android:textSize="18sp"/>
    <TextView
        android:textAppearance="@android:style/TextAppearance.Material.Notification.Line2"
        android:id="@+id/tv_notification_artist"
        android:layout_below="@id/tv_notification_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/iv_icon"
        android:text="刘德华"
        android:textColor="#99000000"
        android:textSize="16sp"/>
    <ImageView
        android:id="@+id/iv_notification_next"
        android:layout_alignParentRight="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/selector_next"
        android:layout_margin="5dp"/>
    <ImageView
        android:id="@+id/iv_notification_pre"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@id/iv_notification_next"
        android:src="@drawable/selector_pre"
        android:layout_margin="5dp"/>
</RelativeLayout>

res/xml/bottombar_tabs.xml

drawable

帧动画布局(animationDra )iv.getDra获取对象 然后.start开启

animation_wave.xml


<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/audio_anim_01" android:duration="200"/>
    <item android:drawable="@drawable/audio_anim_02" android:duration="200"/>
    <item android:drawable="@drawable/audio_anim_03" android:duration="200"/>
    <item android:drawable="@drawable/audio_anim_04" android:duration="200"/>
    <item android:drawable="@drawable/audio_anim_05" android:duration="200"/>
    <item android:drawable="@drawable/audio_anim_06" android:duration="200"/>
    <item android:drawable="@drawable/audio_anim_07" android:duration="200"/>
    <item android:drawable="@drawable/audio_anim_08" android:duration="200"/>
    <item android:drawable="@drawable/audio_anim_09" android:duration="200"/>
 
</animation-list>

audio_seekbardrawable.xml


<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2014 The Android Open Source Project
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
          http://www.apache.org/licenses/LICENSE-2.0
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
 
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="5dip" />
            <solid android:color="#c0c1c7" />
        </shape>
    </item>
    <item android:id="@android:id/secondaryProgress">
        <scale android:scaleWidth="100%">
            <selector>
                <item android:state_enabled="false">
                    <color android:color="@android:color/transparent" />
                </item>
                <item android:drawable="@drawable/abc_scrubber_primary_mtrl_alpha" />
            </selector>
        </scale>
    </item>
    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <corners android:radius="5dip" />
                <solid android:color="@color/colorMusicProgress" />
            </shape>
        </clip>
    </item>
</layer-list>

selector_back.xml

selector_list.xml

selector_next.xml

selector_pause.xml

selector_play.xml

selector_playmode_list.xml

selector_playmode_shuffle.xml

selector_playmode_single.xml

selector_pre.xml


图片放这个里面就可以了

drawable-xhdpi

values 目录下的几个文件

colors.xml

dimens.xml

strings.xml

styles.xml


依赖


compile 'com.jakewharton:butterknife:7.0.1'
 
    compile 'com.roughike:bottom-bar:2.0.2'
    compile 'com.android.support:cardview-v7:24.2.0'

清单文件


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.liuan.phonevideo">
 
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".activity.MainActivity"></activity>
        <activity
            android:name=".activity.SplashActivity"
            android:theme="@style/AppTheme.FullScreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".activity.MusicPlayerActivity"></activity>
        <service android:name=".service.MusicPlayerService"/>
 
    </application>
 
</manifest>

到了这个地方 你肯定要试一试..我衷心的祝福你能成功。但是有的模拟器可能没有这个数据库 。所以最好建议真机测试。

标签:播放器,框架,int,private,new,import,android,public,搭建
From: https://blog.51cto.com/u_14523369/7445142

相关文章

  • ElasticSearch 8.6集群搭建过程​
    ElasticSearch8.6集群搭建过程一、系统信息操作系统版本:CentOSLinuxrelease8.4.2105elasticsearch版本:8.6.1机器信息:主机名ip地址CPU内存(G)数据盘es01192.168.205.251632/data/(500G)es02192.168.205.261632/data/(500G)es03192.168.205.271632/data/(500G)二、操作......
  • 一套框架解决「背包问题」
    动态规划背包问题背包问题是一类经典的动态规划问题,它非常灵活,需要仔细琢磨体会,本文先对背包问题的几种常见类型作一个总结,期望可以用一套框架解决背包问题。常见背包问题可分为:01背包问题:最基本的背包问题就是01背包问题:一共有N件物品,第i(i从1开始)件物品的重量为......
  • RK3568开发笔记(八):开发板烧写buildroot固件(支持hdmi屏),搭建Qt交叉编译开发环境,编译一个D
    前言  前面发现开发板用ubuntu固件发现空间不够,本篇使用buildroot固件,来实现目标板运行qt界面应用。<br>烧写buildroot固件  这部分更详细的参照《RK3568开发笔记(六):开发板烧写ubuntu固件(支持mipi屏)》的步骤,本质上烧写都是一样的,只是不同的update.img。步骤一:下载镜像  ......
  • RK3568开发笔记(八):开发板烧写buildroot固件(支持hdmi屏),搭建Qt交叉编译开发环境,编译一个D
    前言  前面发现开发板用ubuntu固件发现空间不够,本篇使用buildroot固件,来实现目标板运行qt界面应用。 烧写buildroot固件  这部分更详细的参照《RK3568开发笔记(六):开发板烧写ubuntu固件(支持mipi屏)》的步骤,本质上烧写都是一样的,只是不同的update.img。步骤一:下载......
  • 如何实现自己在家搭建全端口P2P穿透?快解析内网穿透
    对于有公网主机,有一定的操作能力,需要独立资源配置使用的,可以选择自行搭建内网映射服务。那么如何实现自己搭建全端口P2P穿透呢?下面为大家提供了不同场景下的不同方法,供大家使用时参考。SSH是一种安全的远程登录协议,可以通过SSH实现内网穿透。以下是关于如何使用SSH实现内网穿透的详......
  • 字幕播放器
    最近磨耳朵,发现没有合适字幕播放器,于是用Flutter3写了一个。虽然Flutter是跨平台,但是因为我只有一个三星平板S8,所以没在其他平台测试过~个人随手写的,但是希望能够帮助其他学英语的小盆友。对软件有什么需求,欢迎留言,我随缘更新~使用说明截图,百度网盘链接,GitHub源代码,如下: ......
  • JAVA集合框架体系
    集合框架--容器包容JAVA集合框架中的类可以用于存储多个队系那个,还可用于保存具有映射关系的关联数组。Collection接口单列数据集合。存储一个一个的数据。#常用方法:增add(Eobj)-->加的是一个addall(Collectionother)-->加基本单元,五个小单元组成的中单元放进......
  • 小巧精悍视频播放器:Foobar2000
    Foobar2000是由原Winamp开发公司Nullsoft成员PeterPawlowski开发的一款视频媒体播放软件。Foobar2000支持的音频格式非常广泛,最常见的MP3、Mp4就不说了,一些不常见的WMA、WAV、AIFF等,它也支持。Foobar2000虽然占用内存很小,但正是其通过第三方插件扩展,使得Foobar2000支持更多的格......
  • 43、linux环境下搭建时间服务器
    1、打开VMware打开虚拟机查看ip地址和网络是否正常 也可以通过ssh连接查询是否安装ntp模块执行:rpm-qa|grepntp查看etc下ntp配置文件 2、vi修改配置文件 增加两条信息,让ntpServer与自身同步,内置的时间服务器不可用时,使用local时间作为ntp服务提供给客户端。......
  • 一个详细且完整的公司局域网搭建案例,跟着操作!
    局域网(LocalAreaNetwork,简称LAN),用于将有限范围内(例如一个实验室、一层办公楼或者校园)的各种计算机、终端与外部设备互联成网。公司局域网怎么建立?首先来了解下不同规模企业网络组建方式。10人以下企业网络组建10人以下,规模比较小的公司一般对网络应用需求较低,由于人数少,基本也......