标签 android 下的文章

Android 开发中,用得最常见的 interface 之一便是 OnClickListener 了。不使用 android:onClick 属性的话,自然就不得不去 implement OnClickListener

假设我们不使用任何框架或是架构模式,只是写一个简单的 Activity;在一个 Activity 中有多个可点击的按钮时,我的同事会这么写:

public class ExampleActivity extends Activity implements OnClickListener {

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);

        findViewById(R.id.first_button).setOnClickListener(this);
        findViewById(R.id.second_button).setOnClickListener(this);
    }

    @Override
    public void onClick(final View v) {
        switch (v.getId()) {
            case R.id.first_button:
                // bla bla bla
                break;
            case R.id.second_button:
                // bra bra bra
        }
    }
    
}

事实上,Android 官方有些 sample 里面也是这么写的。然而在我看来,这么写代码是非常不优雅的,因为一个 OnClickListener 的实现,只应该关注和点击事件本身相关的内容,它的含义和 Activity 的含义是截然无关的,让同一个类继承/实现他们,会使得这个类的含义变得不清晰。同时,这样还给 ExampleActivity 类增加了一个 public 的方法,削弱了这个类的封闭性。

所以如果像下面这样,会好不少:

public class ExampleActivity extends Activity {

    private OnClickListener onClickListener = new OnClickListener() {
        @Override
        public void onClick(final View v) {
            switch (v.getId()) {
                case R.id.first_button:
                    // bla bla bla
                    break;
                case R.id.second_button:
                    // bra bra bra
            }
        }
    };

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);

        findViewById(R.id.first_button).setOnClickListener(onClickListener);
        findViewById(R.id.second_button).setOnClickListener(onClickListener);
    }
    
}

这样写体现了 composition over inheritance 的思想。它避免了上面的所有问题,看起来舒服得多。

不过,这样还是让阅读代码时很不方便——看到 onCreate 里面时,还不得不经常滚动到声明 onClickListener 的地方去,并且在 onClick 中艰难地寻找真正和某个特定按钮相关的代码。当然这两个问题之前那个版本也都无法避免。

另一件糟糕的事情是,不同按钮的 listener 逻辑很可能是相对独立的,放到同一个 onClickListener 里,还是很丑陋。

所以为了进一步避免这几个问题,我一向都是用下面这样的写法:

public class ExampleActivity extends Activity {

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);

        findViewById(R.id.first_button).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(final View v) {
                // bla bla bla
            }
        });
        findViewById(R.id.second_button).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(final View v) {
                // bra bra bra
            }
        });
    }
    
}

这样的话,不同逻辑间相对独立,看起来非常舒服,便于阅读,并且让我找回了写 JavaScript 的舒畅感觉(划掉)。

那么问题来了:为什么有的人不使用最后一种写法呢?

此问题已被提交至 https://segmentfault.com/q/1010000005337426

Recently, I made a decision to disallow programmers to use android:onClick XML attributes to add click event listeners. Existing usages will be removed too. Actually, the attribute itself was a very bad design, considering that web frontend developers has switched from onclick HTML attribute to addEventListener API for a long time.

Here are the real reasons why android:onClick XML attribute should be avoided:

  • If an ancestor element has theme applied, android:onClick no longer works, because the program will look for the click handler from ContextThemeWrapper instead of Activity (see http://stackoverflow.com/questions/29525644/). When applying a theme, programmers may not realize the problem.
  • Android look for the specified method from the parent Activity. When programmers change an Activity into a Fragment, they may be ignoring this problem.
  • To make the two problems above more severe, onClick uses reflection. It therefore prevents the compiler from telling you problems. You app will crash at run time.
  • As it uses reflection, performance will be compromised.
  • Android provides android:onClick attribute, but no android:onChange or any other XML attributes for event listeners. Considering click listeners aren't really different from other event listeners, keeping them consistent will be good.
  • When using android:onClick, one must create a public method with parameter (View view). Android Lint will then complain "unused parameter".
  • Adding a public method is not elegant in any way.

用 Evernote 有三四年了。最早是因为 Google Notebook 关停才不得已转到 Evernote 的。

Evernote 免费版有一些限制,比如同步流量限制、Android 客户端不能离线、Android 客户端不能设置密码;要 Premium 才能使用;而 Premium 又太贵,于是去年我也用了一阵子的有道云笔记,但是后来由于用户体验、隐私等等原因,用了一个月也就没再用了,还是坚守 Evernote。

但是 Evernote 有几大问题,是即使购买 Premium 也无法解决的:一是没有 Linux 客户端,二是出现 conflict 时很傻逼,三是组织层级还是不够。

同时解决这三个问题的方法倒也简单:直接全部笔记用 Markdown 一类的文本写,然后 GitHub 上面弄个 private repo 来放。

缺点在于放短小的笔记即时同步不那么方便,但是那种情况下我用 Google Keep 就好了。

打算正式抛弃 Evernote。