阳春面 | 关注互联网,Android

TAG | android

三/10

27

编写自己的ContentProvider

刚开始看Android NotePad中的ContentProvider的实现时,看的云里雾里,但自己模仿写过一个后,才发现也就这么一回事,就是实现公用的增删改查。下面将需要实现的方法做一个简单的回顾:

1.一个ContentProvider可以实现对多个数据表的操作,但每一个数据表都需要有一个独立URI,也必须有一个独立的类型。URI是其他应用访问这个数据入口,比如:

content://com.chenyc.timeaccount.provider/eventtypes/id

它包括4部分,content://就是固定的头部,com.chenyc.timeaccount.provider部分需要一个唯一的字符串,一般就用ContentProiver类所在的包名,eventtypes部分一般是指在这个ContentProvider下,你需要操作那种类型的数据,一般可以用表名来表示,id部分是指具体操作数据的_id,如果查询某一条数据,则id部分就是其在数据库中的_id字段的值。

每个数据表需要有一个独立的数据类型,需要在getType(Uri uri)中实现,返回一个唯一的字符串即可,比如:vnd.chenyc.cursor.dir/vnd.account.eventtype

2.query方法,查询作为最常用的方法,实现也很简单,projection参数代表要查那些列,selection是where条件部分,selectionArgs是where条件部分参数的值,sortOrder指排序,switch部分判断是查一条数据,还是查一个list,然后根据情况进行查询

public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {

		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		switch (sUriMatcher.match(uri)) {
		case EVENT_TYPES:
			qb.setTables(EVENT_TYPE_TABLE_NAME);
			qb.setProjectionMap(sEventTypesProjectionMap);
			break;
		case EVENT_TYPE_ID:
			qb.setTables(EVENT_TYPE_TABLE_NAME);
			qb.setProjectionMap(sEventTypesProjectionMap);
			qb.appendWhere(EventTypeAdapter.KEY_ROWID + "="
					+ uri.getPathSegments().get(1));
			break;
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}

		SQLiteDatabase db = mDbHelper.getReadableDatabase();
		Cursor c = qb.query(db, projection, selection, selectionArgs, null,
				null, sortOrder);
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;
	}

3.delete方法,处理方式也跟查询差不多,也分删一个和删一批

public int delete(Uri uri, String where, String[] whereArgs) {
		SQLiteDatabase db = mDbHelper.getWritableDatabase();
		int count;
		switch (sUriMatcher.match(uri)) {
		case EVENT_TYPES:
			count = db.delete(EVENT_TYPE_TABLE_NAME, where, whereArgs);
			break;
		case EVENT_TYPE_ID:
			String id = uri.getPathSegments().get(1);
			count = db.delete(EVENT_TYPE_TABLE_NAME,
					EventTypeAdapter.KEY_ROWID
							+ "="
							+ id
							+ (!TextUtils.isEmpty(where) ? " AND (" + where
									+ ')' : ""), whereArgs);
			break;

		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}

		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

4.insert方法,插入成功后需要返回这条记录的URI

public Uri insert(Uri uri, ContentValues initialValues) {
		SQLiteDatabase db = mDbHelper.getWritableDatabase();
		long rowId = 0;
		Uri contentUri;
		switch (sUriMatcher.match(uri)) {
		case EVENT_TYPES:
			rowId = db.insert(EVENT_TYPE_TABLE_NAME, "null", initialValues);
			contentUri = TimeAccount.EVENT_TYPE_CONTENT_URI;
			break;
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}

		if (rowId > 0) {
			Uri returnUri = ContentUris.withAppendedId(contentUri, rowId);
			getContext().getContentResolver().notifyChange(returnUri, null);
			return returnUri;
		}
		throw new SQLException("Failed to insert row into " + uri);
	}

5.update方法,更新成功后需要返回修改的记录数

public int update(Uri uri, ContentValues values, String where,
			String[] whereArgs) {

		SQLiteDatabase db = mDbHelper.getWritableDatabase();
		String id;
		int count;
		switch (sUriMatcher.match(uri)) {
		case EVENT_TYPES:
			count = db.update(EVENT_TYPE_TABLE_NAME, values, where, whereArgs);
			break;
		case EVENT_TYPE_ID:
			id = uri.getPathSegments().get(1);
			count = db.update(EVENT_TYPE_TABLE_NAME, values,
					EventTypeAdapter.KEY_ROWID
							+ "="
							+ id
							+ (!TextUtils.isEmpty(where) ? " AND (" + where
									+ ')' : ""), whereArgs);
			break;
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

·

SimpleCursorAdapter可以把我们从Array或数据库中取出的数据绑定的ListView或其他的组件,这个很好用,但有时候有些View,SimpleCursorAdapter并不能直接绑定,需要自己去实现它的setViewBinder方法,下面是我利用SimpleCursorAdapter绑定RatingBar的例子:

SimpleCursorAdapter timeItems = new SimpleCursorAdapter(this,
                R.layout.timeitem_row, timeItemsCursor, from, to);
        timeItems.setViewBinder(new SimpleCursorAdapter.ViewBinder(){

            @Override
            public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
                int rateIndex = cursor.getColumnIndex("rate");
                if(columnIndex == rateIndex)
                {
                    RatingBar ratingBar = (RatingBar)view;
                    ratingBar.setRating(cursor.getInt(rateIndex));
                    return true;
                }
                return false;
            }
        });

· ·

三/10

27

Android中编写Service相关的知识

1.在Android中编写Service,需要继承android.app.Service,覆盖其onCreate()和onStart(Intent intent, int startId)方法即可,onCreate方法只在service创建时调用,onStart方法每次都会调用。

2.在Service中不能直接访问数据库,只能通过ContentResolver访问,如果访问自己的数据,需要继承ContentProvider并实现其方法,才能被ContentResolver访问,以下使用ContentResolver的方法:

mCursor = getContentResolver().query(
                    Uri.withAppendedPath(
                            TimeAccount.EVENT_TYPE_CONTENT_URI, "/" + pid),
                    new String[] { "name" }, null, null, null);

3.在Service中可以直接使用本应用的SharePreferences,我本来以为本来这个也用数据库一样,需要写成ContentProvider,没想到可以直接使用;例如:

sharePreferences = PreferenceManager.getDefaultSharedPreferences(this);
        INTERVAL = Integer.valueOf(sharePreferences.getString(
                "sync_interval_list", "60"));

4.Service有两种形式,一种是生命周期依赖于所启动的应用,就是BInd形式,通过bindService启动;另一种就是不依赖启动的应用,在应用已被操作系统干掉后,服务还会一直存在,通过startService启动。例如:

Intent serviceIntent = new Intent();
            serviceIntent.setAction("com.chenyc.timeaccount.sync.SYNC_SERVICE");
            context.startService(serviceIntent);

5.自己编写的Service,需要在AndroidManifest.xml中注册,例如:

<service android:name="com.chenyc.timeaccount.sync.SyncService">
    <intent-filter>
        <action android:name="com.chenyc.timeaccount.sync.SYNC_SERVICE" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

6.如果想在开机时就启动Service,需要写一个BroadcastReceiver接收BOOT_COMPLETED消息,系统启动完成后会发出这个消息;编写的BroadcastReceiver也需要在AndroidManifest.xml中注册。

IntentReceiver类接收消息,并启动服务:

public class IntentReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
             Intent serviceIntent = new Intent();
            serviceIntent.setAction("com.chenyc.timeaccount.sync.SYNC_SERVICE");
            context.startService(serviceIntent);
    }

}

