rime输入法

在认识 rime 之前一直都在使用 ibus-pinyin 作为我的 linux 系统的输入法。而在 window 系统中用的是 某狗。 但是 某狗 好像变得越来越讨厌了。一会而弹出该换皮肤了,一会儿又弹出该换桌面背景了,最可恨的是还弹出什么某狗浏览器的广告。又在公司大战中发什么声明。只能说一句贵圈真乱。要不看在其输入法还比较好用的份上,早就把它删除了。 直到我找到了 rime ,这真是个神奇的输入法。支持 mac linux 和 windows 。不需要配置也可以用得很好,速度很快,准确率也很高。而且开源,没广告,可定制,简直是 geek 的理想输入法。我也忍不住为其背书。 若要下载,请点击前往 https://code.google.com/p/rimeime/ ###tips 在 linux 中的 gvim 调出 ibus 可能会出现 ibus 重新关闭导致输入法打不开的情况。这时可以点击 iBus 的 preferences ,把 Embed preedit text in application window 的勾去掉。

December 9, 2013

Android MVC Part7 State Pattern

这部分的内容是之前答应过之后再说的状态模式,这个模式在 TapController 用到。与 MVC 和 DAOs 不同的是,我不会把状态模式归为 android 程序架构必须实现的部分。然而,它的确非常有用。因为我在 TapController 中用它来处理消息,所以还是有必要说一下。 ##状态模式 在之前的系列中我没有提到它是怎样在 android 里工作的。但是在这部分,我们将会更深入一些来探讨它。状态模式是一个面向对象的模式,目的是可以动态切换对象的行为。还记得对象具有属性和行为吗?你会觉得这听起来像是策略模式?对,你说得没错。这里不同的地方就是模式的意图。状态模式是根据属性的不同而改变其行为的。客户端甚至不知道调用的对象已经发生转换。那么是怎么做到动态切换而接口不变的?我们需要把行为封装到对象内部,然后让他妈共享一套接口。接下来我们聊聊具体的例子,这个例子就是 Tap Counter 工程了。 我们要看的连个对象叫做 UnlockedState 和 LockedState。当 CounterVo 设置为被锁,这意味着我们不希望属性改变。例如,如果用户点击了增加按钮,如果状态是锁住得话,值将不会增加。但是如果不是锁住的状态,用户点击将会使值增加。这是基于 CounterVo.locked 的两种不同行为。 当 TapController.handleMessage 被触发,它就会把消息代理给其中一种状态。这里使用了 Controller.messageState 来确定用哪一种转台。让我们看看代码吧。 :::java @Override public boolean handleMessage(int what) { return messageState.handleMessage(what); } 这的确非常简单,但是 messageState 的状态是如何改变的呢? 两个状态之间是彼此知道对方的。当 UnLockedState 接到 MESSAGE_UPDATE_LOCK 这个消息时,它就会把 messageState 的引用改为 LockedState 。然后当 MESSAGE_UPDATE_LOCK 消息被 LockedState 处理时它会更细 messageState 的引用到 UnlockedState。我觉得我们看看代码会更好理解一些。 :::java package com.musselwhizzle.tapcounter.controllers; public class UnlockedState extends TapState { // ... properties public UnlockedState(TapController controller) { super(controller); } @Override public boolean handleMessage(int what) { switch(what) { case TapController.MESSAGE_INCREMENT_COUNT: moveCount(1); return true; case TapController.MESSAGE_DECREMENT_COUNT: moveCount(-1); return true; case TapController.MESSAGE_RESET_COUNT: model.setCount(0); return true; default: return super.handleMessage(what); } } @Override public boolean handleMessage(int what, Object data) { switch(what) { case TapController.MESSAGE_UPDATE_LOCK: updateLock((Boolean)data); return true; case TapController.MESSAGE_UPDATE_LABEL: updateLabel((String)data); return true; case TapController.MESSAGE_KEY_EVENT: return handleKeyEvent((KeyEvent)data); default: return super.handleMessage(what, data); } } private boolean handleKeyEvent(KeyEvent event) { // .. handles the key event } private void moveCount(int amount) { model.setCount(model.getCount()+amount); } private void updateLock(boolean lock) { model.setLocked(lock); controller.setMessageState(new LockedState(controller)); } private void updateLabel(String label) { model.setLabel(label); } } UnLockedState 通知控制器去改变状态为 LockedState 的代码如下。 ...

November 16, 2013

Android MVC Part6 Persistence

