ListView数据项隔行换色控制实现
运行效果图:
[img]
[/img]
工程结构图:
[img]
[/img]
主类:
package com.amaker.list;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
/**
* @Title: ListViewBackgroundDemoActivity.java
* @Package com.xiaoma.listviewbackground
* @Description: 小马学习隔行换色,一定仔细看注释
* @author XiaoMa
*/
public class ListViewBackgroundDemoActivity extends Activity
implements OnItemSelectedListener{
private ListView lv = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
lv = (ListView)findViewById(R.id.listview);
/**
* 设置快速滚动块可用,这个大家注意下,就是设置之后,ListView
* 里面的数据条数必须大于一定数量(小马试了下,这个数量是大于一屏)
* 才会显示这个小拖块,不然无效
*/
lv.setFastScrollEnabled(true);
lv.setOnItemSelectedListener(this);
lv.setAdapter(new ListDemoAdapter(getApplicationContext()));
}
/**
* @Title: ListViewBackgroundDemoActivity.java
* @Package com.xiaoma.listviewbackground
* @Description:适配器实现
* @author XiaoMa
*/
private class ListDemoAdapter extends BaseAdapter{
private static final int ITEM = 0;
private static final int SEPARATOR = 1;
private static final int TYPE_MAX_COUNT = SEPARATOR + 1;
private LayoutInflater inflater = null ;
private List<String> listItem = new ArrayList<String>();
/*
* 用这个set来保存分隔线的位置,这个地方必须注意,小马写两个不同类型的Set来跟大家复习下
* 这两个小东东的不同之处,效果太神奇了,这个Set你该用哪个会直接影响到换色效果的,不同之
* 处是:
*
* TreeSet:基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序
* 或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法
*
* Set:一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2)
* 的元素对 e1 和 e2,并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学
* 上的 set 抽象
*/
@SuppressWarnings("rawtypes")
//private Set set = new HashSet();
private TreeSet set = new TreeSet();
private Map map = new HashMap();
public ListDemoAdapter(Context context ){
this.inflater = LayoutInflater.from(context);
/**
* 大家稍微注意下这个循环,i从1开始?为什么? 小马在这犯的错
* 每5条数据加一条换色Item是用 i%6 == 0来控制的,如果这
* 个i值从0开始的话,会出现上面贴图中错误的换色,看图三,图四,
* 原因是取余的时候小马犯了低级错误,小错,记下:两数取余,前者
* 大于后者时,取余会按正常取余来取,如果前者小于后者时,取余
* 后的值始终是前者,如果这个地方的i从0开始取的话,正好是满
* 足取余后的值等于0这个情况,就出现上面两图中的错误隔色图了,
* 当前者大于等于后者时,这个隔色就又正常了,一定注意下,记在这,
* 提醒大家也提醒自己,吼吼,听不明白的可以看下小马在工程中加的
* 一个测试类(测试取余规律的类PercentTest.java)
*/
//填充ListView
for(int i=1;i<=50;i++){
this.listItem.add("添加的第"+i+"条数据");
if(i%6 == 0){
addSeparatorItem();
}
Log.i("KKK", "添加的第"+i+"条数据");
}
}
/**
* 添加换色项方法实现
* @param item
*/
@SuppressWarnings("unchecked")
public void addSeparatorItem() {
set.add(listItem.size()-1);
notifyDataSetChanged();
}
/**
* 此处是根据特定值(在getView方法中加入)
* 来判断应该绘制选项还是换色选项的分支值
* 由position返回view type id
*/
@Override
public int getItemViewType(int position) {
return set.contains(position) ? SEPARATOR : ITEM;
}
/**
* 返回你有多少个不同的布局
*/
@Override
public int getViewTypeCount() {
return TYPE_MAX_COUNT;
}
@Override
public int getCount() {
return listItem.size();
}
@Override
public Object getItem(int position) {
return listItem.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
/**
* 这个地方也需要注意下,小马这个方法里面写的有点多了,其实官方是不支持在
* getView()方法中写太多的逻辑因为你手拖下屏幕,这个方法是逛调用
* 的,所以太多逻辑不适合放在这个地方,大家可自行调整下,但有些还是必
* 须的,写在这无防...吼吼
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
int type = getItemViewType(position);
XiaoMa xiaoMa = null;
final int location = position;
if(convertView == null){
switch (type) {
//如果需要绘制选项时分支
case ITEM:
xiaoMa = new XiaoMa();
convertView = inflater.inflate(R.layout.listview_items, null);
xiaoMa.iv = (ImageView)convertView.findViewById(R.id.ItemImage);
xiaoMa.tv = (TextView)convertView.findViewById(R.id.ItemTitle);
xiaoMa.text = (TextView)convertView.findViewById(R.id.itemtext);
xiaoMa.btn = (Button)convertView.findViewById(R.id.view_btn);
break;
//如果需要绘制换色选项时分支
case SEPARATOR:
xiaoMa = new XiaoMa();
convertView = inflater.inflate(R.layout.listview_items, null);
xiaoMa.iv = (ImageView)convertView.findViewById(R.id.ItemImage);
xiaoMa.tv = (TextView)convertView.findViewById(R.id.ItemTitle);
xiaoMa.text = (TextView)convertView.findViewById(R.id.itemtext);
xiaoMa.btn = (Button)convertView.findViewById(R.id.view_btn);
convertView = inflater.inflate(R.layout.separator, null);
/*
* 这个地方的drawable2用法跟在ListView换色选项布局中的
* android:background="@drawable/gradient_box"
* 效果是一样的,小马写在这,熟悉下两种方式,大家根据自己需要改
*/
Drawable drawable2 = getResources().getDrawable(R.drawable.gradient_box);
xiaoMa.sep = (TextView)convertView.findViewById(R.id.sep);
xiaoMa.sep.setBackgroundDrawable(drawable2);
break;
}
/*
* 此处小马犯错了,如果下面这句不加的话,
* 加载时正常,拖动列表时就会报空指针了,
* 小点注意下
*/
convertView.setTag(xiaoMa);
}else{
xiaoMa = (XiaoMa)convertView.getTag();
}
xiaoMa.iv.setBackgroundResource(R.drawable.xiaolvzi);
xiaoMa.tv.setText("这是第"+(position+1)+"个标题");
xiaoMa.text.setText("这是第"+(position+1)+"个概述");
/**
* 按钮事件监听实现
*/
xiaoMa.btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//单击Item按钮后,弹出提示
ShowDialog(location);
}
});
return convertView;
}
}
/**
* 弹出提示实现,这个地方扩展一下,细心的话大家会发现小马违反了方法命名规范,其实不是的,
* 安卓Activity有临时弹出对话框方法的,跟下面这个方法只有一个字母之差(小马故意
* 首字母大写的),安卓自带弹出方法在这个方法下面的注释中
* @param posi
*/
private void ShowDialog(int posi){
Toast.makeText(getApplicationContext(), "单击了第"+(posi+1)
+"个按钮", Toast.LENGTH_SHORT).show();
/*
* 下面这两个方法大家熟悉吧?调用系统提供的临时弹出对话框,必须实现下面的
* onCreateDialog(int id)方法
*/
showDialog(1);//弹出时可以做如:从服务器取数据等操作
/**
* 这个地方小马就简单的睡眠5秒种来模拟从服务器下载数据完成后关闭对话框
*/
new Thread(new Runnable() {
@Override
public void run() {
try {
Log.i("KKK", "已进入睡眠");
Thread.sleep(3000);
dismissDialog(1);//隐藏 表示数据下载完毕等的...
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
@Override
protected Dialog onCreateDialog(int id) {
/*
* 下面这个构造器中的this不能用getApplicationContext()来代替,
* 但可用类名.this来代替,没有为什么, 必须这样写!!!这个地方小马晕了
* 不少时间,如果直接get....代码没错,但还是会报错,很邪门的哦 ...
*/
ProgressDialog dialog = new ProgressDialog(this);
//小测试,中文就临时写这了,大家要注意把内容都写到string.xml中去,好习惯从小开始养,嘿嘿
dialog.setCancelable(false);//设置用户不能用返回键取消对话框
dialog.setIcon(getResources().getDrawable(R.drawable.xiaolvzi));
dialog.setTitle("那些年,我们一起追的女孩");
dialog.setMessage("小马果 呆丫头 O_O");
dialog.show();
return dialog;
}
/**
* @Title: ListViewBackgroundDemoActivity.java
* @Package com.xiaoma.listviewbackground
* @Description: 为提高加载效率而写的类,可以看下ListView优化
* @author XiaoMa
*/
public final class XiaoMa{
public ImageView iv ;
public TextView tv ;
public Button btn ;
public TextView text;
public TextView sep;
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position,
long id) {
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
}listview_items.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/RelativeLayout01"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@drawable/item_bg"
>
<ImageView
android:id="@+id/ItemImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:paddingTop="12dip" />
<TextView
android:id="@+id/ItemTitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/view_btn"
android:layout_toRightOf="@id/ItemImage"
android:textColor="#000000"
android:textSize="20dip" />
<TextView
android:id="@+id/itemtext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/ItemTitle"
android:textColor="#000000"
android:layout_marginTop="10dip"
android:layout_toRightOf="@+id/ItemImage"
android:textSize="10dip" />
<Button
android:id="@+id/view_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_alignParentRight="true"
android:text="XiaoMa" />
</RelativeLayout>listview_item2.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/RelativeLayout01"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/item_bg"
android:orientation="horizontal" >
<LinearLayout android:id="@+id/main" >
<ImageView
android:id="@+id/ItemImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:paddingTop="12dip" />
<TextView
android:id="@+id/ItemTitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/view_btn"
android:layout_toRightOf="@id/ItemImage"
android:textColor="#000000"
android:textSize="20dip" />
<TextView
android:id="@+id/itemtext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/ItemTitle"
android:layout_marginTop="10dip"
android:layout_toRightOf="@+id/ItemImage"
android:textColor="#000000"
android:textSize="10dip" />
<Button
android:id="@+id/view_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:gravity="center"
android:text="XiaoMa" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/main" >
<TextView
android:id="@+id/sep"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:text="吼吼" />
</LinearLayout>
</RelativeLayout>main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<!-- Back Up <ListView
android:id="@+id/listview"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:smoothScrollbar="true"
android:drawSelectorOnTop="false"
android:listSelector="#00000000"
/>
-->
<ListView
android:id="@+id/listview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:smoothScrollbar="true"
android:choiceMode="none"
android:focusable="false" android:scrollingCache="false"
android:clickable="false" android:dividerHeight="0.5dip"
/>
<!-- <ListView android:id="@+id/list_coupon"
android:layout_height="wrap_content"
android:drawSelectorOnTop="false" android:choiceMode="none"
android:focusable="false" android:scrollingCache="false"
android:fadingEdge="none" android:focusableInTouchMode="false"
android:clickable="false" android:dividerHeight="0.5dip"
android:divider="@drawable/separator" />
-->
</LinearLayout>separator.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/sep"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:textColor="#000000"
android:gravity="center_horizontal"
android:text="哈哈哈,这就是个换行标志 " />
</LinearLayout>配置文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amaker.list"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" />
<application
android:icon="@drawable/xiaoma"
android:label="@string/app_name" >
<activity
android:name=".ListViewBackgroundDemoActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Mobile应用基于PhoneGap框架搭建教程是本文要介绍的内容,主要是来学习PhoneGap框架的应用,随着Google的Android手机和苹果的iPhone手机成为手机市场的主流,越来越多的开发者加入到移动应用开发的大军中,但是基于Java的Android应用和基于C语言的iPhone应用让开发者开发应用的时候甚为烦恼,同样的应用必须用不同的语言开发两次才能支持不同的手机平台。
为了进一步简化移动应用的开发,Nitobi公司推出了一套开源的移动应用PhoneGap。本文主要介绍PhoneGap的基础知识,并通过一个示例介绍PhoneGap开发测试环境的搭建以及PhoneGap项目的开发和部署,并对PhoneGap提供的主要API做简单介绍.
PhoneGap简介
PhoneGap是一款基于HTML5的开源的手机应用开发框架,它允许用户仅仅通过Web(HTML、JavaScript)技术就可以访问移动设备的本地应用、API以及应用程序库等。PhoneGap提供了一系列丰富的API供开发者调用,这些API抽象和简化了移动设备本身提供的复杂的API,使开发新的手机应用和调用已有的手机功能更简单方便。另外,PhoneGap真正实现了writtenonce,runeverywhere,并且它采用了W3C标准,能和jQueryMobile结合在一起使用。
PhoneGap特性
目前,PhoneGap已实现对iPhone/iPad、Android、Symbian、Palm、黑莓各版本绝大部分功能的支持,其中官方文档中对其支持的详细说明如图1所示:
图1.PhoneGapAPI对各手机平台的支持
PhoneGapAPI简介
Accelerometer
Accelerometer是一个设备移动感应器,它能够检测到设备相对于原来位置的移动,并用三维坐标x,y,z表示。该API提供三个方法:
accelerometer.getCurrentAcceleration:得到用x,y,z值表示的当前设备的移动加速度。
accelerometer.watchAcceleration:以特定的时间间隔得到用x,y,z值表示的当前设备的移动加速度。
accelerometer.clearWatch:取消对设备移动加速度的监控。
Camera
Camera提供了访问和操作移动设备的默认摄像头的API,包括用摄像头动态摄取图片或从移动设备的相册中获取图片。
camera.getPicture():该方法通过配置不同的参数实现动态用摄像头获取图片或从已有的相册中获取图片,返回参数也可以根据设置不同的参数返回图片的二进制数据或者图片的路径。
Compass
Compass提供了获取移动设备指向的API。
compass.getCurrentHeading:获取当前移动设备的指向。
compass.watchHeading:以特定的时间间隔获取当前设备的指向。
compass.clearWatch:取消对当前设备指向的监控。
Contacts
Contacts提供了访问和操作移动设备通讯录数据库的API,包括获取联系人列表(支持过滤条件),增加,删除,编辑通讯录联系人等。
contacts.create:创建一个新的联系人。
contacts.find:查找联系人。
Contacts模块包括几个重要的对象:Contact,ContactName,ContactField,ContactAddress,ContactOrganization,ContactFindOptions,ContactError,通过这些对象和create、init方法共同实现对通讯录的复杂操作,详细的介绍可以参考PhoneGap的官方文档。
Device
Device提供了访问当前移动设备参数的API,包括设备名、设备系统版本、设备平台等。
File
File提供了访问和操作移动设备文件系统的API,其中FileReader和FileWriter提供了对设备文件的读写API。
GeoLocation
GeoLocation提供了访问移动设备的GPS感应器的API。
Media
Media提供了访问和操作移动设备语音文件的API,包括播放、停止、录音等。
NetWork
Network提供了访问移动设备移动网络和无线网络设置的API。
Notification
Notification提供了一组API来模拟移动设备的一些可视(对话框)、可听(提示音)、可感觉(震动)的功能。
notification.alert:弹出警告或者对话框。
notification.confirm:弹出确认框。
notification.beep:播放设备的提示音。
notification.vibrate:使设备震动一段时间。
Storage
Store提供了访问移动设备的存储设备的API。
搭建PhoneGap开发化境
PhoneGap支持开发适用于iPhone、Android、Palm等不同平台的应用,对不同的平台开发环境也有所不同,本文以Android为例搭建开发环境和创建应用。在搭建环境之前,我们需要先下载一些必须的工具和SDK,所有的工具都可以免费下载:
下载并安装Eclipse3.4+。
下载并安装Android SDK。
下载并安装ADTPluginforEclipse。
下载并解压PhoneGap。
安装好Eclipse、AndroidSDK和ADT插件之后,还需要做一些简单的配置才可以创建项目。图2显示了如何在Eclipse里配置AndroidSDK的路径,图3和图4显示了如何在Eclipse里配置AVD(AndroidVirtualDevice)。
图2.在Eclipse里配置AndroidSDK路径
图3.AndroidSDKandAVDManager菜单
图4.在Eclipse里配置AVD
如果需要使用Android手机测试应用,还需要安装手机对应型号的驱动器,把手机和开发机器用USB线连接,并且正确设置手机的开发调试功能。(Settings>Applications>Development)
创建一个简单的PhoneGap应用
创建一个新Android工程,如图5所示:
图5.创建新Android工程
完善项目结构
在项目根目录下创建/libs和/assets/www目录,并从解压后的PhoneGap目录中拷贝phonegap.js(可能带有版本信息)到/assets/www目录下,拷贝phonegap.jar(可能带有版本信息)到libs目录。修改项目的JavaBuildPath信息,把libs下的jar文件包含在编译路径中。
修改项目文件实现简单的获取设备联系人列表功能
com.phonegap.App.java
更改App.java文件为清单1的内容:
清单1.App.java类
AndroidManifest.xml
把清单2的内容拷贝到AndroidManifest.xml文件的manifest标签内,并拷贝android:configChanges="orientation|keyboardHidden到activity标签内做为activity标签的一个属性。
清单2.AndroidManifest.xml
<supports-screens android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:resizeable="true" android:anyDensity="true" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name=" android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
index.html页面
在/assets/www目录下创建一个新的HTML页面命名为index.html,并拷贝清单3的内容到index.html文件。
清单3:Index.html
<!DOCTYPE HTML>
<html>
<head>
<meta name="viewport" content="width=320; user-scalable=no" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" >
<title>PhoneGap</title>
<link rel="stylesheet" href="/blog_article/master.css" type="text/css" media="screen"
title="no title" charset="utf-8" >
<script type="text/javascript" charset="utf-8" src="/blog_article/phonegap.0.9.4.js"
></script>
<script type="text/javascript" charset="utf-8" >
//show device information
var deviceInfo = function (){
document.getElementById("platform").innerHTML = device.platform;
document.getElementById("version").innerHTML = device.version;
document.getElementById("uuid").innerHTML = device.uuid;
document.getElementById("name").innerHTML = device.name;
document.getElementById("width").innerHTML = screen.width;
document.getElementById("height").innerHTML = screen.height;
document.getElementById("colorDepth").innerHTML = screen.colorDepth;
};
//listen the device ready event and get the device information.
function init(){
document.addEventListener("deviceready", deviceInfo, true );
}
// get device contacts list by the contacts API.
function get_contacts()
{
var obj = new ContactFindOptions();
obj.filter="";
obj.multiple=true ;
obj.limit=5;
navigator.service.contacts.find(["name","displayName", "phoneNumbers",
"emails"],contacts_success, fail, obj);
}
function fail(fail)
{
alert(fail);
}
function contacts_success(contacts)
{
var result = "";
for (var i=0;i<contacts.length;i++)
{
result += "Name: " + contacts[i].name.givenName + ", displayName: "+
contacts[i].displayName + ",Email:" + contacts[i].emails[0].value;
result += "<br>";
}
document.getElementById("conlist").innerHTML = result;
alert(contacts.length + ' contacts returned.' );
}
</script>
</head>
<body onload="init();" id="stage" >
<h1>Welcome to PhoneGap!</h1>
<h2>this file is located at assets/index.html</h2>
<div id="info" >
<h4>Platform: <span id="platform" > </span>,
Version: <span id="version" > </span></h4>
<h4>UUID: <span id="uuid" > </span>,
Name: <span id="name" > </span></h4>
<h4>Width: <span id="width" > </span>,
Height: <span id="height" >
</span>, Color Depth: <span id="colorDepth" ></span></h4>
</div>
<a href="#" onclick="get_contacts();">
Get phone's contacts</a>
<a href="http://w3.ibm.com/w3odw/spg/index_default.html"
>Access IBM Home Page</a>
<form action="http://x.x.x.x:8080/WebTest/index.jsp" method="get" >
Username: <input type="text" name="name" />
<input type="submit" />
</form>
<div id="conlist" >
</div>
</body>
</html>
注意:中的“x.x.x.x”应为运行着一个contextroot为WebTest的Web应用的主机地址(IP或者HostName),WebTest项目可从本文档提供的链接中下载。
测试项目
在项目上点击右键,在出现的菜单中选择“RunAs”,然后选择“AndroidApplication”,Eclipse会弹出窗口让你选择合适的AVD,如果还没有配置,请参照“搭建PhoneGap开发环境”小节介绍的方法创建一个AVD。如果选择用Android手机测试程序,请确认驱动已经装好,USB连接正常,并且正确设置手机的开发调试选项,然后选择“RunAs--AndroidApplication”。
运行结果展示
图6显示了该实例在AVD上的运行结果。
图6.示例运行结果
点击“Getphone’scontacts”会出现图7和图8所示的结果:
图7.示例运行结果(对话框)
图8.示例运行结果(联系人信息)
点击“AccessIBMHomePage”将会出现图9所示的画面:
图9.显示IBM主页画面
在图6的输入框中输入“Rendy”,然后点击“Submit”按钮,会出现图10所示的页面:
图10.提交用户名后显示画面
从上面的示例可以看到,开发人员只需掌握HTML和JavaScript就可以开发基于PhoneGap的可以和移动设备的本地应用交互的手机Web应用,极大的简化了移动应用的开发周期。
本文对PhoneGap开源框架做了简单介绍,并通过一个示例介绍了如何开发基于PhoneGap框架的可以和移动设备的本地应用交互的MobileWeb应用。PhoneGap以它开发简单、兼容性好、支持标准化等无与伦比的优势正在不断的占据移动应用开发的市场,虽然它也存在运行速度慢、UI反应延时等问题,但是随着技术的进步,这些不足会不断的改进和消除。
部署和运行示例代码
下载代码到本地计算机,打开Eclipse,点击File->import->ExistingProjectsintoWorkspace如图7所示,选择archivefile并指定到代码所在的本地路径(图11),点击Finish按钮。
图11.导入工程到Eclipse
图12.导入Archive文件
工程导入Eclipse后会自动编译,右键点击项目选择RunAsAndroidApplication就可以运行程序。
用同样的方法将WebTest项目导入Eclipse工作空间中,右键点击项目选择RunAsRunOnServer。
小结:Mobile应用基于PhoneGap框架搭建教程的内容介绍完了,希望通过本文的学习能对你有所帮助!
下载
描述 名字 大小 下载方法 示例代码 SampleAndroidProject.zip 340KB HTTP WebTest 项目代码 WebTest.zip 6KB HTTP
关于下载方法的信息
在使用智能手机的时候,有些程序是一直伴随这我们的,或者说是需要实时反馈和交互的,例如我们手机的主题界面,闹钟程序等等。对于这些程序,我们自然而然的会希望他们能够开机自启动,因为这样子可以避免忘记手动开启某些程序,例如日常闹钟等等,并且省了很多繁琐的事情。
正如高焕堂先生总结Android框架时所说的“Don'tcall me, I'll call you back!”,在Android中,不同组件之间的调用往往是基于消息触发,而不是简单的事件调用。在Android中,广播机制也很好的贯彻了这个思想。下面这个程序,将会演示如何利用BroadcastReceiver来实现Activity和Service的开机自启动。
实现原理:当Android启动时,会发出一个系统广播,内容为ACTION_BOOT_COMPLETED,它的字符串常量表示为android.intent.action.BOOT_COMPLETED。所以,只要在BroadcastReceiver接收到该消息时,创建并启动相应的Activity和Service即可实现。
在该程序中,将创建一个BroadcastReceiver类BootBroadcastReceiver、一个Activity类StartOnBootActivity、一个Service类StartOnBootService。程序的示例程序的代码如下,重点代码的注解见代码中注释部分:
(1)配置文件“AndroidManifest.xml”
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="lulicheng.android.onboot"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" >
</uses-permission>
<uses-sdk android:minSdkVersion="10" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".StartOnBootActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".StartOnBootService" >
</service>
<receiver android:name=".BootBroadcastReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="lulicheng.android.onboot"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" >
</uses-permission>
<uses-sdk android:minSdkVersion="10" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".StartOnBootActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".StartOnBootService" >
</service>
<receiver android:name=".BootBroadcastReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
在该配置文件中,配置了各个组件的基本参数,在使用权限中需要加入“<uses-permissionandroid:name="android.permission.RECEIVE_BOOT_COMPLETED" >”权限,另外还有一点比较重要的就是在BootBroadcastReceiver中添加intent-filter,如此一来BootBroadcastReceiver的onReceiver方法才能被触发。
(2)广播监听类“BootBroadcastReceiver”
package lulicheng.android.onboot;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootBroadcastReceiver extends BroadcastReceiver {
// 系统启动完成
static final String ACTION = "android.intent.action.BOOT_COMPLETED";
@Override
public void onReceive(Context context, Intent intent) {
// 当收听到的事件是“BOOT_COMPLETED”时,就创建并启动相应的Activity和Service
if (intent.getAction().equals(ACTION)) {
// 开机启动的Activity
Intent activityIntent = new Intent(context, StartOnBootActivity.class);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 启动Activity
context.startActivity(activityIntent);
// 开机启动的Service
Intent serviceIntent = new Intent(context, StartOnBootService.class);
// 启动Service
context.startService(serviceIntent);
}
}
}
(3)“StartOnBootActivity”
package lulicheng.android.onboot;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class StartOnBootActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置Activity的显示内容为一个文本域
TextView aTextView = new TextView(this);
aTextView.setText("Wow!I started after cellphone boot.");
setContentView(aTextView);
}
}
(4)“StartOnBootService”
package lulicheng.android.onboot;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;
public class StartOnBootService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
// Service被启动时,将会有弹出消息提示[MyService onStart]
Toast.makeText(this, "[MyService onStart]", Toast.LENGTH_LONG).show();
}
}
package lulicheng.android.onboot;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootBroadcastReceiver extends BroadcastReceiver {
// 系统启动完成
static final String ACTION = "android.intent.action.BOOT_COMPLETED";
@Override
public void onReceive(Context context, Intent intent) {
// 当收听到的事件是“BOOT_COMPLETED”时,就创建并启动相应的Activity和Service
if (intent.getAction().equals(ACTION)) {
// 开机启动的Activity
Intent activityIntent = new Intent(context, StartOnBootActivity.class);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 启动Activity
context.startActivity(activityIntent);
// 开机启动的Service
Intent serviceIntent = new Intent(context, StartOnBootService.class);
// 启动Service
context.startService(serviceIntent);
}
}
}
(3)“StartOnBootActivity”
package lulicheng.android.onboot;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class StartOnBootActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置Activity的显示内容为一个文本域
TextView aTextView = new TextView(this);
aTextView.setText("Wow!I started after cellphone boot.");
setContentView(aTextView);
}
}
(4)“StartOnBootService”
package lulicheng.android.onboot;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;
public class StartOnBootService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
// Service被启动时,将会有弹出消息提示[MyService onStart]
Toast.makeText(this, "[MyService onStart]", Toast.LENGTH_LONG).show();
}
}
程序实现后,在手机或者模拟器上面安装,然后重启机器,就能在开机进入系统之后看到该Activity界面以及Service的弹出消息。程序的运行截屏如下图: