Hub Player播放器 Java mp3播放器

/

package com.hubPlayer.player;


import java.io.File;
import java.io.IOException;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;


import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.DataLine.Info;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JFrame;
import javax.swing.JOptionPane;


import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.AudioHeader;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.tag.TagException;


import com.hubPlayer.ui.tool.TimeProgressBar;


/**
 * 底层播放器,主要控制播放
 * 
 * @date 2014-10-18
 */


public class BasicPlayer {


public SourceDataLine sourceDataLine;
private AudioInputStream audioInputStream;
public URL audio;
public boolean HTTPFlag;


public Thread playThread;


public boolean IsPause = true;// 是否停止播放状态
public boolean NeedContinue;// 当播放同一首歌曲 是否继续播放状态
public boolean IsComplete;
public boolean IsEnd;


// 检测输入流是否阻塞
private boolean IsChoke;


private Timer checkConnection;


// 音量控制
private FloatControl floatVoiceControl;
public TimeProgressBar timerProgressBar;


public synchronized void play() {


try {


// 获取网络音频输入流
if (HTTPFlag) {


try {


HttpURLConnection urlConnection = (HttpURLConnection) audio
.openConnection();
audioInputStream = AudioSystem
.getAudioInputStream(urlConnection.getInputStream());


// 用计时器监测歌曲连接状态 初始启动计时器
checkConnectionSchedule();



} catch (ConnectException e) {


// 进度条清零
timerProgressBar.cleanTimer();


// 连接超时
JOptionPane.showMessageDialog(null, "网络资源连接异常", "",
JOptionPane.PLAIN_MESSAGE);


return;
}


}


//
else
// 获取本地音频输入流
audioInputStream = AudioSystem.getAudioInputStream(audio);


// 获取音频编码格式
AudioFormat audioFormat = audioInputStream.getFormat();
// MPEG1L3转PCM_SIGNED
if (audioFormat.getEncoding() != AudioFormat.Encoding.PCM_SIGNED) {
audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
audioFormat.getSampleRate(), 16,
audioFormat.getChannels(),
audioFormat.getChannels() * 2,
audioFormat.getSampleRate(), false);
audioInputStream = AudioSystem.getAudioInputStream(audioFormat,
audioInputStream);
}


// 根据上面的音频格式获取输出设备信息
DataLine.Info info = new Info(SourceDataLine.class, audioFormat);
// 获取输出设备对象
sourceDataLine = (SourceDataLine) AudioSystem.getLine(info);


// 打开输出管道
sourceDataLine.open();
// 允许此管道执行数据 I/O
sourceDataLine.start();


// 获取总音量的控件
floatVoiceControl = (FloatControl) sourceDataLine
.getControl(FloatControl.Type.MASTER_GAIN);


// 音量minValue -80 maxValue 6
// 设合适的初始音量
floatVoiceControl.setValue(-20);


byte[] buf = new byte[1024];
int onceReadDataSize = 0;


while ((onceReadDataSize = audioInputStream
.read(buf, 0, buf.length)) != -1) {
// 输入流没有阻塞
IsChoke = false;


if (IsEnd) {
return;
}


// 是否暂停
if (IsPause)
pause();


// 将数据写入混频器中 至输出端口写完前阻塞
sourceDataLine.write(buf, 0, onceReadDataSize);


// 预设输入流阻塞
IsChoke = true;
}


IsChoke = false;
// 冲刷缓冲区数据
sourceDataLine.drain();


sourceDataLine.close();
audioInputStream.close();


if (checkConnection != null) {
checkConnection.cancel();
checkConnection.purge();
checkConnection = null;
// System.out.println("EndTimeOutControl");
}


} catch (UnsupportedAudioFileException | IOException
| LineUnavailableException | InterruptedException e) {


e.printStackTrace();


}
}


public void load(URL url) {
this.audio = url;
}


public void checkConnectionSchedule() {


checkConnection = new Timer(true);


checkConnection.schedule(new TimerTask() {


// 阻塞计数
int times = 0;


@Override
public void run() {


if (IsChoke) {
times++;


// 如果检测到阻塞次数有20次
if (times == 20) {
try {


// 进度条清零
timerProgressBar.cleanTimer();


// 使playThread自然执行完
IsEnd = false;


// 输入流关闭
audioInputStream.close();


JOptionPane.showMessageDialog(null, "连接异常中断", "",
JOptionPane.PLAIN_MESSAGE);


} catch (Exception e) {
e.printStackTrace();
}
}


} else
times = 0;
// System.out.println(times);
}


}, 2000, 500);


}


public synchronized void resume() {


IsPause = false;
NeedContinue = false;
this.notify();


}


private synchronized void pause() throws InterruptedException {
NeedContinue = true;
this.wait();


}


public void end() {
try {


if (playThread == null)
return;


IsPause = true;
NeedContinue = false;
IsComplete = false;
IsEnd = true;


// 关闭当前数据输入管道
sourceDataLine.close();
audioInputStream.close();


playThread = null;


} catch (Exception e) {
System.out.println("中断播放当前歌曲");
IsPause = true;
NeedContinue = false;
IsComplete = false;
IsEnd = true;
}


}


// 获取音频文件的长度 秒数
public int getAudioTrackLength(URL url) {
try {


// 只能获得本地歌曲文件的信息
AudioFile file = AudioFileIO.read(new File(url.toURI()));


// 获取音频文件的头信息
AudioHeader audioHeader = file.getAudioHeader();
// 文件长度 转换成时间
return audioHeader.getTrackLength();
} catch (CannotReadException | IOException | TagException
| ReadOnlyFileException | InvalidAudioFrameException
| URISyntaxException e) {
e.printStackTrace();
return -1;
}


}


public String getAudioTotalTime(int sec) {
String time = "0:00";


if (sec <= 0)
return time;


int minute = sec / 60;
int second = sec % 60;
int hour = minute / 60;


if (second < 10)
time = minute + ":0" + second;
else
time = minute + ":" + second;


if (hour != 0)
time = hour + ":" + time;


return time;
}


public SourceDataLine getSourceDataLine() {
return sourceDataLine;
}


public FloatControl getFloatControl() {
return floatVoiceControl;
}


}

///

package com.hubPlayer.player;


import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;


import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;


import com.hubPlayer.song.SongNode;
import com.hubPlayer.ui.tool.TimeProgressBar;


/**
 * 高层播放器 主要控制与外部信息交互
 * 
 * @date 2014-10-18
 */


public class HigherPlayer extends BasicPlayer {


private JTree tree;


private SongNode loadSong;
private SongNode playingSong;


private String loadSongName;// 与loadSong对应
private String playingSongName;// 与playedSong对应


// 当前播放歌曲所在的目录
private TreePath currentListPath;


private JButton play;


private JLabel songNameLabel;
private JLabel audioTotalTimeLabel;


// 播放模式
public int mode;
public boolean IsPlayNextSong;


// 当前音频的总时间
public int audioTotalTime;


public HigherPlayer() {
}


// 本地资源
public void load(TreeNode node) {
this.loadSong = (SongNode) node;
DefaultMutableTreeNode mutablenode = (DefaultMutableTreeNode) node;
File songFile = (File) mutablenode.getUserObject();
loadSongName = songFile.getName();
try {
audio = songFile.toURI().toURL();
this.HTTPFlag = false;
} catch (MalformedURLException e) {
e.printStackTrace();
}
}


// 网络资源
public void load(SongNode song, String dataURL) {
try {


if (dataURL==null || dataURL.length() == 0) {
JOptionPane.showMessageDialog(null, "没有找到歌曲资源链接地址", "",
JOptionPane.PLAIN_MESSAGE);
loadSongName = null;
return;
}


loadSongName = song.toString();
loadSong = song;
audio = new URL(dataURL);
this.HTTPFlag = true;
} catch (MalformedURLException e) {
e.printStackTrace();
}
}


public void open() {
playingSongName = loadSongName;
playingSong = loadSong;


IsComplete = false;


// 网络资源播放时间
if (playingSong.getHTTPFlag())
audioTotalTime = playingSong.getTotalTime();
// 本地资源播放时间
else
audioTotalTime = getAudioTrackLength(audio);


audioTotalTimeLabel.setText(getAudioTotalTime(audioTotalTime));


// 重置计时器
timerProgressBar.cleanTimer();


// 启动新的计时器
timerProgressBar.setAudioTotalTime(audioTotalTime);
timerProgressBar.setCurrentPlayedSongLrcInfo(playingSong.getLrcInfo());


timerProgressBar.startTimer();


// 因为要与播放面板交互 所以让监听歌曲状态的线程在高层播放器初始化
playThread = new Thread(() -> {
// 播放结束前, 线程在此阻塞 不能解码的歌曲 这不阻塞
super.play();


if (IsEnd) {
IsEnd = false;
return;
}


// 播放结束 play按钮显示"播放"状态
play.doClick();


// 播放模式决定接着进行地播放
playSwitch();
});
playThread.start();


}


private void playSwitch() {


IsComplete = true;
// 初始化播放状态
switch (mode) {
// 单曲播放
case 0:
return;
// 单曲循环
case 1:
break;


// 顺序播放 当触发播放按钮时,因为要播放新歌曲 所以进入
// if(!player.getAfterSong().equals(player.getCurrentSong())) {}
// 使得当前线程被终止,往下的播放操作被终止
case 2:
IsPlayNextSong = true;
next();
break;
// 列表播放 情况同顺序播放
case 3:
cycle();
break;
// 随机播放 情况同顺序播放
case 4:
random();
break;
}


play.doClick();
}


// 接着进行地播放
public void next() {
DefaultMutableTreeNode list = (DefaultMutableTreeNode) playingSong
.getParent();
SongNode songNode = null;


if (!IsPlayNextSong) {
songNode = (SongNode) list.getChildBefore(playingSong);


} else {
songNode = (SongNode) list.getChildAfter(playingSong);
}
if (songNode == null) {
IsPause = false;


return;
}
// 在当前所在的歌曲列表路径中加入待播放的歌曲 形成待播放歌曲的路径
TreePath songPath = currentListPath.pathByAddingChild(songNode);
tree.setSelectionPath(songPath);


if (songNode.getHTTPFlag())
load(songNode, songNode.getDataURL());
else
load(songNode);
}


// 列表循环播放
private void cycle() {
DefaultMutableTreeNode list = (DefaultMutableTreeNode) playingSong
.getParent();
SongNode songNode = null;


songNode = (SongNode) list.getChildAfter(playingSong);


if (songNode == null) {
songNode = (SongNode) list.getFirstChild();
}


// 在当前所在的歌曲列表路径中加入待播放的歌曲 形成待播放歌曲的路径
TreePath songPath = currentListPath.pathByAddingChild(songNode);
tree.setSelectionPath(songPath);


if (songNode.getHTTPFlag())
load(songNode, songNode.getDataURL());
else
load(songNode);


}


// 随机播放
private void random() {
DefaultMutableTreeNode list = (DefaultMutableTreeNode) playingSong
.getParent();
int songnum = list.getChildCount();


// 随机歌曲
int songindex = (int) Math.round(Math.random() * songnum) - 1;
if (songindex < 0)
songindex = 0;


SongNode songNode = (SongNode) list.getChildAt(songindex);
// 在当前所在的歌曲列表路径中加入待播放的歌曲 形成待播放歌曲的路径
TreePath songPath = currentListPath.pathByAddingChild(songNode);
tree.setSelectionPath(songPath);


if (songNode.getHTTPFlag())
load(songNode, songNode.getDataURL());
else
load(songNode);


}


// 提供网络资源加入列表及播放的接口
public void setSelectTreeNodeInCurrentList(SongNode songNode, String dataURL) {


TreePath songPath = currentListPath.pathByAddingChild(songNode);
tree.setSelectionPath(songPath);


load(songNode, dataURL);
}


public void end() {
super.end();
timerProgressBar.cleanTimer();
}


public TreeNode getloadSong() {
return loadSong;
}


public TreeNode getPlayingSong() {
return playingSong;
}


public String getPlayingSongName() {
return playingSongName;
}


public void setPlayingSongName(String song) {
playingSongName = song;
}


public String getLoadSongName() {
return loadSongName;
}


public JButton getPlayButton() {
return play;
}


public void setPlayButton(JButton button) {
this.play = button;


}


public void setCurrentListPath(TreePath currentListPath) {
this.currentListPath = currentListPath;
}


public void setJTree(JTree tree) {
this.tree = tree;
}


public JTree getJTree() {
return this.tree;
}


public JLabel getSongNameLabel() {
return songNameLabel;
}


public void setSongNameLabel(JLabel songNameLabel) {
this.songNameLabel = songNameLabel;
}


public void setVoiceValue(float voiceValue) {
super.getFloatControl().setValue(voiceValue);
}


public float getVoiceValue() {
return getFloatControl().getValue();
}


public void setAudioTotalTimeLabel(JLabel label) {
audioTotalTimeLabel = label;
}


public void setCurrentTimeCountLabel(JLabel label) {
}


public void setTimerProgressBar(TimeProgressBar timerProgressBar) {
this.timerProgressBar = timerProgressBar;
}
}

//

package com.hubPlayer.search;


import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import javax.swing.JOptionPane;


import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;


import com.hubPlayer.song.SongInfos;


/**
 * 利用JSoup解析百度音乐 从中得到搜索的歌曲信息(SongInfos)
 *
 * @date 2014-11-06
 */


public class SearchSong {


// http://music.baidu.com/search/song?s=1&key=key&start=00&size=20
// 上面是百度音乐搜索地址形式, key是关键字,start是以库中的第几条歌曲开始,size为页面显示的歌曲数目(最大为20)


// 搜索地址及网页编码集 百度音乐是以utf-8编码
private static final String baseUrl = "http://music.baidu.com";
private String encode = "utf-8";


// 歌曲集合
private Map<String, List<SongInfos>> songLibraryMap;
private int songNumber;


// 与搜索和展示面板交互
private String key;
private int start;
private int page;


// boolean flag;


public SearchSong() {
// songLibraryMap = new HashMap<String, List<SongInfos>>();


// 第一页
page = 1;
// 第一首歌序号
start = 0;



songNumber =20;
}


/**
* 打开搜索地址,获取HTML
*/
public boolean openConnection() {
if (key == null)
return false;


// 拼接搜索地址
String searchUrl = "";
if ("百度音乐新歌榜/月榜".equals(key)) {
// 百度音乐新歌榜/月榜地址
searchUrl = "http://music.baidu.com/top/new/month/";


} else {


String keyEncode = "";
// 将key关键字转成URL编码
try {
keyEncode = URLEncoder.encode(key, encode);
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}


searchUrl = baseUrl + "/search/song?s=1&key=" + keyEncode
+ "&start=" + start + "&size=20";
}


try {
// 打开链接,获取HTML文 档
// 功能用URLConnection一样,下面被注释了的代码
Document document = Jsoup.connect(searchUrl).get();

parseHtml(document);


// // 打开连接
// URLConnection connection = new URL(searchUrl).openConnection();
//
//
// // 打开输入流
// BufferedReader reader = new BufferedReader(new InputStreamReader(
// connection.getInputStream(), encode));
//
// // 获取HTML
// StringBuffer stringbuffer = new StringBuffer();
// String line;
// while ((line = reader.readLine()) != null) {
// stringbuffer.append(line + "\n");
// }
return true;
} catch (IOException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "网络连接超时", "",
JOptionPane.PLAIN_MESSAGE);
return false;
}


}


/**
* 解析HTML 获取信息:歌曲、歌手、专辑名及歌曲所在的地址

* @param document
*            HTML文档
*/
private void parseHtml(Document document) {
// 获取HTML中的歌曲列表区域块


// 获取搜索歌曲数目
songNumber = 20;
Element e = document.select("span[class=number]").first();
if (e != null) {
String number = e.text();
songNumber = Integer.parseInt(number);
}


// 每个歌曲的区域块
Elements songDiv = null;
// 新歌榜 的关键字不一样
if ("百度音乐新歌榜/月榜".equals(key))
songDiv = document.select("div[class=song-item]");
else
songDiv = document.select("div[class^=song-item clearfix]");


List<SongInfos> temporaryList = new Vector<SongInfos>();
// 遍历每个歌曲块
for (Element aSongNode : songDiv) {
// 选择class等于以song-title开头的span标签
Element songTitle = aSongNode.select("span[class^=song-title]")
.first().select("a[href^=/song]").first();
if (songTitle == null)
continue;


// 获取歌曲所在的绝对地址
String songUrl = songTitle.attr("abs:href");


// 获取歌曲名
String songName = songTitle.text();


// 搜索列表保存歌曲信息
temporaryList.add(getSongInfos(songName, songUrl));
}


if (songLibraryMap.get(key) == null)
songLibraryMap.put(key, temporaryList);
else
songLibraryMap.get(key).addAll(temporaryList);
}


// 深度爬取歌曲信息
private SongInfos getSongInfos(String songName, String songUrl) {


SongInfos songInfos = new SongInfos(songName);


try {
// 打开歌曲链接,获取其HTML代码
Document document = Jsoup.connect(songUrl).get();


// 歌曲资源地址
// 格式 http://music.baidu.com/song/7319923
// String songID = songUrl.substring(28, songUrl.length());
// String dataUrl =
// "http://music.baidu.com/data/music/file?link=&song_id="
// + songID;


// 歌手名
String singer = "";
Element SingerElement = document.select("span[class^=author_list]")
.first();
if (SingerElement != null) {
singer = SingerElement.text();


// 如果歌手名格式形如"x1/x2"则转成"x1、x2"
if (singer.contains("/")) {
String[] singers = singer.split("/");
singer = "";
for (String s : singers) {
singer = singer + "、" + s;
}
singer = singer.substring(1);
}
}


// 获取专辑信息 格式-所属专辑:album
String album = "";
Element albumElement = document.select("li[class^=clearfix]")
.first();
if (albumElement != null)
album = albumElement.text();
// 去除"所属专辑:"
if (album.length() >= 5)
album = album.substring(5);


searchDataURL(songInfos, singer, songName);


// 百度音乐改版 此节点已经找不到
// 获取其下载链接,在下载页面获取不到这个地址
// String downloadUrl = "";
// Element downloadElement = document.select("a[data_url]").first();
// if (downloadElement != null)
// downloadUrl = downloadElement.attr("data_url");


// if (!flag) {
// findDataUrl("陈奕迅", "苦瓜");
// flag = true;
// }


// 百度音乐改版 此节点已经找不到
// // 获取歌曲文件长度
// int dataSize = 0;
// // 播放时长
// int totalTime = 0;
//
// Element dataSizeElement =
// document.select("a[data_size]").first();
// if (dataSizeElement != null) {
// // 大概的时间
// String size = dataSizeElement.attr("data_size");
// dataSize = Integer.parseInt(size);
// totalTime = dataSize * 8 / songInfos.getBitRate();
// }


// 获取歌词文件地址
String lrcUrl = "";
Element lrcUrlElement = document.select("a[data-lyricdata]")
.first();
if (lrcUrlElement != null) {
lrcUrl = lrcUrlElement.attr("data-lyricdata");


// 正则匹配
Pattern pattern = Pattern.compile("(/.*\\.lrc)");
Matcher matcher = pattern.matcher(lrcUrl);
if (matcher.find())
lrcUrl = baseUrl + matcher.group();
else
lrcUrl = "";
}


// 保存歌曲信息
songInfos.setSinger(singer);
songInfos.setAlbum(album);
songInfos.setLrcUrl(lrcUrl);


// System.out.println("--------A song item--------");
// System.out.println("song: " + songName + " singer: " + singer
// + " album: " + songInfos.getAlbum() + " dataSize: "
// + songInfos.getDataSize() + " bitRate: "
// + songInfos.getBitRate());
// System.out.println("songDataUrl: " + songInfos.getSongDataUrl());
// System.out.println("lrcUrl: " + songInfos.getLrcUrl());
// System.out.println("---------------------------");


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


// JOptionPane.showMessageDialog(null, "歌曲地址: " + songUrl +
// "\n歌曲名: "
// + songName + "  读取数据异常", "", JOptionPane.PLAIN_MESSAGE);


}


return songInfos;
}


/**
* 由于受百度音乐改版的影响,这里不直接去获取歌曲资源地址和文件总字节数 百度音乐用了登陆和JS加密这个地址
* 我们用百度音乐盒http://box.zhangmen.baidu.com/的xml文件间接获取


*/


private void searchDataURL(SongInfos songInfos, String singer, String song) {


String songBoxUrl = "http://box.zhangmen.baidu.com/x?op=12&count=1&title="
+ song + "$$" + singer + "$$";


Document document = null;
try {
document = Jsoup.connect(songBoxUrl).get();
} catch (IOException e) {
e.printStackTrace();
return;
}


/**
* 提取durl节点中的encode节点的字符串与decode节点的字符串拼接歌曲资源地址
**/
String dataUrl = "";
Elements durlNodes = document.select("durl");


for (Element durlNode : durlNodes) {


Element encode = durlNode.select("encode").first();
Element decode = durlNode.select("decode").first();


if (encode == null || decode == null)
continue;
String encodeText = encode.text();
String decodeText = decode.text();
encodeText = encodeText.substring(0,
encodeText.lastIndexOf("/") + 1);
dataUrl = encodeText + decodeText;


}


/**
* 获取歌曲文件长度和比特率
**/
int dataSize = 0;
int totalTime = 0;


Element p2p = document.select("p2p").first();
if (p2p != null) {
// 文件总字节数
String dataSizeText = p2p.select("size").first().text();
// 比特率
int bitRate = Integer.parseInt(p2p.select("bitrate").text()) * 1000;


dataSize = Integer.parseInt(dataSizeText);
totalTime = dataSize * 8 / bitRate;
songInfos.setBitRate(bitRate);


}


songInfos.setSongDataUrl(dataUrl);
songInfos.setDataSize(dataSize);
songInfos.setTotalTime(totalTime);


}


/**
* 清除已解析的歌曲信息等
*/
public void clear() {


start = 0;
key = "";
page = 1;
songNumber = 20;
}


/**
* 设置搜索的关键字 之后需调用search方法进行搜索

* @param key
*            关键字
* @return SearchSong this
*/
public SearchSong setKey(String key) {
this.key = key;
return this;
}


public String getKey() {
return key;
}


/**
* 设置当前页面

* @param start
*            设置当前页面第一首歌曲在库中的序号,
* @return SearchSong this
*/
public SearchSong setPage(int page) {
this.page = page;
start = (page - 1) * 20;

return this;
}


// 设置页面
public int getPage() {
return page;
}


public void setSongLibraryMap(Map<String, List<SongInfos>> songLibraryMap) {


this.songLibraryMap = songLibraryMap;
}


public int getSongNumber() {
return songNumber;
}


}

/


package com.hubPlayer.search;


import java.io.Serializable;
import java.util.HashMap;


/**
 * 歌曲库集合 保存到本地
 * 
 * @param <V>
 *            String searchKey
 * @param <K>
 *            List<SongInfos> songList
 * 
 * @date 2014-12-6
 */
public class SongLibraryMap<K, V> extends HashMap<K, V> implements Serializable {


    // 歌曲缓存时间
private Long bufferedDatetime;


public long getBufferedDateTime() {
return bufferedDatetime;
}


public void setBufferedDateTime(long bufferedDatetime) {
this.bufferedDatetime = bufferedDatetime;
}


}

/

package com.hubPlayer.song;


import java.io.File;
import java.io.FileFilter;
import java.util.StringTokenizer;


/**
 * AFilter 过滤器  歌曲类型 MP3 WAV MID 歌词类型 LRC
 * 
 * @date 2014-10-15
 */


public class AFilter implements FileFilter {


private String description;


public AFilter(String description) {
this.description = description;
}


public boolean accept(File f) {


String name = f.getName();
StringTokenizer token = new StringTokenizer(description, ",;");


while (token.hasMoreTokens()) {
if (name.toLowerCase().endsWith(token.nextToken().toLowerCase()))
return true;
}


return false;
}


public String getDescription() {


return description;
}


}

/

package com.hubPlayer.song;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * 歌词信息:标题/歌手/专辑/时间-歌词文本组
 * 
 * @date 2014-10-31
 */


public class LrcInfos {


private String title;
private String singer;
private String album;
private int totalTime;


private List<Integer> timeList;
private Map<Integer, String> infos;


private String regex;
private Pattern pattern;


public LrcInfos() {
infos = new HashMap<Integer, String>();
timeList = new ArrayList<Integer>();
regex = "\\[\\d{2}:\\d{2}\\.\\d{2}\\]";
pattern = Pattern.compile(regex);
}


// 读入本地歌词文件
public void read(File file) {


try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(file), "utf-8"));


String line = "";


while ((line = reader.readLine()) != null) {
match(line);
}


reader.close();
} catch (IOException e) {
e.printStackTrace();
}


}


// 读入网络资源
public void read(String lrcUrl) {


try {
HttpURLConnection urlConnection = (HttpURLConnection) new URL(
lrcUrl).openConnection();


BufferedReader reader = new BufferedReader(new InputStreamReader(
urlConnection.getInputStream(), "utf-8"));


String line = "";


while ((line = reader.readLine()) != null) {
match(line);
}


reader.close();


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


}


// 匹配一行字符
private void match(String line) {


// 判断当前行是否为歌手、标题、专辑 不是 满足
if (!line.endsWith("]")) {


// 匹配当前的时间格式
Matcher matcher = pattern.matcher(line);


int endIndex = 0;
// 满足则
while (matcher.find()) {
// 获取内容
String group = matcher.group();


group = group.substring(1, group.length() - 1);


timeList.add(toCurrentTime(group));
endIndex = matcher.end();
}


String strInfos = line.substring(endIndex, line.length());
timeList.forEach(each -> infos.put(each, strInfos));
timeList.clear();


} else if (line.startsWith("[ti:")) {
title = line.substring(4, line.length() - 1);
} else if (line.startsWith("[ar:")) {
singer = line.substring(4, line.length() - 1);
} else if (line.startsWith("[al:")) {
album = line.substring(4, line.length() - 1);
}
}


// 解析当前信息时间 秒制
private int toCurrentTime(String time) {
int currentTime = 0;


String[] spliter = time.split(":");
currentTime += Integer.parseInt(spliter[0]) * 60;
spliter = spliter[1].split("\\.");
currentTime += Integer.parseInt(spliter[0]);
if (Integer.parseInt(spliter[1]) >= 50)
currentTime++;


if (totalTime < currentTime)
totalTime = currentTime;


return currentTime;
}


public String getTitle() {
return title;
}


public String getSinger() {
return singer;
}


public String getAlbum() {
return album;
}


public int getTotalTime() {
return totalTime;
}


public Map<Integer, String> getInfos() {
return infos;
}
}

/

package com.hubPlayer.song;


import java.io.Serializable;


/**
 * 网络爬取得 歌曲信息:歌曲名、歌手、专辑、播放时间、歌词地址、资源地址
 * 
 * @date 2014-11-06
 */


public class SongInfos implements Serializable{


private String song;
private String singer;
private String album;


// 时间=文件长度/比特率*8
private int totalTime;
private int dataSize;


private String songDataUrl;
private String lrcUrl;


// 大概的比特率
private int bitRate;


public SongInfos(String song) {
this.song = song;
totalTime = 0;
lrcUrl = "";


bitRate = 128000;
}


public SongInfos(String song, String singer) {
this(song);
this.singer = singer;
}


public SongInfos(String song, String singer, String album) {
this(song, singer);
this.album = album;
}


public SongInfos(String song, String singer, String album,
String songDataUrl) {
this(song, singer, album);
this.songDataUrl = songDataUrl;
}


public String getSong() {
return song;
}


public void setSong(String song) {
this.song = song;
}


public String getAlbum() {
return album;
}


public void setAlbum(String album) {
this.album = album;
}


public String getSinger() {
return singer;
}


public void setSinger(String singer) {
this.singer = singer;
}


public String getSongDataUrl() {
return songDataUrl;
}


public void setSongDataUrl(String songDataUrl) {
this.songDataUrl = songDataUrl;
}


public int getTotalTime() {
return totalTime;
}


public void setTotalTime(int totalTime) {
this.totalTime = totalTime;
}


public String getLrcUrl() {
return lrcUrl;
}


public void setLrcUrl(String lrcUrl) {
this.lrcUrl = lrcUrl;
}


public int getBitRate() {
return bitRate;
}


public void setBitRate(int bitRate) {
this.bitRate = bitRate;
}


public int getDataSize() {
return dataSize;
}


public void setDataSize(int dataSize) {
this.dataSize = dataSize;
}


public String toString() {
return "Song: " + song + " Singer: " + singer + " Album: " + album
+ "\ndownload URL: " + songDataUrl;
}


public boolean equals(Object object) {
if (this == object)
return true;
if (getClass() != object.getClass())
return false;
SongInfos objectSongInfos = (SongInfos) object;


if (songDataUrl == null)
return song.equals(objectSongInfos.getSong())
&& singer.equals(objectSongInfos.getSinger())
&& album.equals(objectSongInfos.getAlbum());


return songDataUrl.equals(objectSongInfos.getSongDataUrl());
}
}

/

package com.hubPlayer.song;


import java.io.File;


import javax.swing.tree.DefaultMutableTreeNode;


/**
 * SongNode 显示歌曲名字 不显示歌曲路径
 * 
 * @date 2014-10-15
 */


public class SongNode extends DefaultMutableTreeNode {


private File song;
private String dataUrl;
// 标记歌曲是否为网络资源
private boolean HTTPFlag;
// 播放时长
private int totalTime;
// 文件长度
private int dataSize;


// 本地与网络歌词资源
private File lrcFile;
// 解析的歌曲信息
private LrcInfos lrcInfo;


// 歌曲的上级路径
private String parentPath;
// 无扩展名的歌曲名
private String songName;


public SongNode(File song) {
super(song, false);
this.song = song;


parentPath = song.getParent();
songName = song.getName();
songName = songName.substring(0, songName.lastIndexOf("."));


File f = new File(parentPath + "\\" + songName + ".lrc");
lrcInfo = new LrcInfos();
if (f.exists()) {
lrcFile = f;
lrcInfo.read(lrcFile);
}
}


// 网络资源
public SongNode(String songName, int totalTime, int dataSize,
String lrcUrl, String dataUrl) {
try {
song = new File(dataUrl);
} catch (NullPointerException e) {
song = null;
}
this.dataUrl = dataUrl;
this.songName = songName;
this.dataSize = dataSize;


HTTPFlag = true;


// 大概的播放时间 真正播放时长大概是算出的时间-3
this.totalTime = totalTime - 3;


lrcInfo = new LrcInfos();


// 存在歌曲链接
if (lrcUrl.length() > 0) {
lrcInfo.read(lrcUrl);
// 利用歌词来得到精确的播放时间
int time = lrcInfo.getTotalTime();
if (this.totalTime < time) {
this.totalTime = time;
}
}


}


public int getTotalTime() {
return totalTime;
}


public void setLrc(File lrcFile) {
this.lrcFile = lrcFile;
lrcInfo.read(lrcFile);
}


public File getLrc() {
return lrcFile;
}


public LrcInfos getLrcInfo() {
return lrcInfo;
}


public String getSongName() {
return songName;
}


public File getSong() {
return song;
}


public String getDataURL() {
return dataUrl;
}


public boolean getHTTPFlag() {
return HTTPFlag;
}


public int getDataSize() {
return dataSize;
}


@Override
public String toString() {
if (HTTPFlag)
return songName + ".mp3";


return song.getName();


}


public boolean equals(Object object) {

if (this == object)
return true;
if (object == null)
return false;
if (getClass() != object.getClass())
return false;


if (song == null)
return false;


SongNode objectNode = (SongNode) object;


return song.equals(objectNode.getSong());
}
}

/

package com.hubPlayer.ui;


import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
import java.util.List;


import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JToolBar;
import javax.swing.JTree;


import com.hubPlayer.player.HigherPlayer;
import com.hubPlayer.search.SongLibraryMap;
import com.hubPlayer.song.SongInfos;
import com.hubPlayer.ui.tool.ButtonToolBar;
import com.hubPlayer.ui.tool.IconButton;


/**
 * 以 KuGou布局为框架
 * 
 * @date 2014-10-1
 */


public class HubFrame extends JFrame {


    private static final int InitialWidth;
private static final int InitialHeight;
private static final Point InitialPoint;


private static final int ChangedWidth;


private static final String savaPath;
private static final double bufferedTime;


private PlayPanel playPanel;
private PlayListPanel playListPanel;
private SearchPanel searchPanel;
private ShowPanel showPanel;
private ButtonToolBar toolBar;


private SongLibraryMap<String, List<SongInfos>> songLibrary;


static {
Dimension dime = Toolkit.getDefaultToolkit().getScreenSize();
InitialWidth = 975;
InitialHeight = 670;
ChangedWidth = 365;
InitialPoint = new Point((dime.width - InitialWidth) / 2,
(dime.height - InitialHeight) / 2);


savaPath = "E:/Hub/download";
bufferedTime = 2 * Math.pow(10, 8);
}


public HubFrame() {


setTitle("Hub");
setSize(InitialWidth, InitialHeight);


setLocation(InitialPoint);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


// 保存下载歌曲文件夹
File savefolder = new File(savaPath);


// 创建已下载歌曲的存储文件夹
if (!savefolder.exists())
savefolder.mkdirs();


readSongLibrary();


buildPanel();


setVisible(true);


requestFocus();
}


private void buildPanel() {
playPanel = new PlayPanel();


// ToolBar:the left of the Frame
toolBar = new ButtonToolBar(JToolBar.VERTICAL, 4);


JButton[] toolBarButtons = new JButton[] {
new IconButton("本地列表", "icon/note.png"),
new IconButton("网络收藏", "icon/clouds.png"),
new IconButton("我的下载", "icon/download.png"),
new IconButton("更多", "icon/app.png") };


toolBar.addButtons(toolBarButtons);


playListPanel = new PlayListPanel(toolBar.getButtons(), this);


searchPanel = new SearchPanel();


showPanel = new ShowPanel();


// 传递给各面板的属性
JTree[] listTree = playListPanel.deliverTree();
HigherPlayer player = playPanel.getHigherPlayer();


// 沟通播放面板与歌曲列表面板
playPanel.setTrees(listTree);
player.setJTree(listTree[0]);
playListPanel.deliverHigherPlayer(player);


// 沟通乐库面板与歌曲列表面板
showPanel.setListTree(listTree);


// 沟通搜索面板与展示面板
searchPanel.setShowPanel(showPanel);


// 沟通搜索面板与本地
searchPanel.setSongLibraryMap(songLibrary);


// 沟通播放面板与歌词面板
playPanel.setLrcPanelTextArea(showPanel.getTextArea());


// 沟通乐库面板与播放面板
showPanel.setPlayer(player);


playPanel.setParentFrame(this);


// Set the preferredSize of those panels
playPanel.setPreferredSize(new Dimension(350, 115));
playListPanel.setPreferredSize(new Dimension(305, 520));
toolBar.setPreferredSize(new Dimension(47, 520));
searchPanel.setPreferredSize(new Dimension(610, 115));
showPanel.setPreferredSize(new Dimension(610, 520));


buildLayout();


setAction();
}


private void setAction() {


// 设置最大化事件 即展开窗体
this.addWindowStateListener(event -> {
if (event.getNewState() == JFrame.MAXIMIZED_BOTH) {
searchPanel.setVisible(true);
showPanel.setVisible(true);
setSize(InitialWidth, InitialHeight);
setLocation(InitialPoint);
setVisible(true);


}
});


// 折叠窗体
searchPanel.getCollapseButton().addActionListener(event -> {
searchPanel.setVisible(false);
showPanel.setVisible(false);
setSize(ChangedWidth, InitialHeight);
setVisible(true);
});


this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {


saveSongLibrary();


System.exit(0);
}
});
}


