一、主要文件和类
1.Launcher.java:launcher中主要的activity。
2.DragLayer.java:launcher layout的rootview。DragLayer实际上也是一个抽象的界面,用来处理拖动和对事件进行初步处理然后按情况分发下去,角色是一个controller。它首先用onInterceptTouchEvent(MotionEvent)来拦截所有的touch事件,如果是长按item拖动的话不把事件传下去,直接交由onTouchEvent()处理,这样就可以实现item的移动了,如果不是拖动item的话就把事件传到目标view,交有目标view的事件处理函数做相应处理。如过有要对事件的特殊需求的话可以修改onInterceptTouchEvent(MotionEvent)来实现所需要的功能。
3. DragController.java:为Drag定义的一个接口。包含一个接口,两个方法和两个静态常量。接口为DragListener(包含onDragStart(),onDragEnd()两个函数),onDragStart()是在刚开始拖动的时候被调用,onDragEnd()是在拖动完成时被调用。在launcher中典型的应用是DeleteZone,在长按拖动item时调用onDragStart()显示,在拖动结束的时候onDragEnd()隐藏。两个函数包括startDrag()和setDragItemInfo().startDrag()用于在拖动是传递要拖动的item的信息以及拖动的方式,setDragItemInfo()用于传递item的参数信息(包括位置以及大小)。两个常量为DRAG_ACTION_MOVE,DRAG_ACTION_COPY来标识拖动的方式,DRAG_ACTION_MOVE为移动,表示在拖动的时候需要删除原来的item,DRAG_ACTION_COPY为复制型的拖动,表示保留被拖动的item。
4.LauncherModel.java:辅助的文件。里面有许多封装的对数据库的操作。包含几个线程,其中最主要的是ApplicationsLoader和DesktopItemsLoader。ApplicationsLoader在加载所有应用程序时使用,DesktopItemsLoader在加载workspace的时候使用。其他的函数就是对数据库的封装,比如在删除,替换,添加程序的时候做更新数据库和UI的工作。
5.Workspace.java:抽象的桌面。由N个celllaout组成,从cellLayout更高一级的层面上对事件的处理。
6.LauncherProvider.java:launcher的数据库,里面存储了桌面的item的信息。在创建数据库的时候会loadFavorites(db)方法,loadFavorites()会解析xml目录下的default_workspace.xml文件,把其中的内容读出来写到数据库中,这样就做到了桌面的预制。
7.CellLayout.java:组成workspace的view,继承自viewgroup,既是一个dragSource,又是一个dropTarget,可以将它里面的item拖出去,也可以容纳拖动过来的item。在workspace_screen里面定了一些它的view参数。
8.ItemInfo.java:对item的抽象,所有类型item的父类,item包含的属性有id(标识item的id),cellX(在横向位置上的位置,从0开始),cellY(在纵向位置上的位置,从0开始) ,spanX(在横向位置上所占的单位格),spanY(在纵向位置上所占的单位格),screen(在workspace的第几屏,从0开始),itemType(item的类型,有widget,search,application等),container(item所在的)。
9.UserFolder.java: 用户创建的文件夹。可以将item拖进文件夹,单击时打开文件夹,长按文件夹上面标题处可以重命名文件夹。
10.LiveFolder.java:系统自带的文件夹。从系统中创建出的如联系人的文件夹等。
11.DeleteZone:删除框。在平时是出于隐藏状态,在将item长按拖动的时候会显示出来,如果将item拖动到删除框位置时会删除item。DeleteZone实现了DropTarget和DragListener两个接口。
12.LauncherSettings.java:字符串的定义。数据库项的字符串定义,另外在这里定义了container的类型,还有itemType的定义,除此还有一些特殊的widget(如search,clock的定义等)的类型定义。
补充Launcher工程中的类:
Launcher是Android系统的桌面系统,是比较重要也比较复杂的程序,这里对其代码做一个分析,希望起到抛砖引玉的作用。
1. Launcher有什么? live folder , widget , shortcut , wallpaper ,见 onActivityResult
2. UI 分成 3 部分: workspace, slibingdrawer, deletezone
3. Menu: 见 onCreateOptionsMenu in launcher.java
4. launcher 类是个 activity, 遵循 activity 的生命周期。
5. 资源文件比较多,这里只关注 Layout 相关的文件
代码分析的主线:
1. 了解类
2. 了解类的关系
Launcher工程中的类:
AddAdapter: 维护了 live fold , widget , shortcut , wallpaper 4 个 ListItem , 长按桌面会显示该列表
AllAppsGridView :显示 APP 的网格
ApplicationInfo :一个可启动的应用
ApplicationsAdapter : gridview 的 adapter
BubbleTextView: 一个定制了的 textview
CellLayout: 屏幕网格化
DeleteZone : UI 的一部分
DragController , dragscroller, dragsource, droptarget: 支持拖拽操作
DragLayer :内部支持拖拽的 viewgroup
FastBitmapDrawable :工具
Folder : Icons 的集合
FolderIcon: 出现在 workspace 的 icon 代表了一个 folder
FolderInfo: ItemInfo 子类
HandleView :一个 imageview 。
InstallShortcutReceiver , UninstallShortcutReceiver :一个 broadcastrecier
ItemInfo: 代表 Launcher 中一个 Item (例如 folder )
Launcher: Launcher 程序的主窗口
LauncherApplication :在 VM 中设置参数
LauncherAppWidgetHost , LauncherAppWidgetHostView ,: Widget 相关
LauncherModel : MVC 中的 M
LauncherProvider :一个 contentprovider ,为 Launcher 存储信息
LauncherSettings: 设置相关的工具
LiveFolder , LiveFolderAdapter , LiveFolderIcon , LiveFolderInfo : livefolder 相关
Search : 搜索
UserFolder , UserFolderInfo :文件夹包含 applications ,shortcuts
Utilities: 小工具
WallpaperChooser :选择 wallpaper 的 activity
Workspace: 屏幕上的一块区域
widget : 代表启动的 widget 实例,例如搜索
Launcher中类的关系,见下图(由于篇幅有限,不能把所有关系一一画出)。
二、主要模块
1.界面模型:
Launcher的界面的rootview是DragLayer,它是一个FrameLayout,在它上面workspace(应该说是celllayout)占了绝大部分的空间,celllayout的参数文件是workspace_screen.xml。workspace既是一个DropTarget又是一个DragSource,可以从AllAppGridView中拖出应用程序放在它上面,也可以把它里面的item拖走删除或者拖到bottomabr里面去。
(对于想修改launcher的同学,可以自定义DragLay.java,比如改为AbsoluteLayout等,再修改launcher.xml布局文件,就可以实现各种样式的launcher几面。)
2.Drop& Drag模型:
2.1 DragSource:可以拖动的对象来源的容器,在launcher中主要有AllAppGridView,workspace等。
void onDropCompleted(View target, boolean success,int x,int y);
2.2 DropTarget:可以放置被拖动的对象的容器。在launcher中有folder,workspace,bottombar等,一个View既可以是Dragsource也可以是DropTarget。主要包含以下几个接口:
1) boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
acceptDrop 函数用来判断dropTarget是否可以接受item放置在自己里面。
2) void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
onDragEnter是item被拖动进入到一个dropTarget的时候的回调。
3) void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
onDragOver是item在上一次位置和这一次位置所处的dropTarget相同的时候的回调。
4) void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
onDragExit是item被拖出dropTarget时的回调。
5) boolean onDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo);
onDrop是item被放置到dropTarget时的回调。
函数的调用模式为:
DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
if (dropTarget != null) {
if (mLastDropTarget == dropTarget) {//当这一次的 target 跟上一次相同时,根据坐标来移动item
dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1],
(int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo);
} else {
/**
* 当上一次的位置跟这一次不同而且上一次的位置不为空,说明item移 *动出了,将上次的 View 根据上次的坐标重新排列,并根据当前坐标重排*当前的*/
if (mLastDropTarget != null) {
mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
(int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo);
}
dropTarget.onDragEnter(mDragSource, coordinates[0], coordinates[1],
(int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo);
}
} else {//如果这一次为 null ,上一次不为 null ,那么把上一次坐标位置的 cell 去掉
if (mLastDropTarget != null) {
mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
(int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo);
}
}
//记录上次的droptarget
mLastDropTarget = dropTarget;
3.Touch event总结:
由于launcher的事件比较多比较复杂,所以在事件处理的时候一般采用rootview先用onInterceptTouchEvent(MotionEvent)拦截所有的touch事件,经过判断后分发给childview。
判断的规则如下:
a.down事件首先会传递到onInterceptTouchEvent()方法
b.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。
c.如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
d.如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
e.如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
三、几种问题的解决方式
1.将所有的应用都排列在桌面上
将所有的应用都排列在桌面是通过首先创建一个三维的boolean型全局数组来记录item的排列情况,第一维是屏数,第二维是纵向上的排列情况,第三维是横向的排列情况,如果那个位置被item所占用就标记为1,否则标记为0.在启动时把全局数组初始化为0,然后在添加的时候把相应的位置置1.凡是涉及到workspace上item的变化,比如移动、添加、删除操作时都需要维护数组,保持数组的正确性,因为在安装新程序时依据数组的状态去判断把item加到什么位置。
2.动态增加屏幕
动态增加屏幕是通过worksapce .addchild(view)的方式实现。基本思路是:首先预先规定所允许的最大的屏幕数,然后在需要增加屏幕而且当前屏幕数没有超过最大屏幕数的时候通过(CellLayout)mInflater.inflate(R.layout.workspace_screen,null)创建一个celllayout实例出来,然后通过addchild把它加入进去。在屏幕上的item被删除时通过从最后一屏起判断屏幕上是否有item,如果有的话保留,没有的话则删除最后一屏,以此类推。
3.预制桌面
a.添加普通的应用程序快捷方式:
在../res/xml下的default_workspace.xml文件中加入默认要放置的普通的应用程序。加入的格式为:x控制列,y控制行数
<favorite
launcher:packageName="... " //应用的packageName
launcher:className="... " //应用启动时的第一个activity
launcher:screen="..." //放置在第几屏(放在workspace的时候需要,从0开始,0为第一屏,1为第二屏,以此类推...)
launcher:x="..." //放置x方向的位置(在列中的位置)
launcher:y="..." /> //放置y方向的位置(在行中的位置)
packageName和className可以通过点击程序,然后在打印出的log中找到comp={...},例如如下信息:
comp={com.estrongs.android.taskmanager/com.estrongs.android.taskmanager.TaskManager}。其中com.estrongs.android.taskmanager为packageName, com.estrongs.android.taskmanager.TaskManager为className。
workspace的布局如下:
(0,0) |
(1,0) |
(2,0) |
(3,0) |
(4,0) |
(0,1) |
(1,1) |
(2,1) |
(3,1) |
(4,1) |
(0,2) |
(1,2) |
(2,2) |
(3,2) |
(4,2) |
b.添加widget:
在../package/apps/VLauncher/res/xml下的default_workspace.xml文件中加入默认要放置的普通的应用程序。加入的格式为:
<widget
launcher:packageName="..." //widget的packageName
launcher:className=" ..." //实现 widget的 receiver 类的名称.
launcher:container="..." //放置的位置(只能为desktop)
launcher:screen="..." //放置在第几屏上
launcher:x="..." //放置的x位置
launcher:y="..." //放置的y位置
launcher:spanx="..." //在x方向上所占格数
launcher:spany="..."/> //在y方向上所占格数
例如,要在第3屏的第一行第二列放置开始放置一个x方向上占两个单位格,y方向上占两个单位格的时钟,可以加入以下代码:
<appwidget
launcher:packageName="com.android.alarmclock"
launcher:className="com.android.alarmclock.AnalogAppWidgetProvider"
launcher:container="desktop"
launcher:screen="2"
launcher:x="1"
launcher:y="0"
launcher:spanx="2"
launcher:spany="2"/>
4.改变主界面的排列方式
要修改桌面的排列方式,如下,先根据横竖屏设置修改workspace_screen.xml里shortAxisCells和longAxisCells的参数,然后在Launcher.java中修改NUMBER_CELLS_X和NUMBER_CELLS_Y的值,在2.3版本中刚开始往数据库中添加item的时候会去判断,如果不修改NUMBER_CELLS_X和NUMBER_CELLS_Y的话会导致一部分的item显示不出来,导致预制apk的失败。
5.增加worksapce上的屏数
要增加屏数,首先在根据横竖屏在launcher.xml中的<com.android.launcher.Workspace 中删除或增加 <include android:id="@+id/cellN" layout="@layout/workspace_screen" />,然后在Launcher.java中修改SCREEN_COUNT的值即可。
四、xml文件
1.workspace_screen.xml
launcher:cellWidth="95dip" cell(即item)的宽
launcher:cellHeight="93dip" cell(即item)的宽
launcher:longAxisStartPadding="25dip"
较长(屏幕的宽和高中较大的那一方向,根据横竖屏方向有所不同)方向上距离起点的像素数
launcher:longAxisEndPadding="55dip"
较长(屏幕的宽和高中较大的那一方向,根据横竖屏方向有所不同)方向上距离终点的像素数
launcher:shortAxisStartPadding="20dip"
较短(屏幕的宽和高中较大的那一方向,根据横竖屏方向有所不同)方向上距离起点的像素数
launcher:shortAxisEndPadding="120dip"
较短(屏幕的宽和高中较大的那一方向,根据横竖屏方向有所不同)方向上距离起点的像素数
launcher:shortAxisCells="3"
较短的方向上可以容纳的cell的数量
launcher:longAxisCells="5"
较长的方向上可以容纳的cell的数量
shortAxisCells和longAxisCells决定一个workspace(即CellLayout)上可以容纳的item的个数为shortAxisCells*longAxisCells.
2. application_boxed.xml
所有应用程序和系统文件夹中item的定义。
3.application.xml
Workspace的item的layout定义。
在android中的布局有五大类,有的时候你可能用到一种,但有的时候你也可能需要两种或者三种布局同时一起使用。这五种布局为别为:LinearLayout(线性布局),FrameLayout(框架布局),RelativeLayout(相对布局),TableLayout(表格布局),AbsoluteLayout(坐标布局);
LinearLayout:被称为线性布局,分为水平和垂直,设置的垂直或水平的属性值,来排列所有的子元素。所有的子元素都被堆放在其它元素之后,因此一个垂直列表的每一行只会有一个元素,而不管他们有多宽,而一个水平列表将会只有一个行高(高度为最高子元素的高度加上边框高度)。LinearLayout保持子元素之间的间隔以及互相对齐(相对一个元素的右对齐、中间对齐或者左对齐)。
FrameLayout:被称为框架布局,预先在屏幕中预留空白处,之后你可以在其中填充一个单一对象。比如,一张你要发布的图片。所有的子元素将会固定在屏幕的左上角;你不能为FrameLayout中的一个子元素指定一个位置。后一个子元素将会直接在前一个子元素之上进行覆盖填充,把它们部份或全部挡住(除非后一个子元素是透明的)。
RelativeLayout:相对布局,可指定某元素相对于其他的元素的位置,可以通过layout_below="相对控件"。可以以右对齐,或上下,或置于屏幕中央的形式来排列两个元素。元素按顺序排列,因此如果第一个元素在屏幕的中央,那么相对于这个元素的其它元素将以屏幕中央的相对位置来排列。如果使用XML 来指定这个 layout ,在你定义它之前,被关联的元素必须定义。
TableLayout:表格布局,将子元素的位置分配到行或列中。一个TableLayout 由许多的TableRow 组成,每个TableRow 都会定义一个 row (事实上,你可以定义其它的子对象,这在下面会解释到)。TableLayout 容器不会显示row 、cloumns 或cell 的边框线。每个 row 拥有0个或多个的cell ;每个cell 拥有一个View 对象。表格由列和行组成许多的单元格。表格允许单元格为空。单元格不能跨列,这与HTML 中的不一样。
AbsoluteLayout:坐标布局,你给相应的控件通过x,y坐标来设定,(0, 0)为左上角,当向下或向右移动时,坐标值将变大。AbsoluteLayout 没有页边框,允许元素之间互相重叠(尽管不推荐)。我们通常不推荐使用 AbsoluteLayout ,除非你有正当理由要使用它,因为它使界面代码太过刚性,以至于在不同的设备上可能不能很好地工作。
Android中称为四大组件的为别为:Activity/Service/BroadCast Recevicer/Content provider
Activity:activity是用户和应用程序交互的窗口,一个activity相当于我们实际中的一个网页,当打开一个屏幕时,之前的那一个屏幕会被置为暂停状态,并且压入历史堆栈中,用户可以通过回退操作返回到以前打开过的屏幕。activity的生命周期:即“产生、运行、销毁”,但是这其中会调用许多方法onCreate(创建) 、onStart(激活) 、onResume(恢复) 、onPause(暂停) 、onStop(停止) 、onDestroy(销毁) 、onRestart(重启)。
Service:Service是一种程序,它可以运行很长的时间,相当于后台的一个服务,通过startService(Intent service)可以启动一个Service,通过Context.bindService()可以绑定一个Service。
BroadCast Recevicer:接受一种或者多种Intent作触发事件,接受相关消息,做一些简单处理,转换成一条Notification,统一了Android的事件广播模型。可以使用BroadcastReceiver来让应用对外一个外部的事件作出响应。Broadcast Receiver通过NotificationManager来通知用户这些事情发生了,BroadcastReceiver注册的有两种方式,一种是可以在AndroidManifest.xml中注册,另一种可以在运行时的代码中使用Context.registerReceiver()进行注册。用户还可以通过Context.sendBroadcast()将他们自己的intent broadcasts广播给其他的应用程序。
Content provider:内容提供者,可通过它来共享自己的数据给外部调用,给第三方应用提供数据访问的接口。
首先给大家介绍一下android中的activity:
一个activity一般代表手机屏幕的一屏,概念相当与网页。一般来说一个android应用是由一个或多个activity组成,activity直接可进行跳转,直接也可以通过回调方法传递参数。
activity之间的参数传递:
首先我们新建两个按钮,分别在layout1,layout2中,按钮的id为btn1,btn2,然后在分别建立两个activity分别为Activity1,Activity2。Activity1分别和layout1关联,Activity2分别和layout2关联,我们接下来要做的事情就是在Activity1中把参数传递到Activity2中,然后在Activity2运行完成的时候并返回到Activity1的时候,Activity1再从获取Activity2中参数。
这是Activity1中的代码:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setTitle("这是activity1"); setContentView(R.layout.layout1); //单击button2 Button btn1 = (Button) findViewById(R.id.btn1); btn2.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { Intent intent1 = new Intent(MainActivity.this, Activity3.class); intent1.putExtra("activity1", "数据来自activity1"); startActivityForResult(intent1, REQUEST_CODE); } }); }
成时调用onActivityResult方法
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if(requestCode == REQUEST_CODE){ if(resultCode == RESULT_CANCELED){ setTitle("取消"); }else if(resultCode == RESULT_OK){ System.out.println("这里已经执行..."); String txt = ""; Bundle extras = data.getExtras(); if(extras != null){ txt = extras.getString("store"); } setTitle("这里是Activity1:" + txt); } } }
这是Activity2中的代码:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setTitle("这是activity2"); setContentView(R.layout.layout2); Bundle bundle = getIntent().getExtras(); if(bundle != null){ setTitle("这里是Activity2:" + bundle.getString("activity1")); } Button btn = (Button) findViewById(R.id.btn2); btn.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { Bundle bundle = new Bundle(); bundle.putString("store", "这是Activity2中传递过来的数据"); Intent mIntent = new Intent(); mIntent.putExtras(bundle); setResult(RESULT_OK, mIntent); finish(); } }); }
首先解释一下startActivity()和startActivityForResult()这两种方法的不同:前者是启动一个新的Activity,当新的Activity执行完成后不会执行回调函数,
当然也不会有任何的返回值;后者也是启动一个新的Activity,并且当新的Activity运行结束的时候,还必须执行旧的Activity里面的
回调函数,这个回调函数叫做onActivityResult(),其中startActivityForResult()里面有两个参数,第一个参数是你声明的
Intent,第二个参数是请求码,如果请求码大于0的时候,当新的Activity结束的时候,这个请求码返回到onActivityResult(),
然后通过不同的请求码在回调函数onActivityResult()中针对不同的返回值执行不同的代码操作。其中onActivityReuslt()这也是
一个回调函数,当新的Activity执行完成后,还必须执行旧的Activity的这个回调函数。
参数传递:Activity和Activity之间的参数传递是通过Bundle的键值对来传递,Bundle是对HashMap的重新封装,但是Bundle只能
存放基本类型,比如:String/int/byte/boolean/char等。
/** * @Title JSONUtils.java * @Package com.fbtt.site.utils * @author huangsm * @email [email protected] * @date 2011-7-22 下午02:50:42 * @remark * @version V1.0 */ package com.fbtt.axst.utils;import java.beans.PropertyDescriptor;import java.sql.Date;import java.util.List;import java.util.Map;import org.apache.commons.beanutils.PropertyUtils;/** * @ClassName: JSONUtils * @author huangsm * @date 2011-7-22 下午02:50:42 * @remark */public class JSONUtils { public static String stringToJson(String s) { if (s == null) { return nullTOJson(); } StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); switch (ch) { case '"': sb.append("\\\""); break; case '\\': sb.append("\\\\"); break; case '\b': sb.append("\\b"); break; case '\f': sb.append("\\f"); break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; case '/': sb.append("\\/"); break; default: if (ch >= '\u0000' && ch <= '\u001F') { String ss = Integer.toHexString(ch); sb.append("\\u"); for (int k = 0; k < 4 - ss.length(); k++) { sb.append('0'); } sb.append(ss.toUpperCase()); } else { sb.append(ch); } } } return sb.toString(); } /*** * 对象转换成为json * 这里是描述这个方法的作用 * @return * @author Java * @date 2011-7-22 下午02:59:13 */ public static String objectToJson(Object obj){ StringBuffer json = new StringBuffer(); if (obj == null) { json.append("\"\""); } else if (obj instanceof Integer) { json.append("\"").append(numberToJson((Integer)obj)).append("\""); } else if (obj instanceof Boolean) { json.append("\"").append(booleanToJson((Boolean)obj)).append("\""); } else if (obj instanceof String) { json.append("\"").append(stringToJson(obj.toString())).append("\""); } else if (obj instanceof Object[]) { json.append("\"").append(arrayToJson((Object[])obj)).append("\""); } else if (obj instanceof List) { json.append("\"").append(listToJson((List<?>)obj)).append("\""); } else if (obj instanceof Map) { json.append("\"").append(mapToJson((Map<?, ?>)obj)).append("\""); } else if(obj instanceof java.sql.Date){ json.append("\"").append(dateToJson((Date)obj)).append("\""); } else if(obj instanceof java.util.Date){ json.append("\"").append(dateToJson((Date)obj)).append("\""); } else { json.append("\"").append(stringToJson(obj.toString())).append("\""); } return json.toString(); } /** * 这里是描述这个方法的作用 * @param obj * @return {["pname":"val"],["pname":"val"]...} * @author Java * @date 2011-7-22 下午03:02:49 */ public static String beanToJson(Object obj) { StringBuffer json = new StringBuffer("{"); try { PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(obj); if(pds != null && pds.length > 0){ for (PropertyDescriptor pd : pds) { if(pd.getName().equals("class")) continue; String pname = objectToJson(pd.getName()); String val = objectToJson(pd.getReadMethod().invoke(obj)); json.append(pname); json.append(":"); json.append(val); json.append(","); } json.setCharAt(json.length() - 1, '}'); }else{ json.append("}"); } } catch (Exception e) { e.printStackTrace(); } return json.toString(); } public static String beanToJson(Object obj, String params) { StringBuffer json = new StringBuffer("{"); try { PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(obj); if(pds != null && pds.length > 0){ for (PropertyDescriptor pd : pds) { String pname = pd.getName(); if(pname.equals("class")) continue; if(params.indexOf(pname) == -1) continue; pname = objectToJson(pname); String val = objectToJson(pd.getReadMethod().invoke(obj)); json.append(pname); json.append(":"); json.append(val); json.append(","); } json.setCharAt(json.length() - 1, '}'); }else{ json.append("}"); } } catch (Exception e) { e.printStackTrace(); } return json.toString(); } /** * 这里是描述这个方法的作用 * @param obj * @return * @see * @author Java * @date 2011-7-22 下午03:02:27 */ public static String mapToJson(Map<?, ?> map) { StringBuilder json = new StringBuilder(); json.append("{"); if (map != null && map.size() > 0) { for (Object key : map.keySet()) { json.append("" + objectToJson(key) + ""); json.append(":"); json.append("" + objectToJson(map.get(key)) + ""); json.append(","); } json.setCharAt(json.length() - 1, '}'); } else { json.append("}"); } return json.toString(); } /** * 这里是描述这个方法的作用 * @param obj * @return * @author Java * @date 2011-7-22 下午03:02:15 */ public static String listToJson(List<?> list) { StringBuffer json = new StringBuffer(); json.append("["); if (list != null && list.size() > 0) { for (Object obj : list) { json.append(beanToJson(obj)); json.append(","); } json.setCharAt(json.length() - 1, ']'); } else { json.append("]"); } return json.toString(); } public static String listToJson(List<?> list, String params) { StringBuffer json = new StringBuffer(); json.append("["); if (list != null && list.size() > 0) { for (Object obj : list) { json.append(beanToJson(obj, params)); json.append(","); } json.setCharAt(json.length() - 1, ']'); } else { json.append("]"); } return json.toString(); } /** * 这里是描述这个方法的作用 * @param obj * @return * @see * @author Java * @date 2011-7-22 下午03:02:05 */ private static String arrayToJson(Object[] array) { StringBuilder json = new StringBuilder(); json.append("{"); if (array != null && array.length > 0) { for (Object obj : array) { json.append(objectToJson(obj)); json.append(","); } json.setCharAt(json.length() - 1, ']'); } else { json.append("}"); } return json.toString(); } public static String dateToJson(Date date){ return date.toString(); } public static String numberToJson(Number number) { return number.toString(); } public static String booleanToJson(Boolean bool) { return bool.toString(); } public static String nullTOJson(){ return ""; } /** * 非空验证 * 这里是描述这个方法的作用 * @param arg0 * @return * @author Java * @date 2011-7-22 下午02:51:43 */ public static Boolean isNull(Object arg0){ if(arg0 == null) return true; String arg = (String) arg0; return ("".equals(arg.trim()) || "null".equals(arg)) ? true : false; }}有的方法有重构,其中多了一个参数,其中这个参数的作用是当你一个类有的字段不需要转换的时候,你只需把需要转换的字段属性通过逗号分隔传入就可以了!
public List pares(ResultSet rs) throws Exception{ List list = new ArrayList(); //获取所有属性 PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(cla); //获取表头 ResultSetMetaData rsm = rs.getMetaData(); while (rs.next()) { //获取实例 Object obj = cla.newInstance(); for (PropertyDescriptor pd : pds) { String name = pd.getName(); Boolean find = false; for (int i = 0; i < rsm.getColumnCount(); i++) { if(name.toLowerCase().equals(rsm.getColumnName(i + 1).toLowerCase())){ find = true; break; } } if(find){ String val = rs.getString(name); if(val != null && !"".equals(val)){ BeanUtils.setProperty(obj, name, val); } } } list.add(obj); } return list; }通过结果集合resultSet,同时需要借助第三方jar包commons-beanutils-1.7.0.jar,当然你也可通过Field[] filed = class.getDeclaredFields()获取属性但是这样或获取比较的多没有用的数据,比如像类的annotation等属性!
Android常见的布局形式有如下几种:
1:视图组件(VIEW)
在android当中View类是最基本的一个UI类,基本上所有高级的UI组件都继承这个类。一个View在屏幕上占据了一块矩形区域,它负责渲染这个矩形区域,也可以处理这块矩形区域发生的事件,并且可以设置该块区域是否可见,以及获取焦点等操作。
2:视图容器组件(ViewGroup)
一个ViewGroup对象是一个Android.view.ViewGroup的实例,他负责添加进ViewGroup的这些view进行布局,注意的是一个ViewGroup也可以加入到另一个ViewGroup当中。
3:布局组件(Layout)
比较常用的布局组件有:LinearLayout它可以实现水平布局和垂直布局,如果将布局方向设置为“vertical“,则表明竖直布局;设置为"horizontal"设置为水平布局。还有RelativeLayout是相对布局,如果设置A显示在B的左侧,那么B的坐标不是固定的而是相对A的位置,可以通过layout_below属性设置。
4:布局参数(LayoutParams)
当你把每一个View传递到这个RelativeLayout里边的时候,我们需要设定这个View的一些参数(比如:显示的位置是左,还是右等)封装在LayoutParams。如果没有传入系统则会采用默认的值,如果有值的话容易会根据传进来的LayoutParams进行计算。
一些常见的布局的说明:
LinearLayout:将自己包含的子元素,按照一个方向进行排列,方向有两种分别是水平(horizontal)和竖直(vertical)
FrameLayout:是指在屏幕上预留好一块空白的区域,所有的元素都被放置在FrameLayout区域的最左上方,无法给这些元素以个确切的位置,如果有很多值元素则会重叠在前一个元素上
RelativeLayout:相对布局
单位的备注:
1:px(pixel)像素
2:dip(device independent pixels):依赖于设备的像素
3:sp(scaled pixels-best for text size):带比例的像素
4:pt(points):点
5:in(inches):英尺
6:mm(mmillimeters):毫米
两个类分别是Sql和Where,支持hql语句和支持sql语句拼凑,当你传入的参数为空时,拼凑的这个条件就会自动的换成“1=1”,使用方法:Sql sql = Sql.start("select * from tbl_name").add(Where.newIntance().like("property", value), "where")(这里拼接很灵活大家可以看代码)获取sql语句直接可以sql.toString(),如果获取所有参数可以通过sql.getParams()。
Sql.java
package com.fbtt.axst.utils;import java.util.ArrayList;import java.util.List;public class Sql { private Sql(String hql) { this.hql = hql; } public static Sql start(String hql){ return new Sql(hql); } //所有的参数 private List params = new ArrayList(); //最终的hql语句 private String hql = null; /** * 向当前hql语句追加语句 * Example: * hql = Hql.start("from Employee a "); * hql.add("order by a.name") * String str = hql.toString(); * 此处str为:from Employee a order by a.name; */ public Sql add(String str){ hql = hql + " " + str; return this; } /** * 向当前hql语句追加语句,其它的参数 * Example: * hql = Hql.start("from Employee a "); * hql.add("where a.name like ?", new Serializable[]{"张三"}); * String str = hql.toString(); * str为:from Employee a where a.name like ? * Serializable[] params = hql.getParams(); * params为:["张三"] * @param hql * @param params */ @SuppressWarnings("unchecked") public Sql add(String str, Object[] params){ this.add(str); if(params != null){ for(Object obj : params){ this.params.add(obj); } } return this; } public Sql add(String str, Object param){ this.add(str, new Object[]{param}); return this; } //获取所有参数 public Object[] getParams(){ return params.toArray(); } /** * 添加where条件 * Example: * WhereHql wh = WhereHql.newInstance(); * wh.eq("a.name", "张三"); * Hql hql = Hql.start("from Employee a "); * hql.add(wh, "where"); * hql.toString(); * 此时得到:from Employee a where a.name = '张三' * @param wh * @param prefix 当wh.toString不为空时在其前添加的前缀 * @return */ public Sql add(Where wh, String prefix){ String wstr = wh.toString(); if(prefix == null)prefix = ""; if(wstr != null){ this.add(prefix + " " + wstr, wh.getParams()); } return this; } /** * 获取生成的hql语句 */ @Override public String toString() { return hql; }}Where.java
package com.fbtt.axst.utils;import java.util.ArrayList;import java.util.List;public class Where { private Where(){ } public static Where newInstance(){ return new Where(); } //所有的参数 private List params = new ArrayList(); //最终的hql语句 private String hql = null; /** * 向当前hql语句追加语句,及它的参数 * @param params */ @SuppressWarnings("unused") private Where addNotNull(String str, Object param, String prefix){ if(param == null || param.equals(""))return this; this.add(str, param, prefix); return this; } private Where addAndNotNull(String str, Object param){ if(param == null || param.equals(""))return this; this.add(str, param, "and"); return this; } @SuppressWarnings("unchecked") public Where add(String str, Object param, String prefix){ if(prefix == null)prefix = "and"; if(hql == null) hql = str; else hql = hql + " " + prefix + " " + str; if(param != null){ this.params.add(param); } return this; } @Override public String toString() { return hql; } //获取所有参数 public Object[] getParams(){ return this.params.toArray(); } //= public Where eq(String prop, Object param){ if(param == null || param.equals(""))return this; this.addAndNotNull(prop + "=?", param); return this; } //< public Where lt(String prop, Object param){ if(param == null || param.equals(""))return this; this.addAndNotNull(prop + " < ?", param); return this; } //<= public Where le(String prop, Object param){ if(param == null || param.equals(""))return this; this.addAndNotNull(prop + " <= ?", param); return this; } //like public Where like(String prop, Object param){ if(param == null || param.equals(""))return this; this.addAndNotNull(prop + " like ?", "%" + param + "%"); return this; } //> public Where gt(String prop, Object param){ if(param == null || param.equals(""))return this; this.addAndNotNull(prop + " > ?", param); return this; } //>= public Where ge(String prop, Object param){ if(param == null || param.equals(""))return this; this.addAndNotNull(prop + " >= ?", param); return this; } }
android体系结构介绍:如图:
l 应用程序(application)
l 应用程序框架(Application Framework)
l 各种库和android运行环境
l 操作层OS
Ø 应用程序(application):android的应用程序通常涉及用户界面和交互;
Ø 应用程序框架(application Framework):
1. 一组view(UI组件):这些UI组件包括List、textbox、button;
2. Content Provider:提供一种机制,通过这个机制应用程序可是实现数据库共享和互访;
3. Resourse Manager:负责管理非代码的访问,如图片、xml以及国际化资源文件;
4. Notification Manager:让程序将警示信息显示在状态栏上;
5. Activity Manager:管理着应用程序的生命周期,提供了应用程序页面的退出机制,一个应用程序由多个页面你组成,而每个页面的单位就是Activity,android应用程序是由多个activity的交互构成;
Ø 库(Libraries)和运行环境(Run Time):
1. 系统C库:一个从BSD 继承来的标准C系统函数库(LibC),他是专门基于嵌入式Linux的设备定制的;
2. 媒体库:基于packet Video OpenCORE,该库支持多种常用的音频、视频格式回放和录制,同时支持静态图像文件;
3. Surface Manager:对显示子系统的管理,并且为多个应用提供了2D和3D图层的无缝融合;
4. Lib WebCore:一个最新的web浏览器引擎, 用来支持android浏览器和一个可嵌入的web试图;
5. SGL:底层的2D图引擎;
6. 3D libraries:基于openGL ES 1.0 apis实现,该库可以用硬件3d加速或者使用高度优化的3D软加速;
7. FreeType:位图(bitmap)和vector字体显示;
8. Sqlite:关系型数据库;
每个android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例,Dalvik被设计成一个设备,可以同时高效的运行多个虚拟机设备。Dalvik虚拟机执行的(.dex)Dalvik可执行文件,该格式文件针对小内存使用做了优化。同时虚拟机是基于寄存器的,所有的类都经过由Java 编译器,然后通过sdk的“dx”工具转化成dex格式,有虚拟机执行。
Android应用程序介绍:
一般android应用程序包括以下四个部分:
l Activity
l Broadcast Intent Receiver
l Service
l Content Provider
1. Activity介绍:
Activity一般代表手机屏幕的一屏,相当于浏览器的一个页面。当打开一个屏幕时,之前的那一个屏幕会被置为暂停状态,并且压入历史堆栈中,用户可以通过回退操作返回到以前打开过的屏幕。
开发时需要选择行的移除没有必要保留的屏幕,因为打开后的屏幕会保存在堆栈中
Android的生命周期:即“产生、运行、销毁”
Intent和Intent Filter介绍:一个Intent就是一次对将要执行的操作的抽象描述,通过Intent可以在多个Activity之间进行跳转,Intent两个最重要的部分是动作(action)和动作对应的数据,典型的动作类型有,MAIN、view、pick、edit等,而动作对应的数据则以URI的形式表示;例如有两个activity分别为A和B,要从A跳转到B可以这样写
Intent intent = new Intent(A.this, B.class);
startActivity(intent);
与Intent有关联的类叫做Intent Filters,如果Intent是一个请求,一个Intent Filters描述改组件所能相应Activity请求的能力。
2. Broadcast Intent Receiver介绍:
可以使用BroadcastReceiver来让应用对外一个外部的事件作出响应。BroadcastReceiver通过NotificationManager来通知用户这些事情发生了,BroadcastReceiver注册的有两种方式,一种是可以在AndroidManifest.xml中注册,另一种可以在运行时的代码中使用Context.registerReceiver()进行注册。用户还可以通过Context.sendBroadcast()将他们自己的intent broadcasts广播给其他的应用程序。
3. Service介绍:
Service是一种程序,它可以运行很长的时间,相当于后台的一个服务,通过startService(Intent service)可以启动一个Service,通过Context.bindService()可以绑定一个Service
4. Content Provider介绍:
数据在android当中是私有的,这些数据包括文件数据和数据库数据以及其他类型的一些数据。两个程序之间数据的交互是通过Content Provider来实现,一个Content Provider实现了一组标准的接口,能够让其他应用保存或读取此Content Provider的各种数据类型。所以一个程序可以通过实现以个Content Provider的抽象接口将自己的数据暴露在外面,实现常见的接口有:
Query(URI,String[],String,String[],String):通过关键字查询;
Insert(Uri,ContentValues):将一组数据插入到指定的地方 ;
Update(Uri,ContentValues,String,String[]):更新数据;
Delete(Uri, String, Stringp[]) :删除数据;
明天和大家介绍实际开发中的android布局!layout!
android包下的相关介绍
android.util:底层辅助类(特定容器类,xml辅助工具)
android.os:提供基本服务,消息传递和进程通信IPC
android.graphics:核心渲染包,提供图像渲染功能
android.text:丰富的文本处理方式
android.database:底层api处理数据库,方便操作数据表和数据库
android.content:在手机设备上提供各种服务访问数据,程序安装到手机设备和其他相关资源,动态显示数据
android.view:核心用户框架
android.widget:标准用户界面元素,如:List/Button/Layout/Managers
android.app:高层应用程序模型,实现使用Activity
android.provider:调用提供的content provider接口
android.telephone:提供api交互和手机设备的通话接口
android.webikit:包含一系列工作基于web内容的api