精华内容
下载资源
问答
  • Drawable是什么? 一种可以在Canvas上进行绘制的抽象的概念 颜色、图片等都可以是一个Drawable Drawable可以通过XML定义,或者通过代码创建 Android中Drawable是一个抽象类,每个具体的Drawable都是其子类 ...
  • 主要为大家详细介绍了Android自定义Drawable实现圆角效果,实现一个圆形和圆角的背景图片效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • Android Drawable和Bitmap的转换实例详解 通常我们需要通过代码去设置图片,就需要设置图片Bitmap和Drawable的转换,下面整理了几种方式 一、Bitmap转Drawable Bitmap bm=xxx; //xxx根据你的情况获取 ...
  • Android drawable 三角形

    2018-11-22 08:52:12
    drawable实现三角形,原博客地址:https://blog.csdn.net/u010356768/article/details/76040972#comments 大家去原博客地址看下就知道了。不用下载浪费积分
  • 很多开发者表示,不知道Android的Drawable和Bitmap之间如何相关转换。下面Android123给大家两种比较简单高效的方法。  一、Bitmap转Drawable 代码如下: Bitmap bm=xxx; //xxx根据你的情况获取  BitmapDrawable bd...
  • Picasso的placeholder和error的参数也只有int resId和Drawable drawable 于是打算将改变过大小的Drawable传进入显示,咦,效果很满意! 整个过程的思路: 将本地图片(R.drawable.image)变成Drawable对象 将...
  • 主要介绍了Android 将网络的Url资源转换为Drawable资源方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 主要介绍了Android开发基于Drawable实现圆角矩形的方法,结合实例形式分析了Drawable的功能、相关图形绘制函数与使用方法,需要的朋友可以参考下
  • android-gif-drawable 用于在Android上显示动画GIF
  • Drawable天天用,可你是否对Drawable家族有一个完整的认知?今天我们就来系统的学习一下Drawable的使用。 1.概述 用过Drawable的筒子都知道Drawable有很多种,有的时候Drawable是一张图片,有的时候Drawable是我们...
  • Android用drawable实行属性按钮3种状态demo,原博客地址http://blog.csdn.net/u010356768/article/details/78351416
  • 自定义Drawable 实现图片圆角、圆形、椭圆形,帮助理解自定义Drawable; 自定义Drawable 实现图片圆角、圆形、椭圆形,帮助理解自定义Drawable
  • 自定义Drawable实现圆形图片和圆角图片,个人感觉挺简单的
  • 文档中描述了Drawable Bitmap 几种转换方法供初学者使用。方法都是经过实践验证的。
  • Android Drawable 各种高效用法

    千次下载 热门讨论 2015-02-26 09:18:47
    代码为博客的实例代码:http://blog.csdn.net/lmj623565791/article/details/43752383
  • drawable-xhdpi.zip

    2016-01-24 23:35:46
    Download drawable-xhdpi.zip 图片资源
  • DrawableTextView.zip

    2019-09-23 09:02:53
    DrawableTextView.zip,自定义控件 :drawable 跟随TextView居中 The drawable follows the text centered
  • gif-drawable播放本地gif图片
  • 代码实现drawable的selector效果,不用为每个控件写selector样式; 另外,实现圆形图片处理、圆角图片处理功能
  • 开源库 android-gif-drawable使用的jar包,使用详见博客http://blog.csdn.net/u012587637/article/details/18596465
  • android所有的可自定义xml资源的元素及子元素用法
  • 大家都知道,在Android项目当中,drawable文件夹都是用来放置图片资源的,不管是jpg、png、还是9.png,都可以放在这里。除此之外,还有像selector这样的xml文件也是可以放在drawable文件夹下面的。 但是如果你现在...

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/50727753
    好像有挺久时间没更新博客了,最近我为了准备下一个系列的博客,也是花了很长的时间研读源码。很遗憾的是,下一个系列的博客我可能还要再过一段时间才能写出来,那么为了不至于让大家等太久,今天就给大家更新一篇单篇的文章,讲一讲Android drawable方面的微技巧。


    话说微技巧这个词也是我自己发明的,因为drawable这个东西相信大家天天都在使用,每个人都再熟悉不过了,之所以叫微技巧就是对于这个我们再熟悉不过的技术,可能还有一些你所不知道的细节,那今天我们就来一起探究一下这些微小的细节吧。

    大家都知道,在Android项目当中,drawable文件夹都是用来放置图片资源的,不管是jpg、png、还是9.png,都可以放在这里。除此之外,还有像selector这样的xml文件也是可以放在drawable文件夹下面的。

    但是如果你现在使用Android Studio来新建一个项目,你会发现有如下的目录结构:

    嗯?怎么会有这么多mipmap开头的文件夹,而且它们的命名规则和drawable文件夹很相似,也是hdpi、mdpi、xhdpi等等,并且里面还真是放的图片,难道Android项目中放置图片的位置已经改了?

    对于刚刚从Eclipse转向Android Studio的开发者们可能会对mipmap文件夹感到陌生,其实不用担心,我们平时的编程习惯并不需要发生任何改变,因为mipmap文件夹只是用来放置应用程序的icon的,仅此而已。那么在此之前,我们都是把应用程序的icon图标和普通的图片资源一起放到drawable文件夹下的,这样看上去就会比较杂乱,有的时候想从一堆的图片资源里面找icon半天也找不到,而文件一多也就容易出现漏放的情况,但恰恰Android是极度建议我们在每一种分辨率的文件夹下面都放一个相应尺寸的icon的,因此将它们独立出来专门放到mimap文件夹当中就很好地解决了这个问题。

    另外,将icon放置在mipmap文件夹还可以让我们程序的launcher图标自动拥有跨设备密度展示的能力,比如说一台屏幕密度是xxhdpi的设备可以自动加载mipmap-xxxhdpi下的icon来作为应用程序的launcher图标,这样图标看上去就会更加细腻。

    关于建议使用mipmap的原文可以参阅这篇文章:Getting Your Apps Ready for Nexus 6 and Nexus 9, 当然你还是要科学上网的。

    除此之外,对于每种密度下的icon应该设计成什么尺寸其实Android也是给出了最佳建议,icon的尺寸最好不要随意设计,因为过低的分辨率会造成图标模糊,而过高的分辨率只会徒增APK大小。建议尺寸如下表所示:

    密度建议尺寸
    mipmap-mdpi48 * 48
    mipmap-hdpi72 * 72
    mipmap-xhdpi96 * 96
    mipmap-xxhdpi144 * 144
    mipmap-xxxhdpi192 * 192

    然后我们引用mipmap的方式和之前引用drawable的方式是完全一致的,在资源中就使用@mipmap/res_id,在代码就使用R.mipmap.res_id。比如AndroidManifest.xml中就是这样引用ic_launcher图标的:

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

    好的,关于mimap的内容就讲这么多,它并不是本篇文章的重点,接下来我们来真真正正看一些drawable的微技巧。


    首先我准备了一张270*480像素的图片:


    将图片命名为android_logo.png,然后把它放在drawable-xxhdpi文件夹下面。为什么要放在这个文件夹下呢?是因为我的手机屏幕的密度就是xxhdpi的。那么怎么才能知道自己手机屏幕的密度呢?你可以使用如下方法先获取到屏幕的dpi值:

    float xdpi = getResources().getDisplayMetrics().xdpi;
    float ydpi = getResources().getDisplayMetrics().ydpi;

    其中xdpi代表屏幕宽度的dpi值,ydpi代表屏幕高度的dpi值,通常这两个值都是近乎相等或者极其接近的,在我的手机上这两个值都约等于403。那么403又代表着什么意思呢?我们直接参考下面这个表格就知道了:

    dpi范围密度
    0dpi ~ 120dpildpi
    120dpi ~ 160dpimdpi
    160dpi ~ 240dpihdpi
    240dpi ~ 320dpixhdpi
    320dpi ~ 480dpixxhdpi
    480dpi ~ 640dpixxxhdpi

    从表中可以看出,403dpi是处于320dpi到480dpi之间的,因此属于xxhdpi的范围。

    图片放好了之后,下面我在布局文件中引用这张图片,如下所示:

    <?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"
       >
    
        <ImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/android_logo"
            />
    
    </LinearLayout>

    在ImageView控件中指定加载android_logo这张图,并把ImageView控件的宽高都设置成wrap_content,这样图片有多大,我们的控件就会有多大。

    现在运行一下程序,效果如下所示:

    由于我的手机分辨率是1080*1920像素的,而这张图片的分辨率是270*480像素的,刚好是手机分辨率的四分之一,因此从上图中也可以看出,android_logo图片的宽和高大概都占据了屏幕宽高的四分之一左右,大小基本是比较精准的。

    到目前为止一切都挺顺利的,不是吗?下面我们尝试做点改变,将android_logo.png这张图移动到drawable-xhdpi文件夹下,注意不是复制一份到drawable-xhdpi文件夹下,而是将图片移动到drawable-xhdpi文件夹下,然后重新运行一下程序,效果如下图所示:

    嗯?怎么感觉图片好像变大了一点,是错觉吗?

    那么我们再将这张图移动到drawable-mdpi文件夹下试试,重新运行程序,效果如下图所示:

    这次肯定不是错觉了,这实在是太明显了,图片被放大了!

    那么为什么好端端的一张图片会被自动放大呢?而且这放大的比例是不是有点太过份了。其实不然,Android所做的这些缩放操作都是有它严格的规定和算法的。可能有不少做了很多年Android的朋友都没去留意过这些缩放的规则,因为这些细节太微小了,那么本篇的微技巧探索里面,我们就来把这些细节理理清楚。

    首先解释一下图片为什么会被放大,当我们使用资源id来去引用一张图片时,Android会使用一些规则来去帮我们匹配最适合的图片。什么叫最适合的图片?比如我的手机屏幕密度是xxhdpi,那么drawable-xxhdpi文件夹下的图片就是最适合的图片。因此,当我引用android_logo这张图时,如果drawable-xxhdpi文件夹下有这张图就会优先被使用,在这种情况下,图片是不会被缩放的。但是,如果drawable-xxhdpi文件夹下没有这张图时, 系统就会自动去其它文件夹下找这张图了,优先会去更高密度的文件夹下找这张图片,我们当前的场景就是drawable-xxxhdpi文件夹,然后发现这里也没有android_logo这张图,接下来会尝试再找更高密度的文件夹,发现没有更高密度的了,这个时候会去drawable-nodpi文件夹找这张图,发现也没有,那么就会去更低密度的文件夹下面找,依次是drawable-xhdpi -> drawable-hdpi -> drawable-mdpi -> drawable-ldpi。
    总体匹配规则就是这样,那么比如说现在终于在drawable-mdpi文件夹下面找到android_logo这张图了,但是系统会认为你这张图是专门为低密度的设备所设计的,如果直接将这张图在当前的高密度设备上使用就有可能会出现像素过低的情况,于是系统自动帮我们做了这样一个放大操作。

    那么同样的道理,如果系统是在drawable-xxxhdpi文件夹下面找到这张图的话,它会认为这张图是为更高密度的设备所设计的,如果直接将这张图在当前设备上使用就有可能会出现像素过高的情况,于是会自动帮我们做一个缩小的操作。所以,我们可以尝试将android_logo这张图移动到drawable-xxxhdpi文件夹下面将会得到这样的结果:

    可以看到,现在图片的宽和高都达到不手机屏幕的四分之一,说明图片确实是被缩小了。

    另外,刚才在介绍规则的时候提到了一个drawable-nodpi文件夹,这个文件夹是一个密度无关的文件夹,放在这里的图片系统就不会对它进行自动缩放,原图片是多大就会实际展示多大。但是要注意一个加载的顺序,drawable-nodpi文件夹是在匹配密度文件夹和更高密度文件夹都找不到的情况下才会去这里查找图片的,因此放在drawable-nodpi文件夹里的图片通常情况下不建议再放到别的文件夹里面。

    图片被放大的原因现在我们已经搞清楚了,那么接下来还有一个问题,就是放大的倍数是怎么确定的呢?很遗憾,我没有找到相关的文档记载,但是我自己总结出了一个规律,这里跟大家分享一下。

    还是看一下刚才的 dpi范围-密度 表格:

    dpi范围密度
    0dpi ~ 120dpildpi
    120dpi ~ 160dpimdpi
    160dpi ~ 240dpihdpi
    240dpi ~ 320dpixhdpi
    320dpi ~ 480dpixxhdpi
    480dpi ~ 640dpixxxhdpi

    可以看到,每一种密度的dpi范围都有一个最大值,这个最大值之间的比例就是图片会被系统自动放大的比例。
    口说无凭,下面我们来通过实例验证一下,修改布局文件中的代码,如下所示:

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
       >
    
        <ImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/android_logo"
            />
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="获取图片宽高"
            android:onClick="buttonClick"
            />
    
    </LinearLayout>

    可以看到,我们添加了一个按钮,并给按钮注册了一个点击事件。然后在MainActivity中处理这个点击事件:

    public class MainActivity extends AppCompatActivity {
    
        ImageView imageView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            imageView = (ImageView) findViewById(R.id.image);
        }
    
    
        public void buttonClick(View view) {
            Toast.makeText(this, "图片宽度:" + imageView.getWidth(), Toast.LENGTH_SHORT).show();
            Toast.makeText(this, "图片高度:" + imageView.getHeight(), Toast.LENGTH_SHORT).show();
        }
    }

    这里在点击事件中分别获取图片的宽和高并使用Toast提示出来。代码修改这么多就可以了,然后将图片移动到drawable-mdpi文件夹下。

    下面我们来开始分析,mdpi密度的最高dpi值是160,而xxhdpi密度的最高dpi值是480,因此是一个3倍的关系,那么我们就可以猜测,放到drawable-mdpi文件夹下的图片在xxhdpi密度的设备上显示会被放大3倍。对应到android_logo这张图,原始像素是270*480,放大3倍之后就应该是810*1440像素。下面运行程序,效果如下图所示:

    验证通过。我们再来试验一次,将图片移动到drawable-xxxhdpi目录下。xxxhdpi密度的最高dpi值是640,480是它的0.75倍,那么我们就可以猜测,放到drawable-xxxdpi文件夹下的图片在xxhdpi密度的设备上显示会被缩小至0.75倍。270*480的0.75倍应该是202.5*360,由于像素不支持小数点,那么四舍五入就应该是203*360像素。重新运行程序,效果如下图所示:

    再次验证通过。如果你有兴趣的话可以使用其它几种dpi的drawable文件夹来试一试,应该都是适配这套缩放规则的。这样我们就把图片为什么会被缩放,以及具体的缩放倍数都搞明白了,drawable相关的细节你已经探究的非常细微了。

    不过本篇文章到这里还没结束,下面我准备讲一讲我们在实际开发当中会遇到的场景。根据Android的开发建议,我们在准备图片资源时尽量应该给每种密度的设备都准备一套,这样程序的适配性就可以达到最好。但实际情况是,公司的UI们通常就只会给一套图片资源,想让他们针对每种密度的设备都设计一套图片资源,并且还是按照我们上面讲的缩放比例规则来设计,就有点想得太开心了。没错,这个就是现实情况,那么在这种情况下,我们应该将仅有的这一套图片资源放在哪个密度的文件夹下呢?

    可以这样来分析,根据我们刚才所学的内容,如果将一张图片放在低密度文件夹下,那么在高密度设备上显示图片时就会被自动放大,而如果将一张图片放在高密度文件夹下,那么在低密度设备上显示图片时就会被自动缩小。那我们可以通过成本的方式来评估一下,一张原图片被缩小了之后显示其实并没有什么副作用,但是一张原图片被放大了之后显示就意味着要占用更多的内存了。因为图片被放大了,像素点也就变多了,而每个像素点都是要占用内存的。

    我们仍然可以通过例子来直观地体会一下,首先将android_logo.png图片移动到drawable-xxhdpi目录下,运行程序后我们通过Android Monitor来观察程序内存使用情况:

    可以看到,程序所占用的内存大概稳定在19.45M左右。然后将android_logo.png图片移动到drawable-mdpi目录下,重新运行程序,结果如下图所示:

    现在涨到23.40M了,占用内存明显增加了。如果你将图片移动到drawable-ldpi目录下,你会发现占用内存会更高。

    通过这个例子同时也验证了一个问题,我相信有不少比较有经验的Android程序员可能都遇到过这个情况,就是当你的项目变得越来越大,有的时候加载一张drawable-hdpi下的图片,程序就直接OOM崩掉了,但如果将这张图放到drawable-xhdpi或drawable-xxhdpi下就不会崩掉,其实就是这个道理。

    那么经过上面一系列的分析,答案自然也就出来了,图片资源应该尽量放在高密度文件夹下,这样可以节省图片的内存开支,而UI在设计图片的时候也应该尽量面向高密度屏幕的设备来进行设计。就目前来讲,最佳放置图片资源的文件夹就是drawable-xxhdpi。那么有的朋友可能会问了,不是还有更高密度的drawable-xxxhdpi吗?干吗不放在这里?这是因为,市面上480dpi到640dpi的设备实在是太少了,如果针对这种级别的屏幕密度来设计图片,图片在不缩放的情况下本身就已经很大了,基本也起不到节省内存开支的作用了。


    好的,关于drawable微技巧方面的探索我们就讲到这里,本篇文章中也是集合了不少我平时的工作经验总结,以及通过做试验所得出的一些结论,相信还是可以给大家带来不少帮助的。后面我会抓紧时间继续准备新系列的内容,敬请期待。

    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

            

    展开全文
  • Drawable 详解

    2018-05-23 18:33:58
    public abstract class Drawable extends Object java.lang.Object ↳android.graphics.drawable.DrawableKnown direct subclasses 直接子类AdaptiveIconDrawable, AnimatedImageDrawable, ...

    public abstract class Drawable 
    extends Object 

    java.lang.Object
       ↳android.graphics.drawable.Drawable

       

     <com.myapp.MyCustomDrawable xmlns:android="http://schemas.android.com/apk/res/android"
         android:color="#ffff0000" />


        用drawable作为XML元素名并且从类的属性指定完整路径名。这个方法可以用在public 高层级类和public static 内部类。

     <drawable xmlns:android="http://schemas.android.com/apk/res/android"
     class="com.myapp.MyTopLevelClass$InnerCustomDrawable"
     android:color="#ffff0000" />


    总结

    Nested classes(嵌套的类

    接口Drawable.Callback

    如果想要创建一个动画继承 Drawable 的drawable,就实现这个接口

    Drawable.ConstantState

    这个抽象类被Drawables用来存储Drawables之间的共享状态和数据


    Public constructors (公共构造函数

    Drawable()


    Public methods

    voidapplyTheme(Resources.Theme t)

    Applies the specified theme to this Drawable and its children.

    应用指定的theme(主题)在Drawable和它的孩子

    booleancanApplyTheme()
    voidclearColorFilter()

    Removes the color filter for this drawable.

    从drawable中移除颜色过滤器

    final RectcopyBounds()

    Return a copy of the drawable's bounds in a new Rect.

    在新的矩形(Rect)返回一个drawable的约束副本

    final voidcopyBounds(Rect bounds)

    Return a copy of the drawable's bounds in the specified Rect (allocated by the caller).

    在指定的矩形(Rect)返回一个drawable的约束副本

    static DrawablecreateFromPath(String pathName)

    Create a drawable from file path name.

    从路径名(pathName)创建一个drawable

    static DrawablecreateFromResourceStream(Resources res, TypedValue value, InputStream is,String srcName, BitmapFactory.Options opts)

    This method was deprecated in API level P. Prefer the version without an Options object.

    这个方法在API P 中已经废弃了,请使用下面的方法

    static DrawablecreateFromResourceStream(Resources res, TypedValue value, InputStream is,String srcName)

    Create a drawable from an inputstream, using the given resources and value to determine density information.

    从输入流创建一个drawable,使用给定的资源和值去决定密度信息。

    static DrawablecreateFromStream(InputStream is, String srcName)

    Create a drawable from an inputstream

    从输入流创建一个drawable

    static DrawablecreateFromXml(Resources r, XmlPullParser parser)

    Create a drawable from an XML document.

    从一个XML文件中创建一个drawable

    static DrawablecreateFromXml(Resources r, XmlPullParser parser, Resources.Theme theme)

    Create a drawable from an XML document using an optional Resources.Theme.

    从一个XML文档用Resources.Theme创建一个drawable

    static DrawablecreateFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,Resources.Theme theme)

    Create a drawable from inside an XML document using an optional Resources.Theme.

    从一个XML文档使用一个可选的Resources.Theme创建一个drawable

    static DrawablecreateFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)

    Create from inside an XML document.

    从一个XML文档内创建

    abstract voiddraw(Canvas canvas)

    Draw in its bounds (set via setBounds) respecting optional effects such as alpha (set via setAlpha) and color filter (set via setColorFilter).

    在它的约束内绘制,并遵从可选的效果,例如透明度alpha 和颜色过滤器

    intgetAlpha()

    Gets the current alpha value for the drawable.

    获取当前透明度的值

    final RectgetBounds()

    Return the drawable's bounds Rect.

    返回drawable的约束矩形

    Drawable.CallbackgetCallback()

    Return the current Drawable.Callback implementation attached to this Drawable.

    返回当前这个Drawable实现的Drawable.Callback

    intgetChangingConfigurations()

    Return a mask of the configuration parameters for which this drawable may change, requiring that it be re-created.

    返回drawable可能会改变的配置参数,这需要drawable重建

    ColorFiltergetColorFilter()

    Returns the current color filter, or null if none set.

    返回当前的颜色过滤器,返回null如果没设置的话

    Drawable.ConstantStategetConstantState()

    Return a Drawable.ConstantState instance that holds the shared state of this Drawable.

    返回存有这个Drawable的共享状态的 Drawable.ConstantState 实例。

    DrawablegetCurrent()
    RectgetDirtyBounds()

    Return the drawable's dirty bounds Rect.

    返回drawable的脏约束矩形

    voidgetHotspotBounds(Rect outRect)

    Populates outRect with the hotspot bounds.

    使用热点约束填充外矩形

    intgetIntrinsicHeight()

    Returns the drawable's intrinsic height.

    返回drawable的本身高度

    intgetIntrinsicWidth()

    Returns the drawable's intrinsic width.

    返回drawable的本身宽度

    intgetLayoutDirection()

    Returns the resolved layout direction for this Drawable.

    返回Drawable的布局方向

    final intgetLevel()

    Retrieve the current level.

    查询当前的level

    intgetMinimumHeight()

    Returns the minimum height suggested by this Drawable.

    返回Drawable的建议最小高度

    intgetMinimumWidth()

    Returns the minimum width suggested by this Drawable.

    返回Drawable的建议最小宽度

    abstract intgetOpacity()

    Return the opacity/transparency of this Drawable.

    返回Drawable的不透明度或者透明度

    voidgetOutline(Outline outline)

    Called to get the drawable to populate the Outline that defines its drawing area.

    调用此方法去获取drawable去填充定义在绘画区域的轮廓

    booleangetPadding(Rect padding)

    Return in padding the insets suggested by this Drawable for placing content inside the drawable's bounds.

    返回内容与约束边框的内边距

    int[]getState()

    Describes the current state, as a union of primitve states, such as state_focused,state_selected, etc.

    描述当前状态,作为一个联合的基本状态,例如state_focused,state_selected, 等等

    RegiongetTransparentRegion()

    Returns a Region representing the part of the Drawable that is completely transparent.

    返回完全透明的范围

    voidinflate(Resources r, XmlPullParser parser, AttributeSet attrs, Resources.Theme theme)

    Inflate this Drawable from an XML resource optionally styled by a theme.

    从某个主题样式的XML资源填充这个drawable

    voidinflate(Resources r, XmlPullParser parser, AttributeSet attrs)

    Inflate this Drawable from an XML resource.

    从XML 资源填充Drawable

    voidinvalidateSelf()

    Use the current Drawable.Callback implementation to have this Drawable redrawn.

    使用当前 Drawable.Callback实现让这个Drawable重新绘制。

    booleanisAutoMirrored()

    Tells if this Drawable will be automatically mirrored when its layout direction is RTL right-to-left.

    是否自动生成镜像

    booleanisFilterBitmap()
    booleanisStateful()

    Indicates whether this drawable will change its appearance based on state.

    标识是否根据状态来改变它的显示

    final booleanisVisible()
    voidjumpToCurrentState()

    If this Drawable does transition animations between states, ask that it immediately jump to the current state and skip any active animations.

    如果这个Drawable从状态之间执行转换动画,询问他立即调到当前状态并且跳过所有的动画。

    Drawablemutate()

    Make this drawable mutable.

    让这个Drawable易变的。

    booleanonLayoutDirectionChanged(int layoutDirection)

    Called when the drawable's resolved layout direction changes.

    在drawable的布局方向改变时调用。

    static intresolveOpacity(int op1, int op2)

    Return the appropriate opacity value for two source opacities.

    在两种不透明源中返回恰当的不透明度值

    voidscheduleSelf(Runnable what, long when)

    Use the current Drawable.Callback implementation to have this Drawable scheduled.

    abstract voidsetAlpha(int alpha)

    Specify an alpha value for the drawable.

    定义drawable的透明度

    voidsetAutoMirrored(boolean mirrored)

    Set whether this Drawable is automatically mirrored when its layout direction is RTL (right-to left).

    设置自动镜像

    voidsetBounds(int left, int top, int right, int bottom)

    Specify a bounding rectangle for the Drawable.

    定义Drawable的约束矩形

    voidsetBounds(Rect bounds)

    Specify a bounding rectangle for the Drawable.

    定义Drawable的约束矩形

    final voidsetCallback(Drawable.Callback cb)

    Bind a Drawable.Callback object to this Drawable.

    给Drawable绑定Drawable.Callback对象 

    voidsetChangingConfigurations(int configs)

    Set a mask of the configuration parameters for which this drawable may change, requiring that it be re-created.


    voidsetColorFilter(int color, PorterDuff.Mode mode)

    Specify a color and Porter-Duff mode to be the color filter for this drawable.

    abstract voidsetColorFilter(ColorFilter colorFilter)

    Specify an optional color filter for the drawable.

    voidsetDither(boolean dither)

    This method was deprecated in API level 23. This property is ignored.

    voidsetFilterBitmap(boolean filter)

    Set to true to have the drawable filter its bitmaps with bilinear sampling when they are scaled or rotated.

    voidsetHotspot(float x, float y)

    Specifies the hotspot's location within the drawable.

    voidsetHotspotBounds(int left, int top, int right, int bottom)

    Sets the bounds to which the hotspot is constrained, if they should be different from the drawable bounds.

    final booleansetLayoutDirection(int layoutDirection)

    Set the layout direction for this drawable.

    final booleansetLevel(int level)

    Specify the level for the drawable.

    booleansetState(int[] stateSet)

    Specify a set of states for the drawable.

    voidsetTint(int tintColor)

    Specifies tint color for this drawable.

    voidsetTintList(ColorStateList tint)

    Specifies tint color for this drawable as a color state list.

    voidsetTintMode(PorterDuff.Mode tintMode)

    Specifies a tint blending mode for this drawable.

    booleansetVisible(boolean visible, boolean restart)

    Set whether this Drawable is visible.

    voidunscheduleSelf(Runnable what)

    Use the current Drawable.Callback implementation to have this Drawable unscheduled.


    展开全文
  • Drawable动画实现

    千次阅读 2018-05-07 20:47:23
    有种思路就是使用Drawable来实现动画效果,在把它作为View的前景或背景图设置上去,这样的动画效果就被封装到一个简单的Drawable,可以在不同的View之间重复使用。 属性动画实现 既然使用Dr...

    前言

    Android中有很多有趣的简单几何元素自定义动画效果,比如圆形转圈这类加载动画都是很常见的,如果使用自定义的View来实现这种动画效果实际比较复杂而且很难在其他的View中复用。有种思路就是使用Drawable来实现动画效果,在把它作为View的前景或背景图设置上去,这样的动画效果就被封装到一个简单的Drawable,可以在不同的View之间重复使用。

    属性动画实现

    既然使用Drawable来实现动画下过,自然要使用自定义的Drawable对象,同时还要使用一个Animatable接口提供的方法来做动画控制实现。Drawable中需要实现的接口比较多,需要每个方法的功能都简单了解,Animatable比较简单这里不再赘述。

    接口名作用
    draw(Canvas canvas)在附加到的View里绘制Drawable内容
    onBoundsChange(Rect bounds)当View的大小位置改变时回调
    setAlpha(int alpha)设置当前Drawable的透明度
    setColorFilter(ColorFilter colorFilter)设置颜色滤镜
    getOpacity()返回Drawable的具体类型,可以是透明、半透明、全透明等

    这里写图片描述
    现在来实现一个简单的圆圈逐渐放大的动画,Drawable在创建之后还要附加到View中才能真正确定大小,所以需要在它的onBoundsChanged方法里定义各种初始变量和动画。

    @Override
    protected void onBoundsChange(Rect bounds) {
        super.onBoundsChange(bounds);
        width = bounds.width();
        height = bounds.height();
        Log.d(TAG, "width = " + width + ", height = " + height);
        if (valueAnimator != null && valueAnimator.isRunning()) {
            valueAnimator.cancel();
        }
    
        valueAnimator = ValueAnimator.ofInt(CommonUtils.dp2px(10), Math.min(width, height) / 2);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 对圆形的半径值做属性动画
                mRadius = (int) animation.getAnimatedValue();
                Log.d(TAG, "width = " + width + ", height = " + height);
                Log.d(TAG, "mRadius = " + mRadius);
                invalidateSelf();
            }
        });
    
        // 设置值动画的各种参数
        valueAnimator.setStartDelay(mDelay);
        valueAnimator.setDuration(1000);
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
    
        if (mIsStarted) {
            valueAnimator.start();
        }
    }

    在onBoundsChanged方法里获取了当前Drawable要展示的宽高,同时定义了圆形的半径从小到大的值动画效果,每次更新值的时候都会调用Drawable.invalidateSelf()方法来更新当前展示,也就是调用onDraw里的绘制逻辑。

    public CircleDrawable() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
        paint.setStrokeWidth(CommonUtils.dp2px(2));
        paint.setStyle(Paint.Style.STROKE);
        mRadius = CommonUtils.dp2px(10);
    }
    
    @Override
    public void draw(@NonNull Canvas canvas) {
        canvas.drawCircle(width / 2, height / 2, mRadius, paint);
    }
    

    上面的draw代码里只是做了绘制一个圆的操作,随着半径不断的变化Drawable上展示的圆也不断的改变大小,这样就实现了简单的圆形大小不断变化的动画。Drawable可以单独使用也可以包装其他的Drawable来实现复杂的效果,比如ClipDrawable、StateListDrawable内部都可以使用BitmapDrawable,并且通过特殊的接口来实现内部BitmapDrawable的展示。这里定义一个包含多个圆形的Drawable,它会包含多个不同大小的圆形不断改变的动画效果。

    public class MultiCircleDrawable extends Drawable implements Animatable, Drawable.Callback {
        private final int START_DELAY = 200;
    
        private CircleDrawable[] circleDrawables = new CircleDrawable[] {
            new CircleDrawable(),
            new CircleDrawable(),
            new CircleDrawable()
        };
    
        public MultiCircleDrawable() {
            for (int i = 0; i < circleDrawables.length; i++) {
                circleDrawables[i].setStartDelay(i * START_DELAY);
                circleDrawables[i].setCallback(this);
            }
        }
        ....
    
        @Override
        public void draw(@NonNull Canvas canvas) {
            for (CircleDrawable drawable : circleDrawables) {
                drawable.draw(canvas);
            }
        }
    
        @Override
        protected void onBoundsChange(Rect bounds) {
            super.onBoundsChange(bounds);
            for (CircleDrawable drawable : circleDrawables) {
                drawable.onBoundsChange(bounds);
            }
        }
    
        @Override
        public void setAlpha(int alpha) {
            for (CircleDrawable drawable : circleDrawables) {
                drawable.setAlpha(alpha);
            }
        }
    
        @Override
        public void setColorFilter(@Nullable ColorFilter colorFilter) {
            for (CircleDrawable drawable : circleDrawables) {
                drawable.setColorFilter(colorFilter);
            }
        }
    
        @Override
        public int getOpacity() {
            return PixelFormat.OPAQUE;
        }
    
        @Override
        public void invalidateDrawable(@NonNull Drawable who) {
            invalidateSelf();
        }
    
        @Override
        public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
    
        }
    
        @Override
        public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
    
        }
    }

    我们知道Drawable只能依附在View对象上才能正常展示,MultiCircleDrawable会被依附到View里,它内部的CircleDrawable就不会被依附到View上,但是内部的CircleDrawable又需要通知View做更新操作,这时候就需要将MultiCircleDrawable来做内部CircleDrawable的回调对象,当它的invalidateDrawable被回调时调用invalidateSelf通知View对象做刷新操作。

    Drawable.CallBack实现

    public interface Callback {
    
        // 使当前的Drawable对象非法,最终导致Drawable依附的View重新绘制
        void invalidateDrawable(@NonNull Drawable who);
    
        // 调用Drawable相关的Runnable执行
        void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when);
    
        // 取消Drawable相关的Runnable执行
        void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what);
    }

    上面的Drawable.Callback接口是View实现的接口之一,View内部会处理回调发生时的逻辑,查看源码如下所示。

    public class View implements Drawable.Callback, KeyEvent.Callback,
            AccessibilityEventSource {
    
        // View执行    
        @Override
        public void invalidateDrawable(@NonNull Drawable drawable) {
            if (verifyDrawable(drawable)) {
                final Rect dirty = drawable.getDirtyBounds();
                final int scrollX = mScrollX;
                final int scrollY = mScrollY;
    
                invalidate(dirty.left + scrollX, dirty.top + scrollY,
                        dirty.right + scrollX, dirty.bottom + scrollY);
                rebuildOutline();
            }
        }
    
        // 向主线程中post一个Runnable
        @Override
        public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
            if (verifyDrawable(who) && what != null) {
                final long delay = when - SystemClock.uptimeMillis();
                if (mAttachInfo != null) {
                    mAttachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed(
                            Choreographer.CALLBACK_ANIMATION, what, who,
                            Choreographer.subtractFrameDelay(delay));
                } else {
                    getRunQueue().postDelayed(what, delay);
                }
            }
        }    
    
        // 从主线程中清除要执行Runnable
        @Override
        public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
            if (verifyDrawable(who) && what != null) {
                if (mAttachInfo != null) {
                    mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
                            Choreographer.CALLBACK_ANIMATION, what, who);
                }
                getRunQueue().removeCallbacks(what);
            }
        }
    }

    在Drawable源码中会直接调用Callback来实现刷新,调度任务和取消调度任务的操作,从实现的源代码中可以看出调用invalidateSelf就会回调Callback的invalidateDrawable,前面的MultiDrawable就实现了这个方法invalidateDrawable,而附加到View里的Drawable它的callback就是View回调的就是上面的View.invalidateDrawable,实现View的重新绘制,在View.onDraw里会调用Drawable.onDraw最终实现Drawable内容的刷新操作。

    // Drawable内部实现
    public void invalidateSelf() {
        final Callback callback = getCallback();
        if (callback != null) {
            callback.invalidateDrawable(this);
        }
    }
    
    public void scheduleSelf(@NonNull Runnable what, long when) {
        final Callback callback = getCallback();
        if (callback != null) {
            callback.scheduleDrawable(this, what, when);
        }
    }
    
    public void unscheduleSelf(@NonNull Runnable what) {
        final Callback callback = getCallback();
        if (callback != null) {
            callback.unscheduleDrawable(this, what);
        }
    }

    简单涟漪效果

    涟漪效果其实就是用户点击View之后会出现圆形逐渐变大最后再消失的动画效果,有了前面的圆大小改变动画,这里再通过scheduleSelf和unscheduleSelf接口来实现动画效果。首先定一个一个Button它内部包含一个有涟漪效果的Drawable。

    public class RippleButton extends AppCompatButton {
        private RippleDrawable mDrawable;
        public RippleButton(Context context) {
            this(context, null);
        }
    
        public RippleButton(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public RippleButton(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            // 自定义的涟漪效果Drawable
            mDrawable = new RippleDrawable();
            mDrawable.setCallback(this);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            // 在View大小改变的时候设置Drawable大小
            mDrawable.setBounds(0, 0, getWidth(), getHeight());
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            // 绘制Drawable内容
            mDrawable.draw(canvas);
            super.onDraw(canvas);
        }
    
        @Override
        protected boolean verifyDrawable(@NonNull Drawable who) {
            return mDrawable == who || super.verifyDrawable(who);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // 拦截用户触摸事件
            mDrawable.onTouchEvent(event);
            super.onTouchEvent(event);
            return true;
        }
    }

    自定义Button实现非常简单,需要注意onSizeChange的时候需要通知Drawable当前View的尺寸发生变化,Drawable内部实现初始化操作,后面的onTouchEvent一定要返回true,这样ACTION_DOWN之后的ACTION_MOVE、ACTION_UP才会继续派发下来。

    public class RippleDrawable extends Drawable {
        private Paint mPaint;
        private int mRadius;
        private int mWidth;
        private int mHeight;
        private int mMaxRadius;
        private int mPivotX;
        private int mPivotY;
        private int mMotionX;
        private int mMotionY;
        private float mProgress = 0.0f;
        private int mAlpha = 255;
    
        public RippleDrawable() {
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setDither(true);
            mPaint.setColor(Color.CYAN);
            mPaint.setStyle(Paint.Style.FILL);
        }
    
        // 绘制圆形
        @Override
        public void draw(@NonNull Canvas canvas) {
            canvas.drawCircle(mPivotX, mPivotY, mRadius, mPaint);
        }
    
        // 记录当前Drawable的尺寸
        @Override
        protected void onBoundsChange(Rect bounds) {
            super.onBoundsChange(bounds);
            mWidth = bounds.width();
            mHeight = bounds.height();
            mMaxRadius = Math.min(mWidth, mHeight);
        }
    
        @Override
        public void setAlpha(int alpha) {
            mPaint.setAlpha(alpha);
        }
    
        @Override
        public void setColorFilter(@Nullable ColorFilter colorFilter) {
            mPaint.setColorFilter(colorFilter);
        }
    
        @Override
        public int getOpacity() {
            return PixelFormat.OPAQUE;
        }
    
        // 根据ACTION_DOWN展示圆心位置
        public void onTouchEvent(MotionEvent event) {
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    onTouchDown(event);
                    break;
                case MotionEvent.ACTION_MOVE:
                    onTouchMove(event);
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    onTouchUp(event);
                    break;
            }
        }
    
        private void onTouchUp(MotionEvent event) {
    
        }
    
        private void onTouchMove(MotionEvent event) {
    
        }
    
        private void onTouchDown(MotionEvent event) {
            mMotionX = (int) event.getX();
            mMotionY = (int) event.getY();
            mRadius = 0;
            mProgress = 0.0f;
            mAlpha = 255;
            setAlpha(mAlpha);
            unscheduleSelf(mEnterRunnable);
    
            // 每10ms开始一次重绘
            scheduleSelf(mEnterRunnable, SystemClock.uptimeMillis() + 10);
        }
    
        private Interpolator interpolator = new AccelerateInterpolator();
    
        private Runnable mEnterRunnable = new Runnable() {
            @Override
            public void run() {
                // 如果重绘完成,开始执行退出动画
                if (mProgress >= 1.0f) {
                    unscheduleSelf(mExitRunnable);
                    scheduleSelf(mExitRunnable, SystemClock.uptimeMillis() + 10);
                    return;
                }
    
                // 不断增大圆形半径
                mProgress += 0.05f;
                mRadius = (int) (mMaxRadius * interpolator.getInterpolation(mProgress));
                mPivotX = mMotionX;
                mPivotY = mMotionY;
                invalidateSelf();
                scheduleSelf(mEnterRunnable, SystemClock.uptimeMillis() + 10);
            }
        };
    
        // 退出动画
        private Runnable mExitRunnable = new Runnable() {
            @Override
            public void run() {
                int alpha = mAlpha - 10;
                if (alpha > 0) {
                    // 从不透明变成完全透明实现Drawable内容不可见
                    mAlpha = alpha;
                    setAlpha(alpha);
                    invalidateSelf();
                    scheduleSelf(this, SystemClock.uptimeMillis() + 10);
                } else {
                    mAlpha = 0;
                    setAlpha(mAlpha);
                    invalidateSelf();
                }
            }
        };
    }

    RippleDrawable内部onBoundsChange会在View的onSizeChange发生时被回调,会记录下当前Drawable展示的宽高和圆形最大的半径值,在draw方法中会根据轴心和半径绘制圆形。内部的mEnterRunnable表示用户按下时执行的圆形不断变大动画,mExitRunnable表示变大动画执行完成后圆形从不透明变成全透明最终消失的动画,两个动画都是通过Runnable的递归实现。
    这里写图片描述

    展开全文
  • Android中的Drawable基础与自定义Drawable

    万次阅读 多人点赞 2018-01-22 01:47:48
    介绍Android中Drawable的相关知识点,并且介绍如何自定义Drawable。 2. Drawable能实现缩放、渐变、逐帧动画、静态矢量图、矢量图动画等功能 3. Drawable提供一种比自定义View更轻量级的...

    转载请注明链接:http://blog.csdn.net/feather_wch/article/details/79124608

    本文要点:
    1. 介绍Android中Drawable的相关知识点,并且介绍如何自定义Drawable。
    2. Drawable能实现缩放、渐变、逐帧动画、静态矢量图、矢量图动画等功能
    3. Drawable提供一种比自定义View更轻量级的解决办法,用于实现特定的效果
    4. 布局使用xml,代码采用kotlin/java实现

    Android的Drawable(36题)

    版本:2018/2/11


    个人总结的知识点外,部分知识点选自《Android开发艺术探索》-第六章 Drawable

    1、Drawable是什么?

    1. 一种可以在Canvas上进行绘制的抽象的概念
    2. 颜色、图片等都可以是一个Drawable
    3. Drawable可以通过XML定义,或者通过代码创建
    4. Android中Drawable是一个抽象类,每个具体的Drawable都是其子类

    2、Drawable的优点

    1. 使用简单,比自定义View成本低
    2. 非图片类的Drawable所占空间小,能减小apk大小

    3、Drawable的内部宽/高

    1. 一般getIntrinsicWidth/Height能获得内部宽/高
    2. 图片Drawable其内部宽高就是图片的宽高
    3. 颜色Drawable没有内部宽高的概念
    4. 内部宽高不等同于它的大小,一般Drawable没有大小概念(作为View背景时,会被拉伸至View的大小)

    Drawable的分类

    4、BitmapDrawable的作用和使用

    表示一种图片,可以直接引用原始图片或者通过XML进行描述

    <?xml version="1.0" encoding="utf-8"?>
    <bitmap
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:src="@color/colorPrimary"
        android:antialias="true"
        android:dither="true"
        android:filter="true"
        android:gravity="center"
        android:mipMap="false"
        android:tileMode="disabled"
        />

    5、Bitmap的属性

    属性作用备注
    android:src图片资源ID
    android:antialias图片抗锯齿-图片平滑,清晰度降低应该开启
    android:dither开启抖动效果-用于高质量图片在低质量屏幕上保存较好的显示效果(不会失真)应该开启
    android:filter开启过滤-在图片尺寸拉伸和压缩时保持较好的显示效果应该开启
    android:gravity图片小于容器尺寸时,对图片进行定位-选项之间用‘’来组合使用
    android:mipMap纹理映射-图像处理技术默认false
    android:tileMode平铺模式-repeat单纯重复、mirror镜面反射、clamp图片四周像素扩散默认disable关闭

    6、gravity属性详情

    可选项含义
    top/bottom/left/right将图片放在容器上/下/左/右,不改变图片大小
    center_vertical/horizontal垂直居中/水平居中,不改变图片大小
    center水平和垂直方向同时居中,不改变图片大小
    fill_vertical/horizontal垂直/水平方向填充容器
    fill水平和垂直方向同时填充容器
    clip_vertical/horizontal垂直/水平方向的裁剪-较少使用

    7、NinePatchDrawable(.9图片)的作用

    1. 自动根据宽高进行缩放且不会失真
    2. 实际使用,可以直接引用图片或者通过XML描述
    <?xml version="1.0" encoding="utf-8"?>
    <nine-patch
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:src="@color/colorPrimary"
        android:antialias="true"
        android:dither="true"
        android:filter="true"
        android:gravity="center"
        android:mipMap="false"
        android:tileMode="disabled"
        />

    8、ShapeDrawable的作用

    1. 通过颜色构造的图形
    2. 可以是纯色的图形
    3. 也可以是有渐变效果的图形
    4. shape标签创建的Drawable实体是GradientDrawable

    9、ShapeDrawable的使用

    <?xml version="1.0" encoding="utf-8"?>
    <shape
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
    
        <corners
            android:radius="10dp"
            android:topLeftRadius="10dp"
            android:topRightRadius="10dp"
            android:bottomLeftRadius="10dp"
            android:bottomRightRadius="10dp"/>
        <gradient
            android:angle="45"
            android:centerX="30"
            android:centerY="30"
            android:centerColor="@color/colorAccent"
            android:endColor="@color/colorPrimary"
            android:startColor="@color/colorPrimaryDark"
            android:gradientRadius="20"
            android:type="linear"
            android:useLevel="true" />
        <padding
            android:left="10dp"
            android:top="10dp"
            android:right="10dp"
            android:bottom="10dp" />
        <size
            android:width="200dp"
            android:height="200dp" />
        <solid
            android:color="@color/colorPrimary"/>
        <stroke
            android:width="10dp"
            android:color="@color/colorAccent"
            android:dashWidth="5dp"
            android:dashGap="3dp"/>
    
    </shape>

    10、ShapeDrawable的属性介绍

    属性/标签作用备注
    android:shape图形的形状:rectangle矩形、oval椭圆、line横线、ring圆环corners标签对应于矩形;line和ring通过stroke指定线的宽度和颜色; ring圆环有五个特殊的shape属性
    corners标签四个角的角度
    gradient标签渐变效果-android:angle表示渐变角度,必须为45的倍数android:type指明渐变类型:linear线性,radial径向、sweep扫描
    solid标签纯色填充与gradient标签排斥
    stroke标签描边有描边线和虚线
    size标签表示shape的固有大小,并非最终显示的大小没有时getIntrinsicWidth返回-1;能指明Drawable的固有宽高,但如果作为View背景还是会被拉伸

    11、LayerDrawable的作用

    1. XML标签为layer-list
    2. 层次化的Drawable合集
    3. 可以包含多个item,每个item表示一个Drawable
    4. item中可以通过android:drawable直接引用资源
    5. android:top等表示Drawable相当于View上下左右的偏移量

    12、LayerDrawable的使用(微信文本输入框)

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list
        xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item>
            <shape android:shape="rectangle">
                <solid
                    android:color="#0ac39e"/>
            </shape>
        </item>
    
        <item
            android:bottom="6dp">
            <shape android:shape="rectangle">
                <solid
                    android:color="#FFFFFF"/>
            </shape>
        </item>
    
        <item
            android:bottom="1dp"
            android:left="1dp"
            android:right="1dp">
            <shape android:shape="rectangle">
                <solid
                    android:color="#FFFFFF"/>
            </shape>
        </item>
    
    </layer-list>

    13、StateListDrawable的作用

    1. 对应于selector标签
    2. 用于View根据状态选择不同的Drawable

    14、StateListDrawable的使用和要点

    <?xml version="1.0" encoding="utf-8"?>
    <selector
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:constantSize="false" //StateListDrawable的固有大小是否根据状态而改变,默认false=根据状态而改变
        android:dither="true"        //是否开启抖动-让高质量图片在低质量屏幕上依旧效果好,默认true开启
        android:variablePadding="false" //padding是否根据状态的改变而改变,不建议开启(false)
        >
        <item android:state_pressed="true"  //Button被按下后却没有松开的状态
            android:drawable="@color/colorAccent"/>
        <item android:state_focused="true"  //View获取了焦点
            android:drawable="@color/colorPrimary"/>
        <item android:state_selected="true" //用户选择了View
            android:drawable="@color/colorPrimary"/>
        <item android:state_checked="true" //用户选中了View,一般用于CheckBox这类在选中和没有选中状态之间切换的View
            android:drawable="@drawable/ic_launcher_background"/>
        <item android:state_enabled="true" //View处于可用状态
            android:drawable="@drawable/ic_launcher_foreground"/>
        <item android:drawable="#FFFFFF"/> //默认Drawable: 按顺序向下匹配,需要放在最下方,因为可以匹配任何状态
    </selector>

    15、LevelListDrawable的作用

    1. 对应于level-list标签
    2. 拥有多个item,每个item都有maxLevelminLevel
    3. Level的范围为0~10000
    4. 给定level后,会按从上至下的顺序匹配,直到找到范围合适的Drawable,并返回
    5. item的level一定要降序或者升序
    6. 调用View的getBackground获得Drawable对象,并调用setLevel设置等级level
    7. ImageView的setImageLevel()能快速指定src引用的Drawable的Level
    8. LevelListDrawable是根据level改变,选择不同的Drawable,能用于实现进度条、音量调节等等

    16、LevelListDrawable的使用

    <?xml version="1.0" encoding="utf-8"?>
    <level-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:minLevel="0" android:maxLevel="10" android:drawable="@drawable/d1" />
        <item android:minLevel="11" android:maxLevel="20" android:drawable="@drawable/d2" />
        <item android:minLevel="21" android:maxLevel="30" android:drawable="@drawable/d3" />
        <item android:minLevel="31" android:maxLevel="40" android:drawable="@drawable/d4" />
    </level-list>

    17、TransitionDrawable的作用

    1. 对应于transition标签
    2. 实现两个Drawable之前的淡入淡出效果
    3. 获得背景的TransitionDrawable后,通过startTransitionreverseTransition方法实现效果和逆过程

    18、TransitionDrawable的使用

    <?xml version="1.0" encoding="utf-8"?>
    <transition xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:id="@+id/transition_drawable"
            android:drawable="@drawable/ic_launcher"
            android:top="10dp"    //四周的偏移量
            android:bottom="10dp"
            android:right="10dp"
            android:left="10dp"/>
        <item android:drawable="@drawable/ic_launcher_round" />
    </transition>

    19、InsetDrawable的作用和使用

    1. 对应inset标签
    2. 将其他Drawable内嵌到自身,并在四周留出间距
    3. View需要背景比自己实际区域要小的时候,可以使用insetlayer-list也可以实现该需求
    <inset xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/ic_launcher"
        android:insetTop="10dp"
        android:insetBottom="10dp"
        android:insetLeft="10dp"
        android:insetRight="10dp">
    </inset>

    20、ScaleDrawable的作用

    1. 对应于scale标签
    2. 根据自己的等级level(0~10000)将指定的Drawable缩放到一定比例
    3. android:scaleHeight="70%"用于指定宽高的缩放比例=为原来的30%
    4. ScaleDrawable的level为0,不可见。为10000时,不缩放。
    5. 一般将level设置为1,就会按照属性指定的比例缩放。其他值也会改变缩放效果。
    6. android:scaleGravity属性和gravity属性完全一致

    21、ScaleDrawable的使用

    <scale xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/ic_launcher"
        android:scaleGravity="center"
        android:scaleHeight="70%"
        android:scaleWidth="70%">
    </scale>

    22、ClipDrawable的作用

    1. 对应于clip标签
    2. 根据自己当前的等级level(0~10000)来裁剪另一个Drawable
    3. 裁剪方向由clipOrientationgravity属性共同控制
    4. level为0,Drawable不可见;10000表示不裁剪;为8000,表示裁减了2000;为1,表示裁剪了9999

    23、ClipDrawable的gravity

    可选项含义
    top/bottom将图片放在容器上/下。若为垂直裁剪,从另一头开始裁剪;若为水平裁剪,则从水平方向左/右两头开始裁剪
    left/right将图片放在容器左/右。若为水平裁剪,从另一头开始裁剪;若为垂直裁剪,则从垂直方向上/下两头开始裁剪
    center_vertical/horizontal/center垂直居中/水平居中/两个方向均居中。效果只与clipOrientation有关:水平裁剪,左右两头开始裁剪;垂直裁剪,上下两头开始裁剪
    fill_vertical/horizontal垂直/水平方向填充容器。gravity和orientation方向相同时,不裁剪;方向不同时,按照orientation的方向,从两头开始裁剪
    fill水平和垂直方向同时填充容器,没有裁剪效果
    clip_vertical/horizontal效果类似center_center

    24、AnimationDrawable的作用

    1. 对应于animation-list标签
    2. 用于实现逐帧动画效果
    3. android:oneShot决定是循环播放还是播放一次,false:循环播放
    4. item中设置一帧一帧的Drawable以及持续时间
    5. AnimationDrawable的setOneShot(boolean flag)android:oneShot配置一样
    6. addFrame (Drawable frame, int duration) 动态的添加一个图片进入该动画中
    7. stop()和start()用于停止和开始/继续播放,停止时会停留在当前一帧上

    25、AnimationDrawable的使用

    <?xml version="1.0" encoding="utf-8"?>
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
        android:oneshot="false">
        <item android:drawable="@drawable/shake_anim_01" android:duration="100"/>
        <item android:drawable="@drawable/shake_anim_02" android:duration="100"/>
        <item android:drawable="@drawable/shake_anim_03" android:duration="100"/>
        <item android:drawable="@drawable/shake_anim_04" android:duration="100"/>
        <item android:drawable="@drawable/shake_anim_05" android:duration="100"/>
        <item android:drawable="@drawable/shake_anim_06" android:duration="100"/>
        <item android:drawable="@drawable/shake_anim_07" android:duration="100"/>
        <item android:drawable="@drawable/shake_anim_08" android:duration="100"/>
        <item android:drawable="@drawable/shake_anim_09" android:duration="100"/>
        <item android:drawable="@drawable/shake_anim_10" android:duration="100"/>
    </animation-list>
    val imageview = findViewById<ImageView>(R.id.imaview)
    (imageview.drawable as AnimationDrawable).start() //开始播放

    27、ShapeDrawable的OvalShape、RectShape、ArcShape和PaintDrawable的作用和使用

    1. 用于获得有shape形状drawable(椭圆、长方形、扇形以及更为通用PaintDrawable-具有圆角和边界)
            /**===================================================
             *  一个继承自ShapeDrawable更为通用的Drawable:具有圆角
             *====================================================*/
            PaintDrawable drawable3 = new PaintDrawable(Color.GREEN);
            drawable3.setCornerRadius(30);
            findViewById(R.id.textView3).setBackgroundDrawable(drawable3);
    
            /**============================================
             *   通过Shape构造出相应的ShapeDrawable
             *=============================================*/
            //椭圆形形状 : shape赋予ShapeDrawable
            OvalShape ovalShape = new OvalShape();
            ShapeDrawable drawable1 = new ShapeDrawable(ovalShape);
            drawable1.getPaint().setColor(Color.BLUE);
            drawable1.getPaint().setStyle(Paint.Style.FILL);
            findViewById(R.id.textView1).setBackgroundDrawable(drawable1);
    
            //矩形形状  : shape赋予ShapeDrawable
            RectShape rectShape = new RectShape();
            ShapeDrawable drawable2 = new ShapeDrawable(rectShape);
            drawable2.getPaint().setColor(Color.RED);
            drawable2.getPaint().setStyle(Paint.Style.FILL);
            findViewById(R.id.textView2).setBackgroundDrawable(drawable2);
    
            //扇形、扇面形状 : shape赋予ShapeDrawable
            //顺时针,开始角度30, 扫描的弧度跨度180
            ArcShape arcShape = new ArcShape(30, 180);
            ShapeDrawable drawable4 = new ShapeDrawable(arcShape);
            drawable4.getPaint().setColor(Color.YELLOW);
            drawable4.getPaint().setStyle(Paint.Style.FILL);
            findViewById(R.id.textView4).setBackgroundDrawable(drawable4);

    26、其余Drawable及其功能

    Drwable分类xml标签功能
    ColorDrawablecolor纯色Drawable
    RotateDrawablerotate实现旋转效果
    RippleDrawableripple触摸反馈动画,Andorid 5.0推出
    VectorDrawablevector中使用path【绘制静态图】使用矢量图SVG, 能绘制一张图就能适配不同分辨率, Android 5.0推出
    AnimatedVectorDrawableanimated-vector【动画矢量图】针对VectorDrawable来做动画, Android 5.0
    AnimatedStateListDrawableanimated-selector【动画型StateListDrawable】在View状态改变时,展示动画

    自定义Drawable

    27、自定义Drawable

    1. 一般作为ImageView的图像来显示
    2. 另一个是作为View的背景
    3. 自定义Drawable主要就是实现draw方法
    4. setAlphasetColorFiltergetOpacity也需要重写,但是模板固定
    5. 当自定义Drawable有固定大小时(比如绘制一张图片),需要重写getIntrinsicWidth()/getIntrinsicHeight()方法(默认返回-1),会影响到Viewwrap_content布局
    6. 内部固定大小不等于Drawable的实际区域大小,getBounds能获得实际区域大小

    28、自定义Drawable模板代码

    class CustomDrawable(color: Int) : Drawable(){
        var mPaint: Paint
        init {
            mPaint = Paint(Paint.ANTI_ALIAS_FLAG)
            mPaint.color = color
        }
        override fun draw(canvas: Canvas) {
            val rect = bounds
            canvas.drawCircle(rect.exactCenterX(),
                    rect.exactCenterY(),
                    Math.min(rect.exactCenterX(), rect.exactCenterY()),
                    mPaint)
        }
    
        override fun setAlpha(alpha: Int) {
            mPaint.alpha = alpha
            invalidateSelf()
        }
        override fun setColorFilter(colorFilter: ColorFilter?) {
            mPaint.colorFilter = colorFilter
            invalidateSelf()
        }
        override fun getOpacity(): Int {
            //not sure, so be safe
            return PixelFormat.TRANSLUCENT
        }
    }

    SVG矢量图

    29、SVG是什么?(Scalable Vetor Graphics)

    1. 可伸缩矢量图(Android 5.0推出)
    2. 定义用于网络的基于矢量的图形(在Web上应用非常广泛)
    3. 使用XML格式定义图形
    4. 图像缩放不会影响质量
    5. 万维网联盟标准(与DOM和XSL之类的W3C标准是一个整体)

    30、SVG和Bitmap区别

    1. SVG是一个绘图标准。
    2. Bitmap是通过每个像素点上存储色彩信息来表示图像。
    3. SVG放大不会失真, Bitmap会失真。
    4. Bitmap需要为不同分辨率设计多套图表,SVG绘制一张图就能适配不同分辨率。

    31、静态矢量图SVG-VectorDrawable

    1. 基于XML的静态矢量图
    2. 采用标签vector
    3. vectorpath是最小单位,创建SVG-用指令绘制SVG图形
    4. vectorgroup将不同path组合起来

    32、VectorDrawable的vector标签有哪些属性

    <vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="200dp"  // SVG的具体大小
        android:height="200dp"
        android:viewportWidth="100" //将宽度分为多少份,与path配合(50份等于100dp)
        android:viewportHeight="100">
        <group>   //将不同`path`组合起来
            <path    //SVG树形结构的最小单位,用指令绘制SVG图形
                android:name="path1" //该path的名称
                android:pathData="M 20,80 L 50,80 80,80"
                android:strokeColor="@color/colorAccent"
                android:strokeWidth="3"
                android:strokeLineCap="round"/>
            <path
                .../>
        </group>
    </vector>

    33、VectorDrawable的path标签的全部指令

    指令含义
    M = moveto(M X, Y)将画笔移动到指定的坐标位置,但并未绘制
    L = lineto(L X, Y)画直线到指定的坐标位置
    H = horizontal lineto(H X)画水平线到指定X坐标位置
    V = vertical lineto(V Y)画水平线到指定Y坐标位置
    C = curveto(C X1, Y1, X2, Y2, ENDX, ENDY)三次贝赛曲线
    S = smooth curveto(S X2, Y2, ENDX, ENDY)三次贝赛曲线
    Q = quadratic Belzier curve(Q X, Y, ENDX, ENDY)二次贝赛曲线
    T = smooth quadratic Belzier curveTO(T ENDX, ENDY)映射前面路径后的终点
    A = elliptical Arc(A RX, RY, XROTATION, FLAG1, FLAG2, X, Y)弧线(RX/RY:椭圆半轴大小 XROTATION:椭圆X轴与水平方向顺时针方向夹角)
    Z = closepath()关闭路径
    1. 坐标轴以(0, 0)为中心, X轴水平向右, Y轴水平向下
    2. 指令大写-绝对定位,参考全局坐标系;指令小写-相对定位,参考父容器坐标系
    3. 指令和数据间空格可以省略
    4. 同一指令出现多次,可以只用一个。
    5. A的参数:RX/RY:椭圆半轴大小 XROTATION:椭圆X轴与水平方向顺时针方向夹角 FLAG1:1-大角度弧线 0-小角度弧线 FLAG2:起点到终点的方向,1-顺时针,2-逆时针 X/Y:终点坐标

    34、VectorDrawable实例

    //1. 使用`vector`标签定义矢量图VectorDrawable(ic_black_24dp.xml)
    <vector xmlns:android="http://schemas.android.com/apk/res/android"
            android:width="24dp"
            android:height="24dp"
            android:viewportWidth="24.0"
            android:viewportHeight="24.0">
            <group
               android:name="test"> //该组的名称:可以在AnimatedVectorDrawable中指定动画效果
               <path
                  android:fillColor="#FF000000"
                  android:pathData="M12,6c1.11,0 2,-0.9 2,-2 0,-0.38 -0.1,-0.73 -0.29,-1.03L12,0l-1.71,2.97c-0.19,0.3 -0.29,0.65 -0.29,1.03 0,1.1 0.9,2 2,2zM16.6,15.99l-1.07,-1.07 -1.08,1.07c-1.3,1.3 -3.58,1.31 -4.89,0l-1.07,-1.07 -1.09,1.07C6.75,16.64 5.88,17 4.96,17c-0.73,0 -1.4,-0.23 -1.96,-0.61L3,21c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1v-4.61c-0.56,0.38 -1.23,0.61 -1.96,0.61 -0.92,0 -1.79,-0.36 -2.44,-1.01zM18,9h-5L13,7h-2v2L6,9c-1.66,0 -3,1.34 -3,3v1.54c0,1.08 0.88,1.96 1.96,1.96 0.52,0 1.02,-0.2 1.38,-0.57l2.14,-2.13 2.13,2.13c0.74,0.74 2.03,0.74 2.77,0l2.14,-2.13 2.13,2.13c0.37,0.37 0.86,0.57 1.38,0.57 1.08,0 1.96,-0.88 1.96,-1.96L20.99,12C21,10.34 19.66,9 18,9z"/>
            </group>
    </vector>
    //2. 使用矢量图
    <ImageView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:src="@drawable/ic_black_24dp"/>

    35、矢量图动画-AnimatedVectorDrawable

    1. 针对静态矢量图-VectorDrawable来做动画
    2. xml标签为animated-vector
    3. target子标签下指明VectorDrawable的名字(都是android:name="..."属性指明),并指定动画效果android:animation="@animator/..."

    36、AnimatedVectorDrawable实例

    //1. 静态矢量图-VectorDrawable(vector_two_line.xml)
    <?xml version="1.0" encoding="utf-8"?>
    <vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="200dp"
        android:height="200dp"
        android:viewportWidth="100"
        android:viewportHeight="100">
        <group>
            <path
                android:name="path1" //路径1的名称
                android:pathData="M 20,80 L 50,80 80,80"
                android:strokeColor="@color/colorAccent"
                android:strokeWidth="3"
                android:strokeLineCap="round"/>
            <path
                android:name="path2" //路径2的名称
                android:pathData="M 20,20 L 50,20 80,20"
                android:strokeColor="@color/colorAccent"
                android:strokeWidth="3"
                android:strokeLineCap="round"/>
        </group>
    </vector>
    
    //2. 轨迹动画效果-属性动画ObjectAnimator(res/animator/trimpath_animator)
    <?xml version="1.0" encoding="utf-8"?>
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:propertyName="trimPathEnd"
        android:valueFrom="0"
        android:valueTo="1"
        android:valueType="floatType"
        android:interpolator="@android:interpolator/accelerate_decelerate">
    </objectAnimator>
    
    //3. 粘合静态SVG和属性动画:AnimatedVectorDrawable(vector_trimpath_anim.xml)
    <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/vector_two_line"> //静态SVG
       <target
           android:animation="@animator/trimpath_animator" //属性动画
           android:name="path1"> //静态SVG中路径1的名称
       </target>
       <target
           android:animation="@animator/trimpath_animator" //属性动画
           android:name="path2"> //静态SVG中路径2的名称
       </target>
    </animated-vector>
    
    //4. 布局中使用AnimatedVectorDrawable
    <ImageView
                android:id="@+id/trimpath_anim_imageview"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:src="@drawable/vector_trimpath_anim"/> //动画矢量图

    代码中开启动画:

    ImageView imageView = (ImageView) findViewById(R.id.trimpath_anim_imageview);
    ((Animatable)imageView.getDrawable()).start();
    展开全文
  • Android:Drawable的缓存机制

    千次阅读 2019-05-10 09:43:43
    本文通过实例和分析Drawable的缓存机制源码的方式来介绍一下Drawable的缓存机制,并且了解一下Drawable.mutate()的用法。 问题演示 下面我们通过一个实例来演示一个我们在使用Drawable过程中经常...
  • 安卓Drawable的使用

    2016-08-25 09:11:26
    演示了各种Drawable的效果,以及自定义Drawable
  • Android各种Drawable讲解和demo实例

    千次阅读 2018-07-19 15:13:24
    Android的各种Drawable讲解和demo实例 转载自 : https://blog.csdn.net/linghu_java/article/details/42119969 PS:文字内容基本搬自所转载的博客,但是所有代码和图片均为博主实践而来,不过,归根结底,还是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 334,945
精华内容 133,978
关键字:

drawable