失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Swing制作高仿QQ界面包含主界面 聊天窗口 系统设置窗口|圆角界面|透明|颜色|渲染|换肤

Swing制作高仿QQ界面包含主界面 聊天窗口 系统设置窗口|圆角界面|透明|颜色|渲染|换肤

时间:2019-12-01 15:34:59

相关推荐

Swing制作高仿QQ界面包含主界面 聊天窗口 系统设置窗口|圆角界面|透明|颜色|渲染|换肤

最近几天闲着没事,练习了一下。编写了一个模仿QQ的界面,主要是练习Swing。呵呵,基本上使用到了我博客前面讲的各种技术,在这里跟大家分享了。我们先来看看主界面:

左边的界面是用Swing编写的,右边的QQ的原界面,大致的界面已经很像了但qq的按钮的确很难做试了几种方法但效果都不好。还有就是QQ的发光文字用Swing挺难实现的,界面并没有细化。像搜索栏可以再加上一层渐变,联系人列表中的热度和群很简单这里就不实现了。

先来看看菜单按钮的实现:

第一种状态:

第二种状态:

这里使用了两个图片:和

来看看按钮的代码:

package com.flyingwind.tutorial;import java.awt.AlphaComposite;import java.awt.Color;import java.awt.Dimension;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Image;import java.awt.Paint;import java.awt.RadialGradientPaint;import java.awt.Shape;import java.awt.geom.Ellipse2D;import java.awt.geom.Point2D;import javax.swing.JButton;public class RButton extends JButton {Image image;boolean flag=false;boolean isheight=false;public boolean isIsheight() {return isheight;}public void setIsheight(boolean isheight) {this.isheight = isheight;}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}public Image getImage() {return image;}public void setImage(Image image) {this.image = image;}Image rimage;int xs,ys,widths,heights;public RButton(Image im,Image old,int sizes) {super("");image=im;rimage=old;// 这些声明把按钮扩展为一个圆而不是一个椭圆。setPreferredSize(new Dimension(sizes,sizes));//这个调用使JButton不画背景,而允许我们画一个圆的背景。setContentAreaFilled(false);//this.setBackground(Color.GRAY);}// 画圆的背景和标签protected void paintComponent(Graphics g) {Graphics2D g2d = (Graphics2D) g;if (getModel().isArmed()) {// 你可以选一个高亮的颜色作为圆形按钮类的属性//g.setColor(Color.lightGray);if(rimage!=null){g.drawImage(rimage, xs, ys, widths, heights, null);}}else {/*Paint f=new GradientPaint(0.0f, 0.0f, new Color(255, 255, 255, 100), 0,getSize().height,new Color(255, 255, 255, 0),false);*/Paint p;if(isheight){p = new RadialGradientPaint(new Point2D.Double(getWidth() / 2.0f,getHeight() / 2.0f), getWidth() / 2.0f,new float[] { 0.0f, 1.0f },new Color[] { new Color(51, 153, 255),new Color(255, 255, 255, 200) });}else{p = new RadialGradientPaint(new Point2D.Double(getWidth() / 2.0f,getHeight() / 2.0f), getWidth() / 2.0f,new float[] { 0.0f, 1.0f },new Color[] { new Color(51, 153, 255),new Color(255, 255, 255, 100) });}g2d.setPaint(p);g2d.fillOval(0, 0, getSize().width - 1,getSize().height - 1);if(image!=null){if (flag) {AlphaComposite ac2 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);g2d.setComposite(ac2);}g2d.drawImage(image, xs, ys, widths, heights, null);}//g.setColor(getBackground());}//这个调用会画一个标签和焦点矩形。super.paintComponent(g);}public void setPoints(int x,int y,int width,int height){this.xs=x;this.ys=y;this.widths=width;this.heights=height;}// 用简单的弧画按钮的边界。protected void paintBorder(Graphics g) {g.setColor(new Color(0,0,0,30));g.drawOval(0, 0, getSize().width - 1,getSize().height - 1);}// 侦测点击事件Shape shape;public boolean contains(int x, int y) {// 如果按钮改变大小,产生一个新的形状对象。if (shape == null ||!shape.getBounds().equals(getBounds())) {shape = new Ellipse2D.Float(0, 0,getWidth(), getHeight());}return shape.contains(x, y);}}

对于按钮我们也进行了封装:

package com.flyingwind.tutorial;import java.awt.Color;import java.awt.Dimension;import java.awt.GradientPaint;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Image;import java.awt.Paint;import javax.swing.Icon;import javax.swing.JLabel;public class GnButton extends JLabel {private boolean flag=false;private Image image;public Image getImage() {return image;}public void setImage(Image image) {this.image = image;}public GnButton(Icon icon){super(icon);}public GnButton(Image image){super("");}public GnButton(int size){super("");setPreferredSize(new Dimension(size,size));}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}protected void paintComponent(Graphics g) {Graphics2D g2d = (Graphics2D) g;if (isFlag()) {Paint f = new GradientPaint(0.0f, 0.0f, new Color(255, 255, 255,100), 0, 50, new Color(255, 255, 255, 200), true);g2d.setPaint(f);// g2d.fillRect(0, 0, this.getWidth(), this.getHeight());g2d.fillRoundRect(1, 1, this.getWidth() - 2, this.getHeight() - 2,3, 3);f=null;}g2d.drawImage(image, 5, 5, 18, 20, null);}}

在鼠标悬停在按钮上时按钮就会发光。

接下来看看这个按钮,在按钮的边缘使用了黑色边缘调整了透明度,再用一个白色的边框表现出高光。中间使用了线性渐变。

package com.flyingwind.tutorial;import java.awt.AlphaComposite;import java.awt.Color;import java.awt.Dimension;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Image;import java.awt.LinearGradientPaint;import java.awt.Paint;import java.awt.Shape;import java.awt.geom.RoundRectangle2D;import javax.swing.JButton;public class BaseButton extends JButton {Image image;boolean flag=false;boolean isheight=false;int xs,ys,widths,heights;public boolean isIsheight() {return isheight;}public void setIsheight(boolean isheight) {this.isheight = isheight;}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}public Image getImage() {return image;}public void setImage(Image image) {this.image = image;}public BaseButton(Image im,String text,int width,int height){super("");if(text!=null){setText(text);}image=im;setPreferredSize(new Dimension(width,height));//这个调用使JButton不画背景,setContentAreaFilled(false);}// 画圆的背景和标签protected void paintComponent(Graphics g) {Graphics2D g2d = (Graphics2D) g;if (getModel().isArmed()) {// 你可以选一个高亮的颜色作为圆形按钮类的属性//g.setColor(Color.lightGray);if(image!=null){g.drawImage(image, xs, ys, widths, heights, null);}if (!getText().equals("")) {g2d.drawString(getText(), xs+widths+4, ys+heights-heights/4);}}else {/*Paint f=new GradientPaint(0.0f, 0.0f, new Color(255, 255, 255, 100), 0,getSize().height,new Color(255, 255, 255, 0),false);*/if (isheight) {//画出多点线性渐变Paint oldPaint = g2d.getPaint();LinearGradientPaint p;p = new LinearGradientPaint(0.0f, 0.0f, 0.0f, 20.0f,new float[] { 0.0f, 0.5f, 0.501f, 1.0f }, new Color[] {new Color(255,255,255,200), new Color(255,255,255,0),new Color(255,255,255,0), new Color(255,255,255,200) });g2d.setPaint(p);//g2d.fillRect(0, 0, getWidth(), 21);g2d.fillRoundRect(1, 1, getWidth()-4, getHeight()-4, 1, 1);g.setColor(new Color(255, 255, 255, 200));/** g.drawOval(0, 0, getSize().width - 1, getSize().height - 1);*///画出高光部分g.drawRoundRect(1, 1, getSize().width-4, getSize().height-4, 1,1);g2d.setPaint(oldPaint);}if(image!=null){if (flag) {AlphaComposite ac2 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);g2d.setComposite(ac2);}g2d.drawImage(image, xs, ys, widths, heights, null);if (!getText().equals("")) {g2d.drawString(getText(), xs+widths+4, ys+heights-heights/4);}}//g.setColor(getBackground());}//这个调用会画一个标签和焦点矩形。//super.paintComponent(g);}public void setPoints(int x,int y,int width,int height){this.xs=x;this.ys=y;this.widths=width;this.heights=height;}// 用简单的弧画按钮的边界。protected void paintBorder(Graphics g) {if (isheight) {g.setColor(new Color(0, 0, 0, 100));/** g.drawOval(0, 0, getSize().width - 1, getSize().height - 1);*/g.drawRoundRect(0, 0, getSize().width-2, getSize().height-2, 1,1);}}// 侦测点击事件Shape shape;public boolean contains(int x, int y) {// 如果按钮改变大小,产生一个新的形状对象。if (shape == null ||!shape.getBounds().equals(getBounds())) {/*shape = new Ellipse2D.Float(0, 0,getWidth(), getHeight());*/shape=new RoundRectangle2D.Double(0, 0, this.getWidth(),this.getHeight(), 3, 3);}return shape.contains(x, y);}}

我们再来看看发光的文字是怎么实现的例如这样的效果,就是调用以下方法实现的。

private void drawText(Graphics2D g2, int size, float opacity) {Composite oldComposite = g2.getComposite();float preAlpha = 1.0f;if (oldComposite instanceof AlphaComposite &&((AlphaComposite) oldComposite).getRule() == AlphaComposite.SRC_OVER) {preAlpha = ((AlphaComposite) oldComposite).getAlpha();}g2.setFont(getFont());FontMetrics metrics = g2.getFontMetrics();int ascent = metrics.getAscent();int heightDiff = (metrics.getHeight() - ascent) / 2;g2.setColor(new Color(255,255,255,150));double tx = 2.0;double ty = 2.0 + heightDiff - size;g2.translate(tx, ty);for (int i = -size; i <= size; i++) {for (int j = -size; j <= size; j++) {double distance = i * i + j * j;float alpha = opacity;if (distance > 0.0d) {alpha = (float) (1.0f / ((distance * size) * opacity));}alpha *= preAlpha;if (alpha > 1.0f) {alpha = 1.0f;}g2.setComposite(AlphaComposite.SrcOver.derive(alpha));g2.drawString(this.getText(), i + size-3, j + size + ascent-3);}}g2.setComposite(oldComposite);g2.setColor(Color.BLACK);g2.drawString(this.getText(), size-3, size + ascent-3);g2.translate(-tx, -ty);}

好友列表的实现很简单,使用JTree来实现的。有人要问这样的效果是怎样实现的,其实也不难主要是利用了树的样式将树平行化

package com.flyingwind.tutorial;import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.event.*;import javax.swing.plaf.*;import javax.swing.plaf.basic.*;import javax.swing.tree.*;public class TreeUI extends BasicTreeUI{private static JTree tree;private JViewport parentViewport;private VariableLayoutCache layoutCache;public static ComponentUI createUI(JComponent c){return new TreeUI();}public void installUI(JComponent c){if ( c == null )throw new NullPointerException("null component passed to BasicTreeUI.installUI()" );tree = (JTree)c;tree.addTreeSelectionListener(new TreeSelectionListener(){public void valueChanged(TreeSelectionEvent e){layoutCache.invalidatePathBounds(e.getOldLeadSelectionPath());layoutCache.invalidatePathBounds(e.getNewLeadSelectionPath());updateSize();tree.repaint();}});tree.addHierarchyListener(new HierarchyListener(){public void hierarchyChanged(HierarchyEvent e){if (e.getID() == HierarchyEvent.HIERARCHY_CHANGED&& (e.getChangeFlags() & HierarchyEvent.PARENT_CHANGED) != 0&& e.getChangedParent() instanceof JViewport){parentViewport = (JViewport) e.getChangedParent();}}});super.installUI(c);}/*** 安装样式*/protected void installDefaults(){super.installDefaults();if(tree.getBackground() == null ||tree.getBackground() instanceof UIResource) {tree.setBackground(UIManager.getColor("Tree.background"));} if(getHashColor() == null || getHashColor() instanceof UIResource) {setHashColor(UIManager.getColor("Tree.hash"));}if (tree.getFont() == null || tree.getFont() instanceof UIResource)tree.setFont( UIManager.getFont("Tree.font") );setExpandedIcon(null);setCollapsedIcon(null);setLeftChildIndent(0);setRightChildIndent(0);LookAndFeel.installProperty(tree, "rowHeight",UIManager.get("Tree.rowHeight"));largeModel = (tree.isLargeModel() && tree.getRowHeight() > 0);Object scrollsOnExpand = UIManager.get("Tree.scrollsOnExpand");if (scrollsOnExpand != null) {LookAndFeel.installProperty(tree, "scrollsOnExpand", scrollsOnExpand);}UIManager.getDefaults().put("Tree.paintLines", false);UIManager.getDefaults().put("Tree.lineTypeDashed", false);}protected AbstractLayoutCache createLayoutCache(){layoutCache = new VariableLayoutCache();return layoutCache;}private class VariableLayoutCache extends VariableHeightLayoutCache{public Rectangle getBounds(TreePath path, Rectangle placeIn){Rectangle rect = super.getBounds(path, placeIn);if (rect != null && parentViewport != null){rect.width = parentViewport.getWidth() - 2;}return rect;}}}

至于圆角界面的实现大家可以看我以前写的博客,里边有介绍圆角界面的实现,还有靠边自动停靠等内容。为了实现颜色变换界面底层使用了单一的颜色。而上边的组件都是透明的,实现换色就很简单了,如果要换肤功能,也只需要在界面上多花一层透明的图片就可以了。

下面是聊天窗口:

上面的界面是Swing的,下面的是QQ的,效果已经很接近了。按钮部分没有做调整,大家可以自己发挥。聊天窗口制作很简单主要是窗口的布局比较麻烦左边的分割区用到了JSplitPane是下面的部分可以上下拖动。

来看看分割部分的代码:

JSplitPane topSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);topSplitPane.setBorder(null); // 删除缺省边界topSplitPane.setOneTouchExpandable(false);topSplitPane.setOpaque(false);topSplitPane.setResizeWeight(0.7D);topSplitPane.setDividerSize(1);//设置分隔条大小topSplitPane.setLeftComponent(messageShow);topSplitPane.setRightComponent(message);messagePanel.add(topSplitPane,BorderLayout.CENTER);

分割高度不能设置为0不然鼠标会找不到焦点。

然后是基本设置界面:

上面的界面是Swing的,下面的是QQ的,面板右边的区域制作很简单只需要加载一个CardLayout就可以了我在这里就不实现了,左边的面板使用了一个自己编写的组件,呵呵!自我觉得还挺像的,来看看代码吧!

class JGroupContainerextends JPanel {private JButton bttGroupTitle = new JButton();private JPanel pMembers = new JPanel();private JScrollPane sp;public JGroupContainer() {this("");}public JGroupContainer(String name) {this(name, UIManager.getColor("Desktop.background"));}/** * @param name String 组名 * @param background Color 成员组件所在面板背景色 */public JGroupContainer(String name, Color background) {bttGroupTitle.setText(name);bttGroupTitle.setFocusable(false);pMembers.setLayout(new GroupLayout(5, 5));this.setLayout(new BorderLayout());this.add(bttGroupTitle, BorderLayout.NORTH);pMembers.setBackground(background);Color thumbColor = UIManager.getColor("ScrollBar.thumb");Color trackColor = UIManager.getColor("ScrollBar.track");Color trackHighlightColor = UIManager.getColor("ScrollBar.trackHighlight");UIManager.put("ScrollBar.thumb", background);UIManager.put("ScrollBar.track", background);UIManager.put("ScrollBar.trackHighlight", background);sp = new JScrollPane(pMembers);sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);this.add(sp, BorderLayout.CENTER);collapse();UIManager.put("ScrollBar.thumb", thumbColor);UIManager.put("ScrollBar.track", trackColor);UIManager.put("ScrollBar.trackHighlight", trackHighlightColor);}/** * 设置间距 * @param hgap int 横间距 * @param vgap int 竖间距 */public void setMemberGap(int hgap, int vgap) {pMembers.setLayout(new GroupLayout(hgap, vgap));}/** * 取得组的标题按钮 * @return JButton */public JButton getTitleButton() {return bttGroupTitle;}/** * 取得组的成员组件面板 * @return JPanel */public JPanel getMembersContainer() {return pMembers;}/** * 收缩组 */public void collapse() {sp.setVisible(false);this.revalidate();}/** * 展开组 */public void expand() {sp.setVisible(true);this.revalidate();}/** * 设置组名 * @param name String 组名 */public void setName(String name) {bttGroupTitle.setText(name);}/** * 取得组名 * @return String */public String getName() {return bttGroupTitle.getText();}/** * 添加一个成员组件 * @param index int 顺序号 * @param c Component 成员组件 */public void addMember(int index, Component c) {pMembers.add(c, index);pMembers.doLayout();}/** * 删除一个成员组件 * @param index int 顺序号 */public void removeMember(int index) {pMembers.remove(index);pMembers.doLayout();}/** * 取得一个成员组件 * @param index int 顺序号 * @return Component 成员组件 */public Component getMember(int index) {return pMembers.getComponent(index);}/** * 取得全部成员组件 * @return Component[] 成员组件 */public Component[] getMembers() {Component coms[] = new Component[getMemberCount()];for (int i = 0; i < coms.length; i++) {coms[i] = pMembers.getComponent(i);}return coms;}/** * 取得成员组件总数 * @return int 总数 */public int getMemberCount() {return pMembers.getComponentCount();}/** * 重写的toString方法 * @return String */public String toString() {return getName();}}}

大致的QQ界面已经能实现的八九成了,如果大家有什么问题可以给我留言。

如果对java界面方面有兴趣的朋友欢迎加入4601398QQ群。

如果觉得《Swing制作高仿QQ界面包含主界面 聊天窗口 系统设置窗口|圆角界面|透明|颜色|渲染|换肤》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。