从官网上下载Tasky范例,解压缩后编译并运行后,得到一个任务管理程序,这个程序可以实现本地任务数据的增删改查功能,数据保存在sqlite中。并在ListView中显示任务数据。
一、数据接口Tasky.Core.Android模块
在这个模块中定义一个任务类Task、封装针对Task类进行sql数据操作的TaskDatabase类、封装针对Task操作的单例TaskRepositoryADO类(创建sqllite数据库文件、保存、修改、查询、删除Task实例的方法)、TaskManager类调用TaskRepositoryADO单例来提供数据增删改查操作方法。
各个类的作用:
- Task类:与数据库表Tasks相对应的类(实体类定义)
- TaskDatabaseADO类:在构造函数中编写sql创建sqlite数据库文件,并编写sql实现对Task实例的增删改查。是数据操作对象(实体关系映射)。
- TaskRepositoryADO类:提供了操作Task类实例的方法,并在构造函数中构造TaskDatabaseADO实例。这个类对数据操作进一步封装,并创建出sqlite文件。(数据访问层)
- TaskManager类:使用TaskRepositoryADO中的方法,创建操作Task类实例的方法。其实直接使用TaskRepositoryADO就可以完成数据操作了,之所以引入这个类,应该是为了后期程序的扩展性,比如把数据存放到服务器,则在创建一个与TaskRepositoryADO类功能平行的TaskRepositoryService类,修改TaskManager类调用新类提供的数据操作方法,从而实现不变动其他代码单元情况下完成对应用程序的功能扩展。(业务逻辑层)
这个模块中需要注意的地方如下:
- 创建sqlite数据库的方法:
public TaskDatabase (string dbPath)
{
string output = "";
path = dbPath;
bool exists = File.Exists (dbPath);
if (!exists) {
connection = new SqliteConnection ("Data Source=" + dbPath);
connection.Open ();
string[] commands = new string[] {
"CREATE TABLE [Item] (_id INTEGER PRIMARY KEY ASC, Name NTEXT, Notes NTEXT, Done INTEGER);"
};
foreach (var command in commands) {
using (var c = connection.CreateCommand ()) {
c.CommandText = command;
c.ExecuteNonQuery ();
}
}
} else {
}
Console.WriteLine (output);
}
其实就是配置SqliteConnection实例,指定Data Source后打开连接,执行Create Table Sql命令。
- sqlite的ADO操作封装:
Mono.Data.Sqlite.dll中提供的SqliteConnection 、SqliteDataReader、SqliteCommand等类。
二、Android应用程序
这个模块是有UI界面的程序,包括显示任务列表的主界面和任务操作(添加、修改、删除)界面,下面解析一下界面操作的实现步骤。
1、主界面
在任务的Resources\layout目录中,双击HomeScreen.axml,打开界面设计器,可以看到一个按钮控件和一个ListView控件。接着在Screens目录双击HomeScreen.cs文件,打开源码文件,下面对代码中的要点进行简要说明。
- Android中界面都是一个个的Activity,在设计界面的时候,要从Activity类继承一个子类并进行扩展。
- 定义的Activity子类上方有个属性说明(或叫做注解),免去在xml配置文件中注册Activity的麻烦。
[Activity (Label = "Tasky", MainLauncher = true, Icon="@drawable/icon")]
public class HomeScreen : Activity {
//其他代码
}
这里属性指定了这个Activity是项目的默认启动Activity,并指定其图标和标题。
- 从Activity类继承子类时,除了定义自己的数据成员、函数成员外,还需要重写一些重要的方法:
protected override void OnCreate (Bundle bundle)//Activity实例创建的时候执行,用于初始化界面元素和数据成员
{
base.OnCreate (bundle);
// set our layout to be the home screen
SetContentView(Resource.Layout.HomeScreen);
//其他代码
}
这里调用了SetContentView函数指定这个Activity加载Resources\layout目录中设计的界面。并使用FindViewById<T>函数获取界面中的元素实例,为这些元素实例绑定事件、做初始化等操作。
protected override void OnResume ()//界面需要刷新的时候触发,比如Activity第一次显示或覆盖在上方的Activity被关闭后需要重新绘制界面时执行这个函数。
{
base.OnResume ();
tasks = TaskManager.GetTasks();
// create our adapter
taskList = new Adapters.TaskListAdapter(this, tasks);
//Hook up our adapter to our ListView
taskListView.Adapter = taskList;
}
这个方法时界面需要刷新的时候自动调用的,因此将界面中的ListView控件与任务数据关联代码写在这里。首先调用上面数据操作模块中提供的方法获取所有Task对象,创建TaskListAdapter实例,并赋给界面的ListView控件的Adapter属性。这里是从数据库中从新查询所有任务数据的,保证在任务编辑Activity关闭后,从数据库中获取到最新的任务数据。
- 任务编辑Activity与HomeScreenActivity实现方式相似。
- 启动另外一个Activity的方式:
不需要传递参数可使用函数:StartActivity(typeof(Activity类名称));
需要传递参数则使用:
var taskDetails = new Intent (this, typeof (TaskDetailsScreen));
taskDetails.PutExtra ("TaskID", tasks[e.Position].ID);
StartActivity (taskDetails);
需要子Activity返回值的调用方式:
//第二参数为 requestcode(请求编码),主要是设定让 OnActivityResult 可以判断当初发出的动机(区分OnActivityResult函数是针对哪个请求触发的)
StartActivityForResult(typeof(Activity类名称), 1);
接着重写方法:
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
//如果当初的发的requestCode =1
if (requestCode == 1 && resultCode == Result.Ok)
{
Toast.MakeText(this, "选取结果(OnActivityResult):" + data.GetStringExtra("hero"), ToastLength.Short).Show();
}
}
子Activity要返回值必按如下方式设置返回值:
var intent = new Intent(this, typeof(Activity1));//第二个参数指定目标Activity实例的类型信息,这样可以让这种类型的Activity实例都触发执行OnActivityResult函数
//放入一个key 为hero 值为 黑寡妇
intent.PutExtra("hero", "黑寡妇");
//状态设为OK
SetResult(Result.Ok, intent);
//呼叫后将关闭此窗口
Finish();
- 最后重点说明ListView怎么和数据做绑定,怎么指定ListView每行数据显示的格式。所有的一切都是由ListView的Adapter属性决定的,需要从BaseAdapter类中继承一个自定义的适配器。
public class TaskListAdapter : BaseAdapter<Task> {......}
在类的构造函数中将ListView所在Activity实例、要显示的数据集合传递过来,并存储在私有数据成员中。接着重写几个关键虚方法。
public override Task this[int position]
{
get { return tasks[position]; }
}
public override long GetItemId (int position)
{
return position;
}
public override int Count
{
get { return tasks.Count; }
}
//这个方法最为关键,指定每行数据要显示的UI界面,以及这个UI界面中可视化控件显示的内容
public override Android.Views.View GetView (int position, Android.Views.View convertView, Android.Views.ViewGroup parent)
{
// 获取当前位置上要显示的数据对象
var item = tasks[position];
//如果convertView 非空则重用,否则从Resources\layout目录中加载一个设计好的视图界面,这样可以提高界面刷新效率
var view = (convertView ??
context.LayoutInflater.Inflate(
Resource.Layout.TaskListItem,
parent,
false)) as LinearLayout;
// 获取视图上的UI控件引用
var txtName = view.FindViewById<TextView>(Resource.Id.NameText);
var txtDescription = view.FindViewById<TextView>(Resource.Id.NotesText);
//设置UI控件显示的值
txtName.SetText (item.Name, TextView.BufferType.Normal);
txtDescription.SetText (item.Notes, TextView.BufferType.Normal);
//返回视图
return view;
}