基于api28
常用屬性不再贅述,只看一些不常用的或者新添加的屬性。
1 padding
android:paddingHorizontal //同時設(shè)置左右padding
android:paddingVertical //同時設(shè)置上下padding
以上屬性api26添加。
2 scroll
android:scrollX
android:scrollY
以上兩個屬性對應(yīng)的成員變量為mScrollX mScrollX,在繪制之前,會執(zhí)行
canvas.translate(-mScrollX, -mScrollY);
同時影響當(dāng)前View的繪制,及其children的繪制。但對當(dāng)前view的背景沒有影響!
偏移量 > 0, 內(nèi)容向右下偏移;
偏移量 < 0, 內(nèi)容向左上偏移。
注:在xml中設(shè)置View的繼承類的
android:scrollX、android:scrollY不一定起作用,依賴于具體的實(shí)現(xiàn)。
比如ScrollView,由于在滾動時會判斷getChildCount() > 0,而初始化時還沒有子View,所以不起作用。
如下圖為自定義的View,背景為黑色,在onDraw方法中畫了一個圓,寬高均為100dp:

設(shè)置android:scrollX=-50dp,背景不移動,內(nèi)容右移:

至于為什么不影響背景,后續(xù)再學(xué)習(xí)。
3 matrix變換
<!-- 設(shè)置scale、rotation變換的中心點(diǎn) -->
android:transformPivotX
android:transformPivotY
android:rotation
android:rotationX
android:rotationY
android:translationX
android:translationY
android:translationZ
android:scaleX
android:scaleY
對圖2設(shè)置如下屬性:
android:transformPivotX="50dp"
android:transformPivotY="50dp"
android:rotation="30"
android:scaleX="0.8"
android:translationX="30dp"
顯示如下:

藍(lán)色方框是matrix變換之前的位置。
為當(dāng)前View設(shè)置點(diǎn)擊事件,可以發(fā)現(xiàn),僅在點(diǎn)擊View的繪制區(qū)域(非白色區(qū)域)時觸發(fā)onClick方法,在點(diǎn)擊藍(lán)色方框內(nèi)的空白區(qū)域時不會觸發(fā)。
有以下結(jié)論:
- matrix變換影響整個View的顯示(包括子View)
- matrix與scrollX srollY互不影響
- matrix會影響可點(diǎn)擊區(qū)域
有的同學(xué)還知道有個android:elevation屬性,它和android:translationZ有什么區(qū)別呢?
android系統(tǒng)中,View所在的坐標(biāo)系是一個三維的,View最終的坐標(biāo)可以通過getX getY getZ三個方法得到??匆幌逻@三個方法的實(shí)現(xiàn):
public float getX() {
return mLeft + getTranslationX();
}
public float getY() {
return mTop + getTranslationY();
}
public float getZ() {
return getElevation() + getTranslationZ();
}
為了便于理解,我們可以將mLeft mTop elevation看做是View本身的坐標(biāo)值,translationX tranlationY tranlationZ看做是偏移量。則 最終坐標(biāo) = 坐標(biāo)值 + 偏移量。
而x y z的set方法設(shè)置的就是偏移量:
public void setX(float x) {
setTranslationX(x - mLeft);
}
public void setY(float y) {
setTranslationY(y - mTop);
}
public void setZ(float z) {
setTranslationZ(z - getElevation());
}
4 fitsSystemWindows
android:fitsSystemWindows
默認(rèn)false。詳細(xì)分析見 View源碼——fitSystemWindows詳解
5 saveEnabled
android:saveEnabled
設(shè)置View是否意外銷毀時,會調(diào)用onSaveInstanceState方法來保存狀態(tài)。默認(rèn)為true。詳見View源碼——saveInstanceState
6 duplicateParentState
設(shè)置當(dāng)前view是否使用父view的狀態(tài),默認(rèn)false。該狀態(tài)主要影響drawable的顯示。View源碼——duplicateParentState
7 滾動相關(guān)
以如下布局為例:
<ScrollView
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffc">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/emotion1"
android:layout_width="match_parent"
android:layout_height="900dp"
android:background="#9c9"
android:gravity="center"
android:scrollX="30dp"
android:text="TextView"
android:textSize="19dp" />
</LinearLayout>
</ScrollView>
此時的ui是這個樣子的:

1、ScrollView添加兩個屬性:
<ScrollView
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadingEdgeLength="50dp"
android:requiresFadingEdge="vertical"
android:background="#ffc">

