java 实现一个桌面弹幕
程序员文章站
2022-06-28 11:11:28
...
本人菜鸟一枚,最近毕设要做一个桌面弹幕的功能,于是想着能不能用swing做一个。
首先,要实现弹幕,需要一块画布,我们写一个类继承自Jframe 。而且为了不遮挡桌面的其他部分,画布的背景应该是透明。这时我们需要用到
AWTUtilities.setWindowOpaque(this, false);//this 是你的Jframe窗口 把窗口设为透明
这个函数,首先要知道这个函数java默认工程的lib里面是没有加入的,需要额外导入rt.jar包,这个包不需要下载,在你的Jdk安装目录下lib/文件夹就可以找到,找到后直接导入你的工程就行了。然后就是要把窗口最大化
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();//获取桌面的大小
Rectangle bounds = new Rectangle(screenSize);
this.setBounds(bounds); //把窗口设为桌面大小
this.setExtendedState(Frame.MAXIMIZED_BOTH);
弹幕的窗口应该是在桌面的最上面的,所以你还需要设置
this.setAlwaysOnTop(true);
java默认的jframe 是带有边框的,所以要把边框禁用
this.setUndecorated(true);
这样弹幕的背景就算是完成了,接下了就是在背景上吧弹幕画出来。画弹幕就要重写jframe的paint方法。
@Override
public void paint(Graphics gg) {
super.paint(gg);}
这个方法有一个参数Graphics。Jframe每次加载一个控件是都会调用paint方法去绘制。当需要重新绘制时,调用repaint()方法。
后面我们就是通过不断调用repaint方法来让弹幕动起来的。参数Graphics可以理解为画笔,画笔可以设置相关的属性
。比如setColor(Color.RED) 方法把画笔设为红色,setFont()方法设置画笔的字体。先在桌面上画上你个你想要的字符串把。
String hello="Hello" ;
int x=300,y=300;//弹幕的坐标
@Override
public void paint(Graphics gg) {
super.paint(gg);
gg.setColor(Color.BLUE); //把画笔设为蓝色
gg.setFront(new
Front("Dialog",Font.BOLD,40); //设置字体和样式,大小40
gg.drawString(hello,x,y); //在桌面坐标300,300的地方画出 Hello字符串
}
结果就是这样了怎么让弹幕动起来呢,前面讲到,我们需要不断调用repaint方法来进行绘制,于是我们只要不断改变弹幕的坐标
然后repaint,弹幕就动起来了。当然我们要在其他线程里面重新绘制。
while(true){
x--; //改变x的坐标
try {
Thread.sleep(10);// 每10ms执行一次
} catch (InterruptedException e) {
e.printStackTrace();
}
repaint();//重新绘制
}
这样弹幕就动起来啦。
当然弹幕可不只有一条,我们可以用一个list来管理所有的弹幕,每次往list中添加弹幕时就可以实现往屏幕添加弹幕的效果。我们还需要定义一个类DanContent来管理我们的弹幕的各种属性,颜色,大小,字体,坐标等。弹幕的高度可以用随机数控制,只要不超出屏幕就行。
这样pain函数就变为
public void paint(Graphics gg) {
super.paint(gg);
int st=0;
if(list.size()>maxScreenDanmuCount) st=list.size()-maxScreenDanmuCount;
for(int i=st;i<list.size();i++) { //绘制list里面的从st开始有字符串
DanContent text=list.get(i);
if(text.x<-windowwidth)continue; //如果弹幕坐标不在屏幕,就不画了。
gg.setColor(getColor(text.getColor()));
gg.setFont(new Font("楷体",text.getStyle(), text.getSize()));
gg.drawString(text.getText(), text.x, text.y);
}
}
单list里面的弹幕不断变多时,有些弹幕不在屏幕中我们就不需要去绘制了。为了减小list中的遍历次数,我们可以设置一个最大值
maxScreenDanmuCount
来限制每次重新绘制的弹幕数量。
然后绘制线程可以用Timer代替Thread,
private void paintDanmu() {
Timer timer=new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
int st=0; //list的起始遍历位置
if(list.size()>maxScreenDanmuCount) st=list.size()-maxScreenDanmuCount;
for(int i=st;i<list.size();i++) {
DanContent c=list.get(i);
c.x-=spzce; // 改变弹幕的x坐标
}
repaint();
}
}, 100,1000/frame);//定时执行刷新
}
这样弹幕就完成了,
献上效果图和源代码:(代码比较乱,大佬看见了不要喷啊!记得导入rt.jar!)
弹幕类
import java.awt.Font;
import java.io.Serializable;
public class DanContent implements Serializable { //弹幕管理类
public static final int TextLarge=70;
public static final int TextMedium=45;
public static final int TextSmart=30;
public static final int RED=0;
public static final int BLUE=1;
public static final int BLACK=2;
public static final int CYAN=3;
public static final int REDARK_GRAY=4;
public static final int GREEN=5;
public static final int ORANGE=6;
public static final int WHITE=7;
public static final int PINK=8;
public static final int BOLD=1;
public static final int ITALLIC=2;
public int x=0;
public int y=0;
/**
*
*/
private static final long serialVersionUID = 1L;
private int color;
private int size;
private int style;
private String text;
private String senderId;
private String receiverId;
public String getReceiverId() {
return receiverId;
}
public void setReceiverId(String receiverId) {
this.receiverId = receiverId;
}
public String getSenderId() {
return senderId;
}
public void setSenderId(String senderId) {
this.senderId = senderId;
}
public DanContent(int color, int size, int style, String text) {
this.color = color;
this.size = size;
this.style = style;
this.text = text;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public int getStyle() {
return style;
}
public void setStyle(int style) {
this.style = style;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
package paint;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Semaphore;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import com.example.lsj.teachapp.POJO.DanContent;
import com.sun.awt.AWTUtilities;
/**
* java Swing界面编程
* 实现简单的弹幕
*
* @author lsg
*
*/
public class DanmuNew extends JFrame implements Runnable{
/**
*
*/
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
DanmuNew danmuNew=DanmuNew.getInstance();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Scanner in=new Scanner(System.in);
while(true) {
String string=in.nextLine();
DanContent content=new DanContent(DanContent.ORANGE, 70, DanContent.BOLD, string);
danmuNew.add(content);
}
}
}).start();
}
private int windowwidth;
private int windwoheight;
private int spzce;
private int speed=1;
private int frame=200;
private int maxScreenDanmuCount=50;
private void ShowDanmu(){
// Thread t = new Thread(this);//启动面板的动画线程
// t.start();
paintDanmu();
}
public DanmuNew(){
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
windwoheight=(int) screenSize.getHeight();
windowwidth=(int) screenSize.getWidth();
spzce=speed;
System.out.println(windwoheight+" "+windowwidth);
Rectangle bounds = new Rectangle(screenSize);
this.setTitle("发送弹幕");
this.setLayout(null);
this.setBounds(bounds);
this.setExtendedState(Frame.MAXIMIZED_BOTH);
this.setAlwaysOnTop(true);
// 判断是否支持透明度
this.setUndecorated(true); // 禁用或启用此窗体的修饰。只有在窗体不可显示时
//this.setDefaultLookAndFeelDecorated(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
Image image=Toolkit.getDefaultToolkit().getImage(getClass().getClassLoader().getResource("llll.png"));
setIconImage(image);
AWTUtilities.setWindowOpaque(this, false);
this.setVisible(true);
}
// TODO Auto-generated constructor stub
public void add(DanContent c) {
Random random=new Random();
c.y=random.nextInt(windwoheight-150)+50;
c.x=windowwidth;
list.add(c);
}
// Queue<DanContent> list=new LinkedList<>();
private List<DanContent> list=new ArrayList<>();
@Override
public void paint(Graphics gg) {
super.paint(gg);
// gg.setColor(Color.red);
// gg.setFont(new Font("Dialog", Font.BOLD,50 ));
int st=0;
if(list.size()>maxScreenDanmuCount) st=list.size()-maxScreenDanmuCount;
for(int i=st;i<list.size();i++) {
DanContent text=list.get(i);
if(text.x<-windowwidth)continue;
gg.setColor(getColor(text.getColor()));
gg.setFont(new Font("楷体",text.getStyle(), text.getSize()));
gg.drawString(text.getText(), text.x, text.y);
}
}
//使用线程刷新弹幕
@Override
public void run() {
paint2();
}
private void paint2() {
while(true){
int st=0;
if(list.size()>maxScreenDanmuCount) st=list.size()-maxScreenDanmuCount;
for(int i=st;i<list.size();i++) {
DanContent c=list.get(i);
c.x-=spzce;
}
try {
Thread.sleep(1000/frame);//
} catch (InterruptedException e) {
e.printStackTrace();
}
repaint();//
}
}
//使用Timer刷新弹幕
private void paintDanmu() {
Timer timer=new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
int st=0;
if(list.size()>maxScreenDanmuCount) st=list.size()-maxScreenDanmuCount;
for(int i=st;i<list.size();i++) {
DanContent c=list.get(i);
c.x-=spzce;
}
repaint();//
}
}, 100,1000/frame);
}
private Color getColor(int idx) {
switch(idx) {
case 0:return Color.RED;
case 1:return Color.BLUE;
case 2:return Color.BLACK;
case 3:return Color.CYAN;
case 4:return Color.DARK_GRAY;
case 5:return Color.GREEN;
case 6:return Color.ORANGE;
case 7:return Color.WHITE;
case 8:return Color.PINK;
default :return Color.WHITE;
}
}
public void setMaxDanmuNum(int num) {
this.maxScreenDanmuCount=num;
}
private static DanmuNew instacne=null;
private static Semaphore semaphore=new Semaphore(0);
public static synchronized DanmuNew getInstance() {
if(instacne==null) {
try {
show2();
semaphore.acquire();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return instacne;
}
return instacne;
}
private static void show2() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
instacne = new DanmuNew();
instacne.ShowDanmu();
semaphore.release();
}
});
}
}