Hub Player播放器 Java mp3播放器
2015-10-11 14:17
429 查看
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
相关文章推荐
- Eclipse 中常用的快捷键
- Windows 7下JDK与Ant的安装和环境变量配置
- NumberFormat类与DecimalFormat类
- 利用数组实现大数的阶乘
- java 集成开发环境选择对比
- Java开发环境的配置(Windows)
- Java形参个数可变的方法
- Spring Framework 4.2 中的新功能和增强功能
- Spring-----<context:annotation-config/>
- Java加密解密技术系列之RSA
- NetBeans项目中数据库驱动的设置
- Java基础知识强化之IO流笔记45:IO流练习之 把集合中的数据存储到文本文件案例
- You must restart adb and Eclipse的问题解决
- Java加密解密技术系列之DES
- Java加密解密技术系列之HMAC
- Myeclipse学习总结(1)——Myeclipse优化配置
- Myeclipse学习总结(1)——Myeclipse优化配置
- Myeclipse学习总结(1)——Myeclipse优化配置
- Myeclipse优化配置
- Spring mvc 中使用ftl引用共通文件出错 FreeMarker template error: Error reading included file "/WEB-INF/ftl/common/errormessage.ftl"