?
一、概述
自定义InsetDrawable,扩展原生的InsetDrawable类功能。系统原生的InsetDrawable 是Drawable的子类,是用于嵌入一个子Drawable,且给该drawable设置inset属性,组成一个InsetDrawable对象。它可以实现设置的子Drawable与宿主控件区域有一定的间距。由属性 insetTop、insetLeft等体现。?
InsetDrawable类
Drawable getDrawable();
void setDrawable(Drawable dr)
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/home_quick"
android:insetBottom="10dp"
android:insetLeft="10dp"
android:insetRight="10dp"
android:insetTop="10dp" />
??
将一个控件设置背景为InsetDrawable, insetLeft、insetTop等均设置为10dp。由上图可以看出,InsetDrawable在设置inset属性后,会让其子Drawable对象显示区域与控件实际尺寸存在差距,显而易见,这个距离由inset属性值来决定,同时,被设置InsetDrawable对象的控件,变相的设置了Padding值,这个值也是写inset属性值相关。
二、需求:?
创建一个Drawable, 可以设置子Drawable, 同时可以设置Inset属性,但inset属性不能影响宿主控件的padding值,而且可以动态设置inset属性(系统的InsetDrawable是不能动态设置inset属性的,只能在xml中设置)。
三、实现自定义InsetDrawable
3.1 去除inset属性带来的padding效果
查看InsetDrawable源码
public class InsetDrawable extends DrawableWrapper {
@Override
public boolean getPadding(Rect padding) {
//子Drawable的padding值
final boolean pad = super.getPadding(padding);
getInsets(mTmpInsetRect);
padding.left += mTmpInsetRect.left;
padding.right += mTmpInsetRect.right;
padding.top += mTmpInsetRect.top;
padding.bottom += mTmpInsetRect.bottom;
return pad || (mTmpInsetRect.left | mTmpInsetRect.right
| mTmpInsetRect.top | mTmpInsetRect.bottom) != 0;
}
//将原有的padding值进行修改,增加inset属性影响
private void getInsets(Rect out) {
final Rect b = getBounds();
out.left = mState.mInsetLeft.getDimension(b.width());
out.right = mState.mInsetRight.getDimension(b.width());
out.top = mState.mInsetTop.getDimension(b.height());
out.bottom = mState.mInsetBottom.getDimension(b.height());
}
。。。。
}
public abstract class DrawableWrapper extends Drawable implements Drawable.Callback {
@Override
public boolean getPadding(@NonNull Rect padding) {
return mDrawable != null && mDrawable.getPadding(padding);
}
。。。。
}
如上源码可以看出,是将原padding值增加inset属性影响
要去除padding效果,重写getPadding方法如下
@Override
public boolean getPadding(Rect padding) {
// return super.getPadding(padding);
return mDrawable != null && mDrawable.getPadding(padding);
}
只用管Drawable设置padding的逻辑。
3.2 动态设置Inset属性
由于InsetState 等状态类都是final修改且不对外公共使用的类,它无法继承和重写,InsetDrawble也未提供相应的api可以修改inset属性值。但从源中可以看出,inset属性生效的逻辑是在onBoundsChange(Rect recf) 方法内实现的。源码如下:
@Override
protected void onBoundsChange(Rect bounds) {
final Rect r = mTmpRect;
r.set(bounds);
//将原有的bounds区域增加inset属性影响
r.left += mState.mInsetLeft.getDimension(bounds.width());
r.top += mState.mInsetTop.getDimension(bounds.height());
r.right -= mState.mInsetRight.getDimension(bounds.width());
r.bottom -= mState.mInsetBottom.getDimension(bounds.height());
// Apply inset bounds to the wrapped drawable.
super.onBoundsChange(r);
}
?自定义InsetDrawable代码如下:
package com.app.zxjt.widget.widget;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import androidx.annotation.Nullable;
/**
* Created by liuyu
* on 2021/9/13
*/
public class CustomInsetDrawable extends InsetDrawable {
private Drawable mDrawable;
private CustomInsets mInsets = new CustomInsets();
public CustomInsetDrawable(@Nullable Drawable drawable) {
super(drawable, 0);
mDrawable = drawable;
}
@Override
public boolean getPadding(Rect padding) {
//return super.getPadding(padding);
return mDrawable != null && mDrawable.getPadding(padding);
}
@Override
protected void onBoundsChange(Rect r) {
resetBounds(r);
super.onBoundsChange(r);
}
private void resetBounds(Rect r) {
if (r != null) {
r.left += mInsets.left;
r.top += mInsets.top;
r.bottom -= mInsets.bottom;
r.right -= mInsets.right;
}
}
@Override
public void setDrawable(Drawable drawable) {
super.setDrawable(drawable);
mDrawable = drawable;
}
@Nullable
@Override
public Drawable getDrawable() {
return super.getDrawable();
}
public void setInsets(int insetLeft, int insetTop, int insetRight, int insetBottom) {
notifyInsets(insetLeft, insetTop, insetRight, insetBottom);
}
public void setInsets(int insets) {
notifyInsets(insets, insets, insets, insets);
}
public void setInsetLeft(int insetLeft) {
if (mInsets.left != insetLeft) {
mInsets.left = insetLeft;
notifyBounds();
}
}
public void setInsetTop(int insetTop) {
if (mInsets.top != insetTop) {
mInsets.top = insetTop;
notifyBounds();
}
}
public void setInsetRight(int insetRight) {
if (mInsets.right != insetRight) {
mInsets.right = insetRight;
notifyBounds();
}
}
public void setInsetBottom(int insetBottom) {
if (mInsets.bottom != insetBottom) {
mInsets.bottom = insetBottom;
notifyBounds();
}
}
private void notifyBounds() {
onBoundsChange(getBounds());
invalidateSelf();
}
private void notifyInsets(int insetLeft, int insetTop, int insetRight, int insetBottom) {
if (mInsets.left != insetLeft || mInsets.top != insetTop || mInsets.right != insetRight || mInsets.bottom != insetBottom) {
mInsets.left = insetLeft;
mInsets.top = insetTop;
mInsets.right = insetRight;
mInsets.bottom = insetBottom;
notifyBounds();
}
}
public CustomInsets getInsets() {
return mInsets;
}
public static class CustomInsets {
public int left;
public int top;
public int right;
public int bottom;
}
}
上述自定义的Drawable就可以满足需求了。
|