Java模仿Kugou,实现歌词秀 _JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > Java模仿Kugou,实现歌词秀

Java模仿Kugou,实现歌词秀

 2011/9/14 8:38:36  NeighborWolf  http://neighborwolf.iteye.com  我要评论(0)
  • 摘要:Kugou的歌词秀如图:我模拟的效果如图所示.鼠标选中后如图:歌词秀有以下细节注意点:1、没有“窗口”,直接在桌面上绘制歌词2、歌词文字是彩色的,且颜色渐变。已唱歌词与未唱歌词的渐变色不同。歌词、、文字有黑色边框,以便于周围背景清晰区分3、歌词可拖动,当鼠标移上去时会变成可拖动的形状用Java实现,有以下技术点:1、透明窗口这个需要借助JNA来实现,通过System.setProperty("sun.java2d.noddraw","true");WindowUtils
  • 标签:实现 Java
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
发表评论
用户名: 匿名