概览在iOS开发中数据存储的方式可以归纳为两类:一类是存储为文件,另一类是存储到数据库。例如前面IOS开发系列—Objective-C之Foundation框架的文章中提到归档、plist文件存储,包括偏好设置其本质都是存储为文件,只是说归档或者plist文件存储可以选择保存到沙盒中,而偏好设置系统已经规定只能保存到沙盒的Library/Preferences目录。当然,文件存储并不作为本文的重点内容。本文重点还是说数据库存储,做过数据库开发的朋友应该知道,可以通过SQL直接访问数据库,也可以通过ORM进行对象关系映射访问数据库。这两种方式恰恰对应iOS中SQLite和Core Data的内容,在此将重点进行分析: SQLite Core Data FMDBSQLiteSQLite是目前主流的嵌入式关系型数据库,其最主要的特点就是轻量级、跨平台,当前很多嵌入式操作系统都将其作为数据库首选。虽然SQLite是一款轻型数据库,但是其功能也绝不亚于很多大型关系数据库。学习数据库就要学习其相关的定义、操作、查询语言,也就是大家日常说得SQL语句。和其他数据库相比,SQLite中的SQL语法并没有太大的差别,因此这里对于SQL语句的内容不会过多赘述,大家可以参考SQLite中其他SQL相关的内容,这里还是重点讲解iOS中如何使用SQLite构建应用程序。先看一下SQLite数据库的几个特点: 基于C语言开发的轻型数据库 在iOS中需要使用C语言语法进行数据库操作、访问(无法使用ObjC直接访问,因为libsqlite3框架基于C语言编写) SQLite中采用的是动态数据类型,即使创建时定义了一种类型,在实际操作时也可以存储其他类型,但是推荐建库时使用合适的类型(特别是应用需要考虑跨平台的情况时) 建立连接后通常不需要关闭连接(尽管可以手动关闭) 要使用SQLite很简单,如果在Mac OSX上使用可以考虑到SQLite网站下载命令行工具,也可以使用类似于SQLiteManager、Navicat for SQLite等工具。为了方便大家开发调试,建议在开发环境中安装上述工具。在iOS中操作SQLite数据库可以分为以下几步(注意先在项目中导入libsqlite3框架): 打开数据库,利用sqlite3_open()打开数据库会指定一个数据库文件保存路径,如果文件存在则直接打开,否则创建并打开。打开数据库会得到一个sqlite3类型的对象,后面需要借助这个对象进行其他操作。 执行SQL语句,执行SQL语句又包括有返回值的语句和无返回值语句。 对于无返回值的语句(如增加、删除、修改等)直接通过sqlite3_exec()函数执行; 对于有返回值的语句则首先通过sqlite3_prepare_v2()进行sql语句评估(语法检测),然后通过sqlite3_step()依次取出查询结果的每一行数据,对于每行数据都可以通过对应的sqlite3_column_类型()方法获得对应列的数据,如此反复循环直到遍历完成。当然,最后需要释放句柄。 在整个操作过程中无需管理数据库连接,对于嵌入式SQLite操作是持久连接(尽管可以通过sqlite3_close()关闭),不需要开发人员自己释放连接。纵观整个操作过程,其实与其他平台的开发没有明显的区别,较为麻烦的就是数据读取,在iOS平台中使用C进行数据读取采用了游标的形式,每次只能读取一行数据,较为麻烦。因此实际开发中不妨对这些操作进行封装:KCDbManager.h//// DbManager.h// DataAccess//// Created by Kenshin Cui on 14-3-29.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import <Foundation/Foundation.h>#import <sqlite3.h>#import "KCSingleton.h"@interface KCDbManager : NSObjectsingleton_interface(KCDbManager);#pragma mark - 属性#pragma mark 数据库引用,使用它进行数据库操作@property (nonatomic) sqlite3 *database;#pragma mark - 共有方法/** * 打开数据库 * * @param dbname 数据库名称 */-(void)openDb:(NSString *)dbname;/** * 执行无返回值的sql * * @param sql sql语句 */-(void)executeNonQuery:(NSString *)sql;/** * 执行有返回值的sql * * @param sql sql语句 * * @return 查询结果 */-(NSArray *)executeQuery:(NSString *)sql;@endKCDbManager.m//// DbManager.m// DataAccess//// Created by Kenshin Cui on 14-3-29.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import "KCDbManager.h"#import <sqlite3.h>#import "KCSingleton.h"#import "KCAppConfig.h"#ifndef kDatabaseName#define kDatabaseName @"myDatabase.db"#[email protected] KCDbManager()@[email protected] KCDbManagersingleton_implementation(KCDbManager)#pragma mark 重写初始化方法-(instancetype)init{ KCDbManager *manager; if((manager=[super init])) { [manager openDb:kDatabaseName]; } return manager;}-(void)openDb:(NSString *)dbname{ //取得数据库保存路径,通常保存沙盒Documents目录 NSString *directory=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSLog(@"%@",directory); NSString *filePath=[directory stringByAppendingPathComponent:dbname]; //如果有数据库则直接打开,否则创建并打开(注意filePath是ObjC中的字符串,需要转化为C语言字符串类型) if (SQLITE_OK ==sqlite3_open(filePath.UTF8String, &_database)) { NSLog(@"数据库打开成功!"); }else{ NSLog(@"数据库打开失败!"); }}-(void)executeNonQuery:(NSString *)sql{ char *error; //单步执行sql语句,用于插入、修改、删除 if (SQLITE_OK!=sqlite3_exec(_database, sql.UTF8String, NULL, NULL,&error)) { NSLog(@"执行SQL语句过程中发生错误!错误信息:%s",error); }}-(NSArray *)executeQuery:(NSString *)sql{ NSMutableArray *rows=[NSMutableArray array];//数据行 //评估语法正确性 sqlite3_stmt *stmt; //检查语法正确性 if (SQLITE_OK==sqlite3_prepare_v2(_database, sql.UTF8String, -1, &stmt, NULL)) { //单步执行sql语句 while (SQLITE_ROW==sqlite3_step(stmt)) { int columnCount= sqlite3_column_count(stmt); NSMutableDictionary *dic=[NSMutableDictionary dictionary]; for (int i=0; i<columnCount; i++) { const char *name= sqlite3_column_name(stmt, i);//取得列名 const unsigned char *value= sqlite3_column_text(stmt, i);//取得某列的值 dic[[NSString stringWithUTF8String:name]]=[NSString stringWithUTF8String:(const char *)value]; } [rows addObject:dic]; } } //释放句柄 sqlite3_finalize(stmt); return rows;}@end在上面的类中对于数据库操作进行了封装,封装之后数据操作更加方便,同时所有的语法都由C转换成了ObjC。下面仍然以微博查看为例进行SQLite演示。当然实际开发中微博数据是从网络读取的,但是考虑到缓存问题,通常会选择将微博数据保存到本地,下面的Demo演示了将数据存放到本地数据库以及数据读取的过程。当然,实际开发中并不会在视图控制器中直接调用数据库操作方法,在这里通常会引入两个概念Model和Service。Model自不必多说,就是MVC中的模型。而Service指的是操作数据库的服务层,它封装了对于Model的基本操作方法,实现具体的业务逻辑。为了解耦,在控制器中是不会直接接触数据库的,控制器中只和模型(模型是领域的抽象)、服务对象有关系,借助服务层对模型进行各类操作,模型的操作反应到数据库中就是对表中数据的操作。具体关系如下:要完成上述功能,首先定义一个应用程序全局对象进行数据库、表的创建。为了避免每次都创建数据库和表出错,这里利用了偏好设置进行保存当前创建状态(其实这也是数据存储的一部分),如果创建过了数据库则不再创建,否则创建数据库和表。KCDatabaseCreator.m//// KCDatabaseCreator.m// DataAccess//// Created by Kenshin Cui on 14-3-29.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import "KCDatabaseCreator.h"#import "KCDbManager.h"@implementation KCDatabaseCreator+(void)initDatabase{ NSString *[email protected]"IsCreatedDb"; NSUserDefaults *defaults=[[NSUserDefaults alloc]init]; if ([[defaults valueForKey:key] intValue]!=1) { [self createUserTable]; [self createStatusTable]; [defaults setValue:@1 forKey:key]; }}+(void)createUserTable{ NSString *[email protected]"CREATE TABLE User (Id integer PRIMARY KEY AUTOINCREMENT,name text,screenName text, profileImageUrl text,mbtype text,city text)"; [[KCDbManager sharedKCDbManager] executeNonQuery:sql];}+(void)createStatusTable{ NSString *[email protected]"CREATE TABLE Status (Id integer PRIMARY KEY AUTOINCREMENT,source text,createdAt date,\"text\" text,user integer REFERENCES User (Id))"; [[KCDbManager sharedKCDbManager] executeNonQuery:sql];}@end其次,定义数据模型,这里定义用户User和微博Status两个数据模型类。注意模型应该尽量保持其单纯性,仅仅是简单的POCO,不要引入视图、控制器等相关内容。KCUser.h//// KCUser.h// UrlConnection//// Created by Kenshin Cui on 14-3-22.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import <Foundation/Foundation.h>@interface KCUser : NSObject#pragma mark 编号@property (nonatomic,strong) NSNumber *Id;#pragma mark 用户名@property (nonatomic,copy) NSString *name;#pragma mark 用户昵称@property (nonatomic,copy) NSString *screenName;#pragma mark 头像@property (nonatomic,copy) NSString *profileImageUrl;#pragma mark 会员类型@property (nonatomic,copy) NSString *mbtype;#pragma mark 城市@property (nonatomic,copy) NSString *city;#pragma mark - 动态方法/** * 初始化用户 * * @param name 用户名 * @param city 所在城市 * * @return 用户对象 */-(KCUser *)initWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city;/** * 使用字典初始化用户对象 * * @param dic 用户数据 * * @return 用户对象 */-(KCUser *)initWithDictionary:(NSDictionary *)dic;#pragma mark - 静态方法+(KCUser *)userWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city;@endKCUser.m//// KCUser.m// UrlConnection//// Created by Kenshin Cui on 14-3-22.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import "KCUser.h"@implementation KCUser-(KCUser *)initWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{ if (self=[super init]) { self.name=name; self.screenName=screenName; self.profileImageUrl=profileImageUrl; self.mbtype=mbtype; self.city=city; } return self;}-(KCUser *)initWithDictionary:(NSDictionary *)dic{ if (self=[super init]) { [self setValuesForKeysWithDictionary:dic]; } return self;}+(KCUser *)userWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{ KCUser *user=[[KCUser alloc]initWithName:name screenName:screenName profileImageUrl:profileImageUrl mbtype:mbtype city:city]; return user;}@endKCStatus.h//// KCStatus.h// UITableView//// Created by Kenshin Cui on 14-3-1.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import <Foundation/Foundation.h>#import "KCUser.h"@interface KCStatus : NSObject#pragma mark - 属性@property (nonatomic,strong) NSNumber *Id;//微博[email protected] (nonatomic,strong) KCUser *user;//发送用户@property (nonatomic,copy) NSString *createdAt;//创建时间@property (nonatomic,copy) NSString *source;//设备来源@property (nonatomic,copy) NSString *text;//微博内容#pragma mark - 动态方法/** * 初始化微博数据 * * @param createAt 创建日期 * @param source 来源 * @param text 微博内容 * @param user 发送用户 * * @return 微博对象 */-(KCStatus *)initWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text user:(KCUser *)user;/** * 初始化微博数据 * * @param profileImageUrl 用户头像 * @param mbtype 会员类型 * @param createAt 创建日期 * @param source 来源 * @param text 微博内容 * @param userId 用户编号 * * @return 微博对象 */-(KCStatus *)initWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text userId:(int)userId;/** * 使用字典初始化微博对象 * * @param dic 字典数据 * * @return 微博对象 */-(KCStatus *)initWithDictionary:(NSDictionary *)dic;#pragma mark - 静态方法/** * 初始化微博数据 * * @param createAt 创建日期 * @param source 来源 * @param text 微博内容 * @param user 发送用户 * * @return 微博对象 */+(KCStatus *)statusWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text user:(KCUser *)user;/** * 初始化微博数据 * * @param profileImageUrl 用户头像 * @param mbtype 会员类型 * @param createAt 创建日期 * @param source 来源 * @param text 微博内容 * @param userId 用户编号 * * @return 微博对象 */+(KCStatus *)statusWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text userId:(int)userId;@endKCStatus.m//// KCStatus.m// UITableView//// Created by Kenshin Cui on 14-3-1.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import "KCStatus.h"@implementation KCStatus-(KCStatus *)initWithDictionary:(NSDictionary *)dic{ if (self=[super init]) { [self setValuesForKeysWithDictionary:dic]; self.user=[[KCUser alloc]init]; self.user.Id=dic[@"user"]; } return self;}-(KCStatus *)initWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text user:(KCUser *)user{ if (self=[super init]) { self.createdAt=createAt; self.source=source; self.text=text; self.user=user; } return self;}-(KCStatus *)initWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text userId:(int)userId{ if (self=[super init]) { self.createdAt=createAt; self.source=source; self.text=text; KCUser *user=[[KCUser alloc]init]; user.Id=[NSNumber numberWithInt:userId]; self.user=user; } return self;}-(NSString *)source{ return [NSString stringWithFormat:@"来自 %@",_source];}+(KCStatus *)statusWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text user:(KCUser *)user{ KCStatus *status=[[KCStatus alloc]initWithCreateAt:createAt source:source text:text user:user]; return status;}+(KCStatus *)statusWithCreateAt:(NSString *)createAt source:(NSString *)source text:(NSString *)text userId:(int)userId{ KCStatus *status=[[KCStatus alloc]initWithCreateAt:createAt source:source text:text userId:userId]; return status;}@end然后,编写服务类,进行数据的增、删、改、查操作,由于服务类方法同样不需要过多的配置,因此定义为单例,保证程序中只有一个实例即可。服务类中调用前面封装的数据库方法将对数据库的操作转换为对模型的操作。KCUserService.h//// KCUserService.h// DataAccess//// Created by Kenshin Cui on 14-3-29.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import <Foundation/Foundation.h>#import "KCUser.h"#import "KCSingleton.h"@interface KCUserService : NSObjectsingleton_interface(KCUserService)/** * 添加用户信息 * * @param user 用户对象 */-(void)addUser:(KCUser *)user;/** * 删除用户 * * @param user 用户对象 */-(void)removeUser:(KCUser *)user;/** * 根据用户名删除用户 * * @param name 用户名 */-(void)removeUserByName:(NSString *)name;/** * 修改用户内容 * * @param user 用户对象 */-(void)modifyUser:(KCUser *)user;/** * 根据用户编号取得用户 * * @param Id 用户编号 * * @return 用户对象 */-(KCUser *)getUserById:(int)Id;/** * 根据用户名取得用户 * * @param name 用户名 * * @return 用户对象 */-(KCUser *)getUserByName:(NSString *)name;@endKCUserService.m//// KCUserService.m// DataAccess//// Created by Kenshin Cui on 14-3-29.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import "KCUserService.h"#import "KCUser.h"#import "KCDbManager.h"@implementation KCUserServicesingleton_implementation(KCUserService)-(void)addUser:(KCUser *)user{ NSString *sql=[NSString stringWithFormat:@"INSERT INTO User (name,screenName, profileImageUrl,mbtype,city) VALUES('%@','%@','%@','%@','%@')",user.name,user.screenName, user.profileImageUrl,user.mbtype,user.city]; [[KCDbManager sharedKCDbManager] executeNonQuery:sql];}-(void)removeUser:(KCUser *)user{ NSString *sql=[NSString stringWithFormat:@"DELETE FROM User WHERE Id='%@'",user.Id]; [[KCDbManager sharedKCDbManager] executeNonQuery:sql];}-(void)removeUserByName:(NSString *)name{ NSString *sql=[NSString stringWithFormat:@"DELETE FROM User WHERE name='%@'",name]; [[KCDbManager sharedKCDbManager] executeNonQuery:sql];}-(void)modifyUser:(KCUser *)user{ NSString *sql=[NSString stringWithFormat:@"UPDATE User SET name='%@',screenName='%@',profileImageUrl='%@',mbtype='%@',city='%@' WHERE Id='%@'",user.name,user.screenName,user.profileImageUrl,user.mbtype,user.city,user.Id]; [[KCDbManager sharedKCDbManager] executeNonQuery:sql];}-(KCUser *)getUserById:(int)Id{ KCUser *user=[[KCUser alloc]init]; NSString *sql=[NSString stringWithFormat:@"SELECT name,screenName,profileImageUrl,mbtype,city FROM User WHERE Id='%i'", Id]; NSArray *rows= [[KCDbManager sharedKCDbManager] executeQuery:sql]; if (rows&&rows.count>0) { [user setValuesForKeysWithDictionary:rows[0]]; } return user;}-(KCUser *)getUserByName:(NSString *)name{ KCUser *user=[[KCUser alloc]init]; NSString *sql=[NSString stringWithFormat:@"SELECT Id, name,screenName,profileImageUrl,mbtype,city FROM User WHERE name='%@'", name]; NSArray *rows= [[KCDbManager sharedKCDbManager] executeQuery:sql]; if (rows&&rows.count>0) { [user setValuesForKeysWithDictionary:rows[0]]; } return user;}@endKCStatusService.h//// KCStatusService.h// DataAccess//// Created by Kenshin Cui on 14-3-29.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import <Foundation/Foundation.h>#import "KCSingleton.h"@class KCStatus;@interface KCStatusService : NSObjectsingleton_interface(KCStatusService)/** * 添加微博信息 * * @param status 微博对象 */-(void)addStatus:(KCStatus *)status;/** * 删除微博 * * @param status 微博对象 */-(void)removeStatus:(KCStatus *)status;/** * 修改微博内容 * * @param status 微博对象 */-(void)modifyStatus:(KCStatus *)status;/** * 根据编号取得微博 * * @param Id 微博编号 * * @return 微博对象 */-(KCStatus *)getStatusById:(int)Id;/** * 取得所有微博对象 * * @return 所有微博对象 */-(NSArray *)getAllStatus;@endKCStatusService.m//// KCStatusService.m// DataAccess//// Created by Kenshin Cui on 14-3-29.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import "KCStatusService.h"#import "KCDbManager.h"#import "KCStatus.h"#import "KCUserService.h"#import "KCSingleton.h"@interface KCStatusService(){ }@[email protected] KCStatusServicesingleton_implementation(KCStatusService)-(void)addStatus:(KCStatus *)status{ NSString *sql=[NSString stringWithFormat:@"INSERT INTO Status (source,createdAt,\"text\" ,user) VALUES('%@','%@','%@','%@')",status.source,status.createdAt,status.text,status.user.Id]; [[KCDbManager sharedKCDbManager] executeNonQuery:sql];}-(void)removeStatus:(KCStatus *)status{ NSString *sql=[NSString stringWithFormat:@"DELETE FROM Status WHERE Id='%@'",status.Id]; [[KCDbManager sharedKCDbManager] executeNonQuery:sql];}-(void)modifyStatus:(KCStatus *)status{ NSString *sql=[NSString stringWithFormat:@"UPDATE Status SET source='%@',createdAt='%@',\"text\"='%@' ,user='%@' WHERE Id='%@'",status.source,status.createdAt,status.text,status.user, status.Id]; [[KCDbManager sharedKCDbManager] executeNonQuery:sql];}-(KCStatus *)getStatusById:(int)Id{ KCStatus *status=[[KCStatus alloc]init]; NSString *sql=[NSString stringWithFormat:@"SELECT Id, source,createdAt,\"text\" ,user FROM Status WHERE Id='%i'", Id]; NSArray *rows= [[KCDbManager sharedKCDbManager] executeQuery:sql]; if (rows&&rows.count>0) { [status setValuesForKeysWithDictionary:rows[0]]; status.user=[[KCUserService sharedKCUserService] getUserById:[(NSNumber *)rows[0][@"user"] intValue]] ; } return status;}-(NSArray *)getAllStatus{ NSMutableArray *array=[NSMutableArray array]; NSString *[email protected]"SELECT Id, source,createdAt,\"text\" ,user FROM Status ORDER BY Id"; NSArray *rows= [[KCDbManager sharedKCDbManager] executeQuery:sql]; for (NSDictionary *dic in rows) { KCStatus *status=[self getStatusById:[(NSNumber *)dic[@"Id"] intValue]]; [array addObject:status]; } return array;}@end最后,在视图控制器中调用相应的服务层进行各类数据操作,在下面的代码中分别演示了增、删、改、查四类操作。KCMainViewController.m//// KCMainTableViewController.m// DataAccess//// Created by Kenshin Cui on 14-3-29.// Copyright (c) 2014年 Kenshin Cui. All rights reserved.//#import "KCMainTableViewController.h"#import "KCDbManager.h"#import "KCDatabaseCreator.h"#import "KCUser.h"#import "KCStatus.h"#import "KCUserService.h"#import "KCStatusService.h"#import "KCStatusTableViewCell.h"@interface KCMainTableViewController (){ NSArray *_status; NSMutableArray *_statusCells;}@[email protected] KCMainTableViewController- (void)viewDidLoad { [super viewDidLoad]; [KCDatabaseCreator initDatabase]; // [self addUsers];// [self removeUser];// [self modifyUserInfo]; // [self addStatus]; [self loadStatusData]; }-(void)addUsers{ KCUser *user1=[KCUser userWithName:@"Binger" screenName:@"冰儿" profileImageUrl:@"binger.jpg" mbtype:@"mbtype.png" city:@"北京"]; [[KCUserService sharedKCUserService] addUser:user1]; KCUser *user2=[KCUser userWithName:@"Xiaona" screenName:@"小娜" profileImageUrl:@"xiaona.jpg" mbtype:@"mbtype.png" city:@"北京"]; [[KCUserService sharedKCUserService] addUser:user2]; KCUser *user3=[KCUser userWithName:@"Lily" screenName:@"丽丽" profileImageUrl:@"lily.jpg" mbtype:@"mbtype.png" city:@"北京"]; [[KCUserService sharedKCUserService] addUser:user3]; KCUser *user4=[KCUser userWithName:@"Qianmo" screenName:@"阡陌" profileImageUrl:@"qianmo.jpg" mbtype:@"mbtype.png" city:@"北京"]; [[KCUserService sharedKCUserService] addUser:user4]; KCUser *user5=[KCUser userWithName:@"Yanyue" screenName:@"炎月" profileImageUrl:@"yanyue.jpg" mbtype:@"mbtype.png" city:@"北京"]; [[KCUserService sharedKCUserService] addUser:user5];}-(void)addStatus{ KCStatus *status1=[KCStatus statusWithCreateAt:@"9:00" source:@"iPhone 6" text:@"一只雪猴在日本边泡温泉边玩iPhone的照片,获得了\"2014年野生动物摄影师\"大赛特等奖。一起来为猴子配个词" userId:1]; [[KCStatusService sharedKCStatusService] addStatus:status1]; KCStatus *status2=[KCStatus statusWithCreateAt:@"9:00" source:@"iPhone 6" text:@"一只雪猴在日本边泡温泉边玩iPhone的照片,获得了\"2014年野生动物摄影师\"大赛特等奖。一起来为猴子配个词" userId:1]; [[KCStatusService sharedKCStatusService] addStatus:status2]; KCStatus *status3=[KCStatus statusWithCreateAt:@"9:30" source:@"iPhone 6" text:@"【我们送iPhone6了 要求很简单】真心回馈粉丝,小编觉得现在最好的奖品就是iPhone6了。今起到12月31日,关注我们,转发微博,就有机会获iPhone6(奖品可能需要等待)!每月抽一台[鼓掌]。不费事,还是试试吧,万一中了呢" userId:2]; [[KCStatusService sharedKCStatusService] addStatus:status3]; KCStatus *status4=[KCStatus statusWithCreateAt:@"9:45" source:@"iPhone 6" text:@"重大新闻:蒂姆库克宣布出柜后,ISIS战士怒扔iPhone,沙特神职人员呼吁人们换回iPhone 4。[via Pan-Arabia Enquirer]" userId:3]; [[KCStatusService sharedKCStatusService] addStatus:status4]; KCStatus *status5=[KCStatus statusWithCreateAt:@"10:05" source:@"iPhone 6" text:@"小伙伴们,有谁知道怎么往Iphone4S里倒东西?倒入的东西又该在哪里找?用了Iphone这么长时间,还真的不知道怎么弄!有谁知道啊?谢谢!" userId:4]; [[KCStatusService sharedKCStatusService] addStatus:status5]; KCStatus *status6=[KCStatus statusWithCreateAt:@"10:07" source:@"iPhone 6" text:@"在音悦台iPhone客户端里发现一个悦单《Infinite 金明洙》,推荐给大家! " userId:1]; [[KCStatusService sharedKCStatusService] addStatus:status6]; KCStatus *status7=[KCStatus statusWithCreateAt:@"11:20" source:@"iPhone 6" text:@"如果sony吧mp3播放器产品发展下去,不贪图手头节目源的现实利益,就木有苹果的ipod,也就木有iphone。柯达类似的现实利益,不自我革命的案例也是一种巨头的宿命。" userId:2]; [[KCStatusService sharedKCStatusService] addStatus:status7]; KCStatus *status8=[KCStatus statusWithCreateAt:@"13:00" source:@"iPhone 6" text:@"【iPhone 7 Plus】新买的iPhone 7 Plus ,如何?够酷炫么?" userId:2]; [[KCStatusService sharedKCStatusService] addStatus:status8]; KCStatus *status9=[KCStatus statusWithCreateAt:@"13:24" source:@"iPhone 6" text:@"自拍神器#卡西欧TR500#,tr350S~价格美丽,行货,全国联保~iPhone6 iPhone6Plus卡西欧TR150 TR200 TR350 TR350S全面到货 招收各种代理![给力]微信:39017366" userId:3]; [[KCStatusService sharedKCStatusService] addStatus:status9]; KCStatus *status10=[KCStatus statusWithCreateAt:@"13:26" source:@"iPhone 6" text:@"猜到猴哥玩手机时所思所想者,再奖iPhone一部。(奖品由“2014年野生动物摄影师”评委会颁发)" userId:3]; [[KCStatusService sharedKCStatusService] addStatus:status10];}-(void)removeUser{ //注意在SQLite中区分大小写 [[KCUserService sharedKCUserService] removeUserByName:@"Yanyue"];}-(void)modifyUserInfo{ KCUser *user1= [[KCUserService sharedKCUserService]getUserByName:@"Xiaona"]; [email protected]"上海"; [[KCUserService sharedKCUserService] modifyUser:user1]; KCUser *user2= [[KCUserService sharedKCUserService]getUserByName:@"Lily"]; [email protected]"深圳"; [[KCUserService sharedKCUserService] modifyUser:user2];}#pragma mark 加载数据-(void)loadStatusData{ _statusCells=[[NSMutableArray alloc]init]; _status=[[KCStatusService sharedKCStatusService]getAllStatus]; [_status enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { KCStatusTableViewCell *cell=[[KCStatusTableViewCell alloc]init]; cell.status=(KCStatus *)obj; [_statusCells addObject:cell]; }]; NSLog(@"%@",[_status lastObject]);}#pragma mark - Table view data source- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1;}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return _status.count;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *[email protected]"myTableViewCellIdentityKey1"; KCStatusTableViewCell *cell=[self.tableView dequeueReusableCellWithIdentifier:identtityKey]; if(cell==nil){ cell=[[KCStatusTableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identtityKey]; } cell.status=_status[indexPath.row]; return cell;}-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return ((KCStatusTableViewCell *)_statusCells[indexPath.row]).height;}-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ return 20.0f;}@end项目目录结构:运行效果:Core Data基本概念当前,各类应用开发中只要牵扯到数据库操作通常都会用到一个概念“对象关系映射(ORM)”。例如在Java平台使用Hibernate,在.NET平台使用Entity Framework、Linq、NHibernate等。在iOS中也不例外,iOS中ORM框架首选Core Data,这是官方推荐的,不需要借助第三方框架。无论是哪种平台、哪种技术,ORM框架的作用都是相同的,那就是将关系数据库中的表(准确的说是实体)转换为程序中的对象,其本质还是对数据库的操作(例如Core Data中如果存储类型配置为SQLite则本质还是操作的SQLite数据库)。细心的朋友应该已经注意到,在上面的SQLite中其实我们在KCMainViewController中进行的数据库操作已经转换为了对象操作,服务层中的方法中已经将对数据库的操作封装起来,转换为了对Model的操作,这种方式已经是面向对象的。上述通过将对象映射到实体的过程完全是手动完成的,相对来说操作比较复杂,就拿对KCStatus对象的操作来说:首先要手动创建数据库(Status表),其次手动创建模型KCStatus,接着创建服务层KCStatusService。Core Data正是为了解决这个问题而产生的,它将数据库的创建、表的创建、对象和表的转换等操作封装起来,简化了我们的操作(注意Core Data只是将对象关系的映射简化了,并不是把服务层替代了,这一点大家需要明白)。使用Core Data进行数据库存取并不需要手动创建数据库,这个过程完全由Core Data框架完成,开发人员面对的是模型,主要的工作就是把模型创建起来,具体数据库如何创建则不用管。在iOS项目中添加“Data Model”文件。然后在其中创建实体和关系:模型创建的过程中需要注意: 实体对象不需要创建ID主键,Attributes中应该是有意义属性(创建过程中应该考虑对象的属性而不是数据库中表有几个字段,尽管多数属性会对应表的字段)。 所有的属性应该指定具体类型(尽管在SQLite中可以不指定),因为实体对象会对应生成ObjC模型类。 实体对象中其他实体对象类型的属性应该通过Relationships建立,并且注意实体之间的对应关系(例如一个用户有多条微博,而一条微博则只属于一个用户,用户和微博形成一对多的关系)。 以上模型创建后,接下来就是根据上面的模型文件(.xcdatamodeld文件)生成具体的实体类。在Xcode中添加“NSManagedObject Subclass”文件,按照步骤选择创建的模型及实体,Xcode就会根据所创建模型生成具体的实体类。User.h//// User.h// CoreData//// Created by Kenshin Cui on 14/03/27.// Copyright (c) 2014年 cmjstudio. All rights reserved.//#import <Foundation/Foundation.h>#import <CoreData/CoreData.h>@class Status;@interface User : [email protected] (nonatomic, retain) NSString * city;@property (nonatomic, retain) NSString * mbtype;@property (nonatomic, retain) NSString * name;@property (nonatomic, retain) NSString * profileImageUrl;@property (nonatomic, retain) NSString * screenName;@property (nonatomic, retain) NSSet *statuses;@[email protected] User (CoreDataGeneratedAccessors)- (void)addStatusesObject:(Status *)value;- (void)removeStatusesObject:(Status *)value;- (void)addStatuses:(NSSet *)values;- (void)removeStatuses:(NSSet *)values;@endUser.m//// User.m// CoreData//// Created by Kenshin Cui on 14/03/27.// Copyright (c) 2014年 cmjstudio. All rights reserved.//#import "User.h"#import "Status.h"@implementation [email protected] city;@dynamic mbtype;@dynamic name;@dynamic profileImageUrl;@dynamic screenName;@dynamic statuses;@endStatus.h//// Status.h// CoreData//// Created by Kenshin Cui on 14/03/27.// Copyright (c) 2014年 cmjstudio. All rights reserved.//#import <Foundation/Foundation.h>#import <CoreData/CoreData.h>@interface Status : [email protected] (nonatomic, retain) NSDate * createdAt;@property (nonatomic, retain) NSString * source;@property (nonatomic, retain) NSString * text;@property (nonatomic, retain) NSManagedObject *user;@endStatus.m//// Status.m// CoreData//// Created by Kenshin Cui on 14/03/27.// Copyright (c) 2014年 cmjstudio. All rights reserved.//#import "Status.h"@implementation [email protected] createdAt;@dynamic source;@dynamic text;@dynamic user;@end很显然,通过模型生成类的过程相当简单,通常这些类也不需要手动维护,如果模型发生的变化只要重新生成即可。有几点需要注意: 所有的实体类型都继承于NSManagedObject,每个NSManagedObject对象对应着数据库中一条记录。 集合属性(例如User中的status)生成了访问此属性的分类方法。 使用@dynamic代表具体属性实现,具体实现细节不需要开发人员关心。 当然,了解了这些还不足以完成数据的操作。究竟Core Data具体的设计如何,要完成数据的存取我们还需要了解一下Core Data几个核心的类。 Persistent Object Store:可以理解为存储持久对象的数据库(例如SQLite,注意Core Data也支持其他类型的数据存储,例如xml、二进制数据等)。 Managed Object Model:对象模型,对应Xcode中创建的模型文件。 Persistent Store Coordinator:对象模型和实体类之间的转换协调器,用于管理不同存储对象的上下文。 Managed Object Context:对象管理上下文,负责实体对象和数据库之间的交互。Core Data使用Core Data使用起来相对直接使用SQLite3的API而言更加的面向对象,操作过程通常分为以下几个步骤:1.创建管理上下文创建管理上下可以细分为:加载模型文件->指定数据存储路径->创建对应数据类型的存储->创建管理对象上下方并指定存储。经过这几个步骤之后可以得到管理对象上下文NSManagedObjectContext,以后所有的数据操作都由此对象负责。同时如果是第一次创建上下文,Core Data会自动创建存储文件(例如这里使用SQLite3存储),并且根据模型对象创建对应的表结构。下图为第一次运行生成的数据库及相关映射文件:为了方便后面使用,NSManagedObjectContext对象可以作为单例或静态属性来保存,下面是创建的管理对象上下文的主要代码:-(NSManagedObjectContext *)createDbContext{ NSManagedObjectContext *context; //打开模型文件,参数为nil则打开包中所有模型文件并合并成一个 NSManagedObjectModel *model=[NSManagedObjectModel mergedModelFromBundles:nil]; //创建解析器 NSPersistentStoreCoordinator *storeCoordinator=[[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:model]; //创建数据库保存路径 NSString *dir=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSLog(@"%@",dir); NSString *path=[dir stringByAppendingPathComponent:@"myDatabase.db"]; NSURL *url=[NSURL fileURLWithPath:path]; //添加SQLite持久存储到解析器 NSError *error; [storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error]; if(error){ NSLog(@"数据库打开失败!错误:%@",error.localizedDescription); }else{ context=[[NSManagedObjectContext alloc]init]; context.persistentStoreCoordinator=storeCoordinator; NSLog(@"数据库打开成功!"); } return context;}2.查询数据对于有条件的查询,在Core Data中是通过谓词来实现的。首先创建一个请求,然后设置请求条件,最后调用上下文执行请求的方法。-(void)addUserWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{ //添加一个对象 User *us= [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:self.context]; us.name=name; us.screenName=screenName; us.profileImageUrl=profileImageUrl; us.mbtype=mbtype; us.city=city; NSError *error; //保存上下文 if (![self.context save:&error]) { NSLog(@"添加过程中发生错误,错误信息:%@!",error.localizedDescription); }}如果有多个条件,只要使用谓词组合即可,那么对于关联对象条件怎么查询呢?这里分为两种情况进行介绍:a.查找一个对象只有唯一一个关联对象的情况,例如查找用户名为“Binger”的微博(一个微博只能属于一个用户),通过keypath查询-(NSArray *)getStatusesByUserName:(NSString *)name{ NSFetchRequest *request=[NSFetchRequest fetchRequestWithEntityName:@"Status"]; request.predicate=[NSPredicate predicateWithFormat:@"user.name=%@",name]; NSArray *array=[self.context executeFetchRequest:request error:nil]; return array;}此时如果跟踪Core Data生成的SQL语句会发现其实就是把Status表和User表进行了关联查询(JOIN连接)。b.查找一个对象有多个关联对象的情况,例如查找发送微博内容中包含“Watch”并且用户昵称为“小娜”的用户(一个用户有多条微博),此时可以充分利用谓词进行过滤。 -(NSArray *)getStatusesByUserName:(NSString *)name{ NSFetchRequest *request=[NSFetchRequest fetchRequestWithEntityName:@"Status"]; request.predicate=[NSPredicate predicateWithFormat:@"user.name=%@",name]; NSArray *array=[self.context executeFetchRequest:request error:nil]; return array;}注意如果单纯查找微博中包含“Watch”的用户,直接查出对应的微博,然后通过每个微博的user属性即可获得用户,此时就不用使用额外的谓词过滤条件。3.插入数据插入数据需要调用实体描述对象NSEntityDescription返回一个实体对象,然后设置对象属性,最后保存当前上下文即可。这里需要注意,增、删、改操作完最后必须调用管理对象上下文的保存方法,否则操作不会执行。-(void)addUserWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{ //添加一个对象 User *us= [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:self.context]; us.name=name; us.screenName=screenName; us.profileImageUrl=profileImageUrl; us.mbtype=mbtype; us.city=city; NSError *error; //保存上下文 if (![self.context save:&error]) { NSLog(@"添加过程中发生错误,错误信息:%@!",error.localizedDescription); }}4.删除数据删除数据可以直接调用管理对象上下文的deleteObject方法,删除完保存上下文即可。注意,删除数据前必须先查询到对应对象。-(void)removeUser:(User *)user{ [self.context deleteObject:user]; NSError *error; if (![self.context save:&error]) { NSLog(@"删除过程中发生错误,错误信息:%@!",error.localizedDescription); }}5.修改数据修改数据首先也是取出对应的实体对象,然后通过修改对象的属性,最后保存上下文。-(void)modifyUserWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{ User *us=[self getUserByName:name]; us.screenName=screenName; us.profileImageUrl=profileImageUrl; us.mbtype=mbtype; us.city=city; NSError *error; if (![self.context save:&error]) { NSLog(@"修改过程中发生错误,错误信息:%@",error.localizedDescription); }}调试虽然Core Data(如果使用SQLite数据库)操作最终转换为SQL操作,但是调试起来却不像操作SQL那么方便。特别是对于初学者而言经常出现查询报错的问题,如果能看到最终生成的SQL语句自然对于调试很有帮助。事实上在Xcode中是支持Core Data调试的,具体操作:Product-Scheme-Edit Scheme-Run-Arguments中依次添加两个参数(注意参数顺序不能错):-com.apple.CoreData.SQLDebug、1。然后在运行程序过程中如果操作了数据库就会将SQL语句打印在输出面板。注意:如果模型发生了变化,此时可以重新生成实体类文件,但是所生成的数据库并不会自动更新,这时需要考虑重新生成数据库并迁移原有的数据。FMDB基本使用相比于SQLite3来说Core Data存在着诸多优势,它面向对象,开发人员不必过多的关心更多数据库操作知识,同时它基于ObjC操作,书写更加优雅等。但是它本身也存在着一定的限制,例如如果考虑到跨平台,则只能选择SQLite,因为无论是iOS还是Android都可以使用同一个数据库,降低了开发成本和维护成本。其次是当前多数ORM框架都存在的性能问题,因为ORM最终转化为SQL操作,其中牵扯到模型数据转化,其性能自然比不上直接使用SQL操作数据库。那么有没有更好的选择呢?答案就是对SQLite进行封装。其实通过前面对于SQLite的分析,大家应该已经看到KCDbManager就是对于SQLite封装的结果,开发人员面对的只有SQL和ObjC方法,不用过多libsqlite3的C语言API。但它毕竟只是一个简单的封装,还有更多的细节没有考虑,例如如何处理并发安全性,如何更好的处理事务等。因此,这里推荐使用第三方框架FMDB,整个框架非常轻量级但又不失灵活性,也是很多企业开发的首选。1.FMDB既然是对于libsqlite3框架的封装,自然使用起来也是类似的,使用前也要打开一个数据库,这个数据库文件存在则直接打开否则会创建并打开。这里FMDB引入了一个FMDatabase对象来表示数据库,打开数据库和后面的数据库操作全部依赖此对象。下面是打开数据库获得FMDatabase对象的代码:-(void)openDb:(NSString *)dbname{ //取得数据库保存路径,通常保存沙盒Documents目录 NSString *directory=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSLog(@"%@",directory); NSString *filePath=[directory stringByAppendingPathComponent:dbname]; //创建FMDatabase对象 self.database=[FMDatabase databaseWithPath:filePath]; //打开数据上 if ([self.database open]) { NSLog(@"数据库打开成功!"); }else{ NSLog(@"数据库打开失败!"); }}注意:dataWithPath中的路径参数一般会选择保存到沙箱中的Documents目录中;如果这个参数设置为nil则数据库会在内存中创建;如果设置为@””则会在沙箱中的临时目录创建,应用程序关闭则文件删除。2.对于数据库的操作跟前面KCDbManager的封装是类似的,在FMDB中FMDatabase类提供了两个方法executeUpdate:和executeQuery:分别用于执行无返回结果的查询和有返回结果的查询。当然这两个方法有很多的重载这里就不详细解释了。唯一需要指出的是,如果调用有格式化参数的sql语句时,格式化符号使用“?”而不是“%@”、等。下面是两种情况的代码片段:a.无返回结果-(void)executeNonQuery:(NSString *)sql{ //执行更新sql语句,用于插入、修改、删除 if (![self.database executeUpdate:sql]) { NSLog(@"执行SQL语句过程中发生错误!"); }}b.有返回结果-(NSArray *)executeQuery:(NSString *)sql{ NSMutableArray *array=[NSMutableArray array]; //执行查询sql语句 FMResultSet *result= [self.database executeQuery:sql]; while (result.next) { NSMutableDictionary *dic=[NSMutableDictionary dictionary]; for (int i=0; i<result.columnCount; ++i) { dic[[result columnNameForIndex:i]]=[result stringForColumnIndex:i]; } [array addObject:dic]; } return array;}对于有返回结果的查询而言,查询完返回一个游标FMResultSet,通过遍历游标进行查询。而且FMDB中提供了大量intForColumn、stringForColumn等方法进行取值。并发和事务我们知道直接使用libsqlite3进行数据库操作其实是线程不安全的,如果遇到多个线程同时操作一个表的时候可能会发生意想不到的结果。为了解决这个问题建议在多线程中使用FMDatabaseQueue对象,相比FMDatabase而言,它是线程安全的。创建FMDatabaseQueue的方法是类似的,调用databaseQueueWithPath:方法即可。注意这里不需要调用打开操作。-(void)openDb:(NSString *)dbname{ //取得数据库保存路径,通常保存沙盒Documents目录 NSString *directory=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSLog(@"%@",directory); NSString *filePath=[directory stringByAppendingPathComponent:dbname]; //创建FMDatabaseQueue对象 self.database=[FMDatabaseQueue databaseQueueWithPath:filePath];}然后所有的增删改查操作调用FMDatabaseQueue的inDatabase:方法在block中执行操作sql语句即可。-(void)executeNonQuery:(NSString *)sql{ //执行更新sql语句,用于插入、修改、删除 [self.database inDatabase:^(FMDatabase *db) { [db executeUpdate:sql]; }];}-(NSArray *)executeQuery:(NSString *)sql{ NSMutableArray *array=[NSMutableArray array]; [self.database inDatabase:^(FMDatabase *db) { //执行查询sql语句 FMResultSet *result= [db executeQuery:sql]; while (result.next) { NSMutableDictionary *dic=[NSMutableDictionary dictionary]; for (int i=0; i<result.columnCount; ++i) { dic[[result columnNameForIndex:i]]=[result stringForColumnIndex:i]; } [array addObject:dic]; } }]; return array;}之所以将事务放到FMDB中去说并不是因为只有FMDB才支持事务,而是因为FMDB将其封装成了几个方法来调用,不用自己写对应的sql而已。其实在在使用libsqlite3操作数据库时也是原生支持事务的(因为这里的事务是基于数据库的,FMDB还是使用的SQLite数据库),只要在执行sql语句前加上“begin transaction;”执行完之后执行“commit transaction;”或者“rollback transaction;”进行提交或回滚即可。另外在Core Data中大家也可以发现,所有的增、删、改操作之后必须调用上下文的保存方法,其实本身就提供了事务的支持,只要不调用保存方法,之前所有的操作是不会提交的。在FMDB中FMDatabase有beginTransaction、commit、rollback三个方法进行开启事务、提交事务和回滚事务。原文链接:http://www.cnblogs.com/kenshincui/p/4077833.html以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

需要自定义ListView。这里就交FloatDelListView吧。复写onTouchEvent方法。如下:@Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN:<BR>          // 获取按下的条目视图(child view) int childCount = getChildCount(); int[] listViewCoords = new int[2]; getLocationOnScreen(listViewCoords); int x = (int) ev.getRawX() - listViewCoords[0]; int y = (int) ev.getRawY() - listViewCoords[1]; for (int i = 0; i < childCount; i++) { downChild = getChildAt(i); // Rect rect = new Rect(); assert downChild != null; downChild.getHitRect(rect); int childPosition = getPositionForView(downChild); if (rect.contains(x, y)) { downX = ev.getRawX(); int downPosition = childPosition; velocityTracker = VelocityTracker.obtain(); assert velocityTracker != null; velocityTracker.addMovement(ev); break; } } isSwipe = false; break; case MotionEvent.ACTION_MOVE: velocityTracker.addMovement(ev);<BR>          // 计算水平和垂直方向移动速度 velocityTracker.computeCurrentVelocity(1000); float velocityX = Math.abs(velocityTracker.getXVelocity()); float velocityY = Math.abs(velocityTracker.getYVelocity()); <BR>          // 水平移动距离 float deltaX = ev.getRawX() - downX; float deltaMode = Math.abs(deltaX); if (deltaX > 150) {// right swipe(右滑) isSwipeToLeft = false; } else if (deltaX < -150) {// left swipe(左滑) isSwipeToLeft = true; }<BR>          // 如果水平滑动距离大于零,并且水平滑动速率比垂直大,说明是水平滑动 if (deltaMode > 0 && velocityY < velocityX) {<BR>            // 这里的FloatDelButtonLayout是自定义的LinearLayout。 ((FloatDelButtonLayout) downChild).showDelButton(ev, isSwipeToLeft); isSwipe = true; } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: downChild.setSelected(false); if (isSwipe) { isSwipe = false; return true; } break; } return super.onTouchEvent(ev); }FloatDelButtonLayou.java :public class FloatDelButtonLayout extends LinearLayout { <BR>   // 提供删除按钮的接口 private OnDelListener delListener; <BR>   // 当前视图在列表中的索引,在delListener中使用 private int index; <BR>   // 右滑 还是 左滑?<BR> private boolean isSwipeToLeft;<BR> public void setOnDelListener(OnDelListener listener, int i) { delListener = listener; index = i; } public FloatDelButtonLayout(Context context) { super(context, null); } public FloatDelButtonLayout(Context context, AttributeSet attrs) { super(context, attrs, 0); } public FloatDelButtonLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } <BR>   // 用来显示或者隐藏删除按钮。 public void showDelButton(MotionEvent ev, boolean isSwipeToLeft) { this.isSwipeToLeft = isSwipeToLeft; onTouchEvent(ev); } private OnClickListener clickDel = new OnClickListener() { @Override public void onClick(View v) { delListener.onDel(index); } }; <BR>   /**<BR>    * 这里的event是我们显示的从FloatDelListView的onTouchEvent里面传进来的,<BR> */ @Override public boolean onTouchEvent(MotionEvent event) { switch (MotionEventCompat.getActionMasked(event)) { case MotionEvent.ACTION_MOVE:<BR>          // 获取删除按钮对象,视图layout中必须要有id为del_button的Button标签 Button view = (Button) findViewById(R.id.del_button); view.setText(R.string.del);<BR>          // 设置Button的MarginLayoutParams,当然可以做成各种动作,比如渐隐之类的显示出来。 MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams(); assert layoutParams != null; if (isSwipeToLeft) { view.setVisibility(View.VISIBLE); view.setOnClickListener(clickDel); layoutParams.leftMargin = -200; } else { view.setVisibility(View.GONE); layoutParams.leftMargin = 0; } view.setLayoutParams(layoutParams); invalidate(); break; } return super.onTouchEvent(event); } public interface OnDelListener { void onDel(int i); } }以上这篇Android ListView实现仿iPhone实现左滑删除按钮的简单实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

Android的自动化测试有很多框架,其中ui automator是google官方提供的黑盒UI相关的自动化测试工具,(GitHub主页:case使用java写,今天实践了一下官方文档中样例程序,其中还是有一些小问题需要总结一下的。环境准备:1.JDK(是的,你没看错,基础的android开发环境必备),以及对应的环境变量配置,不会的可以自己百度下下2.Android Studio(IDE尊崇个人意愿)3.android SDK以及配置4.ANT(主要用于build我们的脚本,生成jar包)ant的搭建主要分几步:1.下载ant安装文件并且解压安装;2.新建系统环境变量ANT_HOME,参数值是你的ant安装目录;3.在Path环境变量中添加ant安装目录的bin文件夹,比如我的就是C:\cod\apache-ant-1.9.6\bin4.配置完以后,测试一下,在命令行下输入ant -version,如果显示你所安装的ant版本信息,证明环境变量配置成功使用流程1、使用ADT创建一个java的项目在创建项目的时候要加上JUnit与你使用的Android platforms中对应的android.jar与uiautomator.jar2、新建一个包(我这里就只叫com)3、再这个包下创建一个class,输入以下java代码,代码全是官方文档上的代码,除了最上面的packagepackage com;import com.android.uiautomator.core.UiObject;import com.android.uiautomator.core.UiObjectNotFoundException;import com.android.uiautomator.core.UiScrollable;import com.android.uiautomator.core.UiSelector;import com.android.uiautomator.testrunner.UiAutomatorTestCase;public class Runer extends UiAutomatorTestCase { public void testDemo() throws UiObjectNotFoundException { // Simulate a short press on the HOME button. getUiDevice().pressHome(); // We're now in the home screen. Next, we want to simulate // a user bringing up the All Apps screen. // If you use the uiautomatorviewer tool to capture a snapshot // of the Home screen, notice that the All Apps button's // content-description property has the value “Apps”. We can // use this property to create a UiSelector to find the button. UiObject allAppsButton = new UiObject(new UiSelector() .description("Apps")); // Simulate a click to bring up the All Apps screen. allAppsButton.clickAndWaitForNewWindow(); // In the All Apps screen, the Settings app is located in // the Apps tab. To simulate the user bringing up the Apps tab, // we create a UiSelector to find a tab with the text // label “Apps”. UiObject appsTab = new UiObject(new UiSelector() .text("Apps")); // Simulate a click to enter the Apps tab. appsTab.click(); // Next, in the apps tabs, we can simulate a user swiping until // they come to the Settings app icon. Since the container view // is scrollable, we can use a UiScrollable object. UiScrollable appViews = new UiScrollable(new UiSelector() .scrollable(true)); // Set the swiping mode to horizontal (the default is vertical) appViews.setAsHorizontalList(); // Create a UiSelector to find the Settings app and simulate // a user click to launch the app. UiObject settingsApp = appViews.getChildByText(new UiSelector() .className(android.widget.TextView.class.getName()), "Settings"); settingsApp.clickAndWaitForNewWindow(); // Validate that the package name is the expected one UiObject settingsValidation = new UiObject(new UiSelector() .packageName("com.android.settings")); assertTrue("Unable to detect Settings", settingsValidation.exists()); UiObject reportBug = new UiObject(new UiSelector().text("Sound")); reportBug.clickAndWaitForNewWindow(); UiObject soundValidation = new UiObject(new UiSelector() .text("Volumes")); assertTrue("Unable to detect Sound", soundValidation.exists()); getUiDevice().pressHome(); } }4、使用ant工具生成build.xml我这里在使用ADT自已的ant插件时提示build.xml:26: Class not found: javac1.8网上查了查,是插件与我java环境不符,下载最新的ant插件就可以了http://ant.apache.org/bindownload.cgi 下载这个tar.gz包,解压,然后将apache-ant-1.9.4\bin目录添加到环境变量PATH中然后cmd到android sdk的tools目录,使用andrlid list命令,记住你将要在模拟器中运行的(也是你刚刚导入android.jar与uiautomator.jar包时所在的platforms)在cmd下使用android create uitest-project -n <name> -t <android-sdk-ID> -p <path>-n 为生成的jar包名称,自已任意定义,-t 为上面查看到的值,我这里是1-p 为输出路径,这里就是刚才创建的java项目所在的路径android create uitest-project -n AutoRunner -t 1 -p D:\myAndroidStudy\androidTest然后再cmd进入D:\myAndroidStudy\androidTest,使用ant build命令生成AutoRunner.jar文件5、将这个AutoRunner.jar文件push到模拟器中adb push AutoRunner.jar /data/local/tmp6、使用adb shell uiautomator runtest AutoRunner.jar –c com.Runer 使Runer类运行我的代码里又在官方基础上多了一个点击”sound”的操作与点击Home键操作 UiObject reportBug = new UiObject(new UiSelector().text("Sound")); reportBug.clickAndWaitForNewWindow(); UiObject soundValidation = new UiObject(new UiSelector() .text("Volumes")); assertTrue("Unable to detect Sound", soundValidation.exists()); getUiDevice().pressHome(); image这个其实也只是一个简单的玩具代码,没有什么意义,但是官方作为一个引导,其中也使用了一些最常见的接口。以后再深入的学习uiautomator总结优点:1.可以对所有操作进行自动化,操作简单;2.不需要对被测程序进行重签名,且,可以测试所有设备上的程序,比如~某APP,比如~拨号,比如~发信息等等3.对于控件定位,要比robotium简单一点点缺点:1.uiautomator需要android level 16以上才可以使用,因为在level 16及以上的API里面才带有uiautomator工具2.如果想要使用resource-id定位控件,则需要level 18及以上才可以3.对中文支持不好(不代表不支持,第三方jar可以实现)4.个人感觉,控件定位不如robotium那样层级分明,仅仅个人感觉,

简单工厂模式正如此模式的名称一样,简单工厂模式基本上是所有设计模式里最简单的一种,类与类之间的关系一目了然。这次我就用很多地方经常举的例子--计算器,来说明这个模式。首先给大家展示一下类之间的结构图:通过这张结构图,可以清晰的看到,加法类、减法类、乘法类、除法类继承自运算类,简单工厂类依赖于运算类的实例化来实现相应的运算功能,好的,看起来并不复杂,让我们直接展示一下代码吧(鉴于目前点点不支持Objective C的代码高亮,所以就直接写啦,尽量保持整齐吧。另,为了照顾像我一样基础不是很好的同学,我尽量把代码写全,方便大家调试)。注意:本文所有代码均在ARC环境下编译通过。首先是运算类(父类):接口文件:复制代码 代码如下:#import <Foundation/Foundation.h>@interface Operation :NSObject{    double numberA;    double numberB;}@property double numberA;@property double numberB;-(double) GetResult;@end实现文件:复制代码 代码如下:#import"Operation.h"@implementation [email protected] numberA, numberB;-(double) GetResult{    return    -1.0;      //此处默认返回-1.0,无其他意义}@end加法类(运算子类):接口文件:复制代码 代码如下:#import "Operation.h"@interface OperationAdd:[email protected]实现文件:复制代码 代码如下:#import "OperationAdd.h"@implementation OperationAdd-(double) GetResult{    double result =0;    result =numberA+numberB;    return result;}@end减法类(运算子类):接口文件:复制代码 代码如下:#import "Operation.h"@interface OperationSub:[email protected]实现文件:复制代码 代码如下:#import "OperationSub.h"@implementation OperationSub-(double)GetResult{    double result =0;    result = numberA-numberB;    return result;}@end乘法类(运算子类)复制代码 代码如下:#import "Operation.h"@interface OperationMul:[email protected]实现文件:复制代码 代码如下:#import "OperationMul.h"@implementation OperationMul-(double)GetResult{    double result =0;    result = numberA*numberB;    return result;}@end除法类(运算子类):接口文件:复制代码 代码如下:#import "Operation.h"@interface OperationDiv:[email protected]实现文件:复制代码 代码如下:#import "OperationDiv.h"@implementation OperationDiv-(double)GetResult{    double result =0;    @try{        result = numberA/numberB;    }    @catch(NSException *exception) {        NSLog(@"除数不能为0");    }    return result;}@end下面是工厂类(依赖实力化运算类实现具体功能):接口文件:复制代码 代码如下:#import <Foundation/Foundation.h>#import "OperationAdd.h"#import "OperationDiv.h"#import "OperationSub.h"#import "OperationMul.h"@interface OperationFactory:NSObject+(Operation*)CreateOperate:(char)operate;@end实现文件:复制代码 代码如下:#import "OperationFactory.h"+(Operation*)CreateOperate:(char)operate{    Operation *oper;    switch(operate) {        case '+':            oper = [[OperationAdd alloc]init];            break;        case '-':            oper = [[OperationSub alloc]init];            break;        case '*':            oper = [[OperationMul alloc]init];            break;        case '/':            oper = [[OperationDiv alloc]init];            break;        default:            oper = nil;            break;        }        return oper;}具体调用复制代码 代码如下:#import <Foundation/Foundation.h>#import "OperationAdd.h"#import "OperationDiv.h"#import "OperationMul.h"#import "OperationSub.h"#import "OperationFactory.h"int main (int argc,const char* argv[]){    @autoreleasepool{        Operation *oper = [OperationFactory CreateOperate:'*'];        [oper setNumberA:1];        [oper setNumberB:2];        double result = 0;        result = [oper GetResult];        NSLog(@"Result is %f", result);    }    return 0;}好啦,上面罗列的是简单工厂模式的基础代码。其实还是挺简单的,对吧,只有一层继承关系,一个依赖关系,在工厂类里面用switch语句判别需要实例化哪种类型,之后进行计算,获取结果。工厂方法模式上面关于简单工厂模式中就有提到过一次关于“工厂类”模式。为了帮助大家能够回忆一下简单工厂模式,在这里提一下简单工厂模式的优点,简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。其实,工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。但缺点是,由于每加一个产品,就需要加一个产品工厂的类,增加了额外的开发量。下面还是以计算器为例子,详细介绍工厂方法模式,还是老样子,先向大家展示一下类结构图。上面这张图向大家展示了各个类之间的关系。其实和简单工厂模式不同的是,类图的右边抽象工厂接口是相比简单工厂模式多出来的抽象接口。下面直接上代码吧,别的不多说了。注意:本文所有代码均在ARC环境下编译通过。Operation类接口复制代码 代码如下:#import <Foundation/Foundation.h>@interface Operation :NSObject{    double numberA;    double numberB;}@property double numberA;@property double numberB;-(double) GetResult;@endOperation类实现复制代码 代码如下:#import "Operation.h"@implementation [email protected] numberA, numberB;-(double) GetResult{    return -1.0;}@endOperationAdd类接口复制代码 代码如下:#import "Operation.h"@interface OperationAdd :[email protected]类实现复制代码 代码如下:#import "OperationAdd.h"@implementation OperationAdd-(double) GetResult{    double result =0;    result = numberA+numberB;    return result;}@endOperationDiv类接口复制代码 代码如下:#import "Operation.h"@interface OperationDiv :[email protected]类实现复制代码 代码如下:#import "OperationDiv.h"@implementation OperationDiv-(double)GetResult{    double result =0;    @try{        result = numberA/numberB;    }    @catch(NSException *exception) {        NSLog(@"除数不能为0");    }    return result;}@endOperationMul类接口复制代码 代码如下:#import "Operation.h"@interface OperationMul :[email protected]类实现#import "OperationMul.h"@implementation OperationMul-(double)GetResult{    double result =0;    result = numberA*numberB;    return result;}@endOperationSub类接口复制代码 代码如下:#import "Operation.h"@interface OperationSub :[email protected]类实现复制代码 代码如下:#import "OperationSub.h"@implementation OperationSub-(double)GetResult{    double result =0;    result = numberA-numberB;    return result;}@endIFactory类接口复制代码 代码如下:#import <Foundation/Foundation.h>#import "Operation.h"@interface IFactory :NSObject-(Operation*)CreateOperation;@endIFactory类实现复制代码 代码如下:#import "IFactory.h"@implementation IFactory-(Operation*)CreateOperation{    return [[Operation alloc]init];}@endAddFactory类接口复制代码 代码如下:#import "IFactory.h"@interface AddFactory :[email protected]类实现复制代码 代码如下:#import "AddFactory.h"#import "OperationAdd.h"@implementation AddFactory-(Operation*)CreateOperation{    return [[OperationAdd alloc]init];}@endSubFactory类接口复制代码 代码如下:#import "IFactory.h"@interface SubFactory :[email protected]类实现复制代码 代码如下:#import "SubFactory.h"#import "OperationSub.h"@implementation SubFactory-(Operation*)CreateOperation{    return [[OperationSub alloc]init];}@endMulFactory类接口复制代码 代码如下:#import "IFactory.h"@interface MulFactory :[email protected]类实现复制代码 代码如下:#import "MulFactory.h"#import "OperationMul.h"@implementation MulFactory-(Operation*)CreateOperation{    return [[OperationMul alloc]init];}@endDivFactory类接口复制代码 代码如下:#import "IFactory.h"@interfaceDiv Factory :[email protected]类实现复制代码 代码如下:#import "DivFactory.h"#import "OperationDiv.h"@implementation DivFactory-(Operation*)CreateOperation{    return [[OperationDiv alloc]init];}@endMain方法调用复制代码 代码如下:#import <Foundation/Foundation.h>#import "OperationAdd.h"#import "AddFactory.h" //加法工厂,你可以根据需要添加其他运算工厂int main (int argc,const char* argv[]){    @autoreleasepool{        IFactory *operFactory = [[AddFactory alloc]init];        Operation *oper = [operFactory CreateOperation];        [oper setNumberA:1];        [oper setNumberB:2];        double result = [oper GetResult];        NSLog(@"The result is %f", result);    }    return 0;}好啦,上面就是工厂方法模式的Objective C的类代码。

本文实例讲述了Android编程实现GridView控件点击图片变暗效果的方法。分享给大家供大家参考,具体如下:@Overridepublic void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); grid = (GridView) findViewById(R.id.grid); grid.setAdapter(new GridAdapter()); grid.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { Toast.makeText(MainActivity.this, "ddd", 1000).show(); } });}class GridAdapter extends BaseAdapter { @Override public int getCount() { return imgs.length; } @Override public Object getItem(int arg0) { return arg0; } @Override public long getItemId(int arg0) { return arg0; } @Override public View getView(int arg0, View view, ViewGroup arg2) { if (view == null) { holder = new ViewHolder(); view = LayoutInflater.from(MainActivity.this).inflate( R.layout.imgs, null); holder.view = (ImageView) view.findViewById(R.id.gridimg); view.setTag(holder); } else { holder = (ViewHolder) view.getTag(); } holder.view.setImageResource(imgs[arg0]); holder.view.setOnTouchListener(onTouchListener); return view; } class ViewHolder { ImageView view; } public OnTouchListener onTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_UP: changeLight((ImageView) view, 0); // onclick break; case MotionEvent.ACTION_DOWN: changeLight((ImageView) view, -80); break; case MotionEvent.ACTION_MOVE: // changeLight(view, 0); break; case MotionEvent.ACTION_CANCEL: changeLight((ImageView) view, 0); break; default: break; } return true; } };}private void changeLight(ImageView imageview, int brightness) { ColorMatrix matrix = new ColorMatrix(); matrix.set(new float[] { 1, 0, 0, 0, brightness, 0, 1, 0, 0, brightness, 0, 0, 1, 0, brightness, 0, 0, 0, 1, 0 }); imageview.setColorFilter(new ColorMatrixColorFilter(matrix));}更多关于Android相关内容感兴趣的读者可查看本站专题:《Android控件用法总结》、《Android开发入门与进阶教程》、《Android视图View技巧总结》、《Android编程之activity操作技巧总结》、《Android数据库操作技巧总结》及《Android资源操作技巧汇总》希望本文所述对大家Android程序设计有所帮助。

分类:365bet娱乐城官网

时间:2016-10-05 03:18:16