效果图
你们肯定会说没图我怎么做...那好吧 我压缩一下藏在百度云把是整体的所有图片哦 有的可能用不到
链接: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>