October 6th, 2012DirectUI 及相关UI 库

这两天偶然发现了DirectUI相关的技术介绍, 当然这都是很老的技术了(六七年前就有讨论).

技术的原理基本就是用DirectX直接绘制控件, 可以实现很好的视觉效果, 一些动画的支持也不错, 找了一下DirectUI的开源项目, 貌似能找到的就只有DuiLIb了, 它的一些例子很不错, 实现的效果比较帅(像仿360安全卫士/QQ界面).

既然有基于DirectX的库, 那么是不是也有基于另一个图形库-OpenGL的类似项目呢? 再搜索了一下, 果然也有不少. 比如:BeGUI 库.不过这个项目基本已经停工了.

这些项目确实实现了一些很好的特性, 比如很容易支持皮肤, 速度较快等. 可以一试, 不过个人认为这些项目在设计上仍需创新, 理想的C++ GUI框架应该能够大大简化界面开发的复杂度, 而现在用C++写界面的最大困扰就是实在是太麻烦了. 当然QT确实不错, 已经很简单了.

August 1st, 2012Yii Unicode设置

其中最重要的一点就是,

把所有文件以Utf-8 无BOM格式编码保存.

看清楚, 只是utf-8格式保存是不对地!, 要utf-8 bom, 今天花了一晚上才发现这个奇怪的编码问题.

最好保证所有的文件都是这个编码, 否则在有些对编码要求很严格的程序中会出现错误……

August 29th, 2010我的黑苹果

我的这个yy半年之久的黑苹果计划终于在这个月初实现了.
基本配置如下:
主板:  GA-P55A-UD3R x1
CPU:  Intel i5 760
显卡:  铭瑄HD5770 1G   x1
内存:  金士顿DDR3 1600 2G x1
硬盘:  希捷7200转 500G  x1
无线网卡: TP-Link TL-WN851N (在10.6.4下不工作!) x1
显示器:  Dell U2311H x1

机箱电源就不说了.

现在这个配置下在Snow Leopard 10.6.4下面已经可以全部正常运行. 其中需要注意的是 显卡使用的一个有点问题的驱动, 只能工作在32位模式下并需要同时接两条dvi线到显示器上才能正常显示(貌似运行warcraft有点问题, 运行台湾版WOW正常), 板载网卡刚刚使用了tonymacx86.com刚发布的驱动(内测版), 另外无线网卡不工作(非常杯剧, 当时还特地查了一下, 看到网上很多人说能工作, 但貌似是工作在SL之前的系统上….). 另外显示器的显示效果也很赞的说.

另外由于暂时还离不开Win, 所以搞了在硬盘上同时安装了Win7+OSX, 引导使用的EasyBCD (win7 多引导的管理工具), 另外在osx的分区上装了变色龙(Chameleon).
安装过程参考了以下文章:
BIOS设置: http://lnx2mac.blogspot.com/2010/06/updating-gigabyte-bios.html
论坛: http://www.tonymacx86.com/
http://www.insanelymac.com/
安装过程我使用的稍微和上面的有所不同, 因为为了节省预算机器上没有配光驱, 不过还好之前已经有一个能运行的osx, 所以很多准备工作都是在已有的OSX上进行的并且使用usb引导安装.
另外在使用usb引导安装的时候如果遇到waiting for device的错误不妨试试 改一下启动的参数指定root device编号(磁盘编号可能会因为你使用了USB-HDD引导而和你在启动界面里看到的不一样.).

另外用新电脑的感觉倍爽, 特别是在看到解压大文件仅有10%不到的CPU占用率时:)

再另外, 由于为了节省预算内存少了点, 过段时间考虑加条一样的内存, 这样也算能有双通道了…

April 14th, 2010猥琐病毒的解决方案

这两天朋友电脑出现了一奇怪了症状: C盘空间貌似在不断减少, 疑似U盘有毒, 于是拿来看了下.

然后就发现了这个猥琐的病毒, 它的工作原理貌似很简单: 把自身的图标设置成与文件夹图标一模一样, 所以如果你没注意你是看不出来它是程序还是一个文件夹的, 另外它把U盘根目录下所有的文件夹隐藏(设置系统+隐藏属性, 所以即使你打开显示隐藏的文件与目录也看不到这些被隐藏的文件夹), 同时它创建一个与该文件夹同名的.exe文件.  而当毫不知情的用户点击运行这个病毒的时候它会打开与之对应的文件夹, 同时创建一个名为update.exe进程. 然后便向用户主目录下appdata(Win7下)的某个目录里狂写东西. 至于其他的坏事暂时还没发现.

鉴于这个病毒相对简单, 所以自己写了一个批处理来干掉它, 方法也很简单就是枚举磁盘根目录下所有的文件夹, 看是不是有同名的.exe存在, 如果有则删除并去除此文件夹的系统和隐藏属性. (在Win7下试用通过)

@echo off
setlocal 

:BEGIN
set ROOT=
set /P ROOT=输入要清除的盘符(输入0退出): %=%
if %ROOT%==0 goto END

echo 正在清除驱动器 %ROOT%: 
rem kill the update.exe process
taskkill /IM update.exe 2>NUL
for /F "usebackq delims==" %%i in (`dir /AD /B %ROOT%:`) do if exist "%ROOT%:\%%i.exe" if not exist "%ROOT%:\%%i.exe\NUL" (
echo 删除 %ROOT%:\%%i.exe 并去除目录%ROOT%:\%%i的系统与隐藏属性
del "%ROOT%:\%%i.exe"
attrib -H -S "%ROOT%:\%%i"
)

goto BEGIN

:END

November 24th, 2009progress on login

这里是指在linux下, 在我们的环境中由于在登录系统的时候需要比较久的时候(5-15秒), 在那里看着不动的屏幕总归会有些不爽, 于是便写了一个小小的进度条来.
首先呢, 我们先完成一个画一个转圈的直线的程序来(其实就是轮流打印”-\|-/”这几个字符, 它每转完一圈后会打印一个加号.) — 当然每次打印要有一个间隔, 不然会很快, 这个时间间隔我们能过程序参数来传递.

#! /bin/bash
count=0
#c is the array for the characters we'll print
c[0]='-\b' 
c[1]='\\\b'
c[2]='|\b'
c[3]='/\b'
c[4]='-\b'
c[5]='+'

while true
do
     let "count = $count %6"
     echo -n -e "${c[$count]}"
     sleep $1  #sleep sometime to continue....
     let "count = $count + 1"
done

在上面我们使用了echo 的两个参数, -n 是让它不打印一个多余的换行, -e让它解释转义字符, 而\b则是退格符.
好的, 下面就保存这个脚本并让它可执行(chmod +x ), 这里我把它命名为~/bin/timedot, 能过执行 ./timedot 0.2 来看一下效果吧. (请确定你的sleep程序可以接受小数形式的参数).

嗯, 看上去不错, 好的, 让我们把它加入到启动脚本里, 基本思路是把它作为一个后台程序运行.

~/bin/timedot 0.2&
timedotPID=$! #$! 是bash的内置变量, 它保存最后一个后台进行的pid.
...... #把你的很慢的代码放在这里.

kill $timedotPID  #杀掉打印进度条的进程.

好了, 试一下, 嗯, 似乎工作的还不错. 不过似乎有一个问题: 在每次启动的时候你都会看到一个类似下面的消息:

[1234]-  Terminated                 ~/bin/timedot 0.2&

之所以会有这行消息是因为你在后台运行一个程序的时候, bash会关注所有属于它的后台进程的状态, 并在后台进程结束后通知前台程序某个程序已经死掉啦(或者是已经完成了), 不过毫无疑问我们不希望看到关于timedot的通知消息(太丑陋了), 于是我们可以通过disown 命令来让bash放弃对timedot的管理.