scrollview的背景是米黃色??梢钥吹?,在可以滑動的邊上,呈現(xiàn)出淡出的效果。
2、android:scrollbarSize控制滾動條的尺寸。
android:scrollbarStyle控制滾動條的風(fēng)格。有四個值:insideOverlay,insideInset,outsideOverlay,outsideInset。
為了方便區(qū)分這四個風(fēng)格,我們?yōu)镾crollView設(shè)置一個30dp的padding, 同時scrollbarSize設(shè)置為30dp。四種風(fēng)格如下:




3、android:overScrollMode控制scrollView滑到頭時,是否顯示陰影。圖4可以很明顯看出來滑到頭時的陰影部分。有三個取值:
- never: 從不顯示
- always:滑到頭時顯示
- ifContentScrolls(默認(rèn)):若內(nèi)容過短,scrollView無法滑動,則不顯示;否則顯示。
4、android:isScrollContainer與軟鍵盤有關(guān)。參考android:isScrollContainer 屬性的作用
5、android:verticalScrollbarPosition 垂直滾動條的顯示位置,有三個值:left right defaultPosition,很簡單不再多說。
8 android:filterTouchesWhenObscured
view被其他窗口遮擋時,是否過濾觸摸事件。默認(rèn)false。只看說明很難理解,下面看一個例子。
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:gravity="center_horizontal"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/bt1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="toast"
android:textAllCaps="false" />
</FrameLayout>
</LinearLayout>
java
public class ViewActivity extends Activity implements View.OnClickListener {
private View view;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view);
view = new View(getApplicationContext());
view.setBackgroundColor(0x88000000);
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.width = 720;
params.gravity = Gravity.LEFT | Gravity.TOP;
params.format = PixelFormat.TRANSPARENT;
params.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
getWindowManager().addView(view, params);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt1:
Toast.makeText(this, "hahha", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
getWindowManager().removeView(view);
}
}
代碼很簡單,在activity的頁面上蓋了一個window。如下:

透明黑色的部分是蓋的新的window,由圖可知,按鈕被window遮擋的部分也可以響應(yīng)點(diǎn)擊事件。
現(xiàn)在我們給根布局LinearLayout(按鈕本身或包含它的布局都可以)加上android:filterTouchesWhenObscured="true"

從圖11可以看到,按鈕被遮擋的部分,已經(jīng)不再響應(yīng)點(diǎn)擊事件了。
9 android:stateListAnimator
關(guān)于這個屬性的講解有很多,不再贅述
10 outline相關(guān)
api21開始才有的。api21之后,view添加elevation、transitionZ的方法,由二維空間變成了三維空間。在Z軸上,可以通過outline來為view設(shè)置陰影等。
xml文件可以設(shè)置以下三個屬性:
android:outlineAmbientShadowColor
android:outlineSpotShadowColor
android:outlineProvider
第一個用來設(shè)置環(huán)境光顏色,不過肉眼難以分辨。默認(rèn)黑色。
第二個用來設(shè)置陰影的主題顏色。默認(rèn)黑色。
第三個設(shè)置outline的獲取方式,分別為:background(默認(rèn))、none、bounds、paddedBounds。區(qū)別下面會講。
先看個例子:
xml
<ImageView
android:id="@+id/image"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:outlineProvider="bounds"
android:outlineSpotShadowColor="#f00"
android:src="@drawable/sss"
android:translationZ="60px" />

android:outlineProvider對應(yīng)的java方法為:
public void setOutlineProvider(ViewOutlineProvider provider) {
mOutlineProvider = provider;
invalidateOutline();
}
ViewOutlineProvider類很簡單:
public abstract class ViewOutlineProvider {
public abstract void getOutline(View view, Outline outline);
}
android:outlineProvider的四個值,對應(yīng)四個默認(rèn)的ViewOutlineProvider。
background(默認(rèn)):
public static final ViewOutlineProvider BACKGROUND = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
Drawable background = view.getBackground();
if (background != null) {
background.getOutline(outline);
} else {
outline.setRect(0, 0, view.getWidth(), view.getHeight());
outline.setAlpha(0.0f);
}
}
};
none則為null。
bounds:
public static final ViewOutlineProvider BOUNDS = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRect(0, 0, view.getWidth(), view.getHeight());
}
};
paddedBounds:
public static final ViewOutlineProvider PADDED_BOUNDS = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRect(view.getPaddingLeft(),
view.getPaddingTop(),
view.getWidth() - view.getPaddingRight(),
view.getHeight() - view.getPaddingBottom());
}
};
此外,view還有一個setClipToOutline的方法,可以根據(jù)outline裁剪內(nèi)容
對上面的例子,在java代碼中設(shè)置:
ImageView vImage = findViewById(R.id.image);
vImage.setClipToOutline(true);
vImage.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setOval(new Rect(0, 0, view.getHeight(), view.getHeight()));
}
});

