View源碼——android:XX屬性

基于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:

圖1 原圖

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

圖2 android:scrollX=-50dp

至于為什么不影響背景,后續(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"

顯示如下:

圖3 matrix變換

藍(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é)論:

  1. matrix變換影響整個View的顯示(包括子View)
  2. matrix與scrollX srollY互不影響
  3. 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是這個樣子的:

圖4

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">
圖5

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)格如下:

圖6 insideOverlay
圖7 insideInset
圖8 outsideOverlay
圖9 outsideInset

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。如下:

圖10

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

圖11

從圖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" />
圖12 outline陰影效果

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()));
            }
        });
圖13

需要注意的是,這里只影響顯示的內(nèi)容,不影響點(diǎn)擊區(qū)域。
同時,也不是任意的outline形狀都支持裁剪。只有矩形、圓角矩形、圓形這三種支持。其他形狀,包括橢圓都不支持。Outline類的方法canClip()可獲取當(dāng)前outline是否支持裁剪。其他設(shè)置方法可查看Outline類源碼。

11 android:tooltipText

api26新加。給view設(shè)置一個功能提示。與電腦軟件中,鼠標(biāo)停留在一個按鈕上,會彈出一個功能說明一樣。這里通過長按觸發(fā)。
如下面的TextView:

圖14

12 android:keyboardNavigationCluster

api26新增

google鏈接

圖15

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),顯示如下:

圖16

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

圖17

該屬性對應(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>

顯示效果為:

圖18

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

圖19

可以看到,在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)情況下:

圖20

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

圖21

可以看到,只能滑動一個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>
屏幕快照 2019-11-02 上午11.34.28.png

修改android:fillViewport="true"

屏幕快照 2019-11-02 上午11.38.02.png

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>
圖22

注:雖然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" />

顯示效果為:

圖23

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" />
圖24

需要注意的是,這里必須設(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)和handle
handleLeft和handleRight

除光標(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" />
device-2019-11-01-151930.png

該菜單可以通過長按或者雙擊喚出。

12 文字上下邊距

除了可以設(shè)置padding,還有兩個方法:

        android:firstBaselineToTopHeight="30dp"
        android:lastBaselineToBottomHeight="30dp"

參考Android 9.0關(guān)于字體的新特性


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容