【问题标题】:How to set the size of vector drawable inside of a button in Android?如何在Android中设置按钮内可绘制矢量的大小?
【发布时间】:2016-02-14 22:28:21
【问题描述】:

Android Studio Vector Assets 工具将矢量 drawable 转换为适用于 Lollipop 之前的设备的 PNG-s,但我得到的 PNG-s 质量非常差,您可以在此处看到:

更重要的是,按钮的背景纯色应该是您在左侧看到的浅绿色,但可绘制对象会覆盖它:

<item android:state_checked="true"
    android:drawable="@drawable/show">
    <shape android:shape="rectangle">
        <corners android:bottomRightRadius="8dp"/>
        <solid android:color="@color/waveComponentGreen"/>
    </shape>
</item>

<item android:state_checked="false"
    android:drawable="@drawable/hide">
    <shape android:shape="rectangle">
        <corners android:bottomRightRadius="8dp"/>
        <solid android:color="@color/waveComponentGreen"/>
    </shape>
</item>

drawable 的 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">
<path
    android:fillColor="#FF000000"
    android:pathData="M8.59,16.34l4.58,-4.59 -4.58,-4.59L10,5.75l6,6 -6,6z"/>

我还想通过调整值使图标看起来更小一些,我注意到增加视口尺寸会减小图标,但我不确定我理解为什么。

那么:如何使图标和生成的 PNG 看起来更小、不那么模糊并且在资源文件中设置背景颜色?谢谢。

编辑:我设法通过将它们与图层列表组合在一个单独的 xml 文件中来获得带有图标的纯色背景:

<layer-list
xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
    <shape android:shape="rectangle">
        <corners android:bottomRightRadius="10dp"/>
        <solid android:color="@color/waveComponentGreen"/>
    </shape>
</item>
<item android:drawable="@drawable/show"
    android:top="10dp"
    android:bottom="10dp"
    android:left="10dp"
    android:right="10dp"
    />

结果是:

我设法通过增加可绘制矢量的宽度和高度来减少模糊。但是,如果没有 android:top|bottom|left|right 标签,drawable 会延伸到按钮的整个区域。第二个按钮不需要具有纯色背景,因此我没有使用图层列表标签 => 无法为可绘制对象设置 top|bottom|left|right 边距。
如果我减小按钮大小,我所做的就是减少按钮的可点击区域。

我更新的问题是如何在不减小按钮本身大小的情况下设置按钮/切换按钮/单选按钮内可绘制矢量的大小?

更新
我找不到在 API 21 之前的设备上调整可绘制矢量大小的方法。因此,我将按钮本身做得更小,并增加了每个按钮的触摸面积。

【问题讨论】:

  • 视口是绘制图标的空间,pathData 给出了指示形状应该是什么的各种坐标。例如,如果您有一个 24 x 24 的视口并从点 0,12 到点 24,12 绘制一条直线,那么这是一条从视口的中途从一侧向右到另一侧的水平线。如果您将视口大小更改为 48 x 48,但您的线仍然从点 0,12 到点 24,12,那么它不再从中间开始,也不会到达视口的另一侧。

标签: android xml button vector drawable


【解决方案1】:

缩放任何可绘制对象的正确方法是使用 vectorDrawable.setBounds(left,top,right,bottom) , 但不幸的是,这不适用于矢量绘图(为什么是谷歌?)。

因此,作为一种解决方法,我加载了矢量可绘制对象,将它们转换为可绘制位图,这将允许我们在可绘制位图上使用setBounds 方法。请注意,您在此处缩放位图,因此您可能会丢失一些图像的清晰度。当我需要将我的drawable用作文本视图或按钮的复合drawable时,我主要使用这些方法。

我最终编写了一个帮助类,它将加载一个矢量可绘制对象,并为其设置一个色调并返回一个位图可绘制对象,您可以根据需要实际缩放和着色。我已经针对 API 级别 19 到 23 对其进行了测试,并且可以正常工作。

别忘了在你的build.gradle中使用vectorDrawables.useSupportLibrary = true

public class VectorDrawableUtils {

/**
 * Gets a Bitmap from provided Vector Drawable image
 *
 * @param vd VectorDrawable
 * @return Bitmap
 */
public static Optional<Bitmap> createBitmapFromVectorDrawable(final @NonNull Drawable vd) {
    try {
        Bitmap bitmap;
        bitmap = Bitmap.createBitmap(vd.getIntrinsicWidth(), vd.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        vd.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        vd.draw(canvas);
        return Optional.of(bitmap);
    } catch (OutOfMemoryError e) {
        Injector.getDependency(getContext(), IEventTracker.class).logHandledException(e);
        return Optional.empty();
    }
}

/**
 * Loads vector drawable and apply tint color on it.
 */
public static Drawable loadVectorDrawableWithTintColor(final @DrawableRes int vdRes,
                                                       final @ColorRes int clrRes,final Context context) {
    Drawable drawable = ContextCompat.getDrawable(context, vdRes);
    DrawableCompat.setTint(drawable, getContext().getResources().getColor(clrRes));
    return drawable;
}

/**
 * Converts given vector drawable to Bitmap drawable
 */
public static BitmapDrawable convertVectorDrawableToBitmapDrawable(final @NonNull Drawable vd) {
    //it is safe to create empty bitmap drawable from null source
    return new BitmapDrawable(createBitmapFromVectorDrawable(vd).get());
}

/**
 * Loads vector drawable , aplys tint on it and returns a wrapped bitmap drawable.
 * Bitmap drawable can be resized using setBounds method (unlike the VectorDrawable)
 * @param context Requires view context !
 */
public static Drawable loadVectorDrawableWithTint(
        final @DrawableRes int vectorDrawableRes, final @ColorRes int colorRes,final Context context) {
    Drawable vd = VectorDrawableUtils.loadVectorDrawableWithTintColor(vectorDrawableRes,
            colorRes, context);
    final BitmapDrawable bitmapDrawable = VectorDrawableUtils.convertVectorDrawableToBitmapDrawable(vd);
    ColorStateList tint = ContextCompat.getColorStateList(context,colorRes);
    final Drawable wrappedDrawable = DrawableCompat.wrap(bitmapDrawable);
    DrawableCompat.setTintList(wrappedDrawable,tint);
    return wrappedDrawable;
    }
}

现在我会像这样使用这个辅助类:

    Drawable bd = VectorDrawableUtils.loadVectorDrawableWithTint(
                R.drawable.ic_dropdown, R.color.black,getContext());
        bd.setBounds(0, 0, textView.getMeasuredHeight(), textView.getMeasuredHeight());
        textView.setCompoundDrawablesWithIntrinsicBounds(null, null, bd, null);

重要的是使用视图或活动的上下文,而不是应用程序上下文! 希望它能解决你的问题,或者帮助别人。如果有人有更好、更清洁的解决方案,我也很想知道。

【讨论】:

    【解决方案2】:

    MyTextView 类:

    public class MyTextView extends AppCompatTextView {
    
    public MyTextView(Context context) {
        super(context);
    }
    
    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initAttrs(context, attrs);
    }
    