好, 在这里似乎一切都很完美了, 不过假如你在一次登录的时候, 实在忍受不了长时间的等待了, 于是你按下了ctrl+c来中止当前的初始化脚本, 然后你就会发现了另外一个问题… 你会发现在你打字的时候总是会有”|/|-+”这几个字符蹦出来 —- 而这是因为你的ctrl+c虽然中止了你的初始化脚本, 但它却没有中此我们的这个后台进行的进度条进程. 好吧, 让我们修好它:

~/bin/timedot 0.2& 
timedotPID=$! #$! 是bash的内置变量, 它保存最后一个后台进行的pid.
my_control_c()
{
     kill $timedotPID #杀死后台进程<br />
     trap SIGINT  #重置SIGINT, 
}
trap my_control_c SIGINT   #我们设置一个control+c的钩子, 当用户按下control+c的时候会调用我们的my_control_c函数
...... #把你的很慢的代码放在这里.

kill $timedotPID  #杀掉打印进度条的进程.
trap SIGINT #重置SIGINT为默认值. 

这里使用了trap命令来设置ctrl+c的钩子, 这样每次要中止初始化脚本的时候就会调用我们自己的函数, 而它会杀掉后台进度条进程, 当然不要忘记重置钩子, 我们不需要再以后用到它了.

刚搞了个黑莓玩, 在写短信时发现缺少了一个在用wm时常用的功能: 往短信中插入联系人. 这个有啥用呢, 想一想当有朋友问题你要另外一个人的号码时, 你就会知道它的用处了, 简单的通过一个菜单就可以插入联系人的手机号, 邮箱…… 而在黑莓里是没有这个功能的, 只能先切换到桌面然后打开联系人界面然后再找到你要的联系人, 然后再把电话复制出来, 再然后就是切换到短信程序并粘贴电话号码, 这显然是一个比较烦琐的步骤. 再假如你想要同时插入邮件/手机号, 则还要来回复制两遍. 所以我们就会看到一些常用的扩展来辅助插入手机号. 于是闲来无事自己研究了一下具体实现 (已经看到了一些具体的实现, 只是好奇, 所以研究了一下)

让我们分析一下这个问题, 要实现这个功能, 应该可以把功能划分为以下三个步骤:

  1. 往编辑短信的菜单中添加一个菜单项
  2. 当用户点击这个菜单的时候显示一个选择联系人的界面
  3. 当用户选择联系人之后把联系人的电话插入到短信中去.

下面我们分别看这三个步骤的实现, 步骤一很简单, BB提供了相关的api, 在它的文档中就可以找到, 具体请看这里. 首先我们需要实现一个派生自MenuItem类,

