Android - Keep aspect ratio on background image on default Button

[亡魂溺海] 提交于 2019-12-11 09:19:21

问题


Is there any way to maintain the aspect ratio of a drawable when applied as background on a Button (cannot use ImageButton (need possibility for text on the Button), cannot use setCompoundDrawables(), because that also doesn't meet my requirements (I made my own class to support adding backgrounds while still retaining the original android default button, plus I need to set te background in the middle of the button, not left, right, top or bottom)).

I tried a solution with a ScaleDrawable, but either I'm doing something wrong or it doesn't work:

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

Then I use the ScaleDrawable as background, but it get stretched anyway.

I also tried a InsetDrawable, but again, either I'm doing something wrong or it doesn't work, expecting the first:

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

Then I use the InsetDrawable as background, but this also get stretched anyway.

Is there any way to apply a background but maintain aspect ratio on a Button? I can find plenty answers for ImageButtons, but sadly a Button does not have a setImageBitmap() or setScaleType() method.


回答1:


I faced the same problem recently for setting the background image of an ImageView. The solution I came up with works with any View, so it should cover Button as well.

The issue is that View always stretches its background to the full extent of View's width and height, no matter the scaling of the Drawable provided as background. As your report on failing to provide specialized Drawables suggests, this cannot be counteracted by any means of scaling an image prior to setting it as background because View would simply take the image dimensions, whatever they are, and scale them again independently to fill the View's area.

My solution takes a different approach based on the fact that if we can provide a background image with same aspect ratio as the View's, there would be no distortion.

Function scaleKeepingAspect() below takes an image resource and answers a BitmapDrawable that contains the original image scaled to a desired width and height. The scaling is performed in a way that the aspect ratio of the original image is kept, and eventual remaining spaces to fit the desired box are filled with transparent pixels. The original image is centered in the result image.

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

}

A proper place to make use of scaleKeepingAspect() in setting the background of a View is during layout change events, so the background is updated properly when the bounds of the View change due to layout processing. To do so, assuming (a) your View is identified by myView in your layout xml, and (b) your background image is file res/drawable/background.png, do the following:

  1. Declare your Activity as an implementor of View.OnLayoutChangeListener interface:

    public class MyActivity extends Activity implements View.OnLayoutChangeListener {
    
        ...
    
    }
    
  2. Register your Activity as a listener to layout changes of your View:

    @Override public void onCreate(Bundle savedInstanceState) {
    
        ...
    
        myView = (View) findViewById(R.id.myView);
        myView.addOnLayoutChangeListener(this);
    
        ...
    
    }
    
  3. Detect for View layout updates in the layout change handler, and update its background when needed:

    @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;
    
        }
    
    }
    


来源:https://stackoverflow.com/questions/13158305/android-keep-aspect-ratio-on-background-image-on-default-button

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!