Android LinearLayout根据状态切换图片(模拟按钮的效果)

在Android中Button可以根据选中,点击等状态切换图片,我想用LinearLayout实现类似的功能,

但是默认情况下pressed状态“容器类”(继承于ViewGroup的类)是接收不到的,

所以LinearLayout的按下就没有效果,后来分析代码,可以通过设置setAddStatesFromChildren

方法获得内部View的状态,就可以取得pressed状态。

linearLayout.setAddStatesFromChildren(true);

还有一个相反的方法setDuplicateParentStateEnabled,内部类获得外部容器类的状态,

这个方法和setAddStatesFromChildren不能同时设置,同时设置会产生异常。

Android layout布局相关的注意点

1.不要过多的嵌套布局,特别是在ListView中需要重复获取的情况下。嵌套布局的情况可以采用RelativeLayout替代LinearLayout,减少嵌套层数。

2.可以采用SDK工具里的hierarchyviewer,分析layout的执行效率。

3.利用新版的ADT(adt1.6)的提示功能,纠正布局文件中影响性能的部分。

4.在采用LinearLayout布局时,尽量不要嵌套中使用layout_weight属性,这会导致所有的内部VIew执行两次measure.

5.对于公用的layout编写,可以采用<merge>作为父标签,这样include到其他layout中后,merge标签不会作为容器,可以减少layout层次。

<merge xmlns:android="http://schemas.android.com/apk/res/android">

<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/add"/>

<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/delete"/>

</merge>

6.采用ViewStub按需加载View

<ViewStub
    android:id="@+id/stub_import"
    android:inflatedId="@+id/panel_import"
    android:layout="@layout/progress_overlay"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom" />

加载方法:

View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();

加载完成后,ViewStub的Id就不再有效了

7.在ListView中采用ViewHolder保存View的引用,不要重复使用findViewById方法,
这样可加快执行的性能。

ViewHolder holder = new ViewHolder();
holder.icon = (ImageView) convertView.findViewById(R.id.listitem_image);
holder.text = (TextView) convertView.findViewById(R.id.listitem_text);
holder.timestamp = (TextView) convertView.findViewById(R.id.listitem_timestamp);
holder.progress = (ProgressBar) convertView.findViewById(R.id.progress_spinner);
convertView.setTag(holder);

不让ScrollView自动切换子View的焦点

在开发中,在同一个Activity中有多个输入框,不想让ScrollView自动切换EditText的焦点,
需要继承ScrollView,重写onRequestFocusInDescendants方法,然后在layout中使用自定义的ScrollView即可。

public class NonFocusingScrollView extends ScrollView {

    public NonFocusingScrollView(Context context) {
        super(context);
    }

    public NonFocusingScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public NonFocusingScrollView(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected boolean onRequestFocusInDescendants(int direction,
            Rect previouslyFocusedRect) {
        return true;
    }

}

编写自己的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;
	}