Kugou的歌词秀如图:
我模拟的效果如图所示.
鼠标选中后如图:
歌词秀有以下细节注意点:
1、没有“窗口”,直接在桌面上绘制歌词
2、歌词文字是彩色的,且颜色渐变。已唱歌词与未唱歌词的渐变色不同。歌词、、文字有黑色边框,以便于周围背景清晰区分
3、歌词
可拖动,当鼠标移上去时会变成可拖动的形状
用Java实现,有以下技术点:
1、透明窗口
这个需要借助JNA来实现,通过
System.setProperty("sun.java2d.noddraw", "true");
WindowUtils.setWindowTransparent(this,true);
使得窗口透明
2、渐变的彩色文字,使用GradientPaint填充一个BufferedImage,BufferedImage的渐变色即为歌词的渐变色。然后取得歌词的形状,
用此BufferedImage填充即可。比较麻烦的是文字的黑色边框,这个最后想了一个办法就是分别向上下左右偏移一个像素绘制
黑色的歌词,然后在其上绘制正常的彩色渐变歌词,这样最终的叠加相关就正好是我们需要的效果。
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.TexturePaint;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.LinkedList;
import java.util.List;
import javax.swing.*;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.examples.WindowUtils;
import com.sun.jna.examples.win32.User32;
import com.sun.jna.examples.win32.W32API;
import com.sun.jna.ptr.IntByReference;
/**
* 卡拉OK歌词效果,模仿Kugou桌面上的歌词秀
* @author 李涛
* @version v1.0 2011-09-11
*
* 歌词秀有以下细节注意点:
* 1、没有“窗口”,直接在桌面上绘制歌词
* 2、歌词文字是彩色的,且颜色渐变。已唱歌词与未唱歌词的渐变色不同。歌词、、文字有黑色边框,以便于周围背景清晰区分
* 3、歌词可拖动,当鼠标移上去时会变成可拖动的形状
*
* 用Java实现,有以下技术点:
* 1、透明窗口
* 这个需要借助JNA来实现,通过
* System.setProperty("sun.java2d.noddraw", "true");
* WindowUtils.setWindowTransparent(this,true);
* 使得窗口透明
*
* 2、渐变的彩色文字,使用GradientPaint填充一个BufferedImage,BufferedImage的渐变色即为歌词的渐变色。然后取得歌词的形状,
* 用此BufferedImage填充即可。比较麻烦的是文字的黑色边框,这个最后想了一个办法就是分别向上下左右偏移一个像素绘制
* 黑色的歌词,然后在其上绘制正常的彩色渐变歌词,这样最终的叠加相关就正好是我们需要的效果。
*/
public class LyncWin extends JDialog {
/**
*
*/
private static final long serialVersionUID = 1L;
private JLabel infoLabel;
private MyCloseButton closeButton;
static List<String> msgList = new LinkedList<String>();
public LyncWin()
{
setTitle("卡拉OK歌词Demo");
setBounds(300, 200, 800, 110);
final ContentPane panel = new ContentPane();
this.setContentPane(panel);
MyMouseListener m = new MyMouseListener(this, panel);
panel.addMouseListener(m);
panel.addMouseMotionListener(m);
getContentPane().setLayout(null);
this.getRootPane().setOpaque(false);
closeButton = new MyCloseButton(this);
closeButton.setOpaque(false);
closeButton.setBounds(0, 0, 18, 18);
closeButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
System.exit(0);
}});
closeButton.setVisible(false);
add(closeButton);
setResizable(false);
this.setUndecorated(true);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
//com.sun.awt.AWTUtilities.setWindowOpacity(this, 0.93f);
//com.sun.awt.AWTUtilities.setWindowShape(this, new Ellipse2D.Double(0, 0, getWidth(),getHeight()));
this.setAlwaysOnTop(true);
initMsg();
new Timer(30, new ActionListener() {
public void actionPerformed(ActionEvent e) {
LyncWin.this.repaint();//infoLabel.setText(msgList.get(i++));
}
}).start();
panel.addMouseListener(new MouseAdapter(){
@Override
public void mouseEntered(MouseEvent e) {
closeButton.animateShow();
panel.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
}
@Override
public void mouseExited(MouseEvent e) {
closeButton.animateHide();
}});
System.setProperty("sun.java2d.noddraw", "true");
WindowUtils.setWindowTransparent(this,true);
this.setVisible(true);
}
// private void makeWinTransparent() {
// Pointer winPointer = Native.getComponentPointer(this);
// W32API.HWND hwnd = new W32API.HWND();
// hwnd.setPointer(winPointer);
// IntByReference color = new IntByReference(this.getBackground().getRGB());
//
// User32.INSTANCE.SetWindowLong(hwnd, User32.GWL_EXSTYLE, User32.INSTANCE.GetWindowLong(hwnd, User32.GWL_EXSTYLE)|User32.WS_EX_LAYERED);
// User32.INSTANCE.SetLayeredWindowAttributes(hwnd, this.getBackground().getRGB(), (byte)220, User32.LWA_COLORKEY);
// this.setVisible(false);
// this.setVisible(true);
// }
void initMsg(){
try {
LineNumberReader lnr = new LineNumberReader(new InputStreamReader(Class.class.getResourceAsStream("/Msg.ini")));
while(lnr.ready()){
msgList.add(lnr.readLine());
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
class ContentPane extends JPanel{
private static final long serialVersionUID = 1L;
int i = 0; //哪一行歌曲文字
int length = 0; //本行文字的宽度
String msg = null;
BufferedImage bufferedImage;
//已唱文字的渐变色彩
private Color gradientStart = new Color(238,254,218);
private Color gradientCenter = new Color(153,254,17);
private Color gradientEnd = new Color(232,254,3);
//未唱文字的渐变色彩
private Color gradientEndU = new Color(14,104,0);
private Color gradientStartU = new Color(134,242,32);
public ContentPane(){
setFont(new Font("黑体",Font.BOLD,40));
this.setOpaque(false);
this.setForeground(this.getBackground());
}
@Override
protected void paintComponent(Graphics g){
if(msg ==null){
msg = msgList.get(0);
}
Graphics2D g2 = (Graphics2D)g.create();
RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
hints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHints(hints);
g2.setColor(new Color(0,0,0));
g2.setFont(getFont());
FontMetrics fm = getFontMetrics(getFont());
Rectangle2D rect = fm.getStringBounds(msg, g2);
if(length>rect.getWidth()){
length = 0;
if(i>=msgList.size()){
i=0;
msg = msgList.get(i++);
fm = getFontMetrics(getFont());
rect = fm.getStringBounds(msg, g2);
}else{
msg = msgList.get(++i);
fm = getFontMetrics(getFont());
rect = fm.getStringBounds(msg, g2);
}
}
int x = 0;
int y = 48;
//当关闭按钮可见时,说明鼠标移上来了,此时绘制一个半透明的底纹,以便于用户操作(否则只有当鼠标在文字轮廓
//上时才能收到鼠标事件
if(closeButton.isVisible()){
Graphics2D newg = (Graphics2D)g.create();
newg.setColor(Color.gray);
newg.setComposite(AlphaComposite.SrcOver.derive((float)(closeButton.alpha*0.5)));
newg.fillRoundRect(x,y-((int)rect.getHeight()-fm.getDescent()*3),(int)rect.getWidth(),(int)rect.getHeight(),10,10);
newg.dispose();
}
//上下左右各偏离1个像素绘制黑色歌词,经过后面的彩色歌词覆盖后,即变成文字的黑色轮廓
g2.drawString(msg, x-1, y);
g2.drawString(msg, x+1, y);
g2.drawString(msg, x, y+1);
g2.drawString(msg, x, y-1);
//绘制渐变彩色歌词
createBufferedImage(fm, rect,length++);
TexturePaint tp = new TexturePaint(bufferedImage, rect);
FontRenderContext frc = g2.getFontRenderContext();
TextLayout tl = new TextLayout(msg, getFont(), frc);
g2.setPaint(tp);
g2.translate(0, y);
g2.fill(tl.getOutline(null));
g2.translate(0, -y);
//g2.drawImage(bufferedImage, null, x, y+14);
g2.dispose();
}
//采用渐变色绘制文字
protected void createBufferedImage(FontMetrics fm, Rectangle2D rectStr,int length) {
int width = (int)rectStr.getWidth();
int height = (int)rectStr.getHeight()-fm.getDescent()*2;
bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//绘制已唱的文字
Graphics2D g2 = bufferedImage.createGraphics();
GradientPaint painter = new GradientPaint(0, 0, gradientStart, 0, height / 2, gradientCenter);
g2.setPaint(painter);
Rectangle2D rect = new Rectangle2D.Double(0, 0, length, height / 2.0);
g2.fill(rect);
painter = new GradientPaint(0, height / 2, gradientCenter, 0,height, gradientEnd);
g2.setPaint(painter);
rect = new Rectangle2D.Double(0, height / 2.0 , length,height);
g2.fill(rect);
painter = new GradientPaint(0, height / 2-2, gradientCenter, 0,height, gradientCenter);
g2.setPaint(painter);
rect = new Rectangle2D.Double(0, (height / 2.0)-2 , length, 4);
g2.fill(rect);
//绘制未唱的文字
painter = new GradientPaint(0, 0, gradientStartU, 0, height, gradientEndU);
g2.setPaint(painter);
rect = new Rectangle2D.Double(length, 0, width-length, height);
g2.fill(rect);
g2.dispose();
}
}
public static void main(String[] args){
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
new LyncWin();
}
}
- 卡拉OK歌词秀.jar (1.1 MB)
- 描述: 直接运行即可看到效果,在Windows和Mac上都已测试,可正常运行
- 下载次数: 19
- 10_卡拉OK歌词秀.zip (1.1 MB)
- 描述: 源文件
- 下载次数: 12