4月 272015
 

  获取外部存储路径的代码为:

  这个代码开始在htc、三星等机器上工作正常,但是后来发现在小米、华为等一些机器上不能操作外部存储设置上的文件,本来以为是需要特殊权限的问题,在网上查了半天没查到解决办法。后来才发现,原来并不是机器的问题,而是在创建文件夹的时候调用的函数不对,我是使用mkdir去创建文件夹的,但是mkdir不能创建多层目录,所以就造成了后边的文件不能访问。

  不知道有mkdirs为什么还要有mkdir函数,留着迷惑人啊!

4月 192015
 
一、为什么要使用多线程?

  默认情况下,当一个Android应用启动后,应用内部运行的线程只有一个,就是应用的UI线程,负责界面的展示以后用户和屏幕元素的互动。那么当我们需要进行网络下载、图片加载、复杂运算等比较耗时的操作时,如果也要主线程中进行,UI界面就没有足够的CPU时钟进行处理了,就会造成界面卡死的情况,这就是我们所说的ANR(Application Not Responding)。所以,为了防止出现这种情况,像这些耗时的操作我就需要开一个子线程去进行处理。

二、Runnable接口

  我们在网上看线程的讲解的时候经常会看到Runnable这个接口的使用,其实Runnable只声明了一个接口函数,就是run()函数,它的作用就是给各种的线程类去继承并实现run方法。比如:FutureTask<V>、Thread、TimerTask等都实现了这个接口。我们只要记住它是一个只声明了run()函数的接口就好了。

三、使用Thread类实现多线程

  上面是一个Thread线程的例子,在这里要注意:

  1. Thread启动的线程不能直接访问UI组件,要通过组件的.post方法访问,如上面的textView.post(new Runnable() {})
  2. 要使用start()方法启动线程,不可以使用run()方法
四、使用AsyncTask实现多线程

上面是一个AsyncTask的例子,

  • onPreExecute:doInBackground运行之前的函数,可以操作UI的组件
  • doInBackground: 耗时的操作(网络下载、图像处理等)放到这个函数里
  • onPostExecute:doInBackground运行之后的函数,可以操作UI的组件
  • onProgressUpdate:用于在UI显示运算进度,需要在doInBackground里调用publishProgress

AsyncTask的优点:

  1. 结构清晰,易于理解
  2. 可显示进度
  3. 可方便操作UI组件

AsyncTask的缺点:

  1. 不能多个实例同时运行,每次要创建一个新的实例,比如myTask.execute(“OK”);就会抛异常

五、HandlerThread

  HandlerThread适用于在一个线程中执行多个实时性不高的操作.具体参考下面的链接

  HandlerThread的多个任务只能串行执行,不能同时执行多个任务

  对HandlerThread的理解

六、ExectuorService线程池
七、IntentService

  IntentService实际是继承自Service的一个类,用于方便在一个Service创建一个执行比较耗时的任务

  IntentService里面只有一个工作线程,也只能同时执行一个任务

  Service的系统权限比较高,不容易被系统杀死

八、FutureTask、TimerTask继承自Runnable接口,并不是实现多线程的类,只是实现多线程的辅助类,如new Thread(futureTask).start();这样的。他们与直接使用Runnable接口的差别如下:

  1. FutureTask可以取消,可以返回结果
  2. TimerTask可以定时执行

 

4月 172015
 

一般Android的默认图片编辑器是支持对图片进行剪裁操作的,所以如果我们的应用需要对图片进行剪裁操作,调用系统的图片编辑器即可,代码如下:

如果输出到文件(设置MediaStore.EXTRA_OUTPUT的值):

如果在Activity中处理返回的图片流(return-data的值为true):

 

 

2月 262015
 

  Home键:默认情况下,android中的home键只是切换到系统的home界面,并不会销毁当前的activity,即不会执行activity中的ondestroy方法,从任务栈中恢复那个应用的话,activity中的数据不会丢失

  Back键:而android中的back键,则先销毁当前activity,然后回到之前的应用界面中,而且此时activity中的数据都丢失了。

  如果我们想在按back键的时候,不销毁当前的activity,保留它的数据和状态的话,我们可以改写源码中对back键的处理来达到这个目的,代码如下:

  当然这样做的缺点时,之前打开的应用全部都被关闭掉了,比如用户在用浏览器看新闻,通过通知栏消息打开了你的应用,在你的应用中按back键就回到了home界面,前面的浏览器也被关掉了。

12月 252014
 

  SharedPreferences貌似很简单的一个类,却也存在着陷阱,如下面,我们写了几行简单的保存配置的代码,但却隐含着bug

  那么为什么这段代码不能保存runcount的数据呢,因为prefs.edit()每次返回一个新的Editor对象,所以执行两次prefs.edit()其实调用的是两个Editor对象,如下adnroid源码中的解释

  因此,我们应该先将prefs.edit()赋值给一个Editor对象,然后由该对象进行存值和提交操作,如下:

 

12月 212014
 

  出现此问题的原因是你的项目使用到的android的appcompat支持库,但你目前的开发环境中并没有引用此支持库。下面图文介绍如何下载并引用该类库。

  1、首先需要在android sdk manager中下载Android Support Library,在Eclipse中打开菜单Window->Android SDK Manager,在sdk manager窗口中看你是否已经下载了Android Support Library,在最下面的Extras的节点中有此项,如果没有则选中此项并下载,如下图