private void buildLayout() {
Box topBox = Box.createHorizontalBox();


topBox.add(playPanel);
topBox.add(searchPanel);


Box bottomBox = Box.createHorizontalBox();
bottomBox.add(toolBar);
bottomBox.add(playListPanel);
bottomBox.add(showPanel);


Container mainPanel = getContentPane();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));


mainPanel.add(topBox);
mainPanel.add(bottomBox);
mainPanel.add(Box.createVerticalStrut(15));
}


@SuppressWarnings({ "unchecked" })
private void readSongLibrary() {


File library = new File("E:/Hub/SongLibrary.dat");
if (!library.exists()) {
songLibrary = new SongLibraryMap<String, List<SongInfos>>();
} else {


try {
ObjectInputStream inputStream = new ObjectInputStream(
new FileInputStream(library));
songLibrary = (SongLibraryMap<String, List<SongInfos>>) inputStream
.readObject();


// 歌曲库缓存超过约两天,将清除
long bufferedDateTime = songLibrary.getBufferedDateTime();
if ((new Date().getTime() - bufferedDateTime) > bufferedTime) {
songLibrary.clear();
}


inputStream.close();
} catch (IOException | ClassNotFoundException e) {
songLibrary = new SongLibraryMap<String, List<SongInfos>>();
e.printStackTrace();
}
}
}


