仿腾讯QQ界面

     今天,想和大家聊一下有关软件的界面的一些知识,当一个软件程序被设计出来时,首先展现的就是界面效果,平时见过很多软件,早些很多商用的软件为了追求软件运行性能的出色,并没有把软件界面设计的美轮美奂,相反一些大型的桌面应用软件界面往往比较单调,比如一些大型ERP软件。我想原因也许有多种,究其一二,一是软件设计者在设计软件之初就忽略界面元素,只是一味的追求实现系统的核心业务功能。殊不知,如今的软件行业竞争也很大,尤其在同类型的软件中,软件的使用者越来越要求软件有更好的人机交互能力,更方便快捷美观的人性化的用户体验。好的用户界面必然在诸多同行软件中占据先机,备受青睐。二是为了追求高性能的运行稳定,提高界面的响应速度,有意未将软件界面效果纳入非功能需求范畴,以节约系统资源。以我的观点来看待这个问题,现在电脑处理器水平都比较高,3D游戏的推动和涌现带来了个人计算机软硬件技术的日新月异和网络通信技术的高速发展。性能资源消耗方面已不足为虑。很多漂亮软件界面呼之欲出。在国内软件中,有很多比较优秀的软件,在软件界面上更是做足了功夫,提供了诸多漂亮界面主题和皮肤包。比如,我们经常用的迅雷,腾讯QQ等桌面程序。每次版本的升级都带来全新的用户体验。个人简单的认为,软件的界面犹如人的衣装穿着,人人都有一颗爱美之心,而软件也不例外。我在校期间利用业余时间学习了C#、winform界面编程,学习固然很辛苦,都是靠自学,其中的动力就是对编程技术的热衷与追求。

其中第一点就是窗体动态效果的实现:

一,  调用系统API消息函数给窗体添加动态特效

1. 导入系统user32.dll并声明API函数AnimateWindow

//导入系统的COM组件:user32.dll  (user32.dll是Windows用户界面相关应用程序接口,用于包括Windows处理,基本用户界面等特性,如创建窗口和发送消息)
[System.Runtime.InteropServices.DllImport("user32")]
//声明系统API消息函数

private static extern bool AnimateWindow(IntPtr hwnd, int dwTime, int dwFlags); 

 AnimateWindow函数里面的3个传参的说明如下:

 hwnd        界面上控件的句柄

 dwTime    窗体特效执行的持续时间(1=1毫秒、1000=1秒)

 dwFlags   窗体特效的值

2. dwFlags要传的参数是一些INT类型的常量,这些常量定义了该特效具体有哪些动作。

            const int AW_HOR_POSITIVE = 0x0001;   //正面_水平方向
    
        const int AW_HOR_NEGATIVE = 0x0002
;    //负面_水平方向
    
        const int AW_VER_POSITIVE = 0x0004
;    //正面_垂直方向
    
        const int AW_VER_NEGATIVE = 0x0008
;    //负面_垂直方向
    
        const int AW_CENTER = 0x0010;    //由中间四周展开
     
       const int AW_HIDE = 0x10000
;   //隐藏对象
    
        const int AW_ACTIVATE = 0x20000;    //显示对象
       
     const int AW_SLIDE = 0x40000
//拉幕滑动效果
     
       const int AW_BLEND = 0x80000    //淡入淡出渐变效果

 3.在程序事件中调用AnimateWindow方法,执行窗体特效函数

AnimateWindow(this.Handle, 1000, AW_CENTER | AW_HIDE | AW_HOR_NEGATIVE);//动画——窗体由四周向中心缩小
纵然windows消息函数很强大,但也只能实现的窗体效果简单的表现需求,更多的情况是需要对窗体的重绘来实现更好的界面效果。

二、winform窗体重绘

重绘技术主要是对WIN32消息函数来处理的。

在窗体的大小和位置改变时,操作系统需要重绘窗体上的图形或控件。当窗体大小改变时都将触发窗体的重绘事件,下面将介绍窗体的两个重绘事件。

1. Paint事件

在一个窗体被移动或放大之后或在一个覆盖该对象的窗体被移开之后,该窗体部分或全部暴露时,Paint事件发生。如果在代码中使用了各种图形方法的输出,则使用Paint过程可以确保这样的输出在必须时能被重绘。使用窗体Refresh方法时,Paint事件即被调用。窗体Paint事件处理过程为:    Private Sub Form_Paint() 

说明:如果窗体的AutoRedraw属性被设置为True,系统自动进行重新绘图。

2.Resize事件