需要注意的是,這里只影響顯示的內(nèi)容,不影響點(diǎn)擊區(qū)域。
同時,也不是任意的outline形狀都支持裁剪。只有矩形、圓角矩形、圓形這三種支持。其他形狀,包括橢圓都不支持。Outline類的方法canClip()可獲取當(dāng)前outline是否支持裁剪。其他設(shè)置方法可查看Outline類源碼。
11 android:tooltipText
api26新加。給view設(shè)置一個功能提示。與電腦軟件中,鼠標(biāo)停留在一個按鈕上,會彈出一個功能說明一樣。這里通過長按觸發(fā)。
如下面的TextView:

12 android:keyboardNavigationCluster
api26新增

13自動填充
api26新增的自動填充功能,目前使用很少,不再介紹。
沒搞明白的幾個
android:scrollIndicators
ViewGroup的屬性
1 android:clipChildren
該屬性默認(rèn)是true
看這樣一個布局:
<FrameLayout
android:id="@+id/btLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="100dp"
android:layout_height="100dp">
<Button
android:id="@+id/bt1"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:focusedByDefault="true"
android:onClick="onClick"
android:text="按鈕"
android:textAllCaps="false"
android:visibility="visible" />
</FrameLayout>
</FrameLayout>
打開開發(fā)者模式的布局邊界開關(guān),顯示如下:

在外層的FrameLayout上加上android:clipChildren="false",顯示如下:

該屬性對應(yīng)的java方法如下:
public void setClipChildren(boolean clipChildren) {
boolean previousValue = (mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN;
if (clipChildren != previousValue) {
setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
for (int i = 0; i < mChildrenCount; ++i) {
View child = getChildAt(i);
if (child.mRenderNode != null) {
child.mRenderNode.setClipToBounds(clipChildren);
}//child在繪制時,也會自行調(diào)用setClipToBounds方法
}
invalidate(true);
}
}
該方法只會影響直接子view。最終調(diào)用的是RenderNode的setClipToBounds方法。setClipToBounds方法會根據(jù)view的邊界對顯示區(qū)域進(jìn)行裁剪。
RenderNode后續(xù)再講。感興趣的同學(xué)也可以查詢其他資料。也可以把它暫時看做是canvas。
上例中,圖16的顯示應(yīng)該不必說了。關(guān)于圖17的顯示,為什么是這個樣子的呢?對于Button,其父view默認(rèn)setClipChildren(true),所以畫布會根據(jù)Button的邊界進(jìn)行一次裁剪;而對于內(nèi)部的FrameLayout,其父view設(shè)置了setClipChildren(false),所以畫布并不會根據(jù)它的邊界進(jìn)行裁剪,所以就顯示出來了。
需要注意的是,雖然Button顯示完全了,但是其父view邊界之外的區(qū)域不能響應(yīng)點(diǎn)擊事件。所以setClipChildren方法的注釋是這樣寫的:
默認(rèn)情況下,子view在繪制之前會根據(jù)它的邊界進(jìn)行裁剪。ViewGroup可以覆寫該方法以作動畫之用。
我的理解是,看看就得了,別摸!??!
2 android:clipToPadding
若該屬性為true,并且設(shè)置了padding,則ViewGroup會添加一個CLIP_TO_PADDING_MASK的標(biāo)志位。該標(biāo)志位會影響
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
方法中的cancas,如下:
ViewGroup#dispatchDraw
final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
if (clipToPadding) {
clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
mScrollX + mRight - mLeft - mPaddingRight,
mScrollY + mBottom - mTop - mPaddingBottom);
}
對畫布進(jìn)行裁剪,留出padding的余量。
該屬性一般用在可滾動的view上,如ScrollView,ListView,GridView,RecyclerView等。
對如下布局:
<ScrollView
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffc"
android:padding="30dp"
android:visibility="visible">
<LinearLayout
android:id="@+id/layout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/emotion1"
android:layout_width="match_parent"
android:layout_height="900dp"
android:background="#9c9"
android:text="一鄉(xiāng)二里共三夫子不識四書五經(jīng)六藝竟敢教七八九子十分大膽,十室九貧湊得八兩七錢六分五毫四厘尚且三心二意一等下流"
android:textSize="19dp"
android:tooltipText="sssssssssssssss" />
</LinearLayout>
</ScrollView>
顯示效果為:

為ScrollView添加android:clipToPadding="false"后:

可以看到,在overscroll時,兩段陰影部分的位置也變了。
3 動畫相關(guān)
android:layoutAnimation、android:animateLayoutChanges
參考android 動畫系列 (3) - layoutAnimation 視圖動畫,說的很詳細(xì)。
4 android:splitMotionEvents
對應(yīng)的方法如下:
public void setMotionEventSplittingEnabled(boolean split) {
// TODO Applications really shouldn't change this setting mid-touch event,
// but perhaps this should handle that case and send ACTION_CANCELs to any child views
// with gestures in progress when this is changed.
if (split) {
mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
} else {
mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
}
}
true: 父類將多點(diǎn)觸控分配給對應(yīng)的view
false: 父類將多點(diǎn)觸控分配給第一個獲得觸摸事件的view
該屬性默認(rèn)是true。如下:
public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initViewGroup();
//該方法設(shè)置了的屬性才會有默認(rèn)值。對于XML文件沒有設(shè)置的屬性,不用理會該方法中的默認(rèn)值。
initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
}
private void initViewGroup() {
....
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
...
}
默認(rèn)情況下:

可以看到,兩個ScrollView可以獨(dú)立滑動。設(shè)置android:splitMotionEvents="false"

可以看到,只能滑動一個ScrollView,該ScrollView是先獲得Down事件的那個。
至此,View和ViewGroup除focus相關(guān),其他屬性基本都學(xué)習(xí)了。
ScrollView
1 android:fillViewport
設(shè)置ScrollView是否對內(nèi)容進(jìn)行拉伸,來適配ScrollView的高度,默認(rèn)false。對應(yīng)方法如下:
public void setFillViewport(boolean fillViewport) {
if (fillViewport != mFillViewport) {
mFillViewport = fillViewport;
requestLayout();
}
}
看如下布局:
<ScrollView
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="false"
android:overScrollMode="ifContentScrolls">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#9c9"
android:gravity="center"
android:text="TextView"
android:textSize="19dp" />
</ScrollView>

修改android:fillViewport="true":

ImageView
關(guān)于ScaleType比較基礎(chǔ),這里不再多說了。
1 android:adjustViewBounds
使View的尺寸和Drawable保持同樣的寬高比。對應(yīng)的方法為:
public void setAdjustViewBounds(boolean adjustViewBounds) {
mAdjustViewBounds = adjustViewBounds;
if (adjustViewBounds) {
setScaleType(ScaleType.FIT_CENTER);
}
}
可以看到,該屬性會設(shè)置ScaleType為FIT_CENTER,這樣可以保證Drawable剛好填滿View。該屬性對應(yīng)的成員變量為mAdjustViewBounds。
不過ImageView解析XML屬性的順序如下:
setAdjustViewBounds(a.getBoolean(R.styleable.ImageView_adjustViewBounds, false));
...
final int index = a.getInt(R.styleable.ImageView_scaleType, -1);
if (index >= 0) {
setScaleType(sScaleTypeArray[index]);
}
所以如果XML同樣設(shè)置了ScaleType,則以開發(fā)者設(shè)置的為準(zhǔn)。
mAdjustViewBounds在onMeasure時會影響View的尺寸。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
...
int w;
int h;
...
// We are allowed to change the view's width
boolean resizeWidth = false;
// We are allowed to change the view's height
boolean resizeHeight = false;
final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
...
w = mDrawableWidth;
h = mDrawableHeight;
...
//檢查是否設(shè)置了mAdjustViewBounds
if (mAdjustViewBounds) {
//只有在任意一邊沒有設(shè)置固定尺寸的情況下才可以resize
resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
desiredAspect = (float) w / (float) h;
}
...
if (resizeWidth || resizeHeight) {
//根據(jù)寬度重新計(jì)算高度,或者根據(jù)高度重新計(jì)算寬度
...
} else {
...
}
setMeasuredDimension(widthSize, heightSize);
}
這里要注意的是,ImageView的寬高至少有一邊是wrapContent,mAdjustViewBounds才能生效。并且View的尺寸只是和Drawable的尺寸保持寬高比一致,并不是大小一致。通常Drawable還是要被縮放。同時,因?yàn)镮mageView設(shè)置Drawable的方法都要調(diào)用requestLayout(),所以ImageView的尺寸會動態(tài)調(diào)整。
2 android:drawableAlpha
不同于View的屬性android:alpha,android:drawableAlpha用來設(shè)置Drawable的透明度。對應(yīng)的方法為:
public void setImageAlpha(int alpha) {
setAlpha(alpha);
}
@Deprecated
public void setAlpha(int alpha) {
alpha &= 0xFF; // keep it legal
if (mAlpha != alpha) {
mAlpha = alpha;
mColorMod = true;
applyColorMod();
invalidate();
}
}
取值范圍0~255。View的setAlpha:
public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mAlpha != alpha) {
setAlphaInternal(alpha);
if (onSetAlpha((int) (alpha * 255))) {
mPrivateFlags |= PFLAG_ALPHA_SET;
// subclass is handling alpha - don't optimize rendering cache invalidation
invalidateParentCaches();
invalidate(true);
} else {
mPrivateFlags &= ~PFLAG_ALPHA_SET;
invalidateViewProperty(true, false);
mRenderNode.setAlpha(getFinalAlpha());
}
}
}
用來設(shè)置整個View的透明度,取值范圍0~1。
例子如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#9c9"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#c99"
android:src="@drawable/sss" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.5"
android:background="#c99"
android:src="@drawable/sss" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#c99"
android:src="@drawable/sss"
tools:drawableAlpha="128"/>
</LinearLayout>

注:雖然ImageView有
android:drawableAlpha屬性,但直接在XML中設(shè)置,編譯時會報(bào)錯,提示attribute android:drawableAlpha is private。所以上例使用了tools命名空間,截圖是預(yù)覽頁面。
雖然XML中不能設(shè)置該屬性,但我們可以通過java代碼調(diào)用setImageAlpha方法進(jìn)行設(shè)置。不清楚google為什么這么搞,好奇怪,難道是bug?
3 android:cropToPadding
public void setCropToPadding(boolean cropToPadding) {
if (mCropToPadding != cropToPadding) {
mCropToPadding = cropToPadding;
requestLayout();
invalidate();
}
}
該屬性與ViewGroup的android:clipToPadding功能類似。在ImageView#onDraw方法中:
if (mCropToPadding) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,
scrollX + mRight - mLeft - mPaddingRight,
scrollY + mBottom - mTop - mPaddingBottom);
}
canvas.translate(mPaddingLeft, mPaddingTop);
if (mDrawMatrix != null) {
canvas.concat(mDrawMatrix);
}
mDrawable.draw(canvas);
同樣是裁剪畫布的操作。
對如下兩個ImageView:
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#c9c"
android:padding="20dp"
android:scrollY="100dp"
android:src="@drawable/sss" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#999"
android:cropToPadding="true"
android:padding="20dp"
android:scrollY="100dp"
android:src="@drawable/sss" />
顯示效果為:

TextView
1 android:editable
該屬性決定TextView是否可編輯,默認(rèn)值為false,由以下方法獲得:
protected boolean getDefaultEditable() {
return false;
}
EditText就是覆寫該方法,返回了true。
若可編輯,則會創(chuàng)建Editor,以接收處理各種KeyEvent。
如下例子:
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:text="一鄉(xiāng)二里共三夫子不識四書五經(jīng)六藝竟敢教七八九子十分大膽,十室九貧湊得八兩七錢六分五毫四厘尚且三心二意一等下流"
android:textSize="19dp" />