private void saveSongLibrary() {


if (songLibrary != null) {
songLibrary.setBufferedDateTime(new Date().getTime());
try {
ObjectOutputStream outputStream = new ObjectOutputStream(
new FileOutputStream(new File("E:/Hub/SongLibrary.dat")));
outputStream.writeObject(songLibrary);
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}


}

/

package com.hubPlayer.ui;


import java.awt.CardLayout;


import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;


import com.hubPlayer.player.HigherPlayer;
import com.hubPlayer.ui.tool.AppPanel;
import com.hubPlayer.ui.tool.SongListPanel;


/**
 * 播放列表面板
 * 
 * @date 2014-10-12
 */


public class PlayListPanel extends JPanel {


private SongListPanel songListPanel;
private SongListPanel cloudsPanel;
private SongListPanel downloadPanel;
private JScrollPane appPanel;


private JButton[] buttons;
private JFrame parent;


private CardLayout card;


public PlayListPanel(JButton[] buttons, JFrame parent) {


setOpaque(false);


card = new CardLayout();
setLayout(card);


this.buttons = buttons;
this.parent = parent;


initPanel();
}


private void initPanel() {
songListPanel = new SongListPanel("默认列表");
songListPanel.addPopupMenuToTree();


cloudsPanel = new SongListPanel("默认列表", "我喜欢");
cloudsPanel.addPopupMenuToTree();


downloadPanel = new SongListPanel("下载中", "已下载");


appPanel = new JScrollPane(new AppPanel(parent));


add(songListPanel, "0");
add(cloudsPanel, "1");
add(downloadPanel, "2");
add(appPanel, "3");


setAction();
}


private void setAction() {


for (int i = 0; i < buttons.length; i++) {
int k = i;
buttons[i].addActionListener(event -> {
card.show(this, "" + k);
});
}


}


public void deliverHigherPlayer(HigherPlayer HigherPlayer) {
songListPanel.setPlayer(HigherPlayer);
cloudsPanel.setPlayer(HigherPlayer);
downloadPanel.setPlayer(HigherPlayer);
}


public JTree[] deliverTree() {
return new JTree[] { songListPanel.getTree(), cloudsPanel.getTree(),
downloadPanel.getTree() };
}
}

/

package com.hubPlayer.ui;


import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.net.HttpURLConnection;


import javax.swing.DefaultComboBoxModel;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.LayoutStyle;
import javax.swing.SwingConstants;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.JFrame;


import com.hubPlayer.player.HigherPlayer;
import com.hubPlayer.song.SongNode;
import com.hubPlayer.ui.tool.IconButton;
import com.hubPlayer.ui.tool.TimeProgressBar;


/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */


/**
 * 播放面板 使用NetBean Matisse构建.面板包含:3个标签,7个按钮,1个进度条,1个组合框和1个滑块条
 * 
 * @date 2014-10-9
 */


public class PlayPanel extends JPanel {


/**
* Creates new form HubPlayPanel
*/
public PlayPanel() {
initComponents();
initPlayer();
}


/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {


songName = new JLabel("Music");


download = new IconButton("下载", "icon/download.png");
mark = new IconButton("我喜欢", "icon/heart.png");
share = new IconButton("分享", "icon/share.png");


timerProgressBar = new TimeProgressBar();
mode = new JComboBox<>();


backPlay = new IconButton("上一首", "icon/back.png");
play = new IconButton("播放", "icon/play.png");
play.setSelectedIcon(new ImageIcon("icon/pause.png"));
play.setDisabledSelectedIcon(new ImageIcon("icon/play.png"));
play.setMnemonic(KeyEvent.VK_ENTER);


frontPlay = new IconButton("下一首", "icon/front.png");
voiceControl = new IconButton("静音", "icon/voice.png");
voiceAdjust = new JSlider(minVoice, maxVoice);
voiceAdjust.setValue(suitableVoice);
audioTotalTimeLabel = new JLabel("4:00");
currentTimeCountLabel = new JLabel("0:00");


setOpaque(false);
setPreferredSize(new Dimension(360, 110));


download.setMaximumSize(new Dimension(3030, 3030));
download.setMinimumSize(new Dimension(30, 30));
download.setPreferredSize(new Dimension(30, 30));


mark.setPreferredSize(new Dimension(30, 30));


share.setPreferredSize(new Dimension(30, 30));


timerProgressBar.setPreferredSize(new Dimension(340, 7));


mode.setModel(new DefaultComboBoxModel(new String[] { "单曲播放", "单曲循环",
"顺序播放", "列表循环", "随机播放" }));


backPlay.setPreferredSize(new Dimension(30, 30));


play.setPreferredSize(new Dimension(30, 30));


frontPlay.setPreferredSize(new Dimension(30, 30));


voiceControl.setPreferredSize(new Dimension(30, 30));


voiceAdjust.setPreferredSize(new Dimension(50, 20));


audioTotalTimeLabel.setText("0:00");


currentTimeCountLabel.setText("0:00");


GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(layout
.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(
layout.createSequentialGroup()
.addGroup(
layout.createParallelGroup(
GroupLayout.Alignment.LEADING)
.addGroup(
layout.createSequentialGroup()
.addGap(12, 12,
12)
.addComponent(
songName,
GroupLayout.PREFERRED_SIZE,
230,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(
LayoutStyle.ComponentPlacement.RELATED)
.addComponent(
download,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(
LayoutStyle.ComponentPlacement.RELATED)
.addComponent(
mark,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(
LayoutStyle.ComponentPlacement.RELATED)
.addComponent(
share,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE))
.addGroup(
layout.createSequentialGroup()
.addGap(10, 10,
10)
.addGroup(
layout.createParallelGroup(
GroupLayout.Alignment.LEADING)
.addGroup(
layout.createParallelGroup(
GroupLayout.Alignment.TRAILING,
false)
.addGroup(
layout.createSequentialGroup()
.addComponent(
currentTimeCountLabel)
.addPreferredGap(
LayoutStyle.ComponentPlacement.RELATED,
GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE)
.addComponent(
audioTotalTimeLabel))
.addComponent(
timerProgressBar,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE))
.addGroup(
layout.createSequentialGroup()
.addComponent(
mode,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(
LayoutStyle.ComponentPlacement.RELATED,
33,
Short.MAX_VALUE)
.addComponent(
backPlay,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(
LayoutStyle.ComponentPlacement.RELATED)
.addComponent(
play,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(
LayoutStyle.ComponentPlacement.RELATED)
.addComponent(
frontPlay,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addGap(28,
28,
28)
.addComponent(
voiceControl,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(
LayoutStyle.ComponentPlacement.RELATED)
.addComponent(
voiceAdjust,
GroupLayout.PREFERRED_SIZE,
81,
GroupLayout.PREFERRED_SIZE)
.addGap(1,
1,
1)))))
.addGap(0, 7, Short.MAX_VALUE)));


layout.linkSize(SwingConstants.HORIZONTAL, new Component[] { download,
mark, share });


layout.setVerticalGroup(layout
.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(
layout.createSequentialGroup()
.addGroup(
layout.createParallelGroup(
GroupLayout.Alignment.LEADING)
.addGroup(
layout.createSequentialGroup()
.addGroup(
layout.createParallelGroup(
GroupLayout.Alignment.LEADING)
.addGroup(
layout.createSequentialGroup()
.addGroup(
layout.createParallelGroup(
GroupLayout.Alignment.LEADING)
.addGroup(
layout.createSequentialGroup()
.addGap(10,
10,
10)
.addGroup(
layout.createParallelGroup(
GroupLayout.Alignment.BASELINE)
.addComponent(
download,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addComponent(
mark,
GroupLayout.PREFERRED_SIZE,
20,
GroupLayout.PREFERRED_SIZE)
.addComponent(
share,
GroupLayout.PREFERRED_SIZE,
20,
GroupLayout.PREFERRED_SIZE))
.addGap(6,
6,
6))
.addGroup(
GroupLayout.Alignment.TRAILING,
layout.createSequentialGroup()
.addContainerGap()
.addComponent(
songName,
GroupLayout.PREFERRED_SIZE,
20,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(
LayoutStyle.ComponentPlacement.UNRELATED)))
.addComponent(
timerProgressBar,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE))
.addGroup(
layout.createSequentialGroup()
.addGap(55,
55,
55)
.addGroup(
layout.createParallelGroup(
GroupLayout.Alignment.CENTER)
.addComponent(
currentTimeCountLabel)
.addComponent(
audioTotalTimeLabel))))
.addPreferredGap(
LayoutStyle.ComponentPlacement.RELATED)
.addGroup(
layout.createParallelGroup(
GroupLayout.Alignment.CENTER)
.addComponent(
play,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addComponent(
backPlay,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addComponent(
frontPlay,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)))
.addGroup(
layout.createSequentialGroup()
.addGap(76, 76,
76)
.addComponent(
voiceControl,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE))
.addGroup(
GroupLayout.Alignment.CENTER,
layout.createSequentialGroup()
.addGap(80, 80,
80)
.addGroup(
layout.createParallelGroup(
GroupLayout.Alignment.LEADING)
.addComponent(
voiceAdjust,
GroupLayout.Alignment.CENTER,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addComponent(
mode,
GroupLayout.Alignment.CENTER,
GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE))))
.addContainerGap(GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE)));


layout.linkSize(SwingConstants.VERTICAL, new Component[] { download,
mark, share });


}


private void initPlayer() {
player = new HigherPlayer();


player.setPlayButton(play);
player.setSongNameLabel(songName);
player.setAudioTotalTimeLabel(audioTotalTimeLabel);
player.setCurrentTimeCountLabel(currentTimeCountLabel);
player.setTimerProgressBar(timerProgressBar);


timerProgressBar.setTimerControl(player.IsPause);
timerProgressBar.setCurrentTimeCountLabel(currentTimeCountLabel);


setButtonsAction();
setOtherAction();
}


public HigherPlayer getHigherPlayer() {
return player;
}


public void setButtonsAction() {


// 播放 按钮
play.addActionListener(event -> {


if (player.getLoadSongName() == null) {
return;
}


// 在第一次播放时 为了不进入下面一个if判断
// 如果PlayingSongName==null时,则进入,终止当前歌曲播放,可是当前播放线程还没有实例 报异常
if (player.getPlayingSongName() == null) {
player.setPlayingSongName(player.getLoadSongName());
}


// 在播放过程切换歌曲
if (!player.getLoadSongName().equals(player.getPlayingSongName())
&& !player.IsComplete) {


// 终止当前歌曲播放
player.end();
player.setPlayingSongName(player.getLoadSongName());
}


// 是暂停 则播放
if (player.IsPause) {


if (player.NeedContinue) {
// 继续播放
player.resume();
// 进度条的控制
timerProgressBar.setTimerControl(false);
timerProgressBar.resumeTimer();
play.setIcon(play.getSelectedIcon());
play.setToolTipText("暂停");
return;
}

// 进度条的控制 不阻塞
timerProgressBar.setTimerControl(false);
// 播放歌曲
player.open();

// 声音控制
voiceAdjust.setValue(suitableVoice);
songName.setText(player.getPlayingSongName());
play.setIcon(play.getSelectedIcon());
play.setToolTipText("暂停");
player.IsPause = false;


} else {
// 是播放 则暂停
player.IsPause = true;


timerProgressBar.setTimerControl(true);


play.setIcon(play.getDisabledSelectedIcon());
play.setToolTipText("播放");
}


songName.updateUI();
play.updateUI();
parentFrame.setVisible(true);
});


//前一首按钮
backPlay.addActionListener(event -> {


if (player.getLoadSongName() == null) {
return;
}


player.IsPlayNextSong = false;
player.next();
play.doClick();


});


// 后一首按钮
frontPlay.addActionListener(event -> {


if (player.getLoadSongName() == null) {
return;
}


player.IsPlayNextSong = true;
player.next();
play.doClick();


});


//声音控制按钮
voiceControl.addActionListener(event -> {


if (!player.IsPause) {
if (voiceAdjust.getValue() != minVoice) {
player.setVoiceValue(minVoice);
voiceAdjust.setValue(minVoice);
} else {
player.setVoiceValue(suitableVoice);
voiceAdjust.setValue(suitableVoice);
}
}


});


// 下载歌曲按钮
download.addActionListener(event -> {


// 无载入歌时
if (player.getLoadSongName() == null) {
return;
}


if (!player.audio.toString().startsWith("http://")) {
JOptionPane.showMessageDialog(null, "播放中歌曲是本地资源,无需下载 ", "",
JOptionPane.PLAIN_MESSAGE);
return;
}


SongNode playedSong = (SongNode) player.getPlayingSong();


// 下载面板"下载中"列表添加播放中的歌曲
addSongNodeToTreeList(trees[2], 0, playedSong);


JOptionPane.showMessageDialog(null, "已成功添加到下载列表", null,
JOptionPane.PLAIN_MESSAGE, null);


download(playedSong);


});


// 标记按钮
mark.addActionListener(event -> {


if (player.getLoadSongName() == null) {
return;
}


SongNode playedSong = (SongNode) player.getPlayingSong();


// 加入标记列表
addSongNodeToTreeList(trees[1], 1, playedSong);


JOptionPane.showMessageDialog(null, "已成功添加到收藏列表", null,
JOptionPane.PLAIN_MESSAGE, null);
});


}


public void setOtherAction() {
timerProgressBar.addChangeListener(event -> {
});


voiceAdjust.addChangeListener(event -> {
if (!player.IsPause) {
player.setVoiceValue(voiceAdjust.getValue());
}
});


mode.addActionListener(event -> {
player.mode = mode.getSelectedIndex();


});


}


// 下载歌曲
public void download(SongNode songNode) {
new Thread(
() -> {


try {


// 打开资源链接
HttpURLConnection httpURLConnection = (HttpURLConnection) player.audio
.openConnection();


// 开启IO流 读写数据
BufferedInputStream inputStream = new BufferedInputStream(
httpURLConnection.getInputStream());


BufferedOutputStream outputStream = new BufferedOutputStream(
new FileOutputStream(new File("E:/Hub/download/"
+ player.getLoadSongName())));


byte[] buff = new byte[1024];
int onceRead = 0;
while ((onceRead = inputStream.read(buff, 0,
buff.length))!=-1) {
outputStream.write(buff, 0, onceRead);
}


outputStream.flush();
outputStream.close();
inputStream.close();


// 下载中列表移除节点
removeSongNodeInTreeList(trees[2], 0, songNode);


// 下载完成列表加入节点
addSongNodeToTreeList(trees[2], 1, songNode);


JOptionPane.showMessageDialog(null, "下载完成,文件存至E:/Hub/download",
"", JOptionPane.PLAIN_MESSAGE);
} catch (Exception e) {
e.printStackTrace();
}


}).start();
}


public void addSongNodeToTreeList(JTree tree, int index, SongNode songNode) {


DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel()
.getRoot();
DefaultMutableTreeNode list = (DefaultMutableTreeNode) root
.getChildAt(index);


list.add(songNode);


// 列表名更新
String listName = (String) list.getUserObject();
listName = listName.substring(0, listName.lastIndexOf("[")) + "["
+ list.getChildCount() + "]";
list.setUserObject(listName);


// 如果这里不更新树的话 会不正确显示
tree.updateUI();


}


public void removeSongNodeInTreeList(JTree tree, int index,
SongNode songNode) {
DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel()
.getRoot();
DefaultMutableTreeNode list = (DefaultMutableTreeNode) root
.getChildAt(index);


list.remove(songNode);


// 列表名更新
String listName = (String) list.getUserObject();
listName = listName.substring(0, listName.lastIndexOf("[")) + "["
+ list.getChildCount() + "]";
list.setUserObject(listName);


// 如果这里不更新树的话 会不正确显示
tree.updateUI();


}


public void setTrees(JTree[] trees) {
this.trees = trees;
}


public void setLrcPanelTextArea(JTextArea textArea) {
timerProgressBar.setLrcPanelTextArea(textArea);
}


public void setParentFrame(JFrame frame) {
this.parentFrame = frame;
}


// Variables declaration - do not modify
private JLabel songName;
private JButton download;
private JButton mark;
private JButton share;


private TimeProgressBar timerProgressBar;
private JLabel currentTimeCountLabel;
private JLabel audioTotalTimeLabel;


private JComboBox<String> mode;
private JButton backPlay;
private JButton play;
private JButton frontPlay;
private JSlider voiceAdjust;
private JButton voiceControl;


private JTree[] trees;


private JFrame parentFrame;


private HigherPlayer player;

// 由FloatControl.Type.MASTER_GAIN得到的数据
private final int minVoice = -80;
private final int maxVoice = 6;
private final int suitableVoice = -20;
// End of variables declaration


}


/


package com.hubPlayer.ui;


import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JTextField;
import javax.swing.JToolBar;


import com.hubPlayer.search.SearchSong;
import com.hubPlayer.song.SongInfos;
import com.hubPlayer.ui.tool.ButtonToolBar;
import com.hubPlayer.ui.tool.IconButton;
import com.hubPlayer.ui.tool.LibraryPanel;
import com.hubPlayer.ui.tool.LibraryTableModel;


/**
 * 搜索功能面板
 * 
 * @date 2014-10-26
 */


public class SearchPanel extends JPanel {


// 搜索功能相关
private JTextField textField;
private JButton searchButton;
// 记录前一次输入的文本
private String beforeKey;
// 解析由关键字产生的搜索网页
private SearchSong searchSong;
private Thread searchThread;


private JButton userButton;


// 主要用于切换展示面板页面
private ButtonToolBar hubToolBar;


private JButton[] toolBarButtons;


// 与展示面板相关
private ShowPanel showPanel;
private CardLayout cardLayout;


private LibraryPanel libraryPanel;
private LibraryTableModel libraryTableModel;


// 最大搜索页数
private int maxPage;


// 音乐库数据集
private Map<String, List<SongInfos>> songLibraryMap;


public SearchPanel() {


setLayout(new BorderLayout());
setOpaque(false);


init();
setAction();
createLayout();
}


private void init() {
textField = new JTextField();
textField.setPreferredSize(new Dimension(200, 30));


searchButton = new IconButton("搜索", "icon/search.png");
userButton = new IconButton("用户", "icon/user.png");


searchButton.setPreferredSize(new Dimension(50, 30));
userButton.setPreferredSize(new Dimension(50, 30));


hubToolBar = new ButtonToolBar(JToolBar.HORIZONTAL, 6);
hubToolBar.setPreferredSize(new Dimension(300, 65));


toolBarButtons = new JButton[6];


toolBarButtons[0] = new IconButton("折叠", "icon/collapse.png");
toolBarButtons[1] = new IconButton("乐库");
toolBarButtons[1].setText("乐库");
toolBarButtons[2] = new IconButton("MV");
toolBarButtons[2].setText("MV");
toolBarButtons[3] = new IconButton("歌词");
toolBarButtons[3].setText("歌词");
toolBarButtons[4] = new IconButton("电台");
toolBarButtons[4].setText("电台");
toolBarButtons[5] = new IconButton("直播");
toolBarButtons[5].setText("直播");


hubToolBar.addButtons(toolBarButtons);


searchSong = new SearchSong();
maxPage = 100;
}


private void createLayout() {


Box Box1 = Box.createHorizontalBox();
Box1.add(Box.createHorizontalStrut(10));
Box1.add(userButton);
Box1.add(Box.createHorizontalStrut(20));
Box1.add(textField);
Box1.add(Box.createHorizontalStrut(5));
Box1.add(searchButton);
Box1.add(Box.createHorizontalStrut(10));


Box Box2 = Box.createVerticalBox();
Box2.add(Box.createVerticalStrut(7));
Box2.add(Box1);
Box2.add(Box.createVerticalStrut(5));


add(Box2, BorderLayout.NORTH);
add(hubToolBar, BorderLayout.CENTER);
}


private void setAction() {


for (int i = 1; i < toolBarButtons.length; i++) {
int k = i;
toolBarButtons[i].addActionListener(event -> {
cardLayout.show(showPanel, String.valueOf(k));
});


}


searchButton.addActionListener(event -> {


String key = textField.getText();


if (!prejudgeForSearchButton(key))
return;


searchThread = new Thread(() -> {
searchForSearchButton(key);
});


searchThread.start();


});


// 初始显示百度音乐新歌
// textField.setText("百度音乐新歌榜/月榜");
// searchButton.doClick();
}


private void setMoreSearchAction(JButton moreSearch) {


moreSearch.addActionListener(event -> {


String key = textField.getText();


if (!prejudgeForMoreSearch(key))
return;


if (!key.equals(beforeKey)) {
textField.setText(beforeKey);
key = beforeKey;
}


// 判断歌曲库映射是否包含此关键字,没有则进行searchButton的搜索
if (songLibraryMap.containsKey(key)) {


int searchPage = searchSong.getPage() + 1;


if (searchPage > maxPage) {
JOptionPane.showMessageDialog(null, "已经没有更多数据", "",
JOptionPane.PLAIN_MESSAGE);
return;
}


String searchKey = key;
searchThread = new Thread(() -> {


searchForMoreSearch(searchKey, searchPage);


});
searchThread.start();
}


else {
searchButton.doClick();
}


});
}


// 计算歌曲分页
private int countPage(int songNumber) {
int page = songNumber / 20;
if (songNumber % 20 != 0)
page++;
return page;
}


// 预判搜索关键字
private boolean prejudgeForSearchButton(String key) {
if (searchThread != null && searchThread.isAlive()) {
JOptionPane.showMessageDialog(null, "正在搜索数据中,请耐心等待", "",
JOptionPane.PLAIN_MESSAGE);
return false;
}


if (key == null || key.length() == 0)
return false;


// 搜索关键字没变且不是要进行分页搜索
if (key.equals(beforeKey))
return false;


return true;


}


// 预判搜索关键字
private boolean prejudgeForMoreSearch(String key) {
// 正在搜索中
if (searchThread != null && searchThread.isAlive()) {
JOptionPane.showMessageDialog(null, "正在搜索数据中,请耐心等待", "",
JOptionPane.PLAIN_MESSAGE);
return false;
}


if (beforeKey == null || beforeKey.length() == 0)
return false;


return true;
}


// 搜索过程
private void searchForSearchButton(String key) {
// <-----------搜索数据------------>
if (!songLibraryMap.containsKey(key)) {
// 清除前次解析的信息
searchSong.clear();


// 设置关键字 进行此次解析
if (!searchSong.setKey(key).openConnection()) {
// 连接失败
beforeKey = "";
return;
}


if (songLibraryMap.containsKey(key)) {


// 获取它的上限页数
maxPage = countPage(searchSong.getSongNumber());
}
}


// <-----------数据加入表格------------>
// 表格选中数据和取消选中,是为了去掉一个Bug:在当前页面操作单元格时,当换新页时,那个被操作的单元格数据不更新
libraryPanel.getDataTable().selectAll();


// 表格数据清空
libraryTableModel.deleteTableData();



List<SongInfos> songList = songLibraryMap.get(key);
int addSongNum = songList.size();


// 更新乐库表格数据
songList.subList(0, addSongNum).forEach(
each -> libraryTableModel.updateData(each));


libraryPanel.getDataTable().clearSelection();


// 保留此次搜索的关键字
beforeKey = key;


int page = countPage(addSongNum);
searchSong.setPage(page);


}


// 搜索过程
private void searchForMoreSearch(String key, int page) {


searchSong.setPage(page);


List<SongInfos> songList = songLibraryMap.get(key);


int songNum = songList.size();
// 设置关键字 进行此次解析
if (!searchSong.setKey(key).openConnection()) {
// 连接失败
return;
}


maxPage = countPage(searchSong.getSongNumber());


songList.subList(songNum, songList.size()).forEach(
each -> libraryTableModel.updateData(each));


// libraryPanel.getTableScrollBar()
// .setValue(
// libraryPanel.getTableScrollBar().getMaximum()+1);


}


public void setShowPanel(ShowPanel showPanel) {
this.showPanel = showPanel;
this.cardLayout = (CardLayout) showPanel.getLayout();


// 沟通乐库面板与搜索歌曲信息
libraryPanel = showPanel.getLibraryPanel();
libraryTableModel = libraryPanel.getLibraryTableModel();


setMoreSearchAction(libraryPanel.getMoreSearch());


}


// HubFrame传进来的歌曲库集合
public void setSongLibraryMap(Map<String, List<SongInfos>> songLibraryMap) {


this.songLibraryMap = songLibraryMap;
searchSong.setSongLibraryMap(songLibraryMap);
}


// 折叠面板按钮
public JButton getCollapseButton() {
return toolBarButtons[0];
}


}

/


package com.hubPlayer.ui;


import java.awt.CardLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Insets;


import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTree;


import com.hubPlayer.player.HigherPlayer;
import com.hubPlayer.ui.tool.LibraryPanel;


/**
 * 展示面板
 * 
 * @date 2014-10-26
 *
 */


public class ShowPanel extends JPanel {


private LibraryPanel libraryPanel;


// 歌词面板相关
private JPanel lrcPanel;
private JTextArea textArea;


// 其他面板
private JScrollPane MVPanel;
private JScrollPane radioPanel;
private JScrollPane livePanel;


public ShowPanel() {
setLayout(new CardLayout());


init();
createLayout();
}


private void init() {
libraryPanel = new LibraryPanel();


// 歌词面板处理
lrcPanel = new JPanel(new GridLayout());
textArea = new JTextArea();


// 文本域设置不可编辑、透明、左间距、自动换行
textArea.setEditable(false);
textArea.setLineWrap(true);
textArea.setMargin(new Insets(0, 175, 0, 0));
textArea.setOpaque(false);


textArea.setFont(new Font("PLAN", Font.PLAIN, 14));
lrcPanel.add(textArea);


// 其他面板
MVPanel = new JScrollPane(new JLabel("MV"));
radioPanel = new JScrollPane(new JLabel("电台"));
livePanel = new JScrollPane(new JLabel("直播"));
}


private void createLayout() {
add(libraryPanel, "1");
add(MVPanel, "2");
add(lrcPanel, "3");
add(radioPanel, "4");
add(livePanel, "5");
}


public JTextArea getTextArea() {
return textArea;
}


public LibraryPanel getLibraryPanel() {
return libraryPanel;
}


public void setListTree(JTree[] trees) {
libraryPanel.setListTree(trees);
}


public void setPlayer(HigherPlayer player) {
libraryPanel.setPlayer(player);
}
}

/


package com.hubPlayer.ui.tool;


import java.awt.GridLayout;


import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;


import com.hubPlayer.expansion.game_2048.My2048;
import com.hubPlayer.expansion.game_snake.MySnake;
import view.ChessFrame;


/**
 * 拓展功能面板,为播放器增加一些游戏程序
 * 
 * @date 2014-10-13
 *
 */


public class AppPanel extends JPanel {


private JButton game2048;
private JButton gameSnake;
private JButton gameGobang;


private JFrame parent;


public AppPanel(JFrame parent) {
this.parent = parent;


setLayout(new GridLayout(3, 3));
setSize(300, 500);


addButtons();
setButtonsAction();
}


private void addButtons() {


game2048 = new IconButton("2048", "icon/item.png");
add(game2048);


gameSnake = new IconButton("贪吃蛇", "icon/item.png");
add(gameSnake);


gameGobang = new IconButton("网络五子棋", "icon/item.png");
add(gameGobang);


add(new IconButton(null, "icon/item.png"));


add(new IconButton(null, "icon/item.png"));
add(new IconButton(null, "icon/item.png"));
add(new IconButton(null, "icon/item.png"));


add(new IconButton(null, "icon/item.png"));
add(new IconButton(null, "icon/item.png"));


}


private void setButtonsAction() {


game2048.addActionListener(event -> {
new My2048(parent);
});


gameSnake.addActionListener(event -> {
new MySnake(parent);
});


gameGobang.addActionListener(event -> {
new ChessFrame(parent);
});
}
}

/

package com.hubPlayer.ui.tool;


import java.awt.GridLayout;
import java.awt.Insets;


import javax.swing.JButton;
import javax.swing.JToolBar;


/**
 * The HubToolBar is on the left of the HubFrame. It extends JToolBar. There are
 * four HubButtons (note\download\application\clouds) in the ToolBar
 * 
 * @date 2014-10-1
 */


public class ButtonToolBar extends JToolBar {




private JButton[] buttons;




public ButtonToolBar() {
setBorderPainted(false);


setMargin(new Insets(0, 5, 5, 5));
setFloatable(false);
setOpaque(false);
setBorderPainted(false);


}


public ButtonToolBar(int orentation, int buttonNum) {
this();


setOrientation(orentation);


if (orentation == JToolBar.VERTICAL)
setLayout(new GridLayout(buttonNum, 1));


else
setLayout(new GridLayout(1, buttonNum));


}


public void addButtons(JButton[] buttons) {


this.buttons = buttons;


for (int i = 0; i < buttons.length; i++) {
add(buttons[i]);
}



}




public JButton[] getButtons() {
return buttons;
}
}

/

package com.hubPlayer.ui.tool;


import javax.swing.ImageIcon;
import javax.swing.JButton;


/**
 * 图片按钮
 * 
 * @date 2014-10-7
 */


public class IconButton extends JButton {


public IconButton(String tip) {
setToolTipText(tip);
setBorderPainted(false);
setOpaque(false);
setContentAreaFilled(false);


}


public IconButton(String tip, String imgUrl) {
this(tip);
setIcon(new ImageIcon(imgUrl));
}


public String toString() {
return getToolTipText();
}
}

/

package com.hubPlayer.ui.tool;


import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;


import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;


import com.hubPlayer.player.HigherPlayer;
import com.hubPlayer.song.SongInfos;
import com.hubPlayer.song.SongNode;


/**
 * HubLibraryOperation处理外层交互 乐库数据表格中的操作面板(OperationPanel),主要包含3个按钮:播放,加到列表,下载
 * 
 * @date 2014-11-07
 */


public class LibraryOperation {


private JTree[] trees;


// 播放器
private HigherPlayer player;


// 下载完成提示 只为第一次下载作提示
private static boolean TipFlag = true;


private final static String savePath = "E:/Hub/download";


public void setListTree(JTree[] trees) {
this.trees = trees;
}


public void setPlayer(HigherPlayer player) {
this.player = player;
}


public class OperationPanel extends JPanel {


private JButton play;
private JButton toList;
private JButton download;


private String song;
private String singer;
private String dataURL;


private SongNode songNode;


public OperationPanel() {
initComponent();
setAction();
}


// 接收歌曲信息
public OperationPanel(SongInfos songInfos) {


this();


song = songInfos.getSong();


singer = songInfos.getSinger();


dataURL = songInfos.getSongDataUrl();


songNode = new SongNode(singer + "-" + song,
songInfos.getTotalTime(), songInfos.getDataSize(),
songInfos.getLrcUrl(), dataURL);
}


private void initComponent() {
play = new IconButton("播放", "icon/note2.png");
toList = new IconButton("添加到列表", "icon/add.png");
download = new IconButton("下载", "icon/download2.png");


setLayout(new BoxLayout(this, BoxLayout.X_AXIS));


Box box = Box.createHorizontalBox();
box.add(play);
box.add(toList);
box.add(download);


add(Box.createHorizontalStrut(40));
add(box);
}


private void setAction() {


// 播放
play.addActionListener(event -> {


// 选中默认播放列表
trees[0].setSelectionRow(0);


addTreeList(trees[0], 0);


player.setSelectTreeNodeInCurrentList(songNode, dataURL);


// 播放按钮播放
player.getPlayButton().doClick();
});


// 加到播放列表
toList.addActionListener(event -> {
addTreeList(trees[0], 0);
});


// 下载
download.addActionListener(event -> {


if (dataURL == null || dataURL.length() == 0) {
JOptionPane.showMessageDialog(null, "没有找到资源相应的下载链接", "",
JOptionPane.PLAIN_MESSAGE);
return;
}


new Thread(() -> {
try {


// 打开资源链接
HttpURLConnection httpURLConnection = (HttpURLConnection) new URL(
dataURL).openConnection();


// 开启IO流 读写数据
BufferedInputStream inputStream = new BufferedInputStream(
httpURLConnection.getInputStream());


String songName = savePath + "/" + singer + "-" + song;
if (!songName.endsWith(".mp3"))
songName += ".mp3";


BufferedOutputStream outputStream = new BufferedOutputStream(
new FileOutputStream(new File(songName)));


// 加入到下载面板 的"下载中"节点
addTreeList(trees[2], 0);


byte[] buff = new byte[1024];
int onceRead = 0;
while ((onceRead = inputStream.read(buff, 0,
buff.length)) != -1) {
outputStream.write(buff, 0, onceRead);
}


outputStream.flush();
outputStream.close();
inputStream.close();


// 移除"下载中"的歌曲信息
removeSongNodeInTreeList(trees[2], 0);
// 将歌曲信息移入"已下载"
addTreeList(trees[2], 1);


// 下载完成提示
if (TipFlag) {


JOptionPane.showMessageDialog(null, "下载完成,文件存至  "
+ savePath, "", JOptionPane.PLAIN_MESSAGE);
TipFlag = false;
}


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


}).start();


});


}


public void addTreeList(JTree tree, int index) {


DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree
.getModel().getRoot();
DefaultMutableTreeNode list = (DefaultMutableTreeNode) root
.getChildAt(index);


list.add(songNode);


// 列表名更新
String listName = (String) list.getUserObject();
listName = listName.substring(0, listName.lastIndexOf("[")) + "["
+ list.getChildCount() + "]";
list.setUserObject(listName);


// 如果这里不更新树的话 会不正确显示
tree.updateUI();


}


public void removeSongNodeInTreeList(JTree tree, int index) {
DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree
.getModel().getRoot();
DefaultMutableTreeNode list = (DefaultMutableTreeNode) root
.getChildAt(index);


list.remove(songNode);


// 列表名更新
String listName = (String) list.getUserObject();
listName = listName.substring(0, listName.lastIndexOf("[")) + "["
+ list.getChildCount() + "]";
list.setUserObject(listName);


// 如果这里不更新树的话 会不正确显示
tree.updateUI();


}


public void setSong(String song) {
this.song = song;


}


public void setSinger(String singer) {
this.singer = singer;
}


public void setDataURL(String dataURL) {
this.dataURL = dataURL;
}


}


}

/

package com.hubPlayer.ui.tool;


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;


import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;


import com.hubPlayer.player.HigherPlayer;


/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */


/**
 * netBean 构建 乐库面板
 * 
 * @date 2014-11-8
 */
public class LibraryPanel extends JPanel {


/**
* Creates new form HubLibraryPanel
*/
public LibraryPanel() {
initComponents();
}


/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings({ "unchecked", "serial" })
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {


aScrollPanel = new JScrollPane();
dataTable = new JTable();
libraryTableModel = new LibraryTableModel();
libraryOperation = new LibraryOperation();


aToolBar = new JToolBar();
moreSearch = new JButton();


setLayout(new BorderLayout());


aScrollPanel
.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
aScrollPanel
.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
aScrollPanel.setMaximumSize(new Dimension(615, 481));


// 设置20行空数据
dataTable.setModel(libraryTableModel);
libraryTableModel.setLibraryOperation(libraryOperation);


// 定义"操作栏"的渲染器 显示按钮
dataTable.getColumn("操作").setCellRenderer(
new DefaultTableCellRenderer() {


@Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {


return value instanceof JPanel ? (JPanel) value : super
.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
}
});
// 定义"操作栏"的编辑器 响应按钮事件
dataTable.getColumn("操作").setCellEditor(new CellEditor());


dataTable.setColumnSelectionAllowed(true);
dataTable.setRowHeight(23);
aScrollPanel.setViewportView(dataTable);
dataTable.getColumnModel().getSelectionModel()
.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);


add(aScrollPanel, BorderLayout.CENTER);


aToolBar.setFloatable(false);
aToolBar.setRollover(true);
aToolBar.setOpaque(false);


moreSearch.setText("更多数据");
moreSearch.setFocusable(false);
moreSearch.setHorizontalTextPosition(SwingConstants.CENTER);
moreSearch.setVerticalTextPosition(SwingConstants.BOTTOM);
// moreSearch.setEnabled(false);
aToolBar.add(moreSearch);


Box box = Box.createVerticalBox();
box.setBorder(BorderFactory.createLineBorder(Color.BLACK));
box.setOpaque(true);
box.add(aToolBar);


add(box, BorderLayout.SOUTH);


}// </editor-fold>


// 传给搜索面板,用以展示搜索结果
public LibraryTableModel getLibraryTableModel() {
return libraryTableModel;
}


public JTable getDataTable() {
return dataTable;
}


public JScrollBar getTableScrollBar() {
return aScrollPanel.getVerticalScrollBar();
}


public JButton getMoreSearch() {
return moreSearch;
}


public void setListTree(JTree[] trees) {


libraryOperation.setListTree(trees);
}


public void setPlayer(HigherPlayer player) {
libraryOperation.setPlayer(player);
}


// Variables declaration - do not modify
private JScrollPane aScrollPanel;
private JTable dataTable;
private LibraryTableModel libraryTableModel;
private LibraryOperation libraryOperation;


private JToolBar aToolBar;
private JButton moreSearch;


// End of variables declaration
}


class CellEditor extends AbstractCellEditor implements TableCellEditor {


private JPanel panel;


// 当单元格从编辑状态退出时,调用此方法,将此单元渲染器的value设为这个Object,即显示这个Object
@Override
public Object getCellEditorValue() {
return panel;
}


// 当需要编辑单元格时,调用此方法,编辑的是返回的组件对象
@Override
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
return value instanceof JPanel ? panel = (JPanel) value : null;
}


}

/

package com.hubPlayer.ui.tool;


import javax.swing.JPanel;
import javax.swing.table.DefaultTableModel;


import com.hubPlayer.song.SongInfos;


/**
 * 一个歌曲信息表格模型,继续默认表格模式DefaultTableModel,实现单元格渲染器TableCellRenderer
 * 表格头为歌曲(String),歌手(String),专辑(String),操作(JPanel) 定义渲染器显示操作面板
 * 其中操作面板包含3个按钮:播放,加入列表,下载
 * 
 * @date 2014-11-7
 */


public class LibraryTableModel extends DefaultTableModel {


// 标题栏
private static final String[] title = new String[] { "歌曲", "歌手", "专辑", "操作" };
// 标题栏对应的数据类型
private static final Class[] types = new Class[] { String.class,
String.class, String.class, Object.class };


// 标题栏对应的可编辑状态
private static final boolean[] canEdit = new boolean[] { false, false,
false, true };


// 表格初始数据
private static final Object[][] initData = new Object[][] {
{ null, null, null, null }, { null, null, null, null },
{ null, null, null, null }, { null, null, null, null },
{ null, null, null, null }, { null, null, null, null },
{ null, null, null, null }, { null, null, null, null },
{ null, null, null, null }, { null, null, null, null },
{ null, null, null, null }, { null, null, null, null },
{ null, null, null, null }, { null, null, null, null },
{ null, null, null, null }, { null, null, null, null },
{ null, null, null, null }, { null, null, null, null },
{ null, null, null, null }, { null, null, null, null } };


// 操作面板
private LibraryOperation libraryOperation;


public LibraryTableModel() {


super(initData, title);


}


public void deleteTableData() {


// 通知表格,数据要改变
fireTableDataChanged();
// 清除表格行
for (int i = getRowCount() - 1; i >= 0; i--) {
removeRow(i);
}
// 清除表格数据
getDataVector().clear();
        // 垃圾回收
try {
this.finalize();
} catch (Throwable e) {
e.printStackTrace();
}


}


// 检测每列对应数据类型
public Class getColumnClass(int columnIndex) {
return types[columnIndex];
}


// 检测单元格对应的可编辑状态
public boolean isCellEditable(int rowIndex, int columnIndex) {
return canEdit[columnIndex];
}


public void updateData(SongInfos songInfos) {


String song = songInfos.getSong();
String singer = songInfos.getSinger();
String album = songInfos.getAlbum();


// 操作面板,接受歌曲信息
JPanel panel = libraryOperation.new OperationPanel(songInfos);


Object[] data = new Object[] { song, singer, album, panel };


addRow(data);


}


public void setLibraryOperation(LibraryOperation libraryOperation) {
this.libraryOperation = libraryOperation;
}


}

/


package com.hubPlayer.ui.tool;


import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;


import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;


import com.hubPlayer.player.HigherPlayer;
import com.hubPlayer.song.AFilter;
import com.hubPlayer.song.SongNode;


/**
 * 歌曲列表面板:播放列表,标记列表、下载列表
 * 
 * @date 2014-10-12
 */


public class SongListPanel extends JScrollPane {
// 菜单项相关
private JMenuItem newList;
private JMenuItem removeList;
private JMenuItem cleanList;


private JMenuItem addSongFile;
private JMenuItem addSongFolder;


private JMenuItem removeSong;


private JMenuItem addLrcFile;
private JMenuItem addLrcFloder;


private JPopupMenu popupMenu;


// 歌曲、歌词列表相关
private JTree tree;
private DefaultMutableTreeNode topNode;
private int defaultNodes;
private List<SongNode> songlist;


// 文件对话框相关
private JFileChooser fileChooser;
private FileNameExtensionFilter songFilter;
private FileNameExtensionFilter lrcFilter;


// 播放器
private HigherPlayer higherPlayer;


public SongListPanel(String... defaultNodes) {
this.defaultNodes = defaultNodes.length;


initComponent(defaultNodes);
createPopupmenu();
createAction();


}


private void initComponent(String... defaultNodes) {


// 树组件
topNode = new DefaultMutableTreeNode();


// node表默认的列表 [0]表示列表中的歌曲数
for (int i = 0; i < defaultNodes.length; i++) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(
defaultNodes[i] + "[0]");
topNode.add(node);
}


tree = new JTree(topNode);
// tree.setPreferredSize(new Dimension(290, 400));
tree.startEditingAtPath(new TreePath(new Object[] { topNode,
topNode.getFirstChild() }));


// 隐藏根节点
tree.setRootVisible(false);


getViewport().add(tree);


// 文件选择器处理
fileChooser = new JFileChooser();
songFilter = new FileNameExtensionFilter("音频文件(*.mid;*.mp3;*.wav)",
"mid", "MID", "mp3", "MP3", "wav", "WAV");
lrcFilter = new FileNameExtensionFilter("歌词文件(*.lrc)", "lrc", "LRC");


songlist = new Vector<SongNode>();
}


private void createPopupmenu() {
popupMenu = new JPopupMenu();


newList = new JMenuItem("新建列表");
removeList = new JMenuItem("移除列表");
cleanList = new JMenuItem("清空列表");


addSongFile = new JMenuItem("添加本地歌曲");
addSongFolder = new JMenuItem("添加本地歌曲文件夹");


JMenu addSong = new JMenu("添加歌曲");
addSong.add(addSongFile);
addSong.add(addSongFolder);


addLrcFile = new JMenuItem("添加本地歌词");
addLrcFloder = new JMenuItem("添加本地歌词文件夹");


addLrcFile.setEnabled(false);
addLrcFloder.setEnabled(false);


JMenu addLrc = new JMenu("添加歌词");
addLrc.add(addLrcFile);
addLrc.add(addLrcFloder);


removeSong = new JMenuItem("删除歌曲");


popupMenu.add(newList);
popupMenu.add(removeList);
popupMenu.add(cleanList);


popupMenu.addSeparator();
popupMenu.add(addSong);


popupMenu.addSeparator();
popupMenu.add(addLrc);


popupMenu.addSeparator();
popupMenu.add(removeSong);


}


private void createAction() {


// 新建列表
newList.addActionListener(event -> {
String listName = JOptionPane.showInputDialog(this, "请输入新建列表的名称",
null, JOptionPane.DEFAULT_OPTION);


if (listName == null || listName.length() == 0)
return;


addList(listName);


});


// 移除列表
removeList
.addActionListener(event -> {


TreePath path = tree.getSelectionPath();


if (path == null)
return;


DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
.getLastPathComponent();


// 判断是否在歌曲上触发事件 是返回此歌曲的目录
if (path.getPathCount() == 3) {
node = (DefaultMutableTreeNode) node.getParent();
}


// 该目录是否为默认目录
int nodeIndex = topNode.getIndex(node);
if (nodeIndex < defaultNodes && nodeIndex != -1)
return;


// 该目录是否含歌曲 不含则返回true
if (node.isLeaf())
node.removeFromParent();


// 改模录含有歌曲 询问是否移除
else if (JOptionPane
.showConfirmDialog(this, "列表内包含歌曲,是否删除?", null,
JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) {


// 终止当前歌曲播放
TreeNode playedSong = higherPlayer.getPlayingSong();
if (playedSong != null
&& node.getIndex(playedSong) != -1
&& playedSong != null) {


higherPlayer.end();


// 触发下播放按钮 使它处于待播放状态
higherPlayer.IsPause = false;
higherPlayer.getPlayButton().doClick();
higherPlayer.getSongNameLabel().setText("");
}


// 清空集合中的歌曲
Enumeration<SongNode> e = node.children();
while (e.hasMoreElements()) {
songlist.remove(e.nextElement());
}


if (songlist.isEmpty()) {
addLrcFile.setEnabled(false);
addLrcFloder.setEnabled(false);
}


node.removeFromParent();
}
tree.updateUI();
});


// 清空列表
cleanList.addActionListener(event -> {
// 返回选中节点的路径
TreePath path = tree.getSelectionPath();


if (path == null)
return;


DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
.getLastPathComponent();


if (node.getChildCount() == 0)
node = (DefaultMutableTreeNode) node.getParent();


if (node == topNode)
return;


// 终止当前歌曲播放
TreeNode playedSong = higherPlayer.getPlayingSong();


if (playedSong != null && node.getIndex(playedSong) != -1
&& playedSong != null) {


higherPlayer.end();
higherPlayer.IsPause = false;
higherPlayer.getPlayButton().doClick();
higherPlayer.getSongNameLabel().setText("");
}


Enumeration<SongNode> e = node.children();
while (e.hasMoreElements()) {
songlist.remove(e.nextElement());
}


if (songlist.isEmpty()) {
addLrcFile.setEnabled(false);
addLrcFloder.setEnabled(false);
}


node.removeAllChildren();


// 设歌曲列表的歌曲数为0
updateSongNumInList(node);


tree.updateUI();
});


// 删除歌曲
removeSong.addActionListener(event -> {
TreePath path = tree.getSelectionPath();


if (path == null)
return;


DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
.getLastPathComponent();


// 该选中节点是歌曲目录
if (path.getPathCount() == 2)
return;


// 获取当前歌曲的列表 用于更新列表中歌曲数
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node
.getParent();


// 终止当前歌曲播放
if (node == higherPlayer.getPlayingSong()
&& higherPlayer.playThread != null) {


higherPlayer.end();
higherPlayer.IsPause = false;
higherPlayer.getPlayButton().doClick();
higherPlayer.getSongNameLabel().setText("");
}


songlist.remove(node);
if (songlist.isEmpty()) {
addLrcFile.setEnabled(false);
addLrcFloder.setEnabled(false);
}


node.removeFromParent();
updateSongNumInList(parent);
tree.updateUI();
});


// 增加本地歌曲
addSongFile.addActionListener(event -> {
TreePath path = tree.getSelectionPath();


if (path == null)
return;


DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
.getLastPathComponent();


// 选中节点是第3级
if (path.getPathCount() == 3)
node = (DefaultMutableTreeNode) node.getParent();


// 设置JFileChooser可多选音频文件
fileChooser.setMultiSelectionEnabled(true);
fileChooser.setFileFilter(songFilter);
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);


if (fileChooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION)
return;


File[] files = fileChooser.getSelectedFiles();


addSongs(node, files);


});


// 增加本地歌曲文件夹
addSongFolder.addActionListener(event -> {


// JTree的处理和"增加本地歌曲"一样
TreePath path = tree.getSelectionPath();


if (path == null)
return;


DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
.getLastPathComponent();


// 选中节点是第3级
if (path.getPathCount() == 3)
node = (DefaultMutableTreeNode) node.getParent();


// 设置JFileChooser只选文件夹
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChooser.setMultiSelectionEnabled(false);


if (fileChooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION)
return;


// 获取选中的文件夹
File directory = fileChooser.getSelectedFile();


// 获取文件夹中的文件,以SongFilter过滤
File[] files = directory
.listFiles(new AFilter(".mid;.mp3;.wav"));


if (files.length == 0)
return;


addSongs(node, files);


});


// 歌词文件
addLrcFile
.addActionListener(event -> {
fileChooser.setMultiSelectionEnabled(true);
fileChooser.setFileFilter(lrcFilter);
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);


if (fileChooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION)
return;


File[] files = fileChooser.getSelectedFiles();


addLrcs(files);


});


// 歌词文件夹
addLrcFloder
.addActionListener(event -> {


fileChooser
.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fileChooser.setMultiSelectionEnabled(false);


if (fileChooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION)
return;


// 获取选中的文件夹
File directory = fileChooser.getSelectedFile();


// 获取文件夹中的文件,以SongFilter过滤
File[] files = directory.listFiles(new AFilter(".lrc"));


if (files.length == 0)
return;


addLrcs(files);
});




tree.addMouseListener(new MouseAdapter() {


@Override
public void mousePressed(MouseEvent e) {


// 右击选中歌曲
if (e.getButton() == MouseEvent.BUTTON3) {


// 获得一个最接近点击点的节点路径
TreePath path = tree.getPathForLocation(e.getX(), e.getY());


if (path != null)
tree.setSelectionPath(path);


}
}


@Override
public void mouseClicked(MouseEvent e) {


// 鼠标左击两次 播放歌曲
if (e.getButton() == MouseEvent.BUTTON1
&& e.getClickCount() == 2) {


TreePath path = tree.getSelectionPath();


// 该选中节点是否为歌曲
if (path != null && path.getPathCount() == 3) {


SongNode songNode = (SongNode) path
.getLastPathComponent();


if (!tree.equals(higherPlayer.getJTree())) {
higherPlayer.setJTree(tree);
}


// 如果此歌曲节点是网络资源,则载入其URL
if (songNode.getHTTPFlag())
higherPlayer.load(songNode, songNode.getDataURL());
else
higherPlayer.load(songNode);


higherPlayer.getPlayButton().doClick();


}
}
}
});


tree.addTreeSelectionListener(event -> {


TreePath path = tree.getSelectionPath();


// 选中的节点是列表
if (path.getPathCount() == 2) {
// 从当前选中的列表开始操作


higherPlayer.setCurrentListPath(path);
tree.startEditingAtPath(path);


}
});


tree.addKeyListener(new KeyAdapter() {


@Override
public void keyPressed(KeyEvent e) {


if (KeyEvent.VK_SPACE == e.getKeyCode())
higherPlayer.getPlayButton().doClick();
}
});


}


public DefaultMutableTreeNode addList(String listName) {
listName = listName + "[0]";
DefaultMutableTreeNode songList = new DefaultMutableTreeNode(listName);
topNode.add(songList);


tree.updateUI();


return songList;
}


public void addSongs(DefaultMutableTreeNode parent, File... files) {


for (File f : files) {


if (!f.exists())
continue;


SongNode node = new SongNode(f);


// 如果当前列表存在此首歌,count不等于0,则不加入当前列表
// songList存放了所有歌曲列表中的歌曲
long count = songlist.stream()
.filter(each -> parent.equals(each.getParent()))
.filter(each -> each.equals(node)).count();


// 不用判断 if(parent.isNodeChild(node))
// 因为前面new了一个节点,同时TreeNode没调用它子节点的equals判断
if (count != 0)
continue;


parent.add(node);
// 歌曲集合加入文件
songlist.add(node);
}


if (!songlist.isEmpty()) {
addLrcFile.setEnabled(true);
addLrcFloder.setEnabled(true);
}


updateSongNumInList(parent);


tree.updateUI();
}


public void addLrcs(File... files) {


for (File f : files) {


if (!f.exists())
continue;
String name = f.getName();
String lrcName = name.substring(0, name.lastIndexOf("."));


// 当前歌曲集合中转成流式,选出那些与歌词名相同的歌曲作为一个子集合,设置子集合中歌曲的歌词
songlist.stream()
.filter(each -> each.getSongName().equals(lrcName))
.forEach(each -> {
if (each.getLrc() == null)
each.setLrc(f);


});


}


}


// 设置列表中的歌曲数
private void updateSongNumInList(DefaultMutableTreeNode node) {
String listName = (String) node.getUserObject();
listName = listName.substring(0, listName.lastIndexOf("[")) + "["
+ node.getChildCount() + "]";
node.setUserObject(listName);
}


public void addPopupMenuToTree() {
tree.setComponentPopupMenu(popupMenu);
tree.updateUI();
}


public void setPlayer(HigherPlayer player) {
this.higherPlayer = player;
}


public JTree getTree() {
return tree;
}
}


/

package com.hubPlayer.ui.tool;


import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;


import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
import javax.swing.text.BadLocationException;


import com.hubPlayer.song.LrcInfos;


/**
 * 播放面板的进度条,绑定一个计时器 计时打印歌词
 *
 * @date 2014-10-23
 */


public class TimeProgressBar extends JProgressBar {


private boolean timerPause;
private int audioTotalTime;
private int counter;


private Timer timer;
private Task task;


private JLabel currentTimeCountLabel;


private LrcInfos lrcInfos;
private Map<Integer, String> lrcInfosMap;
private int nextLrcTime;


// 歌词面板
private JTextArea textArea;


public TimeProgressBar() {
counter = 0;
setMinimum(0);
setStringPainted(false);


}


// 启动计时器
public void startTimer() {


// 初始显示23条歌词
if (lrcInfosMap.size() != 0)
printNextLrcInTheTime(0, 23);


timer = new Timer(true);
task = new Task();
timer.schedule(task, 500, 1000);


}


// 重启计时器
public void resumeTimer() {
synchronized (timer) {
timer.notify();
}
}


// 重置计时器
public void cleanTimer() {
counter = 0;


if (timer != null) {
timer.cancel();
timer.purge();
}


currentTimeCountLabel.setText("0:00");
this.setValue(0);

textArea.setText("\n");


}


public void setAudioTotalTime(int n) {
audioTotalTime = n;
super.setMaximum(n);
}


public void setTimerControl(boolean IsPause) {
this.timerPause = IsPause;
}


public void setCurrentTimeCountLabel(JLabel currentTimeCountLabel) {
this.currentTimeCountLabel = currentTimeCountLabel;
}


public void setCurrentPlayedSongLrcInfo(LrcInfos lrcInfos) {
this.lrcInfos = lrcInfos;
lrcInfosMap = lrcInfos.getInfos();
}


public void setLrcPanelTextArea(JTextArea textArea) {
this.textArea = textArea;


}


/**
* 打印time时间后的line条歌词 audioTotalTime当前播放歌曲的时长 nextLrcTime搜寻下一条歌词的开始时间
*/


private void printNextLrcInTheTime(int time, int line) {


for (int i = 0; i < line && time <= audioTotalTime; time++) {
String content = lrcInfos.getInfos().get(time);
if (content != null) {
textArea.append(content + "\n");
nextLrcTime = time + 1;
i++;
}
}
}


class Task extends TimerTask {


@Override
public void run() {
synchronized (timer) {
if (counter == audioTotalTime) {
// 终止计数器 但当前任务还会被执行
cleanTimer();
return;
}


if (timerPause) {
try {
timer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}


try {


if (lrcInfosMap.size() != 0) {
String content = lrcInfosMap.get(counter);
if (content != null) {


// 剪去第一行歌词,同时整体上移
textArea.select(textArea.getLineStartOffset(1),
textArea.getLineEndOffset(textArea
.getLineCount() - 1));
textArea.setText(textArea.getSelectedText());


// 显示最后一条歌词的下一条
printNextLrcInTheTime(nextLrcTime, 1);
}
}
} catch (BadLocationException e) {
e.printStackTrace();
}


counter += 1;
TimeProgressBar.this.setValue(counter);
TimeProgressBar.this.currentTimeCountLabel
.setText(getCurrentTime(counter));
}
}


// 转换时间
public String getCurrentTime(int sec) {
String time = "0:00";


if (sec <= 0)
return time;


int minute = sec / 60;
int second = sec % 60;
int hour = minute / 60;


if (second < 10)
time = minute + ":0" + second;
else
time = minute + ":" + second;


if (hour != 0)
time = hour + ":" + time;


return time;
}


}


}

/




























  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值