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 中将数字转换为带零字符串的方法

代码与范例:

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

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

Android 中使用 ProgressDialog 显示进度对话框

代码与范例:

public class XXXX extends Activity implements Runnable {
    private static ProgressDialog waitDialog;
    private Thread thread;

    private void popupProgressDialog(){
    // 打开 ProgressDialog
    createProgressDialog();
    }
 
    private void createProgressDialog(){
        // 初始化进度对话框
        waitDialog = new ProgressDialog(this);
        // 设定消息
        waitDialog.setMessage("正在处理...");
        // 设定进度显示风格(此处为循环风格)
        waitDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        // 现实进度对话框
        waitDialog.show();
 
        thread = new Thread(this);
        /* 在显示进度对话框的同时,
         * 在其他线程中执行繁重处理
         */
        thread.start();
    }
 
    @Override
    public void run() {
        try {
            // 为确保对话框正常弹出,延迟开始处理
            //(m:任意延迟一段时间,单位毫秒)
            Thread.sleep(m);
        } catch (InterruptedException e) {
            // 捕获异常。此处省略细节
        }
        // run 方法中不支持 UI 操作
        // 因此通过 handler 来处理
        handler.sendEmptyMessage(0);
    }
 
    private Handler handler = new Handler() {
        public void handleMessage(Message msg){
            // backgroudJob() 执行实际的处理,可以是其他类中的方法
            backgroudJob();
 
            // 结束 ProgressDialog
            waitDialog.dismiss();
            waitDialog = null;
        }
    };
}

说明:通过进度对话框,可以提示用户应用当前正在执行处理,减少用户因 UI 长时间阻塞而产生的烦躁感。此外,ProgressDialog 类还支持 setSecondaryProgress(),以及继承自 Dialog 类的 setCancelable() 和 AlertDialog 类的 setButton() 等方法。

Android 中获取设备数据网络状态

代码与范例:

ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;

说明:通过 isConnected 与 isWiFi 可以判断设备当前是否建立数据通信连接,及连接类型是否为 WiFi。