当窗体第一次显示或窗体的状态改变时将触发Resize事件,如对窗体进行最大化、最小化、还原、被鼠标拖动进行放大、缩小操作。使用Resize过程可以调整窗体的大小,也可以用来调整窗体上控件的大小与位置,使控件的大小随着窗体大小改变而改变,从而使得窗体布局更加合理和美观。窗体Resize事件处理过程为:

Private Sub Form_Resize() 

比如:用 OnPaint 进行实时绘图的

protected override void OnPaint(PaintEventArgs e)

{

        base.OnPaint(e);
        Graphics dc = e.Graphics;
        //以下是绘图内容

}

三. GUI重绘过程中带来的问题

主要是窗体的闪烁问题,网上处理的方式有很多种,常见的有二种:

1. 使用双缓冲(two way soft-closing)技术

     如果窗体在响应WM_PAINT消息的时候要进行复杂的图形处理,那么窗体在重绘时由于过频的刷新而引起闪烁现象。解决这一问题的有效方法就是双缓冲技术。
     因为窗体在刷新时,总要有一个擦除原来图象的过程OnEraseBkgnd,它利用背景色填充窗体绘图区,然后在调用新的绘图代码进行重绘,这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,这种反差也就越发明显。于是我们就看到了闪烁现象。
我们会很自然的想到,避免背景色的填充是最直接的办法。但是那样的话,窗体上会变的一团糟。因为每次绘制图象的时候都没有将原来的图象清除,造成了图象的残留,于是窗体重绘时,画面往往会变的乱七八糟。所以单纯的禁止背景重绘是不够的。我们还要进行重新绘图,但要求速度很快,于是我们想到了使用BitBlt函数。它可以支持图形块的复制,速度很快。我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪烁。以上也就是双缓冲绘图的基本的思路。
    首先给出实现的程序,然后再解释,同样是在OnDraw(CDC *pDC)中: 

  CDC MemDC; //首先定义一个显示设备对象 

  CBitmap MemBitmap;//定义一个位图对象 //随后建立与屏幕显示兼容的内存显示设备 MemDC.CreateCompatibleDC(NULL); //这时还不能绘图,因为没有地方画 ^_^ 

  //下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小,也可以自己定义

  (如:有滚动条时就要大于当前窗口的大小,在BitBlt时决定拷贝内存的哪部分到屏幕上)

  MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight); //将位图选入到内存显示设备中 //只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上 

  CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap); //先用背景色将位图清除干净,这里我用的是白色作为背景 //你也可以用自己应该用的颜色

  MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255)); //绘图 

  MemDC.MoveTo(……); MemDC.LineTo(……); //将内存中的图拷贝到屏幕上进行显示 

  pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY); //绘图完成后的清理 //把前面的pOldBit选回来.在删除MemBitmap之前 要先从设备中移除它。

     MemDC.SelectObject(pOldBit); MemBitmap.DeleteObject(); MemDC.DeleteDC(); (来源于百度)

2. 调用Control.SetStyle 窗体样式方法

如:public form_From()
{
            InitializeComponent();
   
            this.SetStyle(ControlStyles.UserPaint, true);   

            this.SetStyle(ControlStyles.ResizeRedraw, true);//调整大小时重绘边界。
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);// 禁止擦除背景.
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);// 双缓冲

            this.UpdateStyles();
}

      经过以上的步骤闪烁问题依然存在,最好的解决之道恐怕要在系统消息函数上再做文章,提供消息钩子设置窗口句柄函数。例如:WM_ERASEBKGND消息传递函数,这一点有待深究。

经过一阶段的学习,发现微软的.NET平台有很多可取之处,在大学过过好几种编程语言,编程语言之间相识的地方有很多。比如c#和java。用.NET开发软件周期短且效率高的。个人认为对一个程序员而言,时间应该是最宝贵的。学习.NET的人越来越多,我也是浩浩大军中的一员,出于对编程的热衷,最大限度的追求和实现软件界面的完美,下边用图片方式展示目前自己已经实现的精仿QQ窗体的界面效果。之所以模仿QQ界面,原因很简单就是要实现一个企业级使用的即时通讯组件。在以后的博文中将陆续介绍。

目前已经实现的QQ界面功能需求有:

1.实现窗体皮肤透明化处理,实现窗体半透明效果。

2.实现包含类似于QQ的主题板,调色板,纯色板的皮肤控制器。

3.实现软件退出皮肤动态保存,动态主题图片排序功能。

4.实现窗体主题皮肤动态预览功能。

5.实现用户自定义皮肤功能。

6.实现皮肤主题动态删除功能。

程序使用的是QQ图片资源,在此声明不做任何商业操作,仅仅是用于技术上的探究。下面是一些效果图以飨大家:

 

 

    个人觉得使用用.NET平台设计软件界面的确很出色。WPF的出现将带来更多更炫的用户体验。当然文章中的有些观点并不成熟。只是一孔之见,一口之言,望各位牛人大虾不吝拍砖。本人今年大四,即将毕业,希望和志同道合的朋友一起交流技术心得。联系方式QQ:875297974

 

仿QQ聊天 登录界面 import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.UIManager; import javax.swing.text.BadLocationException; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.StyledDocument; public class Test extends JFrame { private JScrollPane scrollPane = null; // 滚动 private JTextPane text = null; private Box box = null; // 放输入组件的容器 private JButton b_insert = null, b_remove = null, b_icon = null; // 插入按钮;清除按钮;插入图片按钮 private JTextField addText = null; // 文字输入框 // 字体名称;字号大小;文字样式;文字颜色;文字背景颜色 private JComboBox fontName = null, fontSize = null, fontStyle = null, fontColor = null, fontBackColor = null; private StyledDocument doc = null; // 非常重要插入文字样式就靠它了 public Test() { super("JTextPane Test"); try { // 使用Windows的界面风格 UIManager.setLookAndFeel ("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); } catch (Exception e) { e.printStackTrace(); } text = new JTextPane(); text.setEditable(false); // 不可录入 doc = text.getStyledDocument(); // 获得JTextPane的Document scrollPane = new JScrollPane(text); addText = new JTextField(18); String[] str_name = { "宋体", "黑体", "Dialog", "Gulim" }; String[] str_Size = { "12", "14", "18", "22", "30", "40" }; String[] str_Style = { "常规", "斜体", "粗体", "粗斜体" }; String[] str_Color = { "黑色", "红色", "蓝色", "黄色", "绿色" }; String[] str_BackColor = { "无色", "灰色", "淡红", "淡蓝", "淡黄", "淡绿" }; fontName = new JComboBox(str_name); // 字体名称 fontSize = new JComboBox(str_Size); // 字号 fontStyle = new JComboBox(str_Style); // 样式 fontColor = new JComboBox(str_Color); // 颜色 fontBackColor = new JComboBox(str_BackColor); // 背景颜色 b_insert = new JButton("插入"); // 插入 b_remove = new JButton("清空"); // 清除 b_icon = new JButton("图片"); // 插入图片 b_insert.addActionListener(new ActionListener() { // 插入文字的事件 public void actionPerformed(ActionEvent e) { insert(getFontAttrib()); addText.setText(""); } }); b_remove.addActionListener(new ActionListener() { // 清除事件 public void actionPerformed(ActionEvent e) { text.setText(""); } }); b_icon.addActionListener(new ActionListener() { // 插入图片事件 public void actionPerformed(ActionEvent arg0) { JFileChooser f = new JFileChooser(); // 查找文件 f.showOpenDialog(null); insertIcon(f.getSelectedFile()); // 插入图片 } }); box = Box.createVerticalBox(); // 竖结构 Box box_1 = Box.createHorizontalBox(); // 横结构 Box box_2 = Box.createHorizontalBox(); // 横结构 box.add(box_1); box.add(Box.createVerticalStrut(8)); // 两行的间距 box.add(box_2); box.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); // 8个的边距 // 开始将所需组件加入容器 box_1.add(new JLabel("字体:")); // 加入标签 box_1.add(fontName); // 加入组件 box_1.add(Box.createHorizontalStrut(8)); // 间距 box_1.add(new JLabel("样式:")); box_1.add(fontStyle); box_1.add(Box.createHorizontalStrut(8)); box_1.add(new JLabel("字号:")); box_1.add(fontSize); box_1.add(Box.createHorizontalStrut(8)); box_1.add(new JLabel("颜色:")); box_1.add(fontColor); box_1.add(Box.createHorizontalStrut(8)); box_1.add(new JLabel("背景:")); box_1.add(fontBackColor); box_1.add(Box.createHorizontalStrut(8)); box_1.add(b_icon); box_2.add(addText); box_2.add(Box.createHorizontalStrut(8)); box_2.add(b_insert); box_2.add(Box.createHorizontalStrut(8)); box_2.add(b_remove); this.getRootPane().setDefaultButton(b_insert); // 默认回车按钮 this.getContentPane().add(scrollPane); this.getContentPane().add(box, BorderLayout.SOUTH); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(600, 400); this.setLocationRelativeTo(null); this.setVisible(true); addText.requestFocus(); } /** * 插入图片 * * @param icon */ private void insertIcon(File file) { text.setCaretPosition(doc.getLength()); // 设置插入位置 text.insertIcon(new ImageIcon(file.getPath())); // 插入图片 insert(new FontAttrib()); // 这样做可以换行 } /** * 将文本插入JTextPane * * @param attrib */ private void insert(FontAttrib attrib) { try { // 插入文本 doc.insertString(doc.getLength(), attrib.getText() + "\n", attrib.getAttrSet()); } catch (BadLocationException e) { e.printStackTrace(); } } /** * 获取所需要的文字设置 * * @return FontAttrib */ private FontAttrib getFontAttrib() { FontAttrib att = new FontAttrib(); att.setText(addText.getText()); att.setName((String) fontName.getSelectedItem()); att.setSize(Integer.parseInt((String) fontSize.getSelectedItem())); String temp_style = (String) fontStyle.getSelectedItem(); if (temp_style.equals("常规")) { att.setStyle(FontAttrib.GENERAL); } else if (temp_style.equals("粗体")) { att.setStyle(FontAttrib.BOLD); } else if (temp_style.equals("斜体")) { att.setStyle(FontAttrib.ITALIC); } else if (temp_style.equals("粗斜体")) { att.setStyle(FontAttrib.BOLD_ITALIC); } String temp_color = (String) fontColor.getSelectedItem(); if (temp_color.equals("黑色")) { att.setColor(new Color(0, 0, 0)); } else if (temp_color.equals("红色")) { att.setColor(new Color(255, 0, 0)); } else if (temp_color.equals("蓝色")) { att.setColor(new Color(0, 0, 255)); } else if (temp_color.equals("黄色")) { att.setColor(new Color(255, 255, 0)); } else if (temp_color.equals("绿色")) { att.setColor(new Color(0, 255, 0)); } String temp_backColor = (String) fontBackColor.getSelectedItem(); if (!temp_backColor.equals("无色")) { if (temp_backColor.equals("灰色")) { att.setBackColor(new Color(200, 200, 200)); } else if (temp_backColor.equals("淡红")) { att.setBackColor(new Color(255, 200, 200)); } else if (temp_backColor.equals("淡蓝")) { att.setBackColor(new Color(200, 200, 255)); } else if (temp_backColor.equals("淡黄")) { att.setBackColor(new Color(255, 255, 200)); } else if (temp_backColor.equals("淡绿")) { att.setBackColor(new Color(200, 255, 200)); } } return att; } public static void main(String args[]) { new Test(); } /** * 字体的属性类 */ private class FontAttrib { public static final int GENERAL = 0; // 常规 public static final int BOLD = 1; // 粗体 public static final int ITALIC = 2; // 斜体 public static final int BOLD_ITALIC = 3; // 粗斜体 private SimpleAttributeSet attrSet = null; // 属性集 private String text = null, name = null; // 要输入的文本和字体名称 private int style = 0, size = 0; // 样式和字号 private Color color = null, backColor = null; // 文字颜色和背景颜色 /** * 一个空的构造(可当做换行使用) */ public FontAttrib() { } /** * 返回属性集 * * @return */ public SimpleAttributeSet getAttrSet() { attrSet = new SimpleAttributeSet(); if (name != null) StyleConstants.setFontFamily(attrSet, name); if (style == FontAttrib.GENERAL) { StyleConstants.setBold(attrSet, false); StyleConstants.setItalic(attrSet, false); } else if (style == FontAttrib.BOLD) { StyleConstants.setBold(attrSet, true); StyleConstants.setItalic(attrSet, false); } else if (style == FontAttrib.ITALIC) { StyleConstants.setBold(attrSet, false); StyleConstants.setItalic(attrSet, true); } else if (style == FontAttrib.BOLD_ITALIC) { StyleConstants.setBold(attrSet, true); StyleConstants.setItalic(attrSet, true); } StyleConstants.setFontSize(attrSet, size); if (color != null) StyleConstants.setForeground(attrSet, color); if (backColor != null) StyleConstants.setBackground(attrSet, backColor); return attrSet; } /** * 设置属性集 * * @param attrSet */ public void setAttrSet(SimpleAttributeSet attrSet) { this.attrSet = attrSet; } /* 后面的注释就不写了,一看就明白 */ public String getText() { return text; } public void setText(String text) { this.text = text; } public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } public Color getBackColor() { return backColor; } public void setBackColor(Color backColor) { this.backColor = backColor; } public String getName() { return name; } public void setName(String name) { this.name = name; } 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; } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值