Android ViewGroup 中的 ClipChildren 方法

ClipChildren属性对需要做动画的View非常有用,特别是对ScaleAnimation。 需要注意的是ClipChildren = false只是告诉他的子View可以超出他本身的大小,并不是说他自己可以超出他的父View的大小,所以如果你想允许某个View超过其父View的边界,你需要一直找到最顶层的父View并设置ClipChildren = false

下面需要提一下的是一个关于ClipChildren的坑,在4.2的系统上(4.3未测试,4.4及5.0版本没有这个 BUG),在启用了hardware accelerated的情况下,ScaleAnimation会出现撕裂等不流畅的现象,处理方法如下:

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
	parentView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

这里的parentView一定要是你启用了ClipChildren = false的那个ViewGroup,否则没有效果,最佳实践是只在需要做ScaleAnimation的时候才关闭硬件加速,做完动画后应该重新开启。

关于这个BUG的更多信息请移步:这里 关于更多Android Hardware Accelerated信息请阅读:这里

Android 电量监控

获取初始电量

Android系统发送的电量广播是一个sticky broadcast,所以可以通过给registerReceiver传递一个null参数来获取上次系统发送的电量广播。

private int getLastBatteryLevel() {
        Intent batteryIntent = registerReceiver(null, new IntentFilter(
            Intent.ACTION_BATTERY_CHANGED));
            return batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
    }

监控电量

mBatteryReceiver = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, Intent intent) {
                final int level = intent.getIntExtra(
                        BatteryManager.EXTRA_LEVEL, -1);

                Runnable updateBatteryLevel = new Runnable() {

                    @Override
                    public void run() {
                        setBatteryLevel(level);
                    }
                };
                mHandle.post(updateBatteryLevel);
            }
        };

        registerReceiver(mBatteryReceiver, new IntentFilter(
                Intent.ACTION_BATTERY_CHANGED));

Android 时间显示

private void initUpdateTimeThread() {
        Calendar calendar = Calendar.getInstance();
        mHour = calendar.get(Calendar.HOUR_OF_DAY);
        mMinute = calendar.get(Calendar.MINUTE);
        setTimeString(mHour, mMinute);

        mUpdateTimeThread = new Runnable() {

            @Override
            public void run() {
                if (++mMinute >= 60) {
                    mMinute = 0;
                    ++mHour;
                }

                if (mHour >= 24) {
                    mHour = 0;
                }
                setTimeString(mHour, mMinute);
                mHandle.postDelayed(this, 60000);
            }
        };
    }

    private void setTimeString(int hour, int minute) {
        if (null != mMirrorLayout) {
            myView.setTimeString(String
                    .format("%02d:%02d", hour, minute));
        }
    }

Android 获取 Raw 文件夹中资源地址

Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.adv);

使用 ProcessBuilder 处理通配符

喝着可乐听着歌;敲着代码哼着曲。没有一点点防备,就这样掉进了坑里。下面的代码是用通过ProcessBuilder调用命令来删除以system_开头的文件。 因为之前删除具体文件的时候这个方法非常好用,于是就想当然了。

ArrayList<String> cmd = new ArrayList<String>();

        cmd.add("rm");

        cmd.add("-vrf");

        cmd.add("/tmp/system_*");

        ProcessBuilder pb = new ProcessBuilder(cmd);

        pb.redirectErrorStream(true);

        try {

            Process process = pb.start();

            InputStream is = process.getInputStream();

            BufferedReader read = new BufferedReader(new InputStreamReader(is));

            String line;

            while ((line = read.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

正确的调用是这样的,通配符需要传给bash才能正确处理。

ArrayList<String> cmd = new ArrayList<String>();

        cmd.add("sh");

        cmd.add("-c");

        cmd.add("rm -vrf /tmp/system_*");

        ProcessBuilder pb = new ProcessBuilder(cmd);

        pb.redirectErrorStream(true);

        try {

            Process process = pb.start();

            InputStream is = process.getInputStream();

            BufferedReader read = new BufferedReader(new InputStreamReader(is));

            String line;

            while ((line = read.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }