在 Ubuntu 中安装 Jenkins

Jenkins 是目前流行的 CI 平台,终于得闲,便想到试用看看,略作笔记。

官方文档: https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins+on+Ubuntu

Jenkins 依赖于 JDK 和 JRE,可以通过以下代码安装。

$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java7-installer

安装完成后,继续安装 Jenkins 本身。

wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins

Jenkins 的默认端口为 8080,在安装完成后即可使用。

 

Android 中设置 EditText 的输入类型

在 Android 中,EditText 可支持句首字母大写、词首字母大写、纯数字等数十种专门优化的输入类型。有两种方式指定输入类型。

方式一,在布局资源文件中为 EditText 控件添加属性,如:

<EditText
    android:id="@+id/etSample"
    android:inputType="textCapSentences" />

其中,textCapWords 表示在可能的情况下,为每个句子的首字母大写。
textCapSentences 属性的值为 0x00004001 ,textCapWords 则为 0x00002001,它们以掩码的形式制定,这些属性可通过 | 运算符组合。

方式二,在代码中动态添加或更改输入类型,如:

// 在可能的情况下,为每个单词的首字母大写
etSample.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS); 

更多可选的输入类型请参见官方文档:
http://developer.android.com/intl/ja/reference/android/widget/TextView.html#attr_android%3ainputType

Android 中在代码运行时为 Activity 动态设置主题

代码与范例:

setTheme(android.R.style.Theme_Dialog);

说明:为使该方法生效,需要在 setContentView(…) 与 super.oncreate() 之前调用。系统内置主题可以通过 android.R.Style 查找。部分主题对 Android API level 有要求,例如,material 系主题要求 API level 至少为19。

Android 中创建弹出式菜单 PopupMenu

代码与范例:

final View menuButton = findViewById(R.id.menuButton);
PopupMenu popup = new PopupMenu(MainActivity.this, menuButton);
popup.getMenuInflater().inflate(R.menu.menus, popup.getMenu());
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
    @Override
    public boolean onMenuItemClick(MenuItem item) {
        Toast.makeText(MainActivity.this,
            "menu clicked", Toast.LENGTH_SHORT).show();
        return true;
    }
});
popup.show();

说明:PopupMenu 是 Android API Level11 引入的新特性,可以在屏幕上渲染一个弹出式的菜单窗口,用于显示一些常用的操作。

Android 中为 EditText 提供平滑滚动功能

代码与范例:

ScrollView sv = (ScrollView) layout.findViewById(R.id.ScrollViewContainingAnEditText);
sv.setSmoothScrollingEnabled(true);

说明:Android 中的 EditText 是一个相当复杂的控件,与 TextView 不同,它并不支持平滑滚动及相关的视觉反馈,不过,我们可以将其置于某个 ScrollView 内,以实现类似的效果。需要注意的是,此时由于尺寸可变, EditText 的高度和宽度即使设为 fill_parent 也不会起效,可通过 ScrollView 添加 android:fillViewport=”true” 属性解决。

Android 中 java.lang.IllegalArgumentException: pointerIndex out of range 异常的处理方法

在记事帐的应用后台收到了一条罕见的崩溃报告:

java.lang.IllegalArgumentException: pointerIndex out of range
at android.view.MotionEvent.nativeGetAxisValue(Native Method)
at android.view.MotionEvent.getX(MotionEvent.java:2189)
at android.support.v4.view.MotionEventCompatEclair.getX(MotionEventCompatEclair.java:32)
at android.support.v4.view.MotionEventCompat$EclairMotionEventVersionImpl.getX(MotionEventCompat.java:86)
at android.support.v4.view.MotionEventCompat.getX(MotionEventCompat.java:184)
at android.support.v4.view.ViewPager.onTouchEvent(ViewPager.java:1462)
at android.view.View.dispatchTouchEvent(View.java:8149)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2416)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2140)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2422)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2155)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2295)
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1622)
at android.app.Activity.dispatchTouchEvent(Activity.java:2565)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2243)
at android.view.View.dispatchPointerEvent(View.java:8357)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4778)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4644)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4202)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4256)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4225)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4336)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4233)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4393)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4202)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4256)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4225)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4233)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4202)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6569)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6486)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6457)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6422)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6649)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:138)
at android.os.Looper.loop(Looper.java:131)
at android.app.ActivityThread.main(ActivityThread.java:5602)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
at dalvik.system.NativeStart.main(Native Method)

经调查,发现这是 Android 的 ViewPager 的一个 bug,偶尔会在用户执行多点触控操作时发生。直到至少 Android 4.4,该问题依然存在。
解决方案也很简单,覆写 ViewPager 的 onTouchEvent 与 onInterceptTouchEvent 方法,catch 该异常即可,如下所示:

@Override
    public boolean onTouchEvent(MotionEvent me) {
        try {
            return super.onTouchEvent(me);
        } catch (IllegalArgumentException ex) {
            ex.printStackTrace();
        }
        return false;
    }

Android 中基于 PendingIntent 与 AlarmManager 的定时广播

代码与范例:

Calendar cal = new GregorianCalendar();
cal.set(2014, 6, 19, 9, 23)  // 2014年6月19日9点23分
Intent intent = new Intent(MainActivity.this, AlarmReceiver.class); // AlarmReceiver 需要继承 BroadcastReceiver,于是它将在接受到广播后执行 onReceive() 方法
intent.putExtra("intentID", 0);  // 亦可以向 Intent 中添加数据
PendingIntent pintent = PendingIntent.getBroadcast(MainActivity.this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarm.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pintent); // 将在先前指定时刻发出广播

说明:上述代码将创建并注册一个定时发出的广播,广播接收器 AlarmReceiver 将接收该广播。广播接收器需要事先在 Manifest 文件中注册,如下所示。

此外,如果要为 AlarmManager 设置间隔,需要注意的是,参数(单位为毫秒)大小有一定的限制,且 AlarmManager 无法在系统关机后保留。

Android 中为 TextView 的文本添加下划线

代码与范例:

// 方案一
String udata="Underlined Text";
SpannableString content = new SpannableString(udata);
content.setSpan(new UnderlineSpan(), 0, udata.length(), 0);
mTextView.setText(content);

// 方案二
mTextView.setPaintFlags(mTextView.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
mTextView.setText("This text will be underlined");

// 方案三
String htmlString="This text will be underlined";
mTextView.setText(Html.fromHtml(htmlString));

说明:我们可以通过 SpannableString、TextView 的 setPaintFlags(),或者 Html.fromHtml() 这三种方式为 TextView 的文本添加下划线。继承于 TextView 的控件也支持这些操作。

Java 中线程的停止与启动

代码与范例:

public class TestThread extends Thread {
    private volatile boolean halt_;

    public TestThread() {
        halt_ = false;
    }

    public void run() {
        while (!halt_) {
            // TODO 
        }
    }

    public void halt() {
        halt_ = true;
        interrupt();
    }
}

说明:出于线程安全的考虑,Java 禁止使用 stop() 来结束线程。用于启动线程的 start() 只能执行一次,在执行 run() 之后即使再次调用 start(),run() 也不会被调用,必须重新调用 new()。而且根据虚拟机的具体实现,start() 与 run() 之间也可能有一定的延迟。

参考资料

Java 中将数字转换为带零字符串的方法

代码与范例:

String d = String.format("%04d", 12);             // 结果为 "0012"
String o = String.format("%05o", 12);             // 结果为 "00014"
String x = String.format("%06x", 12 && 0xFFFFFF); // 结果为 "00000C"

说明:JDK1.5 以后开始支持该方法。可以将数字转换为指定格式的字符串,并在数字前补零至足够位数。其中,百分号后为字符串长度。