需要注意的是,這里必須設(shè)置android:focusable="true"和android:focusableInTouchMode="true",否則TextView無法獲取焦點(diǎn)。
2 限制輸入的字符類型
參考Android EditText限制輸入字符的5種實(shí)現(xiàn)方式
3 android:selectAllOnFocus
默認(rèn)false,對應(yīng)方法:
@android.view.RemotableViewMethod
public void setSelectAllOnFocus(boolean selectAllOnFocus) {
createEditorIfNeeded();
mEditor.mSelectAllOnFocus = selectAllOnFocus;
if (selectAllOnFocus && !(mText instanceof Spannable)) {
setText(mText, BufferType.SPANNABLE);
}
}
以EditText為例,設(shè)置為true,在獲取焦點(diǎn)時,會選中全部內(nèi)容
4 鏈接
android:autoLink設(shè)置是否將電話、郵箱、網(wǎng)址等顯示為鏈接形式。默認(rèn)都不顯示為鏈接形式。
android:linksClickable設(shè)置鏈接是否可點(diǎn)擊。默認(rèn)true
5 尺寸相關(guān)
android:ems
控制TextView的寬度,只在android:layout_width="wrap_content"時有效。ems表示字符的寬度,可粗略理解為1ems相當(dāng)于1個漢字的寬度。英文的非等寬字體每個字符寬度不同,所以1ems所能容下的數(shù)量也不同。
android:lines android:height
控制TextView的高度,只在android:layout_height="wrap_content"時有效。并且兩個屬性互斥,看源碼:
public void setLines(int lines) {
mMaximum = mMinimum = lines;
mMaxMode = mMinMode = LINES;
requestLayout();
invalidate();
}
public void setHeight(int pixels) {
mMaximum = mMinimum = pixels;
mMaxMode = mMinMode = PIXELS;
requestLayout();
invalidate();
}
此外還有單獨(dú)的android:maxEms等屬性設(shè)置。
以上屬性影響的是TextView的尺寸,與內(nèi)容無關(guān),注意與android:maxLength區(qū)分開。android:maxLength控制顯示的最大字符數(shù),實(shí)現(xiàn)如下:
if (maxlength >= 0) {
setFilters(new InputFilter[] { new InputFilter.LengthFilter(maxlength) });
} else {
setFilters(NO_FILTERS);
}
通過LengthFilter控制最大字符數(shù)。
6 android:includeFontPadding
默認(rèn)是true。設(shè)置為false,可以一定程度去掉上下的留白。注意,和設(shè)置padding屬性是毫無關(guān)系的。
7 android:cursorVisible
設(shè)置是否隱藏掉光標(biāo)。
8 android:textScaleX
設(shè)置橫向拉伸/壓縮內(nèi)容
9 android:freezesText
該屬性決定TextView被意外銷毀時,是否保存當(dāng)前內(nèi)容。對應(yīng)的java方法:
public void setFreezesText(boolean freezesText) {
mFreezesText = freezesText;
}
public boolean getFreezesText() {
return mFreezesText;
}
看源碼是如何起作用的:
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
// Save state if we are forced to
final boolean freezesText = getFreezesText();
...
SavedState ss = new SavedState(superState);
if (freezesText) {
if (mText instanceof Spanned) {
final Spannable sp = new SpannableStringBuilder(mText);
if (mEditor != null) {
removeMisspelledSpans(sp);
sp.removeSpan(mEditor.mSuggestionRangeSpan);
}
ss.text = sp;
} else {
ss.text = mText.toString();
}
}
...
return superState;
}
可以看到設(shè)置freezesText后,在onSaveInstanceState時就可以保存當(dāng)前內(nèi)容。
需要注意的是,想要保存內(nèi)容,還需要設(shè)置當(dāng)前視圖樹唯一的id,原因見View源碼——saveInstanceState
EditText覆寫了get方法:
@Override
public boolean getFreezesText() {
return true;
}
所以在有唯一id的情況下,默認(rèn)會正確保存當(dāng)前內(nèi)容。
10 cursor|handle相關(guān)
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="一鄉(xiāng)二里共三夫子,不識四書五經(jīng)六義,竟敢教七八九子,十分大膽"
android:textSelectHandleLeft="@drawable/cursor_red"
android:textSelectHandleRight="@drawable/cursor_green"
android:textSelectHandle="@drawable/cursor_blue"
android:textCursorDrawable="@drawable/cursor_purple"<!-- 光標(biāo)顏色 -->
android:textSize="19dp" />


除光標(biāo)外,其他三個設(shè)置在MIUI上無效
11 android:textIsSelectable
設(shè)置文本是否可被選中,選中態(tài)會彈出上下文菜單,默認(rèn)false。如:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="雞叫一聲撅一撅,\n雞叫兩聲撅兩撅。\n三聲喚出扶桑日,\n掃敗殘星與曉月。"
android:textColor="#000"
android:textIsSelectable="true"
android:textSize="20dp" />

該菜單可以通過長按或者雙擊喚出。
12 文字上下邊距
除了可以設(shè)置padding,還有兩個方法:
android:firstBaselineToTopHeight="30dp"
android:lastBaselineToBottomHeight="30dp"