Android - Scaling my UI to fit all screen sizes?

2019-06-14 11:58发布

So far I have tried many ways of scaling images to screen sizes and none of them seem to work for me. I am still new to java and the idea of having a UI that fits to any screen size seems foreign to me. I am drawing bitmaps with the Canvas class. I have tried creating a scaledBitmap and have seen no difference. I want a simple main menu screen like this

enter image description here

This must be a really common issue because all apps would need to do this. I really could use some help understanding how this works. On some phones it looks alright but on others the images are either too large or off centered.

My code is below. If someone could please help me out here, this is for a school project due pretty soon :P Thanks guys!

public MainMenu(Context context) {
    super(context);

    titleBounds = new RectF();
    playBounds = new RectF();
    scoreBounds = new RectF();
    soundBounds = new RectF();
    creditBounds = new RectF();

    titlePaint = new Paint();
    playPaint = new Paint();

    play = BitmapFactory.decodeResource(getResources(), R.drawable.play);
    playGlow = BitmapFactory.decodeResource(getResources(), R.drawable.playglow);


    sound = BitmapFactory.decodeResource(getResources(), R.drawable.sound);
    soundGlow = BitmapFactory.decodeResource(getResources(), R.drawable.soundglow);


    // Get window size and save it to screenWidth and screenHeight.
    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();
    Point size = new Point();
    display.getSize(size); 
    screenWidth = size.x;
    screenHeight = size.y;

    playy = Bitmap.createScaledBitmap(play, convertToPx(screenWidth), convertToPx(screenHeight), true);


    playSound(context, R.raw.loop);


}

public int convertToPx(int dp) {
    // Get the screen's density scale
    final float scale = getResources().getDisplayMetrics().density;
    // Convert the dps to pixels, based on density scale
    return (int) (dp * scale + 0.5f);
}


@Override protected void onDraw(Canvas canvas) {


    ViewGroup parent = (ViewGroup)getParent();

    playBounds.set(screenWidth / 2 - play.getWidth() / 2, screenHeight * 1/3 - play.getHeight() / 2, playBounds.left + play.getWidth(), playBounds.top + play.getHeight());
    soundBounds.set(playBounds.centerX() - sound.getWidth() / 2, playBounds.bottom + 10, soundBounds.left + sound.getWidth(), soundBounds.top + sound.getHeight());


    if (playClick == false) canvas.drawBitmap(playy, null, playBounds, null);
    else canvas.drawBitmap(playGlow, null, playBounds, null);   

    if (soundClick == false) canvas.drawBitmap(sound, null, soundBounds, null);
    else canvas.drawBitmap(soundGlow, null, soundBounds, null); 

2条回答
叼着烟拽天下
2楼-- · 2019-06-14 12:00

Try this, on the image use scaleType="fitXY" with this it would span to the bound of the view.

查看更多
兄弟一词,经得起流年.
3楼-- · 2019-06-14 12:18

The screen sizes you've gotten from wm.getDefaultDisplay().getSize() are in pixels, not in dp. So you can just use them without converting them.

Something like...

WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size); 
screenWidth = size.x;
screenHeight = size.y;
playy = Bitmap.createScaledBitmap(play, screenWidth, screenHeight, true);

If you are trying to create a game, I would advise against using Canvas. Instead, you should use the faster GLSurfaceView. Better yet, use a cross platform game engine like Corona SDK or Unity or Cocos2dx. Development will be much easier that way, and the performance would be great too.

EDIT: What you ultimately want is a consistent scaling for all your assets without changing their aspect ratio. You need to write an helper method to figure out this ratio, then multiply this ratio to all your dimentions.

// The code below assume some variables are already populated by the above code
// I HAVE NOT TESTED THIS CODE. I'M JUST WRITING IT HERE OFF THE TOP OF MY HEAD.

private static float dimRatio;
private static float xOffset;
private static float yOffset;
private static Matrix transformMatrix;

private void initializeBitmapTransformMatrix {
    float ratio = (float)screenWidth / (float)gameWidth; //gameWidth is the width of your background image asset - so it's base size of the game
    if(gameHeight * ratio > screenHeight ){
        // the ratio we calculated above is for the width based scaling.
        // seems like if we use this ratio, the height will go off the boundary.
        // so use the height based ratio instead
        dimRatio = (float)screenHeight / (float)gameHeight;   
        xOffset = (screenWidth - (gameWidth * dimRatio)) / 2;
    } else {
        dimRatio = ratio;
        yOffset = (screenHeight - (gameHeight * dimRatio)) / 2;
    }

    // Now you have the ratio and offset values make a transform matrix

    transformMatrix = new Matrix();
    transformMatrix.setScale(dimRatio, dimRatio);
    transformMatrix.setTranslate(xOffset, yOffset);
}

Explanation: We have dimRatio to figure out the final pixel value of all your bitmap assets. When you position them, you have to add xOffset or yOffset accordingly to correctly offset the blank region of the screen, which results from the fact that device screen's aspect ratio is different than your game's intended aspect ratio. The matrix combines all that so we don't have to use these variables every time we draw. We can just use the matrix.

Draw Example:

canvas.drawBitmap(play, transformMatrix, null); // for background
// for a button
Matrix buttonMatrix = new Matrix(transformMatrix);
buttonMatrix.postTranslate(20f*dimRatio, 100f*dimRatio);
canvas.drawBitmap(button, buttonMatrix, null);

I just wrote this without really testing it. It's just for the idea. Hope you get the idea.

EDIT2: Realized xOffset and yOffset were reversed. Fixed it.

查看更多
登录 后发表回答