Download Android Support Library

  类库将会下载到你的sdk子目录中,路径为<sdk目录>extrasandroidsupport。如果你使用的Android Studio开发环境,则需要下载Android Support Respository。

  2、将类库项目导入到开发环境中。在Eclipse菜单中打开File->New->Project,选择Android Project From Existing Code

Create Project From Existing Code

  3、点击”Browser…”按钮选择appcompat所在的路径,<sdk目录>extrasandroidsupportv7appcompat,勾选Copy projects into workspace,最后点击“Finish”按钮,导入项目成功。

Import android v7 appcompat

  4、将appcompat库引用到你的项目中。在你的项目上右键选择属性菜单,在左侧的导航列表中选择“Android”,在右侧内容最下的Library区域点击“Add”按钮,将导入的类库项目添加进来就完成了。

Add android v7 appcompat reference

  5、重新Clean你的项目。点击Eclipse的菜单Project->Clean…,在弹出的窗口中选择你要清除的项目,点击ok,清除结束后上述问题应该就可以解决了。

  关于Android支持库的其他特性可以点击这里查询。

 

12月 012014
 

  在有些APP中我们需要实现一个悬浮按钮,比如图片浏览应用左右翻页功能,比如左侧悬浮功能按钮。我们要实现此功能时,最开始想到的就是用FrameLayout来实现,但是如果把按钮简单的放到FrameLayout中,按钮只能在布局的左上角,并不能达到我们的要求。

  那么如何才能让按钮放到布局的左侧中间位置呢,开始时想把按钮高度设置成match_parent,然后图片垂直居中,但是这样整个最左侧的部分都是按钮了,显然会影响其他功能的使用,后来想哪个组件才能自由的让子控件放到想放的位置呢,这个得是RelativeLayout了吧。于是在FrameLayout中放了一个RelavtiveLayout中,然后把按钮放到里面,Layout Parameters中的Align Parent Left设置成true,Center Vertical设置成true,再看一下,一个居中的悬浮按钮就弄好了。

  同样的方式可以设置其他需要的悬浮按钮了。

 

 Posted by on 2014-12-01
11月 142014
 

  做了一个小的app,其中用到了tab形式的actionBar和多个fragment,后来因为需求需要在某两个fragment中加入context menu,加入后添加onContextItemSelected事件响应菜单按下的效果,一开始运行正常,在两个fragment中打开context menu正常,两个fragment中的菜单响应事件也正常,后来改动了一些代码后突然发现菜单响应的不正确了,只有一个fragment中的菜单有响应,另外一个没响应。经过一番修改后,发现是onContextItemSelected的返回值影响到了菜单点击事件的执行。

  我们先来看一下onContextItemSelected的官方描述:

  我们主要看这个返回值部分,返回为了一布尔型变量,当返回false值时由系统去决定此菜单事件的响应流程,返回true则表示菜单响应到此为止,停止后面的流程。

  本人没做深入研究,猜想大概流程是这样的,因为在fragment中调用context menu中通过如下方式

  是通过父activity来填充这个弹出菜单,所以首先是父activity先接收到onContextItemSelected事件,如果父activity中不做处理,则activity会把这个事件分发到各个fragment中去,本人在此次犯的错误就是在子fragment中的onContextItemSelected事件中返回了true值,所以只在一个fragment中响应了事件,另外一个没有响应。

  既然在所有的fragment中都会响应onContextItemSelected这个事件,那么我们在每个fragment中处理该事件的时候就需要做一些判断,以避免多个fragment同时执行这个事件,这个是非常重要的,因为不做判断很可能出现一些莫名奇妙的而且不好测试的问题。如下例子:

 

11月 012014
 

  ContextMenu相当于windows中的右键弹出菜单,在android中一般要长按某个view才会激活这个弹出菜单,但是有些情况下我们想单击就可以打开这个菜单,以提高用户的使用体验。那么如何才能单击就能打开这个菜单呢,其实很简单,我们在view的onClick事件中调用openContextMenu()即可打开这个弹出菜单了。下面是一个代码片断:

  imgcapture是一个ImageView,此代码执行后,单击imgcapture即可触发onCreateContextMenu事件,然后弹出菜单。另外,必须要调用registerForContextMenu注册这个菜单,否则单击和长按都无法弹出这个菜单。

  如果要弹出比较复杂的菜单的话就用使用Dialog来实现了。

10月 292014
 

  对于获取数据库中的记录数量的方法,其实应该是很简单的问题,之所以发文把此方法写出来,是因为有一些需要地方需要注意,避免其他人再在此问题上浪费时间。

方法一:

  这是一个很普通的方法,通过sql语句获取数量并赋给一个sql别名字段,然后通过这个别名来获取记录的数量,但是需要注意的是必须要有cursor.moveToFirst(),这个函数,因为rawQuery执行完成后返回的Cursor不是定位到第一条记录的,而是定位到第一条记录之前,所以必须要moveToFirst让记录定位一下才能读取数据,同样地获取其他的数据的时候也要在其之前调用此函数。

  如果是读取多条记录的时候我们一般会用

方法二:

  此方法需要注意的是,sql语句select的时候最好不要select *这样会把所有字段的数据都读出来,浪费内存,效率低。