基于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:
设置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"
显示如下:
蓝色方框是matrix变换之前的位置。
为当前View设置点击事件,可以发现,仅在点击View的绘制区域(非白色区域)时触发onClick方法,在点击蓝色方框内的空白区域时不会触发。
有以下结论:
- matrix变换影响整个View的显示(包括子View)
- matrix与scrollX srollY互不影响
- 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是这个样子的:
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的背景是米黄色。可以看到,在可以滑动的边上,呈现出淡出的效果。
2、android:scrollbarSize
控制滚动条的尺寸。android:scrollbarStyle
控制滚动条的风格。有四个值:insideOverlay,insideInset,outsideOverlay,outsideInset
。
为了方便区分这四个风格,我们为ScrollView设置一个30dp的padding, 同时scrollbarSize设置为30dp。四种风格如下:
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。如下:
透明黑色的部分是盖的新的window,由图可知,按钮被window遮挡的部分也可以响应点击事件。
现在我们给根布局LinearLayout(按钮本身或包含它的布局都可以)加上android:filterTouchesWhenObscured="true"
从图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" />
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()));
}
});
需要注意的是,这里只影响显示的内容,不影响点击区域。
同时,也不是任意的outline形状都支持裁剪。只有矩形、圆角矩形、圆形这三种支持。其他形状,包括椭圆都不支持。Outline类的方法canClip()
可获取当前outline是否支持裁剪。其他设置方法可查看Outline类源码。
11 android:tooltipText
api26新加。给view设置一个功能提示。与电脑软件中,鼠标停留在一个按钮上,会弹出一个功能说明一样。这里通过长按触发。
如下面的TextView:
12 android:keyboardNavigationCluster
api26新增
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>
打开开发者模式的布局边界开关,显示如下:
在外层的FrameLayout上加上android:clipChildren="false"
,显示如下:
该属性对应的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>
显示效果为:
为ScrollView添加android:clipToPadding="false"
后:
可以看到,在overscroll时,两段阴影部分的位置也变了。
3、动画相关
android:layoutAnimation
、android:animateLayoutChanges
参考android 动画系列 (3) - layoutAnimation 视图动画,说的很详细。