Drawable

本文详细介绍了Drawable的概念及使用方式,包括各种类型的Drawable如Bitmap、NinePatch、Shape等,以及如何通过setBounds确定绘制位置和大小,利用setAlpha设置透明度等方法。此外还探讨了Drawable的状态管理与动画实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Drawable是一种抽象的可以被绘制的对象。和View不同的是,Drawable不能接收事件,也不能和用户进行交互。
Drawable定义了一些通用机制,供客户端调用。例如setBounds是用来确定Drawable绘制的位置和大小的,因此必须被调用。可以通过缩放来改变Drawable大小。可以通过getIntrinsicHeight和getIntrinsicWidth获取Drawable宽和高。
Drawable的展现形式有多种:
Bitmap:最简单的Drawable,是一张PNG或JPEG图片。
Nine Patch:一种扩展的PNG,(.9)可以进行拉伸和指定内容显示范围。
Shape:不是原始位图,可以更好的调整大小。
Layers:多层Drawable,层层绘制
States:(selector)根据状态从集合中选择Drawable。
Levels:根据level选择drawable
Scale:根据level对图片进行缩放。

Drawable 提供了一个回调接口,Callback。如果你想为Drawable子类创建一个动画drawable,可以实现这个接口。
这个回调接口提供了三个方法,

当drawable需要重画的时候会调用这个方法,此时设置drawable的view会被刷新。
public void invalidateDrawable(Drawable who);
可以调用这个方法执行下一帧动画,实现这个接口的类可以简单的调用Handler.postAtTime(Runnable, Object, long)方法
public void scheduleDrawable(Drawable who, Runnable what, long when);
与scheduleDrawable相对应,取消要执行的任务,通过调用Handler.removeCallbacks(Runnable, Object)方法。
public void unscheduleDrawable(Drawable who, Runnable what);

Drawable的经常使用的方法有以下几个:

为Drawable指定一个矩形的边界
public void setBounds(int left, int top, int right, int bottom) {
        Rect oldBounds = mBounds;

        if (oldBounds == ZERO_BOUNDS_RECT) {
            oldBounds = mBounds = new Rect();
        }

        if (oldBounds.left != left || oldBounds.top != top ||
                oldBounds.right != right || oldBounds.bottom != bottom) {
                //如果要设置的边界和之前的不相同,并且不是第一次设置,会调用invalidateSelf()方法,这个方法里会判断是否设置了回调,如果设置了,调用callback的invalidateDrawable()方法。
            if (!oldBounds.isEmpty()) {
                // first invalidate the previous bounds
                invalidateSelf();
            }
            //给mBounds重新赋值。
            mBounds.set(left, top, right, bottom);
            onBoundsChange(mBounds);//子类重写这个方法来实现自己的逻辑。
        }
    }
根据bounds(必须)、透明度、颜色过滤器等进行绘制。
public abstract void draw(Canvas canvas);
为drawable设置透明度
public abstract void setAlpha(int alpha);
给支持Xfermode的drawable设置Xfermode。
public void setXfermode(Xfermode mode) {
        // Base implementation drops it on the floor for compatibility. Whee!
    }
设置颜色过滤器,如果设置了ColorFilter ,每个像素会被修改。
public abstract void setColorFilter(@Nullable ColorFilter colorFilter);
为drawable设置着色的颜色。在绘制到屏幕之前,Drawable绘制的内容会和这个颜色混合在一起。
public void setTint(@ColorInt int tintColor) {
        setTintList(ColorStateList.valueOf(tintColor));
    }
为Drawable指定一组状态,例如获取焦点,按下,选中等。如果新的状态引起drawble外观的变化,会调用invalidateSelf,进行重画。
需要注意的是:Drawable持有stateSet的引用,直到被设置了新的状态数组,所以stateSet使用期间不能对它进行修改。
 public boolean setState(final int[] stateSet) {
        if (!Arrays.equals(mStateSet, stateSet)) {
            mStateSet = stateSet;
            return onStateChange(stateSet);
        }
        return false;
    }
获取drawable的宽度,如果没有宽度返回-1,例如颜色值。
public int getIntrinsicWidth() {
        return -1;
    }
使Drawable状态可变,这个操作不能逆转。一个可变的Drawable不会和其他Drawable共享状态。如果需要修改从resources加载的drawable的属性,这个方法非常有用。默认情况下,同一个资源文件,虽然有多个实例,但是它们共享共同的状态,如果修改了其中任意一个实例的状态,其他的也会接收到相同的变化。调用这个方法得到的Drawable,修改状态后不会影响其他实例。
public Drawable mutate() {
        return this;
    }

Drawable还有一个很重要的类是ConstantState,每一种Drawable都有自己的实现类。这个类用来存储drawables之间的共享常量状态和数据。例如从同一个资源文件创建的BitmapDrawables,它们实例里的ConstantState里会存储同一个位图。

public static abstract class ConstantState {
        在不提供Resources情况下生成一个Drawable。
        注意:使用此方法会导致依赖于密度的drawable(例如Bitmap)无法正确的更新目标密度(图片的大小取决于屏幕密度和所在drawable的文件夹的密度)。所以应该调用newDrawable(Resources)这个方法。
        public abstract Drawable newDrawable();


        根据ConstantState生成新的Drawable。基于密度变化的drawable必须实现此方法。(例如Bitmap)
        public Drawable newDrawable(Resources res) {
            return newDrawable();
        }

    根据ConstantState生成新的Drawable。设置了主题的drawable必须实现此方法。
        public Drawable newDrawable(Resources res, Theme theme) {
            return newDrawable(null);
        }
    }

根据资源id获取Drawable一般调用这个方法
getResources().getDrawable(@DrawableRes int id, @Nullable Theme theme);(api21)
这个方法里调用了loadDrawable(value, id, theme);重点来看下这个方法是如何加载drawable的。

@Nullable
    Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException {
        //...................根据value.data 得到key值

        // 首先根据key从DrawableCache里查找是否有drawable缓存,drawable已经被加载过,并设置了指定的主题。DrawableCache.getInstance方法先根据key和theme找到ConstantState,如果ConstantState有值,会调用newDrawable(Resources res, Theme theme)方法生成一个新的Drawable对象,所以尽管实例不一样,但是共享同一个ConstantState。
        if (!mPreloading) {
            final Drawable cachedDrawable = caches.getInstance(key, theme);
            if (cachedDrawable != null) {
                return cachedDrawable;
            }
        }

        //对drawable进行预加载,没有设置主题的一些属性

        final ConstantState cs;
        if (isColorDrawable) {
            cs = sPreloadedColorDrawables.get(key);
        } else {
            cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
        }

        Drawable dr;
        if (cs != null) {
            dr = cs.newDrawable(this);
        } else if (isColorDrawable) {
            dr = new ColorDrawable(value.data);
        } else {
        //如果是xml文件,会使用XmlResourceParser生成drawable。否则mAssets会根据资源文件生成InputStream,然后调用Drawable.createFromResourceStream生成drawable。
            dr = loadDrawableForCookie(value, id, null);
        }

       //生成drawble以后,如果提供了主题,会为drawable设置主题,
        final boolean canApplyTheme = dr != null && dr.canApplyTheme();
        if (canApplyTheme && theme != null) {
            dr = dr.mutate();
            dr.applyTheme(theme);
            dr.clearMutated();
        }

        最后把DrawableCache 、theme等进行缓存。
        if (dr != null) {
            dr.setChangingConfigurations(value.changingConfigurations);
            cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
        }

        return dr;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值