MaterialDesign设计0:定义阴影与裁剪视图

来源:互联网 发布:如何做网络推广员 编辑:程序博客网 时间:2024/05/01 02:45

最近在读谷歌的官方文档,整理一下,以备不时之需。部分内容如有错误,欢迎留言指正。

前言

本篇博客解读MaterialDesign中的视图高度 和剪裁视图。视图高度说的通俗点,就是我们在MaterialDesign中看到的View的阴影效果。官方给定的示意图:
视图高度
该效果图分为上下两个View,上图阴影较大,下图阴影较小,从阴影效果带来的直观感受来看,上面的View明显比下面的View要“高”一些。这种感觉和生活中的常识是一致的:假设阳光下有一张纸,放在桌子上时的阴影几乎只有边缘部分,而当我们把它拿起时,阴影会随着我们拿起的高度升高而随之变大。

谷歌便把这种感官添加到了View中:默认情况下的View(放在桌子上的纸),应该如下图所示,只有一条淡淡的阴影(2dp),当我们对View进行类似单击等操作时(拿起桌子上的纸),View的阴影发生改变(升高了6dp),从而使用户有一种View被“拿起来”等待操作的感觉。

下面我们来看看各类View在MD中的阴影效果,先放效果图:
效果图

  • Button按钮:自带阴影交互效果,所以这里这里就不拿Button举例子了。
  • 背景可绘制View:如TextView、CardView、ImageView等可以设置背景的View。

    以下内容都基于背景可绘制的View进行讲解,为什么要求背景可绘制呢?因为有了背景才有阴影,你想想空气这类透明的事物会产生阴影效果吗?所以这里要求View的背景是可绘制的,并且背景不能为透明


动手操作:设置阴影交互

组成:阴影交互效果由两部分组成:默认高度(elevation)和动态转换高度(translationZ),即官方文档中说的“Z轴高度 = elevation + translationZ“
  • 设置默认高度elevation,以TextView为例子

     <TextView    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:layout_margin="10dp"    android:background="#03A9F4"    android:clickable="true"    android:elevation="2dp"    android:gravity="center"    android:text="TextView有阴影"    android:textSize="20sp"/>

    background——>设定背景,一定要注意背景不能为透明
    clickable——>让其能响应用户的点击
    elevation——>2dp的默认阴影效果。
    *注意:CardView中的默认阴影效果需要调用app:cardElevation=”2dp”
    对比效果如下

    这里写图片描述
    仔细看,是能看出来下图的2dp阴影的。但是此时单击TextView并没有使阴影变大。

  • 设置动态转换高度translationZ

    阴影变大的操作,是通过translationZ属性来操作的。translationZ指的是View在Z轴上的显示高度(也就是把View”拿起来”的高度)。默认的translationZ应该为0(因为默认的View没有被”拿起来”),只有当我们和View产生交互时,才应该让translationZ的值增加,产生更大的阴影效果。

    因此,translationZ这个属性不是让我们在Veiw的布局中直接使用固定值的,而是应该根据View的不同交互状态而做出大小的调整。Google文档方案为”selector+属性动画”来动态改变translationZ的数值。

    1. 下面我们建立一个selector命名为selector_view_raise.xml,里面根据View的不同状态设置不同的属性动画

      <selector xmlns:android="http://schemas.android.com/apk/res/android">    <!--按下状态,translationZ变为6dp-->    <item android:state_pressed="true">        <objectAnimator            android:duration="@android:integer/config_shortAnimTime"            android:propertyName="translationZ"            android:valueTo="6dp"            android:valueType="floatType"></objectAnimator>    </item>    <!--默认状态,translationZ变回0dp-->    <item>        <objectAnimator            android:duration="@android:integer/config_shortAnimTime"            android:propertyName="translationZ"            android:valueTo="0dp"            android:valueType="floatType"></objectAnimator>    </item></selector>
    2. 把定义好的selector_view_raise.xml设置到TextView中,即可产生阴影交互

      android:stateListAnimator="@animator/selector_view_raise"     

      如果不想要View的阴影交互行为,请将 android:stateListAnimator 属性设置为 @null。

    3. 运行后点击TextView,即可出现文章开头展示的动态效果
  • 只要是背景可绘制View,都可产生阴影交互。阴影的效果,会根据绘制的背景边缘进行自动填充。下面我们来自定义一个背景,看看效果


自定义shape的阴影交互

上面例子中只使用的颜色作为背景。除了颜色,我们还可以自定义shape,用shape作为View的背景,阴影会根据我们定义的shape边缘,自动产生阴影效果。

  • 定义一个圆角shape命名为shape_background_blue.xml

    <shape xmlns:android="http://schemas.android.com/apk/res/android"       android:shape="rectangle">    <solid android:color="@color/colorPrimary"></solid>    <corners android:radius="5dp"></corners></shape>
  • 设置View的background属性

    android:background="@drawable/shape_background_blue"      
  • 该View将自动绘制一个带有圆角的阴影


剪裁视图和使用揭露动画

在5.0后,我们可以对边界为矩形、圆形和圆角矩形的View进行剪裁操作。可使用View.setClipToOutline() 方法或 android:clipToOutline 属性将视图裁剪至其轮廓区域,也可调用View.setClipBounds(Rect)来裁剪制定的区域。

谷歌指出,裁剪View是一个成本高昂的操作,因此不可以把剪裁操作作为属性动画(比如想把一个正方形一点点的剪成一个圆)。 如果要实现类似效果,请使用揭露效果动画。

下面我们先来看一下揭露效果 + 裁剪操作
揭露效果和裁剪操作
通过效果图我们可以发现:
1.首次点击搜索按钮时,蓝色区域收起,再次点击,蓝色展开
2.点击裁剪按钮,蓝色区域被裁剪为左下角的1/4
3.再次点击搜索按钮,被裁剪后的蓝色区域收起,再次点击,蓝色展开

  • 编写布局,这一步比较简单,我用了3个ImageView来分别显示蓝色区域、搜索按钮、裁剪按钮。大家copy过去就可以了,图标自行替换

    <LinearLayout    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:orientation="horizontal">    <ImageView        android:id="@+id/iv_blue_scope"        android:layout_width="0dp"        android:layout_height="match_parent"        android:layout_weight="1"        android:background="@color/colorPrimary"        />    <ImageView        android:id="@+id/iv_search"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center_vertical"        android:background="@android:drawable/ic_menu_search"        />    <ImageView        android:id="@+id/iv_cut"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center_vertical"       android:background="@drawable/ic_content_cut_black_24dp"/></LinearLayout>
  • 找到控件,对控件设置单击事件

    //控件ImageView iv_blue_scope;ImageView iv_search;ImageView iv_cut;@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_0);    iv_blue_scope = (ImageView) findViewById(R.id.iv_blue_scope);    iv_search = (ImageView) findViewById(R.id.iv_search);    iv_cut = (ImageView) findViewById(R.id.iv_cut);    iv_search.setOnClickListener(this);    iv_cut.setOnClickListener(this);}//单击事件public void onClick(View view) {    int id = view.getId();    switch (id) {        case R.id.iv_search:            //揭露ImageView            exposeImageView();            break;        case R.id.iv_cut:            //裁剪操作            clipImageView();            break;    }}
  • 在单击事件中,我们先来看揭露操作exposeImageView(),对于揭露操作,官方给了很简明的示例,我这里就是根据官方的示例,进行了部分的修改。直接上代码了,注意看注释

       private void exposeImageView() {        //获取剪裁中心点,即扩散点,此处以View的右上角为中心点        int cx = iv_blue_scope.getRight();        int cy = iv_blue_scope.getTop();        //获取最终剪裁的半径,以View的最长边为准        int finalRadius = Math.max(iv_blue_scope.getWidth(), iv_blue_scope.getHeight());        //创建动画        Animator anim = null;        //根据View的当前显示状态进行展开和收起的操作        if (iv_blue_scope.getVisibility() == View.INVISIBLE) {            //创建圆形的揭露动画,从指定View(iv_blue_scope)的中心点( cx, cy,)开始,揭露半径从0到View的最长边(finalRadius)            anim = ViewAnimationUtils.createCircularReveal(iv_blue_scope, cx, cy, 0, finalRadius);            //显示View,开启动画            iv_blue_scope.setVisibility(View.VISIBLE);            anim.start();        } else {            //创建圆形的反向揭露动画,揭露半径从最长边到0即可            anim = ViewAnimationUtils.createCircularReveal(iv_blue_scope, cx, cy, finalRadius, 0);            //注意,这里需要先展示动画,等动画结束后才可以把View隐藏掉。所以要对动画进行侦听            anim.addListener(new AnimatorListenerAdapter() {                @Override                public void onAnimationEnd(Animator animation) {                    super.onAnimationEnd(animation);                    iv_blue_scope.setVisibility(View.INVISIBLE);                }            });            anim.start();        }    }
  • 裁剪View,我这里是裁剪了部分区域,未执行剪裁到边界的操作,因为大部分的View边界和View大小一致,剪裁到边界相当于不剪裁。

    private void clipImageView() {    //获取iv_clipped的中心坐标    int cx = (iv_blue_scope.getLeft() + iv_blue_scope.getRight()) / 2;    int cy = (iv_blue_scope.getTop() + iv_blue_scope.getBottom()) / 2;    //实例化裁剪区域,此处我们裁剪该View的左下角的1/4区域    Rect rect = new Rect(iv_blue_scope.getLeft(), cy, cx, iv_blue_scope.getBottom());    //根据以上区域裁剪iv_clipped    iv_blue_scope.setClipBounds(rect);}

源码:MaterialDesignDemo

0 0
原创粉丝点击