    public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttrs(context, attrs);
    }
    
    void initAttrs(Context context, AttributeSet attrs) {
        if (attrs != null) {
            TypedArray attributeArray = context.obtainStyledAttributes(
                    attrs,
                    R.styleable.MyTextView);
    
            int defaultWidthHeight = 0;
            int widthHeight = 0;
            Drawable drawableLeft = null;
            Drawable drawableStart = null;
            Drawable drawableRight = null;
            Drawable drawableEnd = null;
            Drawable drawableBottom = null;
            Drawable drawableTop = null;
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                drawableLeft = attributeArray.getDrawable(R.styleable.MyTextView_drawableLeftCompatTextView);
                drawableStart = attributeArray.getDrawable(R.styleable.MyTextView_drawableStartCompatTextView);
                drawableRight = attributeArray.getDrawable(R.styleable.MyTextView_drawableRightCompatTextView);
                drawableEnd = attributeArray.getDrawable(R.styleable.MyTextView_drawableEndCompatTextView);
                drawableBottom = attributeArray.getDrawable(R.styleable.MyTextView_drawableBottomCompatTextView);
                drawableTop = attributeArray.getDrawable(R.styleable.MyTextView_drawableTopCompatTextView);
            } else {
                final int drawableLeftId = attributeArray.getResourceId(R.styleable.MyTextView_drawableLeftCompatTextView, -1);
                final int drawableStartId = attributeArray.getResourceId(R.styleable.MyTextView_drawableStartCompatTextView, -1);
                final int drawableRightId = attributeArray.getResourceId(R.styleable.MyTextView_drawableRightCompatTextView, -1);
                final int drawableEndId = attributeArray.getResourceId(R.styleable.MyTextView_drawableEndCompatTextView, -1);
                final int drawableBottomId = attributeArray.getResourceId(R.styleable.MyTextView_drawableBottomCompatTextView, -1);
                final int drawableTopId = attributeArray.getResourceId(R.styleable.MyTextView_drawableTopCompatTextView, -1);
    
                if (drawableLeftId != -1)
                    drawableLeft = AppCompatResources.getDrawable(context, drawableLeftId);
                if(drawableStartId != -1)
                    drawableStart = AppCompatResources.getDrawable(context, drawableStartId);
                if (drawableRightId != -1)
                    drawableRight = AppCompatResources.getDrawable(context, drawableRightId);
                if(drawableEndId != -1)
                    drawableEnd = AppCompatResources.getDrawable(context, drawableEndId);
                if (drawableBottomId != -1)
                    drawableBottom = AppCompatResources.getDrawable(context, drawableBottomId);
                if (drawableTopId != -1)
                    drawableTop = AppCompatResources.getDrawable(context, drawableTopId);
            }
    
            if(!attributeArray.hasValue(R.styleable.MyTextView_drawableWidthHeightCompatTextView)) {
                if (attributeArray.hasValue(R.styleable.MyTextView_drawableLeftCompatTextView)) {
                    defaultWidthHeight = drawableLeft.getIntrinsicWidth();
                } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableStartCompatTextView)) {
                    defaultWidthHeight = drawableStart.getIntrinsicWidth();
                } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableRightCompatTextView)) {
                    defaultWidthHeight = drawableRight.getIntrinsicWidth();
                } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableEndCompatTextView)) {
                    defaultWidthHeight = drawableEnd.getIntrinsicWidth();
                } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableBottomCompatTextView)) {
                    defaultWidthHeight = drawableBottom.getIntrinsicWidth();
                } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableTopCompatTextView)) {
                    defaultWidthHeight = drawableTop.getIntrinsicWidth();
                }
    
                widthHeight = attributeArray.getInt(R.styleable.MyTextView_drawableWidthHeightCompatTextView, defaultWidthHeight);
            } else
                widthHeight = attributeArray.getInt(R.styleable.MyTextView_drawableWidthHeightCompatTextView, defaultWidthHeight);
    
            if(attributeArray.hasValue(R.styleable.MyTextView_drawableColorCompatTextView)){
                ColorStateList tintColor = attributeArray.getColorStateList(R.styleable.MyTextView_drawableColorCompatTextView);
                if (attributeArray.hasValue(R.styleable.MyTextView_drawableLeftCompatTextView)) {
                    //drawableLeft.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                    DrawableCompat.setTintList(drawableLeft, tintColor);
                } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableStartCompatTextView)) {
                    //drawableStart.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                    DrawableCompat.setTintList(drawableStart, tintColor);
                } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableRightCompatTextView)) {
                    //drawableRight.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                    DrawableCompat.setTintList(drawableRight, tintColor);
                } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableEndCompatTextView)) {
                    //drawableEnd.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                    DrawableCompat.setTintList(drawableEnd, tintColor);
                } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableBottomCompatTextView)) {
                    //drawableBottom.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                    DrawableCompat.setTintList(drawableBottom, tintColor);
                } else if (attributeArray.hasValue(R.styleable.MyTextView_drawableTopCompatTextView)) {
                    //drawableTop.setColorFilter(new PorterDuffColorFilter(tintColor.getDefaultColor(), PorterDuff.Mode.MULTIPLY));
                    DrawableCompat.setTintList(drawableTop, tintColor);
                }
            }
    
            WrappedDrawable drawableLeftWrapped = new WrappedDrawable(drawableLeft);
            drawableLeftWrapped.setBounds(0, 0, widthHeight, widthHeight);
            WrappedDrawable drawableStartWrapped = new WrappedDrawable(drawableStart);
            drawableStartWrapped.setBounds(0, 0, widthHeight, widthHeight);
            WrappedDrawable drawableRightWrapped = new WrappedDrawable(drawableRight);
            drawableRightWrapped.setBounds(0, 0, widthHeight, widthHeight);
            WrappedDrawable drawableEndWrapped = new WrappedDrawable(drawableEnd);
            drawableEndWrapped.setBounds(0, 0, widthHeight, widthHeight);
            WrappedDrawable drawableBottomWrapped = new WrappedDrawable(drawableBottom);
            drawableBottomWrapped.setBounds(0, 0, widthHeight, widthHeight);
            WrappedDrawable drawableTopWrapped = new WrappedDrawable(drawableTop);
            drawableTopWrapped.setBounds(0, 0, widthHeight, widthHeight);
    
            setCompoundDrawablesWithIntrinsicBounds(drawableLeftWrapped, drawableTopWrapped, drawableRightWrapped, drawableBottomWrapped);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
                setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStartWrapped, drawableTopWrapped, drawableEndWrapped, drawableBottomWrapped);
    
            attributeArray.recycle();
        }
    }
    
    class WrappedDrawable extends Drawable {
    
        private final Drawable _drawable;
        protected Drawable getDrawable() {
            return _drawable;
        }
    
        public WrappedDrawable(Drawable drawable) {
            super();
            _drawable = drawable;
        }
    
        @Override
        public void setBounds(int left, int top, int right, int bottom) {
            //update bounds to get correctly
            super.setBounds(left, top, right, bottom);
            Drawable drawable = getDrawable();
            if (drawable != null) {
                drawable.setBounds(left, top, right, bottom);
            }
        }
    
        @Override
        public void setAlpha(int alpha) {
            Drawable drawable = getDrawable();
            if (drawable != null) {
                drawable.setAlpha(alpha);
            }
        }
    
        @Override
        public void setColorFilter(ColorFilter colorFilter) {
            Drawable drawable = getDrawable();
            if (drawable != null) {
                drawable.setColorFilter(colorFilter);
            }
        }
    
        @Override
        public int getOpacity() {
            Drawable drawable = getDrawable();
            return drawable != null
                    ? drawable.getOpacity()
                    : PixelFormat.UNKNOWN;
        }
    
        @Override
        public void draw(Canvas canvas) {
            Drawable drawable = getDrawable();
            if (drawable != null) {
                drawable.draw(canvas);
            }
        }
    
        @Override
        public int getIntrinsicWidth() {
            Drawable drawable = getDrawable();
            return drawable != null
                    ? drawable.getBounds().width()
                    : 0;
        }
    
        @Override
        public int getIntrinsicHeight() {
            Drawable drawable = getDrawable();
            return drawable != null ?
                    drawable.getBounds().height()
                    : 0;
        }
    }
    }
    

    attrs.xml:

    <declare-styleable name="MyTextView">
        <attr name="drawableColorCompatTextView" format="reference|color"/>
        <attr name="drawableWidthHeightCompatTextView" format="integer"/>
        <attr name="drawableLeftCompatTextView" format="reference"/>
        <attr name="drawableStartCompatTextView" format="reference"/>
        <attr name="drawableRightCompatTextView" format="reference"/>
        <attr name="drawableEndCompatTextView" format="reference"/>
        <attr name="drawableTopCompatTextView" format="reference"/>
        <attr name="drawableBottomCompatTextView" format="reference"/>
    </declare-styleable>
    

    用法:

    <com.packagename.MyTextView
        android:id="@+id/txtUserName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:drawableLeftCompatTextView="@drawable/ic_username"
        app:drawableStartCompatTextView="@drawable/ic_username"
        app:drawableWidthHeightCompatTextView="48"
        app:drawableColorCompatTextView="@color/blue" />
    

    注意:这里唯一的问题是一个未修改的向量(drawableWidthHeightCompatTextView没有使用),其中向量的widthheight是24,
    不是 在设备上大小相等,带有调整大小的矢量(矢量的 widthheight 是 12 和 drawableWidthHeightCompatTextView="24")。

    【讨论】:

      【解决方案3】:

      看看我发现的这个,它适用于 Vector 和常规 Drawables! 我认为这是我见过的最短的解决方案。

      这个想法是创建一个具有固定内在尺寸的自定义可绘制对象,并将绘图作业传递给原始可绘制对象。

      final Drawable d = container.getContext().getDrawable(R.drawable.your_drawable);
          Drawable icon = new ColorDrawable(){
              Drawable iconOrig = d;
      
              @Override
              public void setBounds(int left, int top, int right, int bottom){
                  super.setBounds(left, top, right, bottom);//This is needed so that getBounds on this class would work correctly.
                  iconOrig.setBounds(left, top, right, bottom);
              }
      
              @Override
              public void draw(Canvas canvas){
                  iconOrig.draw(canvas);
              }
      
              @Override
              public int getIntrinsicWidth(){
                  return  27;//the width you want
              }
      
              @Override
              public int getIntrinsicHeight(){
                  return  19;// the height you want
              }
          };
      

      【讨论】:

        【解决方案4】:

        我使用FrameLayoutImageButtonImageView 放在一起,并能够调整ImageView 的大小:

        <FrameLayout
          xmlns:app="http://schemas.android.com/apk/res-auto"
          android:layout_width="60dp"
          android:layout_height="60dp"
        >
          <ImageButton
            android:layout_width="match_parent"
            android:layout_height="match_parent"
          />
          <ImageView
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_gravity="center"
            app:srcCompat="@drawable/baseline_arrow_right_24"
          />
        </FrameLayout>
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-15
          • 1970-01-01
          • 2019-05-09
          • 1970-01-01
          相关资源
          最近更新 更多