2010年3月30日星期二

腾讯版Android QQ是个烂产品

今晚在cnBeta看到消息说腾讯发布了Android版QQ,马上上菜市场下载了一个。

心想腾讯磨蹭了这么久才搞出来的东西,应该不会差吧。结果大跌眼镜。

最大的问题,应该归类为Bug。在设置里面,可以设置接收或者屏蔽群消息,设置完了之后,所有的群都会变成一样的接收或屏蔽状态。单独的群可以设置个别的接收屏蔽设定。问题是,如果有任何一个群设置为接收,那么下次启动的时候(或者进入设置了之后退出之后),所有的群又都变成接收状态了。

当年PC版的QQ,是强制开机启动的,直到最近两年才在安装的时候让你选是否开机启动。而作为手机QQ来说,开机启动不能说更加有需要,但是应该也是很有需要的选项,这个版本的QQ反倒没有提供自动启动的选项。

另外,对于聊天记录,不能复制,不能点击里面的链接。

作为一个QQ号小于10200的QQ老用户,用过的QQ产品其实不算多,但是这个是我用过的最烂的QQ产品。入手XT800后先后从菜市场下载过近百个应用,这个绝对是最烂的,没有之一。

2010年3月8日星期一

Google Map API 国内地图偏移的解决

由于国内地图采用的坐标系统和国际通用的不一样,所以如果直接用GPS取得的经纬度来请求Google Map,会发现有偏移。CSDN上有个牛人找到了Google修正偏移的接口,完美解决了这个问题

在使用他所说的方法之前,为了解决地图偏移问题,找了不少资料,发现对引起国内地图偏移的原因有所误解,也就是说,其实不是以前一直以为的有关部门瞎折腾。

简单地说,地球不是一个完美的椭圆体,所以如何以经纬度及海拔来标识地球上面的一个点,与选择什么样的近似椭圆体是有关的。(外行表述,行家请指教)国际上比较通用的体系叫做WGS84,中国现在的地图则基本采用了北京54体系,另外国内还有一个西安80体系,看到资料貌似现在新建设的GIS系统还要采用一个2000年的标准。

一个国家采用自己的标准,也不是只有中国这么干,貌似现在有上百个这类的坐标系。坐标系之间是可以互相转换的。首先,要将一个坐标系的经纬度和海拔BLH这三个数据,根据选择的椭圆形的长径和扁率来转换成称为大地坐标的XYZ,然后使用另外一个体系的长径和扁率来将XYZ转换成新的BLH。所有的坐标体系所选择的椭圆形的半径和扁率都是公开的,包括国内的这些。

这样看起来貌似没问题,但是实际上通过这样的转换是无法得到正确的坐标的。因为各个坐标体系选择地球的哪个点来作为椭球的中心,以及坐标轴的方向,都不一样的。所以,在将XYZ转换成新的BLH之前,还需要对XYZ进行一次变换。外行的我不知道原因,只知道地球上各个点要进行变换所需要的参数是不一样的,而且是没法通过公式计算的。

其它的坐标体系会把变换所需要的参数公开,但是北京54和西安80则只公开了这两者之间互转的参数,原因是基于国家安全的理由。所以,世界上的GIS系统支持很多坐标系,但是就是没法支持这两个,也增加了国内搞GIS的人很多麻烦。

总而言之,国内采用自己的坐标系,不是特立独行瞎折腾,只不过……

2010年3月6日星期六

Android开发:在Camera Preview上叠加信息

自从买了XT800之后,对Android的应用开发热情暴增。最近想尝试做一个现实增强的试验。所以,需要在摄像头的预览画面上面增加文字或者图形信息。

摄像头的预览网上的示例很多,都是用一个SurfaceView来做容器。所以要往预览画面上面写字,最直接的想法,当然是从SurfaceView那里锁定一个Canvas来画东西了。但是马上就会发现这种方法是不行的,因为这个SurfaceView必须设成SURFACE_TYPE_PUSH_BUFFERS这个类型,也就是不自己管理buffer,这样才能让预览画面由摄像头自己管理,所以无法取得Canvas。

第二个思路,就是在用于预览的SurfaceView上面再叠加另外一个SurfaceView,用第二个SurfaceView来显示叠加信息。但是马上就会发现,此路依然不通,因为SurfaceView是没法设成透明的,叠加的信息显示出来了,但是预览画面却又会被遮盖掉。

冥思苦想了几分钟,找到了办法:用一个AbsoluteLayout来装预览SurfaceView,然后编程添加类似TextView等内容动态添加到AbsoluteLayout里面。

试验成功。下一步试验内容是获取罗盘状态和GPS坐标。

关键代码:

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
layout=new AbsoluteLayout(this);
setContentView(layout);
surfaceCamera=new SurfaceView(this);
holderCamera=surfaceCamera.getHolder();
holderCamera.addCallback(new CameraCallback());
holderCamera.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holderCamera.setKeepScreenOn(true);
layout.addView(surfaceCamera);
layout.setOnTouchListener(new View.OnTouchListener() {
int i=0;
@Override
public boolean onTouch(View v, MotionEvent event) {
try
{
TextView tv=new TextView(Main.this);
tv.setText("x");
AbsoluteLayout.LayoutParams lp=new AbsoluteLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
(int)event.getX(),(int)event.getY());
layout.addView(tv,lp);
i+=10;
}catch(Exception e)
{
Toast.makeText(Main.this, e.toString(), Toast.LENGTH_LONG).show();
}
return false;
}
});
}