Android - Запазване на пропорциите на фоновото изображение на бутона по подразбиране

Има ли някакъв начин да се поддържа съотношението на чертежите, когато се прилага като фон на Button (не мога да използвам ImageButton (нуждая се от възможност за текст на Button), не мога да използвам setCompoundDrawables(), защото това също не отговаря на изискванията ми (направих своя собствен клас за поддръжка на добавяне на фонове, като същевременно запазва оригиналния бутон по подразбиране на android, плюс трябва да задам фона в средата на бутона, а не отляво, отдясно, отгоре или отдолу)).

Опитах решение с ScaleDrawable, но или правя нещо нередно, или не работи:

Bitmap scaled = Bitmap.createScaledBitmap(original, (int)newBmpWidth, (int)newBmpHeight, false);
BitmapDrawable bd = new BitmapDrawable(scaled);
ScaleDrawable sd = new ScaleDrawable(bd, Gravity.CENTER, a, b);
//for a and b I tried lots of different values, ranging from 0.07f to 1000
sd.setLevel(1);

След това използвам ScaleDrawable като фон, но така или иначе се разтяга.

Опитах и ​​InsetDrawable, но отново, или правя нещо нередно, или не работи, очаквайки първото:

BitmapDrawable mBitmapDrawable = new BitmapDrawable(mBitmap);
InsetDrawable mInsetDrawable = new InsetDrawable(mBitmapDrawable, mLeftOffset, 0, mRightOffset, 0);

След това използвам InsetDrawable като фон, но това също се разтяга.

Има ли някакъв начин да се приложи фон, но да се запази съотношението на страните върху Button? Мога да намеря много отговори за ImageButtons, но за съжаление Button няма метод setImageBitmap() или setScaleType().


person stealthjong    schedule 31.10.2012    source източник
comment
каква е разделителната способност (в пиксели) на вашето изображение? и искате ли и фона по подразбиране на Button?   -  person Gagan    schedule 31.10.2012
comment
@Gagan Това зависи от това, което съм пробвал, няколко между 40x30 и 256x256 пиксела. И да.   -  person stealthjong    schedule 31.10.2012
comment
Обикновено ширината на Buttons варира, но височината остава постоянна. Така че ще бъде доста трудно да запазите съотношението на изображението непокътнато.   -  person Gagan    schedule 31.10.2012
comment
@Gagan Добре, ImageButton може да направи всичко по-горе, включително мащабиране и поддържане на пропорциите, но не мога да приложа текст върху тях. Опитвам се сега дали мога просто да начертая текста върху платното и да го направя ImageButtons   -  person stealthjong    schedule 31.10.2012


Отговори (1)


Наскоро се сблъсках със същия проблем за настройка на фоновото изображение на ImageView. Решението, което измислих, работи с всеки View, така че трябва да покрива и Button.

Проблемът е, че View винаги разтяга своя фон до пълния обхват на ширината и височината на View, независимо от мащабирането на Drawable, предоставен като фон. Както подсказва докладът ви за непредоставяне на специализирани Drawables, това не може да бъде противодействано с никакви средства за мащабиране на изображение, преди да го зададе като фон, защото View просто ще вземе размерите на изображението, каквито и да са те, и ще ги мащабира отново независимо, за да запълни View ■ площ.

Моето решение използва различен подход въз основа на факта, че ако можем да предоставим фоново изображение със същото съотношение на страните като View, няма да има изкривяване.

Функция scaleKeepingAspect() по-долу взема ресурс за изображение и отговаря на BitmapDrawable, който съдържа оригиналното изображение, мащабирано до желаната ширина и височина. Мащабирането се извършва по начин, при който съотношението на оригиналното изображение се запазва, а евентуално оставащите пространства, за да пасне на желаното поле, се запълват с прозрачни пиксели. Оригиналното изображение е центрирано в резултата.

private BitmapDrawable scaleKeepingAspect(Resources res, int id, int dstWidth, int dstHeight) {

    Bitmap b = (new BitmapFactory()).decodeResource(res, id);
    float scaleX = (float) dstWidth / b.getWidth();
    float scaleY = (float) dstHeight / b.getHeight();
    float scale = scaleX < scaleY ? scaleX : scaleY;
    int sclWidth = Math.round(scale * b.getWidth());
    int sclHeight = Math.round(scale * b.getHeight());
    b = Bitmap.createScaledBitmap(b, sclWidth, sclHeight, false);
    int[] pixels = new int[sclWidth * sclHeight];
    b.getPixels(pixels, 0, sclWidth, 0, 0, sclWidth, sclHeight);
    b = Bitmap.createBitmap(dstWidth, dstHeight, b.getConfig());
    b.setPixels(pixels, 0, sclWidth, (dstWidth - sclWidth) / 2, (dstHeight - sclHeight) / 2, sclWidth, sclHeight);
    return new BitmapDrawable(res, Bitmap.createBitmap(b));

}

Подходящо място за използване на scaleKeepingAspect() при настройване на фона на View е по време на събития за промяна на оформлението, така че фонът се актуализира правилно, когато границите на View се променят поради обработката на оформлението. За да направите това, ако приемем (a) че вашият View е идентифициран от myView във вашия xml оформление и (b) вашето фоново изображение е файл res/drawable/background.png, направете следното:

  1. Декларирайте вашия Activity като изпълнител на интерфейс View.OnLayoutChangeListener:

    public class MyActivity extends Activity implements View.OnLayoutChangeListener {
    
        ...
    
    }
    
  2. Регистрирайте своя Activity като слушател на промени в оформлението на вашия View:

    @Override public void onCreate(Bundle savedInstanceState) {
    
        ...
    
        myView = (View) findViewById(R.id.myView);
        myView.addOnLayoutChangeListener(this);
    
        ...
    
    }
    
  3. Открийте за View актуализации на оформлението в манипулатора за промяна на оформлението и актуализирайте неговия фон, когато е необходимо:

    @Override public void onLayoutChange (View v,
            int left, int top, int right, int bottom,
            int oldLeft, int oldTop, int oldRight, int oldBottom) {
    
        if (left == right || top == bottom || (
                left == oldLeft && top == oldTop &&
                right == oldRight && bottom == oldBottom))
            return;
    
        switch (v.getId()) {
    
            case R.id.myView:
    
                v.setBackground(
                    scaleKeepingAspect(
                        getResources(),
                        R.drawable.background,
                        v.getWidth(),
                        v.getHeight()));
                break;
    
        }
    
    }
    
person coolparadox    schedule 08.03.2016
comment
Това работи за мен, но резултатът беше наистина пикселиран. Хубав код обаче. - person arlomedia; 04.05.2018
comment
Добре е да се знае; изглежда createScaledBitmap, както се използва, предпочита скоростта вместо качеството. (В моите тестове не забелязах пикселизация; може би защото работех с изображения с висока разделителна способност по това време?) Както и да е, може да искате да промените параметъра на филтъра на true и да видите дали ще подобри резултатите ви. - person coolparadox; 05.05.2018