最难的 MVC 已经讲解过了。如果你可以在 android 上实现 MVC, 这系列剩下的部分将会很容易。奖赏一下自己吧,接下来我们要讨论一下数据持久化。 我会默认你已经知道 sqlite 和其他 android 中持久化的方法。如果不知道,现在是时候去补补了。我会建议你了解 Data Access Objects 或者叫 DAOs 的概念。这个概念非常简单:我们传递一个模型的实例给 DAOs ,由它来保存,更新或者删除数据。再也不用编写一大堆 sql 语句了。妈妈再也不用担心我调用数据库了。废话不说,看看代码: :::java public class CounterDao { protected static final String TABLE = "Counter"; protected static final String _ID = "_id"; protected static final String LABEL = "label"; protected static final String COUNT = "count"; protected static final String LOCKED = "locked"; public CounterDao() { // } public ArrayList<CounterVo> getAll() { // ... truncated.... returns all CounterVos } public CounterVo get(int id) { SQLiteDatabase db = new DatabaseHelper().getWritableDatabase(); Cursor cursor = db.query(TABLE, null, _ID+"=?", new String[] {Integer.toString(id)}, null, null, null); CounterVo vo = null; if (cursor.moveToFirst()) { vo = new CounterVo(); vo.setId(cursor.getInt(cursor.getColumnIndex(_ID))); vo.setLabel(cursor.getString(cursor.getColumnIndex(LABEL))); vo.setCount(cursor.getInt(cursor.getColumnIndex(COUNT))); vo.setLocked(cursor.getInt(cursor.getColumnIndex(LOCKED)) == 1); } cursor.close(); db.close(); return vo; } public long insert(CounterVo counterVo) { SQLiteDatabase db = new DatabaseHelper().getWritableDatabase(); ContentValues values = new ContentValues(); if (counterVo.getId() > 0) values.put(_ID, counterVo.getId()); values.put(LABEL, counterVo.getLabel()); values.put(COUNT, counterVo.getCount()); values.put(LOCKED, counterVo.isLocked()); long num = db.insert(TABLE, null, values); db.close(); return num; } public int update(CounterVo counterVo) { // ... truncated.... } public void delete(int id) { SQLiteDatabase db = new DatabaseHelper().getWritableDatabase(); db.delete(TABLE, _ID+"=?", new String[]{Integer.toString(id)}); db.close(); } } CounterDao 就是用来持久化 CounterVo 的。里面的持久化实现可以使用 sqlite, json, xml 或者 shared preferences ,也就是外部不用知道内部是如何实现持久化的,调用接口就可以了。 ...

November 15, 2013

Android MVC Part4 The Controller

在 MVC 的三个部分中,控制器是最繁忙的。回顾一下,模型只是一个比较优雅的键值对象。一个好的视图是一个呆子,它只是当模型改变的时候更新自己和控制器告诉他什么就做什么。而控制器主要做三件事: 更新模型 处理视图传过来的信息 发信息给试图 这里我们主要关注后面两件事情。 ##处理消息 在所有部分当中,控制器获取消息的实现是最随意的,主要决定权在你手中,它不需要多态。你不需要跟着教程来做。而下面只是我的实现方式。但要记住 MVC 只是一个框架,不需要太注重细节,你应该关注你想要实现什么。下面是 TapListController 的示例代码。 为什么选取 TapListController 而不是 TapController ?坑爹吗? 其实主要是因为 TapController 实现了 状态模式来代理消息。所以选择 TapListController 这个更简单的类来说,以避免越说越乱。 :::java public class TapListController extends Controller{ public static final int MESSAGE_GET_COUNTERS = 1; public static final int MESSAGE_MODEL_UPDATED = 2; public static final int MESSAGE_DELETE_COUNTER = 3; public static final int MESSAGE_INCREMENT_COUNTER = 4; public static final int MESSAGE_DECREMENT_COUNTER = 5; private ArrayList<CounterVo> model; public ArrayList<CounterVo> getModel(){ return model; } public TapListController(ArrayList<CounterVo> model){ this.model = model; } @Override boolean handleMessage(int what, Object data) { switch(what){ case MESSAGE_GET_COUNTERS: getCounters(); return true; case MESSAGE_DELETE_COUNTER: deleteCounter((Integer)data); getCounters(); return true; case MESSAGE_INCREMENT_COUNTER: changeCount(1, (CounterVo)data); getCounters(); return true; case MESSAGE_DECREMENT_COUNTER: changeCount(-1, (CounterVo)data); getCounters(); return true; } return false; } } 记得视图是通过 handleMessage 来传递消息给控制器的。这个是我们消息系统的入口。很简单是吗?这个函数的第一个参数是动作,是一个常量,第二个参数是数据,用来传递额外的信息给控制器的。当控制器接受了信息后将返回 true 。 ...

November 14, 2013

Android MVC Part5 Putting It Together

之前我们已经讨论了模型(Models),视图(Views)和控制器(Controllers)。现在我们需要把他们组合在一次成为真正符合 MVC 规范。 Activity 是我们程序的每一个新视图的入口, 我们需要在 Activity 的 onCreate 方法实例化模型和控制器。其实一个典型的 MVC 模型视图是不会去实例化模型和控制器的,但是..android 有点特殊。模型一旦创建了,我们需要把 activity 注册到模型中。再者,如果你想从控制器获取消息,你也需要向控制器注册。让我们看看他们长什么模样的。 ##Activity: :::java @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); counter = new CounterVo(); counter.addListener(this); controller = new TapController(counter); controller.addOutboxHandler(new Handler(this)); // ... other set up code like referencing widgets/views ... } 这里视图在模型和控制器上都注册了。让我们看看模型对于视图的回调函数。 :::java @Override public void onChange(CounterVo counter) { mHandler.sendEmptyMessage(UPDATE_VIEW); } 一旦模型通知视图有改变,我们就在 UI 线程修改 UI控件。这就是你数据的绑定。现在让我们来看看处理从控制器发来的信息。 :::java @Override public boolean handleMessage(Message msg) { switch(msg.what) { case TapListController.MESSAGE_MODEL_UPDATED: runOnUiThread(new Runnable() { @Override public void run() { adapter.notifyDataSetChanged(); } }); return true; } return false; } 就跟之前讨论的那样,这个实现很像控制器处理消息的实现。这里视图选择性地接收 MESSAGE_MODEL_UPATED 消息,然后通知列表适配器去更新显示。如果有其他非 MESSAGE_MODEL_UPDATED 的消息传进来,它会忽略掉的。也就是说它不是处理所有的信息。你可以增加或减少视图可接收的信息。 ...

November 14, 2013