class InsertContactMenuItem extends ApplicationMenuItem {
   InsertContactMenuItem() {
                     super(2000000);  // This sets the item just before the "Add To:" menuitem          }

   public String toString() {             return "Insert Contact";
   }

  public Object run(Object context) {              ....

  }

上面就是一个简单的程序菜单项, 它在初始化的时候通过调用父类的构造函数来调整它在菜单项中的顺序(这个没有试过, 具体不知道工作不工作…), toString方法返回的字符串决定了它显示在菜单中的文字, 至于run方法则会在点击菜单项时会调用到, 它接受一个context参数, 系统会传入一个上下文相关的对像.

有了Menu类之后就可以把它注册到短信编辑界面里去了:

   private static void registerMenuItem() {
        System.out.println("*** Registering Insert contact menuitem");
        InsertContactMenuItem menuItem = new InsertContactMenuItem();
        ApplicationMenuItemRepository amir = ApplicationMenuItemRepository.getInstance();
        amir.addMenuItem(ApplicationMenuItemRepository.MENUITEM_SMS_EDIT, menuItem);
        }

上面的逻辑很简单, 首先取得系统ApplicationMenuItemRepository的实例, 并向注册InsertContactMenuItem到SMS_EDIT中去, 好了, 到了这里, 第一步基本完成了. 完成这一步, 你就可以在模拟器里看到相应的菜单项了, 如下图:

menu

下面开始第二步, 这里需要在用户点击菜单之后弹出一个选择联系人的界面, 这个可以通过BlackBerryContactList.choose()方法实现:

  PIM pim = PIM.getInstance();
  BlackBerryContactList list = (BlackBerryContactList) pim.openPIMList(PIM.CONTACT_LIST, PIM.READ_ONLY);
  PIMItem pimItem = list.choose ();
  String phoneNum = pimItem.getString  (Contact.TEL, 0);

这里首取得系统PIM的实例, 然后联系人列表并调用它的choose ()方法, 运行的结果应该如下图所示, 出现一个选择联系人的窗口:

choosecontact 到了这里, 最后一步就是要把联系人信息插入到短信输入窗口里了, 在这里我走了一段弯路:

在前面提到在系统调用run (Object context)方法时会传入一个上下文相关的对象, 在看4.5.0os的文档里提到这里会传入一个TextMessage对象, 具体参见这里, 当时我是想尝试直接通过修改这个对象来往消息里插入文本, 不过可惜的是行不能: 因为在这里传入的TextMessage对象是只读的, 尝试调用它的set方法会引发一个异常. 而且在老版本的系统里根本就没有传入一个对象(8800 4.2.0模拟器里显示传入的对象为空). 所以在经历了若干次失败后只好转求它法.

在阅读相关的代码的时候, 终于发现了一个UiApplication类, 通过它可以得到当前在最前面的屏幕, 并且我们可以遍历这个屏幕上的所有元素, 貌似有点希望! 让我们尝试遍历一当前屏幕并打印出这些元素, 看看它们有些什么!

  Screen screen = UiApplication.getUiApplication().getActiveScreen();
  for (int i = 0; i < screen.getFieldCount(); i++)
   {
        Field field = screen.getField(i);
        printFields (field);
   }
      void printFields (Field field)
         {
             try
             {
                    System.out.println("*** Field class: " + field.getClass().getName());

                         if (field instanceof  TextField) {
                             TextField textField = (TextField) field;
                             System.out.println("*** Found a text field");
                             System.out.println("*** text field label: "
                                     + textField.getLabel());

                                 System.out.println("*** Found text field");
                                 System.out.println ("** value: " + textField.getText ());
                         }
                         else if (field instanceof  Manager)
                         {
                             Manager mgrField = ( Manager) field;
                               System.out.println ("** Subfields..");
                                for (int i = 0; i < mgrField.getFieldCount(); i++) {
                                        Field subFields = mgrField.getField(i);
                                        printFields (subFields);
                        }
                    }
                }
                catch (Exception e)
                {
                    System.out.println ("printFields excetion:" + e.getMessage ());
                }
        }

对于这段代码, 前几行是得到当前活动的屏幕,  然后调用函数printFields函数, 在这个函数里我们基本是把一个field相关信息打印了出来: 它的标签的文字, 它的文字. 由于在screen里可以嵌套包含一些屏幕元素的管理对象, 所以我们需要一个递归来打印. 下面是得到的结果, 请注意使用蓝颜色标记的文字,

BB 8800 4.2.1.74的Debug输出

JVM: bklt @8044: timer

JVM: bklt @8044: idle 0

JVM: bklt @8044: setTimer 22

*** Field class: net.rim.device.api.ui.container.VerticalFieldManager

** Subfields..

*** Field class: net.rim.device.apps.internal.phone.model.ReadOnlyPhoneNumberField

** Subfields..

*** Field class: net.rim.device.apps.internal.phone.model.SmartPhoneNumberLabelField

*** Field class: net.rim.device.api.ui.component.SeparatorField

*** Field class: net.rim.device.apps.internal.sms.ui.SMSEditField

*** Found a text field

*** text field label: null

*** Found Using field

** value: Afdsf

BB  9500 的Debug输出:

FRIDG: could not find background_popup__Landscape.png

*** Field class: net.rim.device.api.ui.container.VerticalFieldManager

** Subfields..

*** Field class: net.rim.device.apps.api.ui.LeftRightFieldManager

** Subfields..

*** Field class: net.rim.device.api.ui.container.HorizontalFieldManager

** Subfields..

*** Field class: net.rim.device.api.ui.component.LabelField

*** Field class: net.rim.device.apps.internal.sms.ui.MessageComposeComboField

** Subfields..

*** Field class: net.rim.device.apps.api.addressbook.AddressBookComboField$EmailComposeEditable

*** Found a text field

*** text field label: To:

*** Found text field

** value: 12313

*** Field class: net.rim.device.apps.api.ui.LeftRightFieldManager

** Subfields..

*** Field class: net.rim.device.api.ui.container.HorizontalFieldManager

** Subfields..

*** Field class: net.rim.device.api.ui.component.LabelField

*** Field class: net.rim.device.apps.internal.sms.ui.MessageComposeComboField

** Subfields..

*** Field class: net.rim.device.apps.api.addressbook.AddressBookComboField$EmailComposeEditable

*** Found a text field

*** text field label: To:

*** Found text field

** value:

*** Field class: net.rim.device.apps.internal.smscompose.message.SMSEditorScreen$BodyVerticalFieldManager

** Subfields..

*** Field class: net.rim.device.apps.internal.smscompose.ui.SMSEditField

*** Found a text field

*** text field label: null

*** Found text field

** value: Wet

我们可以看到实际在这两个系统里的短信编辑输入框实际都是一个net.rim.device.apps.internal.smscompose.ui.SMSEditField 类, 看上去似乎我们可以通过判断一个Field对象是不是一个此类的实例来找到短信的实际输入框了, 十分遗憾的是请注意前面的那一连串类名里的一个.internal.: 对不起, 这是一个内部类, 外界开发人员是不能使用这个类的.不过我们还是可以利用这一点的, 在这里直接判断它的类名里是不是包含SMSEditField就可以达到预定效果.

      Screen screen = UiApplication.getUiApplication().getActiveScreen();
                    for (int i = 0; i < screen.getFieldCount(); i++)
                    {
                        Field field = screen.getField(i);
                        printFields (field, phoneNum);
                    }

      void printFields (Field field, String str)
         {
             try
             {
                           System.out.println("*** Field class: "
                                 + field.getClass().getName());

                         if (field instanceof  TextField) {
                             TextField textField = (TextField) field;
                             System.out.println("*** Found a text field");
                             System.out.println("*** text field label: "
                                     + textField.getLabel());

                                 System.out.println("*** Found text field");
                                 System.out.println ("** value: " + textField.getText ());
                                 if (textField.getClass ().getName ().endsWith(".SMSEditField")
                                 {
                                     System.out.println ("insertting text to...");
                                    textField.insert (str);
                                }

                         }
                         else if (field instanceof  Manager)
                         {
                             Manager mgrField = ( Manager) field;
                               System.out.println ("** Subfields..");
                                for (int i = 0; i < mgrField.getFieldCount(); i++) {
                                        Field subFields = mgrField.getField(i);
                                        printFields (subFields, str);
                        }
                    }
                }
                catch (Exception e)
                {
                    System.out.println ("printFields excetion:" + e.getMessage ());
                }
        }
        }

修改完代码后, 编译, 运行, 启动程序, 然后打开短信输入窗口, 按照前面的步骤选择联系人, 确认! 电话被成功的插入到短信编辑窗口里了!:)

resultsms

很好, 这里我们基本大功告成了! 不过其实还有很事情要做: 怎么把程序弄成开机启动的, 然后应该在选择联系人之后我们应该弹出一个对话框让用户选择需要插入的项(移动电话, 家庭电话, email 等). 不过做为一个自己瞎折腾型的项目, 到这里咱们就告一段落了:)

最后再告诉你一个不幸的消息, 即便你有了这个程序你也不能把它安装到你的手机里的, 这是因为黑莓开发的api中有一些是需要官方的验证的, 而这个验证在当前是需要钱滴(20刀). 不过即使没有申请官方的验证也是可以在模拟器里支持你的程序的, 这就是我写这篇文章的环境:)

具体的代码在这里:InsertContact.java


© 2007 pangwa's Blog | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress