项目中突然想用Content Provider实现跨应用操作数据的功能,虽然后来没有利用Content Provider来实现。但还是写了个简单demo程序,熟悉一下Content Provider的开发。这篇文章只是简单记录下,下一篇文章具体解释需要注意的问题
两个项目,一个继承Content Provider将数据库的CRUD操作显露给其他应用,另一个项目,则是进行CRUD操作。很简单的功能。
-----------------------
项目1:
首先创建 DatabaseHelper
private static String DATABASE_NAME = "db_name"; public static String TABLE_NAME = "db_table_name"; public static String COL_ID = "_id"; public static String COL_TXT = "txt"; public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, 1); } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL("create table if not exists " + TABLE_NAME + "(" + COL_ID + " integer primary key autoincrement, " + COL_TXT + " text )"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub db.execSQL("drop table if exists " + TABLE_NAME); onCreate(db); }
然后实现 Content Provider功能
private static final String TAG = "TestContentProvider"; private DatabaseHelper dbHelper; // public constants for client development public static final String AUTHORITY = "info.zhegui.contentprovider.provider"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + DatabaseHelper.TABLE_NAME); // helper constants for use with the UriMatcher private static final int LENTITEM_LIST = 1; private static final int LENTITEM_ID = 2; private static final UriMatcher URI_MATCHER; /** * Column and content type definitions for the LentItemsProvider. */ public static interface LentItems extends BaseColumns { public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.info.zhegui.provider." + DatabaseHelper.TABLE_NAME; public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.info.zhegui.provider." + DatabaseHelper.TABLE_NAME; } static { URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); URI_MATCHER.addURI(AUTHORITY, DatabaseHelper.TABLE_NAME, LENTITEM_LIST); URI_MATCHER.addURI(AUTHORITY, DatabaseHelper.TABLE_NAME + "/#", LENTITEM_ID); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int rowNum = dbHelper.getWritableDatabase().delete( DatabaseHelper.TABLE_NAME, selection, selectionArgs); return rowNum; } @Override public String getType(Uri uri) { switch (URI_MATCHER.match(uri)) { case LENTITEM_LIST: return LentItems.CONTENT_TYPE; case LENTITEM_ID: return LentItems.CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } } @Override public Uri insert(Uri uri, ContentValues values) { long rowId = dbHelper.getWritableDatabase().insert( DatabaseHelper.TABLE_NAME, null, values); return Uri.parse(CONTENT_URI + "/" + rowId); } @Override public boolean onCreate() { dbHelper = new DatabaseHelper(getContext()); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { if (URI_MATCHER.match(uri) != LENTITEM_LIST) { throw new IllegalArgumentException( "Unsupported URI for insertion: " + uri); } return dbHelper.getReadableDatabase().query(DatabaseHelper.TABLE_NAME, projection, selection, selectionArgs, null, null, null); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int rowNum = dbHelper.getWritableDatabase().updateWithOnConflict( DatabaseHelper.TABLE_NAME, values, selection, selectionArgs, SQLiteDatabase.CONFLICT_FAIL); return rowNum; }
接下来在manifest中声明
<provider android:name="TestContentProvider" android:authorities="info.zhegui.contentprovider.provider" android:exported="true" />
ok,项目就完成了。就是这么简单!
-------------------------------------------------------
项目2:
只是简单调用CRUD操作,直接上代码
public static String TABLE_NAME = "db_table_name"; public static final String AUTHORITY = "info.zhegui.contentprovider.provider"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + TABLE_NAME); public static String COL_ID = "_id"; public static String COL_TXT = "txt"; public static interface LentItems extends BaseColumns { public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.info.zhegui.provider." + TABLE_NAME; public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.info.zhegui.provider." + TABLE_NAME; } private EditText etIccid; private ListView listView; private CustomAdapter mAdapter; private Handler handler; private List<Item> listData = new ArrayList<Item>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); handler = new Handler(this); etIccid = (EditText) findViewById(R.id.et); listView = (ListView) findViewById(R.id.listview); mAdapter = new CustomAdapter(); ((Button) findViewById(R.id.btn_add)) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final String txt = etIccid.getEditableText().toString(); if (!TextUtils.isEmpty(txt)) { new Thread() { public void run() { ContentValues values = new ContentValues(); values.put(COL_TXT, txt); Uri uri = getContentResolver().insert( CONTENT_URI, values); if (uri != null) { getData(); } } }.start(); } etIccid.setText(""); } }); listView.setAdapter(mAdapter); getData(); } private synchronized void getData() { new Thread() { public void run() { listData.clear(); Cursor cursor = getContentResolver().query(CONTENT_URI, null, null, null, null); Log.i(TAG, "line 100 cursor:" + cursor); if (cursor != null) { while (cursor.moveToNext()) { long id = cursor.getLong(cursor.getColumnIndex(COL_ID)); String str = cursor.getString(cursor .getColumnIndex(COL_TXT)); listData.add(new Item(id, str)); } } handler.sendEmptyMessage(0); } }.start(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } public static class ViewHolder { EditText et; Button btnUpdate; Button btnDelete; } public class Item { long id; public Item(long id, String str) { this.id = id; this.txt = str; } public long getId() { return id; } public String getTxt() { return txt; } String txt; } public class CustomAdapter extends BaseAdapter { public CustomAdapter() { } @Override public int getCount() { return listData.size(); } @Override public Object getItem(int position) { return listData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); convertView = LayoutInflater.from(MainActivity.this).inflate( R.layout.list_item, parent, false); holder.et = (EditText) convertView.findViewById(R.id.et_item); holder.btnUpdate = (Button) convertView .findViewById(R.id.btn_update); holder.btnDelete = (Button) convertView .findViewById(R.id.btn_delete); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.et.setText(listData.get(position).getTxt()); holder.et.setTag("et_" + position); holder.btnDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // listData.remove(position); // mAdapter.notifyDataSetChanged(); int rowId = getContentResolver() .delete(CONTENT_URI, COL_ID + " =? ", new String[] { listData.get(position) .getId() + "" }); if (rowId != 0) { getData(); } } }); holder.btnUpdate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String et = ((EditText) listView.findViewWithTag("et_" + position)).getText().toString(); if (!TextUtils.isEmpty(et)) { // listData.set(position, et); // mAdapter.notifyDataSetChanged(); ContentValues values = new ContentValues(); values.put(COL_TXT, et); int rowId = getContentResolver().update( CONTENT_URI, values, COL_ID + " =? ", new String[] { listData.get(position).getId() + "" }); if (rowId != 0) { getData(); } } } }); return convertView; } } @Override public boolean handleMessage(Message msg) { mAdapter.notifyDataSetChanged(); return false; }
。
附2个项目源码
直接看例子!!!
// // main.m // PointerPointer // // Created by Rayln Guan on 8/30/13. // Copyright (c) 2013 Rayln Guan. All rights reserved. // #import <Foundation/Foundation.h> void test1(NSString *str){ str = @"567"; } void test2(NSString **str){ *str = @"678"; } int main(int argc, const char * argv[]) { @autoreleasepool { NSString *str = @"12345"; test1(str); NSLog(@"str:%@", str); //打印出12345 test2(&str); NSLog(@"&str:%@", str); //打印出678 } return 0; }
最近要做一个读入Word,PDF格式等的文件并且加以编辑的程序,本来以为使用Text Kit结合Text View来打开doc文件是完全没问题的,结果用了各种方法打开要么是数据是nil,要么打开的文字中很多乱码。对于Word,PDF这种格式或许必须要用底层的Core Text来做了(如果用WebView来做的话,很难对内容进行操作)。
所以接下来又要从Core Text从头学起了。首先看了Core Text Programming Guide,理解的并不算深入,但是写个博客来做个笔记吧。
Core Text是一个iOS中一个比较底层的框架,借用iOS7 Text Kit介绍视频中的一个图:
其中Core Text在Core Graphics之上,Text Kit在Core Text之上构建(UIWebView不在Text Kit之上,很多Text Kit中很棒的特性UIWebView无法使用)。
在文档中,Core Text被描述为一个高级的、底层的框架,主要用来对文本进行排版布局和字体处理。
Core Text有一个很强大的功能就是进行Character-to-glyph转换,其实之前看Text Kit的时候glyph这个词就是一个高频出现的词,个人觉得必须要分清楚character,glyph,font这些概念之间的分别。
按照个人的理解(语文不好,说错了请指出):
(1)character:字符,指一个符号,数字或者文字等。
(2)glyph:字形,对于同一套字符有不同的写法,形态或者说是样式。字形是字符的一族形态,例如Helvetica, New Roman
(3)font:字体,在字形的基础上进行的加工修饰便构成了字体,如加粗,倾斜,加颜色,改变磅数等。例如
9pt Helvetica Bold是一个字体。
回到文档来看看Character-to-glyph conversion:
对应同一个字符A,有着各种不同的形态,对应不同的字形glyph,当然同一种风格的写法便构成了一种字形。
同样地,对于f加l两个字符连起来写便又变成了一种新的写法,对应另外一种字形。
通过Core Text可以快速高效地进行字符到字形之间的转换。
使用Core Text可以直接使用Core Foundation的对象,这些对象是toll-free bridging的(一种允许某些OC类与其对应的CoreFoundation类之间可以互换使用的机制),所以无需进行特殊的对象类型转换。另外Core Text构建于Core Graphics框架之上,所以可以通过Core Graphics的方法进行高效高质量的文字描画。
接下来是Core Text的一些Base Objects。
1.Layout Objects:Framesetters, Frames, Typesetters, Lines and Glyph runs
还是先上个图吧:
其中framesetter通过attributed string对象持有文本的内容,并调用typesetter来创建line对象从而填满由CGPath所描述的区域。输出的结果是包含了一系列行对象的一个frame。
其中每一个line中对于相同属性内的一段文字使用同一个的CTRun对象去描画该段文字:
下面这张图(来自raywenderlich)更加容易理解:
其中CTRun对象会由Core Text自动生成,不需要也不应该由开发者自己去创建,所以整个程序跑起来有很多细节是开发者不用考虑的。
2.Font Objects:Fonts, Font Descriptors, Font Collections
Font Descriptor用来描述字体的特性,是Font的核心部分,Font Collections是许多个Font Descriptors的集合。
说完一大堆Core Text的基本对象后,还是写个Demo看看(主要参考了http://blog.csdn.net/andypan1314/article/details/7614469)
首先要使用Core Text框架的话,第一步是要导入CoreText.framework,然后在要使用的头文件中导入:
#import <CoreText/CoreText.h>
在使用Core Text来draw文字的时候,关键是要使用UIView中的drawRect:方法,所以先新建一个ViewController,然后新建一个UIView的子类,并将二者在视图上关联起来。
然后重写View中的drawRect:方法:
- (void)drawRect:(CGRect)rect { [super drawRect:rect]; // 1.创建一个字符串用来保存文本内容 CFAttributedString NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:@"First Core Text Demo"]; // 2.创建一个framesetter用来管理描画文字的frame CTFramesetter CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString); // 3.创建一个用来描画文字的路径,其区域为当前视图的bounds CGPath CGMutablePathRef path = CGPathCreateMutable(); CGRect rectForPath = CGRectMake(0.0, 0.0, self.bounds.size.width, self.bounds.size.height - 20.0); CGPathAddRect(path, NULL, rectForPath); // 4.创建由framesetter管理的frame,是描画文字的一个视图范围 CTFrame CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attrString.length), path, NULL); // 5.获取当前视图的上下文环境 CGContextRef context = UIGraphicsGetCurrentContext(); // 6.通过context在frame中描画文字内容 CTFrameDraw(frame, context); // 7.所有创建的对象必须被release CFRelease(frame); CFRelease(path); CFRelease(frameSetter); }
以上代码中各个对象的创建顺序基本与Figure 1.3相同。
其中首先对NSAttributedString强制类型转换得到CFAtttributedString对象用来保存文本内容,然后创建一个CTFramesetter对象用来保存文本内容、在所管理的Frame中对文字进行布局、输出文字等,接着创建CGPath对象和CTFrame对象,二者定义了一个输出文字内容的区域。注意CTTypesetter在CTFramesetter创建时随之而生成了,不需要再手动去创建。
在基本对象创建好后,获取当前视图上下文并设置好一些仿射变换后,直接在Frame中draw就完成了文字的输出:
注意最后所有创建的Core Foundation对象都必须被释放。
还有一个大问题,draw出来的文字是倒过来的,必须要将它们倒回来。
这就涉及到仿射变换的内容了,有篇文章挺好理解的:http://hi.baidu.com/cqhg1981/item/1a527bf4bda2fb0fc6dc45aa
主要的仿射变换有下面几种:
其中translation是平移,flip是翻转,rotation是旋转,scaling是缩放,shear是错切,identity是保持不变。
再看看基本的仿射变换代码:
// 平移仿射变换:tx为x正方向上的位移量,ty为y正方向上的位移量 CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty) // 缩放仿射变换:在原点的基础上进行缩放,sx为x正方向上的缩放倍数,sy为y正方向上的缩放倍数。如果缩放倍数为负数则为反向缩放 CGContextScaleCTM(CGContextRef c, CGFloat sx, CGFloat sy); // 旋转仿射变换:angle为旋转角度,逆时针为正,顺时针为负 CGContextRotateCTM(CGContextRef c, CGFloat angle);
在CGContextRef context =UIGraphicsGetCurrentContext();下面加上以下代码。
首先设置好初始的仿射变换矩阵(原封不动):
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 10.0, self.bounds.size.height);
最后倒过来(方向放大):
CGContextScaleCTM(context, 1.0, -1.0);
Run一下:
完成了。
Core Text还有很多内容,随着我学习的深入会继续更新博客的。