在AndroidManifest.xml中注册:

<receiver android:name="com.chenyc.timeaccount.sync.IntentReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.HOME" />
            </intent-filter>
</receiver>

·

三/10

27

如何在Android中创建自己的View

在Android中创建自己的VIEW只要继承android.view.View类,然后在onDraw方法中使用canvas画自己所需要的东东就可以了。当然你还需要做一些实际处理,比如对用户点击的处理,向前或向后滚动的处理等。

以下是我创建的CalendarView中,部分显示日历的代码:

public CalendarView(Context context) {
		super(context);

		calendarBrowse = (CalendarBrowse) context;

		setFocusable(true);
		setFocusableInTouchMode(true);

		cPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		cPaint.setStyle(Paint.Style.FILL_AND_STROKE);
		cPaint.setColor(Color.RED);
		cPaint.setTextSize(20f);

		tPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		tPaint.setStyle(Paint.Style.FILL_AND_STROKE);
		tPaint.setColor(Color.WHITE);
		tPaint.setTextSize(20f);

		setBackgroundColor(getResources().getColor(R.color.background));

		mGestureDetector = new GestureDetector(
				new GestureDetector.SimpleOnGestureListener() {

					@Override
					public boolean onFling(MotionEvent e1, MotionEvent e2,
							float velocityX, float velocityY) {

						if (e1.getX() > e2.getX()) {
							cal.add(Calendar.MONTH, 1);
							CalendarView.this.invalidate();
						}

						if (e1.getX() < e2.getX()) {
							cal.add(Calendar.MONTH, -1);
							CalendarView.this.invalidate();
						}

						return true;

					}

					@Override
					public boolean onSingleTapUp(MotionEvent e) {
						day = getDay(e.getX(), e.getY());
						if (day != INVALID_POSITION) {
							calendarBrowse.showTimeItemList(year, month, day);
						}
						return true;
					}

				});
	}
 

GestureDetector类是用于对手势操作的处理,它可以截取你对屏幕的单击,长按,
向前向后滚动等操作,这个功能在Android的框架中封装的很好,使用起来很舒服。

·

三/10

27

时间账本开发总结

时间账本是我写的第一个Android应用,想法来自于《把时间当作朋友》《奇特的一生》这两本书,功能很简单,就是登记自己每天做了什么,后来我又引入了反思想法,可以对每一天写一些反思,其实我觉得登记账本本身就是一种反思;现在主要完成了手机端的,下一步准备利用GAE开发WEB端,与手机端同步,然后再WEB端再做一些分析和生成报告等工作,例如可以做一些不同时期的时间执行效率比较,年末对这一年所做过的事出一份报告:比如在这一年中你读过什么书,看过什么电影,学习到什么技术等等;这还只是有一个初步的概念,具体怎么做还在一步的探索中;Android手机端也一样,会根据需要,进行改变,目的就是打造一个符合自己需要的时间管理软件。

时间账本的代码我已经放到google code上了,希望与大家一起交流Android开发知识,有兴趣的朋友也可以和我一起来完善这个时间账本。

· ·

Older posts >>