簡體   English   中英

如何在Android中的按鈕內設置矢量可繪制的大小?

[英]How to set the size of vector drawable inside of a button in Android?

Android Studio Vector Assets 工具將可繪制的矢量轉換為 Lollipop 之前的設備的 PNG-s,但我得到的 PNG-s 質量非常差,正如您在此處看到的:

將矢量轉換為 PNG

更重要的是,按鈕的背景純色應該是您在左側看到的淺綠色,但可繪制覆蓋它:

<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 之前的設備上調整可繪制矢量大小的方法。 因此,我將按鈕本身做得更小,並增加了每個按鈕的觸摸面積。

縮放任何可繪制對象的正確方法是使用vectorDrawable.setBounds(left,top,right,bottom) ,但不幸的是,這不適用於矢量可繪制對象(為什么是谷歌?)。

因此,作為一種解決方法,我加載我的矢量可繪制對象,將它們轉換為位圖可繪制對象,這將允許我們setBounds圖可繪制對象上使用setBounds方法。 請注意,您在此處縮放位圖,因此可能會丟失一些圖像的清晰度。 例如,當我需要將我的可繪制對象用作文本視圖或按鈕的復合可繪制對象時,我主要使用這些方法。

我最終編寫了一個輔助類,它將加載一個矢量可繪制對象,為其設置色調並返回一個位圖可繪制對象,您可以根據需要對其進行實際縮放和着色。 我已經針對 API 級別 19 到 23 對其進行了測試,並且可以正常工作。

不要忘記使用vectorDrawables.useSupportLibrary = true在你的build.gradle。

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);

使用視圖或活動的上下文很重要,而不是應用程序上下文! 希望它能解決您的問題,或幫助其他人。 如果有人有更好更簡潔的解決方案,我也很想知道。

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" )。

看看我發現的這個,它適用於 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
        }
    };

我使用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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM