见微知著 - drawable 浅析

1、Bitmap、Drawable和View 三者之间的联系和区别

1.1 Drawable 和 View

Drawable 的注释:

A Drawable is a general abstraction for "something that can be drawn."  Most
often you will deal with Drawable as the type of resource retrieved for
drawing things to the screen; the Drawable class provides a generic API for
dealing with an underlying visual resource that may take a variety of forms.
Unlike a {@link android.view.View}, a Drawable does not have any facility to
receive events or otherwise interact with the user.

Drawable 就是 “something that can be drawn” 的抽象,也就是说,它表达了我们希望在屏幕上绘制的图像。例如,Bitmap 文件也可以说成是 Drawable ,因为它承载了图片的数据,这些数据不依赖于设备平台,只是图片自身内容的放映。那我们的 View 类不是也有数据存储功能吗,它与 Drawable 有什么区别?区别就是 View 不仅仅包含了图片的数据,还需要处理各种事件(比如触摸事件、按键时间等),关系可以表示如下:

view和drawable

1.2 Drawable 和 Bitmap

 Though usually not visible to the application, Drawables may take a variety of forms:
 1. Bitmap: the simplest Drawable, a PNG or JPEG image.
 2. Nine Patch: an extension to the PNG format allows it to specify information about how to stretch it and place things inside of it.
 3. Shape: contains simple drawing commands instead of a raw bitmap, allowing it to resize better in some cases.
 4. Layers: a compound drawable, which draws multiple underlying drawables on top of each other.
 5. States: a compound drawable that selects one of a set of drawables based on its state.
 6. Levels: a compound drawable that selects one of a set of drawables based on its level.
 7. Scale : a compound drawable with a single child drawable, whose overall size is modified based on the current level.

注释上说到 Bitmap 是一个最简单的 Drawable 。但是我们来看看 Bitmap 类的定义:

public final class Bitmap implements Parcelable {
	// ......
}

可以看出 Bitmap 并没有实现 Drawable ,那么他们是如何联系起来的呢?看到 BitmapDrawable 应该就有答案了:

public class BitmapDrawable extends Drawable {

	// ......

    /**
     * Returns the bitmap used by this drawable to render. May be null.
     */
    public final Bitmap getBitmap() {
        return mBitmapState.mBitmap;
    }
}

在 Drawable 有个方法是:

 private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np,
            Rect pad, Rect layoutBounds, String srcName) {

        if (np != null) {
            return new NinePatchDrawable(res, bm, np, pad, layoutBounds, srcName);
        }

        return new BitmapDrawable(res, bm);
    }

这样这两者就联系起来了

1.3 Drawable 和 mipmap

drawable目录用于存放应用程序中的其他绘图资源,如按钮背景、图片、形状等。这些资源通常与密度无关。以及根据屏幕密度放置应用内图标。mipmap主要用于存放应用图标和启动图标等与设备分辨率和密度相关的图像资源。
具体应该怎么放置资源?

drawable

1.自定义的drawable标签,常用的就是shape、selector

2.页面背景图片、广告图片,注意需要根据屏幕密度放置

3.应用内图标,根据屏幕密度放置或使用SVG格式图标

mipmap

1.应用图标,AndroidManifest中的app icon

2.启动图标,AndroidManifest中的shotcuts

3.任务切换图标,手机上切换应用时显示的图标,一般与应用图标一致

有一个问题,图标一般有两种格式SVG与PNG,应该怎么选择呢?

SVG(矢量图形):无论放大或缩小,都不会失真;能够在不同屏幕密度和尺寸的设备上提供一致的高质量显示;适用于需要缩放或动画的图标,但Android自带的支持SVG的机制有限。所以图形不要太复杂或采用第三方SVG解析库。

PNG(位图图形):基于像素的图像。适用于静态图标,Android原生支持PNG图像,PNG图标可能需要多个版本以适应不同的屏幕密度

一般使用PNG,如图标需要进行动画或交互、自适应屏幕大小和密度就选择SVG。

1.4 Drawable 的回调

Drawable 除了必要的图形数据外,还封装了必不可少操作函数。主要分为创建、销毁、简单的图像处理、绘制。既然 Drawable 只是图像数据的抽象,那么当图形有变化或需要重绘时,就必须要通知 View 。完成这个操作的就是 Drawable 的 CallBack 的接口:

  /**
     * Implement this interface if you want to create an animated drawable that
     * extends {@link android.graphics.drawable.Drawable Drawable}.
     * Upon retrieving a drawable, use
     * {@link Drawable#setCallback(android.graphics.drawable.Drawable.Callback)}
     * to supply your implementation of the interface to the drawable; it uses
     * this interface to schedule and execute animation changes.
     */
public interface Callback {
   
        /**
         *  当 drawable 需要被重绘是调用
         */
        void invalidateDrawable(@NonNull Drawable who);

        /**
         *  用于 drawable 规划动画的下一帧
         */
        void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when);

        /**
         * 用于取消 scheduleDrawable 的操作
         */
        void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what);
    }

2、view.setVisible和drawable.setVisible的区别

view 关于 setVisible 的源码:

    /**
     * Set the visibility state of this view.
     *
     * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
     * @attr ref android.R.styleable#View_visibility
     */
    @RemotableViewMethod
    public void setVisibility(@Visibility int visibility) {
        setFlags(visibility, VISIBILITY_MASK);
    }

drawable 关于 setVisible 的源码:

    /**
     *  设置可绘制对象是否可见。这通常不会影响 Drawable 的行为,但是对于一些 
     *  Drawables 对象可被用作提示,例如,Drawable 用于是否运行动画
     *
     * @param visible 可见设置为true(如果可见),否则设置为false(如果不可见)
     * @param restart 可以在此处提供true以强制drawable执行操作就好像它刚刚变可
     * 见,即使它是最后一个已设置为可见。例如,用于强制动画重新启动。
     * 
     * @return boolean 如果新可见性与其以前的状态不同,则返回true。
     */
    public boolean setVisible(boolean visible, boolean restart) {
        boolean changed = mVisible != visible;
        if (changed) {
            mVisible = visible;
            invalidateSelf();
        }
        return changed;
    }

虽然这两个类的方法名都一样,但是参数和返回值都不一样。View 中 setVisibility 就是对 View 进行设置的,和平常用的一样。drawable 中是对本身的绘制对象进行显示与否的设置,可强制动画进行重启,此方法还有一个 boolean 值的返回值。

3、About SimpleDraweeView and ShareElementTransition的问题

在使用过渡动画的时候,如果过渡元素中的控件有 SimpleDraweeView ,当退出过渡动画的时候,使用 SimpleDraweeView 的 view 不可见。
出现这个的原因是 SimpleDraweeView 是继承 DraweeView,在 DraweeView 的 onVisibilityChanged 方法,当 drawable 对象不为空时就不会执行 drawable.setVisible(getVisibility() == VISIBLE, false); 照成图片不显示的问题。

DraweeView源码

这个问题需要重写 SimpleDraweeView

class TransitionDraweeView : SimpleDraweeView {

    constructor(context: Context) : super(context) {}
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}
    constructor(context: Context, hierarchy: GenericDraweeHierarchy) : super(context, hierarchy) {}

    override fun onVisibilityChanged(changedView: View, visibility: Int) {
        super.onVisibilityChanged(changedView, visibility)
        maybeOverrideVisibilityHandling()
    }

    /**
     * 重新设置 drawable 的 visible
     */
    private fun maybeOverrideVisibilityHandling() {
        val drawable = drawable
        drawable?.setVisible(visibility == VISIBLE, false)
    }
}

frcsco 的issues 链接 https://github.com/facebook/fresco/issues/2532

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值