这次我们准备做一个仿QQ的通信项目,首先要做的是把一些图形界面先准备好,那么我们开始第一步吧
绘制登录界面
我们不妨先看一眼QQ真实的登录界面长啥样
当然由于能力问题,而且Java的GUI确实很一般,所以我们做个简易版本的就好。把功能大概地抽象一下
-
首先画一个窗口,分两个子窗口,上面画成蓝色背景,下面的用于绘制组件
-
下面的窗口要有提示文本提醒你输入账户和密码,且设置一个文本框一个密码框用于获取账号和密码
-
设置两个按钮,注册和登录,并对它们添加监听器
-
如果登录成功,打印登录信息,如果登录失败,在原界面显示失败信息,这里我们设置随机数让登录成功的可能性为50%
代码如下
import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class LoginUI extends JFrame { private JTextField jtf1; private JPasswordField jtf2; private JPanel north; public static void main(String[] args) { LoginUI ui = new LoginUI("登录界面"); ui.draw(); } public LoginUI(String title) { super(title); } public void draw() { //窗体设置 setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(400, 400); setLocation(800, 400); //Panel设置 north = new JPanel(); north.setBackground(new Color(59, 122, 199));//north设置颜色 north.setPreferredSize(new Dimension(0, 200)); JPanel south = new JPanel(); south.setPreferredSize(new Dimension(0, 200)); add(north, BorderLayout.NORTH); add(south, BorderLayout.SOUTH); //south处理 JLabel user = new JLabel("用户名"); JLabel password = new JLabel("密码"); jtf1 = new JTextField(20); jtf2 = new JPasswordField(20); JButton jb1 = new JButton("登录"); JButton jb2 = new JButton("注册"); jtf1.setEditable(true); jtf2.setEditable(true); //设置绝对位置 user.setBounds(70, 60, 40, 40); password.setBounds(70, 90, 40, 40); jtf1.setBounds(150, 70, 150, 20); jtf2.setBounds(150, 100, 150, 20); jb1.setBounds(240, 130, 60, 20); jb2.setBounds(150, 130, 60, 20); //将组件添加进south south.setLayout(null); south.add(user); south.add(jtf1); south.add(password); south.add(jtf2); south.add(jb1); south.add(jb2); //可视化 setVisible(true); //添加监听器 LoginUIListener ll = new LoginUIListener(this); jb1.addActionListener(ll); jb2.addActionListener(ll); } public void drawfault() { JLabel label = new JLabel("账号或密码错误,请重新输入!"); label.setBounds(40, 140, 40, 40); north.add(label); setVisible(true); } private class LoginUIListener implements ActionListener { LoginUI ui; public LoginUIListener(LoginUI ui) { this.ui = ui; } @Override public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("注册")) { String name = jtf1.getText(); String password = String.valueOf(jtf2.getPassword()); System.out.println("新用户注册成功!\n用户名:\t" + name + "\n密码:\t" + password); //todo:UserData.allusers.put(name, password); } if (e.getActionCommand().equals("登录")) { //todo:检查是否能够登录??在UserData里面查询该用户 if (Math.random() > 0.5) { String name = jtf1.getText(); String password = String.valueOf(jtf2.getPassword()); System.out.println("用户登录成功!\n用户名:\t" + name + "\n密码:\t" + password); dispose(); Client client = new Client(name); client.start(); } else { ui.drawfault(); } } } } }
来看看我们模拟的效果
上面输入123,下面输入456
或者
那么我们第一部分完工了,下面第二部分,绘制QQ好友列表界面
绘制好友列表界面
同样的,我们先看看QQ是咋做的
- 一个高瘦的界面,分两个部分上面是个人信息,下面是好友列表
- 个人信息有背景,有头像、用户名和个性签名
- 每一个好友的信息,包括头像、用户名、个性签名
- 好友列表中有若干个好友,由于好友数量较多时屏幕画不下,因此采用要加滚动条
- 双击好友可以开启与他的聊天界面
这里我们仍然分两个部分,上面个人信息下面的好友列表各一个panel,下面由于要装滑动条,采用JScrollPane,好友列表中采用JList实现,而由于每一个好友在屏幕中的显示有图片和文字,因此我们重写Jlist的单元渲染器(ListCellRenderer)。
关于检测双击,一般我们用e.getClickCount检测,我们可以在MouseAdapter中的mouseClicked检测这一行为。
关于如何重写单元渲染器,javax.swing.ListCellRenderer中有一个官方示范,截取部分如下
照着这个抄就行,至于单元渲染器的介绍,这里三篇博客讲的挺详细的,贴在这里自取
https://blog.csdn.net/yanhanhui1/article/details/104983677
https://blog.csdn.net/weixin_36571185/article/details/71616952
https://www.cnblogs.com/hesi/p/6242699.html
该说的说完了直接上代码吧,这次我们先写一个Client类,用户列表界面作为其子类,并且我们修改在上一部分代码里面增加一点内容,使得登录成功后登录页面关闭,并且启动一个客户端,关于Clinet类的通信有关内容我们下期再讲,本期主要解决GUI相关问题。
接下来开始写登录界面的主要代码
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class Client {
private final String userID;
public Client(String userID){
this.userID=userID;
}
public void start(){
ListUI lui = new ListUI("好友列表",this.userID);
lui.draw();
}
private class ListUI extends JFrame {
private JPanel lnorth;
private JScrollPane lsouth;
private String userID;
public ListUI(String title,String userID) {
super(title);
this.userID=userID;
}
public void draw() {
//窗体设置
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400, 700);
setLocation(200, 100);
//Panel设置
lnorth = new JPanel();
lnorth.setBackground(new Color(59, 122, 199));//north设置颜色
lnorth.setPreferredSize(new Dimension(0, 150));
lsouth = new JScrollPane();
lsouth.setBackground(new Color(7, 11, 14));//north设置颜色
lsouth.setPreferredSize(new Dimension(0, 515));
add(lnorth, BorderLayout.NORTH);
add(lsouth, BorderLayout.SOUTH);
//lnorth的处理:加上个人信息
JLabel userName = new JLabel(this.userID);
//todo:从UserData数据库里面取头像
ImageIcon headimage = (new ImageIcon("img/QQicon.png"));
headimage.setImage(headimage.getImage().getScaledInstance(100,100,Image.SCALE_DEFAULT));
JLabel head = new JLabel(headimage);
//todo:从UserData数据库里面取个人签名
JLabel signature = new JLabel("I am so vegetable qwq");
//将组件设置位置加入lnorth
userName.setBounds(150,20,200,20);
head.setBounds(20,20,100,100);
signature.setBounds(150,40,200,100);
lnorth.setLayout(null);
lnorth.add(userName);
lnorth.add(head);
lnorth.add(signature);
//lsouth处理
//首先我们重写JList的单元渲染器类
@SuppressWarnings({ "rawtypes", "serial" })
class MyCellRenderer extends JLabel implements ListCellRenderer {
public MyCellRenderer() {
setOpaque(true);
}
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
boolean cellHasFocus) {
Color background;
Color foreground;
// check if this cell represents the current DnD drop location
JList.DropLocation dropLocation = list.getDropLocation();
if (dropLocation != null&& !dropLocation.isInsert()&& dropLocation.getIndex() == index) {
background = Color.RED;
foreground = Color.WHITE;
// check if this cell is selected
}else if (isSelected) {
background = Color.BLUE;
foreground = Color.WHITE;
// unselected, and not the DnD drop location
} else {
background = Color.WHITE;
foreground = Color.BLACK;
};
/*******设置JLable的文字******/
String text="<html>姓名<br/>个性签名 <html/>";
setText(text);//设置JLable的文字
/*******设置JLable的图片*****/
// 得到此图标的 Image,然后创建此图像的缩放版本。
ImageIcon headimage = (new ImageIcon("img/QQicon.png"));
headimage.setImage(headimage.getImage().getScaledInstance(50,50,Image.SCALE_DEFAULT));
setIcon(headimage);
setIconTextGap(30);//设置JLable的图片与文字之间的距离
setBackground(background);
setForeground(foreground);
return this;
}
}
//todo:从Userdata中找出用户的每一个好友,并取他们的头像和用户名和个人标签
JPanel[] jl = new JPanel[50];
for(int i=0;i<50;i++){
JPanel jf = new JPanel();
jf.setLayout(null);
jf.setPreferredSize(new Dimension(200,200));
jl[i]=jf;
}
ListModel<JPanel> jListModel = new DefaultComboBoxModel<JPanel>(jl); //数据模型
JList<JPanel> myJlist = new JList<JPanel>();
myJlist.setModel(jListModel);
myJlist.setCellRenderer(new MyCellRenderer());
myJlist.setFont(new Font(Font.SERIF, Font.PLAIN, 18));
//加滚动条
lsouth.setViewportView(myJlist);
//加监听器
myJlist.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if(e.getClickCount() == 2){
System.out.println("双击");
JList myList = (JList) e.getSource();
int index = myList.getSelectedIndex(); //已选项的下标
Object obj = myList.getModel().getElementAt(index); //取出数据
System.out.println(obj.toString());
DialogUI du = new DialogUI("111");
du.draw();
}
}
});
setVisible(true);
}
}
}
我们来看看效果咋样
效果还是可以的,图中的蓝色是因为该元素被选中了,这个背景色你可以自己调,在JList的单元渲染器里面可以设置,beselected为true时的颜色。我们再看看双击后java控制台的输出
也是很符合预期的,那么这部分基本上就完成了。
绘制对话界面
接下来我们完成第三部分,对话界面。
同样的,首先我们来看看QQ正版的对话界面长啥样吧,这部分应该是最简单的
观察一下我们发现屏幕大致分3块,右边是图像,这里我们给个背景色好了,左边分三块,上面是对话界面,下面的发送界面是一个文本框加一个发送按钮,中间界面里面有截屏、表情、文件、发送等按钮。
基本没啥难度,直接放代码吧
package Clinet;
import javax.swing.*;
import java.awt.*;
public class DialogUI extends JFrame {
private String friendName;
public DialogUI(String friendName){
super("与"+friendName+"的对话");
this.friendName=friendName;
}
public void draw(){
//窗体设置
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(800, 800);
setLocation(100, 100);
//Panel设置
JPanel east = new JPanel();
JPanel west = new JPanel();
JPanel north = new JPanel();
JPanel south = new JPanel();
JPanel center = new JPanel();
add(east,BorderLayout.EAST);
add(west,BorderLayout.WEST);
west.setLayout(new BorderLayout());
west.add(north, BorderLayout.NORTH);
west.add(south, BorderLayout.SOUTH);
west.add(center,BorderLayout.CENTER);
east.setBackground(new Color(0x4D62C1));//east设置颜色
north.setBackground(new Color(0xE07456));//north设置颜色
north.setVisible(true);
east.setPreferredSize(new Dimension(200, 0));
west.setPreferredSize(new Dimension(600, 0));
north.setPreferredSize(new Dimension(0, 500));
south.setPreferredSize(new Dimension(0, 200));
//south处理
JButton send = new JButton("发送");
JButton file = new JButton("文件");
JButton emoji = new JButton("表情");
JButton catchScreen = new JButton("截屏");
JTextArea sender = new JTextArea();
sender.setText("在这里输入文字");
//设置绝对位置
send.setBounds(0,0,100,100);
file.setBounds(150,0,100,100);
emoji.setBounds(300,0,100,100);
catchScreen.setBounds(450,0,100,100);
sender.setBounds(50,50,500,200);
center.add(send);
center.add(file);
center.add(emoji);
center.add(catchScreen);
south.add(sender);
//可视化
setVisible(true);
}
}
看看效果咋样
不错,完工,我们下一期再介绍服务器的代码,下下期介绍客户端服务器通信的代码。