View源码——android:XX属性(持续更新)

基于api28

常用属性不再赘述,只看一些不常用的或者新添加的属性。

1 padding

android:paddingHorizontal //同时设置左右padding
android:paddingVertical //同时设置上下padding

以上属性api26添加。

2 scroll

android:scrollX
android:scrollY

以上两个属性对应的成员变量为mScrollX mScrollX,在绘制之前,会执行

canvas.translate(-mScrollX, -mScrollY);

同时影响当前View的绘制,及其children的绘制。但对当前view的背景没有影响!
偏移量 > 0, 内容向右下偏移;
偏移量 < 0, 内容向左上偏移。

注:在xml中设置View的继承类的android:scrollX、android:scrollY不一定起作用,依赖于具体的实现。
比如ScrollView,由于在滚动时会判断getChildCount() > 0,而初始化时还没有子View,所以不起作用。

如下图为自定义的View,背景为黑色,在onDraw方法中画了一个圆,宽高均为100dp:

4272648-c4c5d3c1426723eb.png
图1 原图

设置android:scrollX=-50dp,背景不移动,内容右移:

4272648-9292ae3b5df62b31.png
图2 android:scrollX=-50dp

至于为什么不影响背景,后续再学习。

3 matrix变换

<!-- 设置scale、rotation变换的中心点 -->
android:transformPivotX
android:transformPivotY

android:rotation
android:rotationX
android:rotationY

android:translationX
android:translationY
android:translationZ

android:scaleX
android:scaleY

对图2设置如下属性:

        android:transformPivotX="50dp"
        android:transformPivotY="50dp"
        android:rotation="30"
        android:scaleX="0.8"
        android:translationX="30dp"

显示如下:

4272648-16d158cdadd59245.png
图3 matrix变换

蓝色方框是matrix变换之前的位置。
为当前View设置点击事件,可以发现,仅在点击View的绘制区域(非白色区域)时触发onClick方法,在点击蓝色方框内的空白区域时不会触发。
有以下结论:

  1. matrix变换影响整个View的显示(包括子View)
  2. matrix与scrollX srollY互不影响
  3. matrix会影响可点击区域

有的同学还知道有个android:elevation属性,它和android:translationZ有什么区别呢?

android系统中,View所在的坐标系是一个三维的,View最终的坐标可以通过getX getY getZ三个方法得到。看一下这三个方法的实现:

    public float getX() {
        return mLeft + getTranslationX();
    }

    public float getY() {
        return mTop + getTranslationY();
    }

    public float getZ() {
        return getElevation() + getTranslationZ();
    }

为了便于理解,我们可以将mLeft mTop elevation看做是View本身的坐标值,translationX tranlationY tranlationZ看做是偏移量。则 最终坐标 = 坐标值 + 偏移量。

x y z的set方法设置的就是偏移量:

    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

默认false。详细分析见 View源码——fitSystemWindows详解

5 saveEnabled

android:saveEnabled

设置View是否意外销毁时,会调用onSaveInstanceState方法来保存状态。默认为true。详见View源码——saveInstanceState

6 duplicateParentState

设置当前view是否使用父view的状态,默认false。该状态主要影响drawable的显示。View源码——duplicateParentState

7 滚动相关

以如下布局为例:

    <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是这个样子的:

4272648-bbd4edad93fad586.gif
图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">
4272648-9c151cfb998c56be.gif
图5

scrollview的背景是米黄色。可以看到,在可以滑动的边上,呈现出淡出的效果。

2、android:scrollbarSize控制滚动条的尺寸。
android:scrollbarStyle控制滚动条的风格。有四个值:insideOverlay,insideInset,outsideOverlay,outsideInset
为了方便区分这四个风格,我们为ScrollView设置一个30dp的padding, 同时scrollbarSize设置为30dp。四种风格如下:

4272648-771e42fe7a0faf5d.png
图6 insideOverlay
4272648-ffbc1c8c6b10fafc.png
图7 insideInset
4272648-9682c3ba4fce1b39.png
图8 outsideOverlay
4272648-1088adea35de2fb4.png
图9 outsideInset

3、android:overScrollMode控制scrollView滑到头时,是否显示阴影。图4可以很明显看出来滑到头时的阴影部分。有三个取值:

  • never: 从不显示
  • always:滑到头时显示
  • ifContentScrolls(默认):若内容过短,scrollView无法滑动,则不显示;否则显示。

4、android:isScrollContainer与软键盘有关。参考android:isScrollContainer 属性的作用

5、android:verticalScrollbarPosition 垂直滚动条的显示位置,有三个值:left right defaultPosition,很简单不再多说。

8 android:filterTouchesWhenObscured

view被其他窗口遮挡时,是否过滤触摸事件。默认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。如下:

4272648-4b90adc562b1520f.gif
图10

透明黑色的部分是盖的新的window,由图可知,按钮被window遮挡的部分也可以响应点击事件。
现在我们给根布局LinearLayout(按钮本身或包含它的布局都可以)加上android:filterTouchesWhenObscured="true"

4272648-4a71173e53187756.gif
图11

从图11可以看到,按钮被遮挡的部分,已经不再响应点击事件了。

9 android:stateListAnimator

关于这个属性的讲解有很多,不再赘述

10 outline相关

api21开始才有的。api21之后,view添加elevation、transitionZ的方法,由二维空间变成了三维空间。在Z轴上,可以通过outline来为view设置阴影等。

xml文件可以设置以下三个属性:

        android:outlineAmbientShadowColor
        android:outlineSpotShadowColor
        android:outlineProvider

第一个用来设置环境光颜色,不过肉眼难以分辨。默认黑色。
第二个用来设置阴影的主题颜色。默认黑色。
第三个设置outline的获取方式,分别为:background(默认)、none、bounds、paddedBounds。区别下面会讲。

先看个例子:
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" />
4272648-b755b5c9820a5694.png
图12 outline阴影效果

android:outlineProvider对应的java方法为:

    public void setOutlineProvider(ViewOutlineProvider provider) {
        mOutlineProvider = provider;
        invalidateOutline();
    }

ViewOutlineProvider类很简单:

public abstract class ViewOutlineProvider {
    public abstract void getOutline(View view, Outline outline);
}

android:outlineProvider的四个值,对应四个默认的ViewOutlineProvider
background(默认)

    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的方法,可以根据outline裁剪内容
对上面的例子,在java代码中设置:

        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()));
            }
        });
4272648-6ec325a35e580e25.png
图13

需要注意的是,这里只影响显示的内容,不影响点击区域。
同时,也不是任意的outline形状都支持裁剪。只有矩形、圆角矩形、圆形这三种支持。其他形状,包括椭圆都不支持。Outline类的方法canClip()可获取当前outline是否支持裁剪。其他设置方法可查看Outline类源码。

11 android:tooltipText

api26新加。给view设置一个功能提示。与电脑软件中,鼠标停留在一个按钮上,会弹出一个功能说明一样。这里通过长按触发。
如下面的TextView:

4272648-b341ce9998c3ba1f.gif
图14

12 android:keyboardNavigationCluster

api26新增

google链接

4272648-ff1388c3f9233ba6.png
图15

13自动填充

api26新增的自动填充功能,目前使用很少,不再介绍。

没搞明白的几个

android:scrollIndicators


ViewGroup的属性

1 android:clipChildren

该属性默认是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>

打开开发者模式的布局边界开关,显示如下:

4272648-0c1ee0d414229655.png
图16

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

4272648-3875f530f94ad615.png
图17

该属性对应的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在绘制时,也会自行调用setClipToBounds方法
            }
            invalidate(true);
        }
    }

该方法只会影响直接子view。最终调用的是RenderNode的setClipToBounds方法。setClipToBounds方法会根据view的边界对显示区域进行裁剪。

RenderNode后续再讲。感兴趣的同学也可以查询其他资料。也可以把它暂时看做是canvas。

上例中,图16的显示应该不必说了。关于图17的显示,为什么是这个样子的呢?对于Button,其父view默认setClipChildren(true),所以画布会根据Button的边界进行一次裁剪;而对于内部的FrameLayout,其父view设置了setClipChildren(false),所以画布并不会根据它的边界进行裁剪,所以就显示出来了。

需要注意的是,虽然Button显示完全了,但是其父view边界之外的区域不能响应点击事件。所以setClipChildren方法的注释是这样写的:

默认情况下,子view在绘制之前会根据它的边界进行裁剪。ViewGroup可以覆写该方法以作动画之用。

我的理解是,看看就得了,别摸!!!

2 android:clipToPadding

若该属性为true,并且设置了padding,则ViewGroup会添加一个CLIP_TO_PADDING_MASK的标志位。该标志位会影响

    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);
        }

对画布进行裁剪,留出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="一乡二里共三夫子不识四书五经六艺竟敢教七八九子十分大胆,十室九贫凑得八两七钱六分五毫四厘尚且三心二意一等下流"
                android:textSize="19dp"
                android:tooltipText="sssssssssssssss" />
        </LinearLayout>
    </ScrollView>

显示效果为:

4272648-8491a5d4f7557acd.gif
图18

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

4272648-bcfbccd2ebefd551.gif
图19

可以看到,在overscroll时,两段阴影部分的位置也变了。

3、动画相关

android:layoutAnimationandroid:animateLayoutChanges
参考android 动画系列 (3) - layoutAnimation 视图动画,说的很详细。

猜你喜欢

转载自blog.csdn.net/weixin_34381666/article/details/87346795
今日推荐