3

I am having problems when trying to use multiple horizontal images on different size devices. I have 7 separate Ratingbars, each using a different custom image. I have scaled all the images and placed them into their respective dpi drawable folders. The problem I'm having is that on the xlarge mdpi screen (top left of the image below), they fit perfectly, but when I'm viewing them on a smaller screens, the entire 7 ratingbars are too wide to fit within the bounds of the device, as shown below:

Screens Overview

I have followed the correct scaling for the images (based on the original base size of the mdpi) as stated in the following answer: https://stackoverflow.com/a/11581786/1634369

ldpi | mdpi | tvdpi | hdpi | xhdpi | xxhdpi | xxxhdpi
0.75 | 1    | 1.33  | 1.5  | 2     | 3      | 4

Based on the scaling above, the following are all my drawable folders, showing the dimensions of each of the images within their respective dpi folders (all based off the mdpi ratio):

Drawable Folder Structure

Then in order to display each of the Ratingbars, I set up a selector for each rating bar which will use a default grey image and a coloured image based on the rating value. The following shows the code I use to display each of the Ratingbars using the layout. NOTE: I have based the width and height from the base mdpi values.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <RatingBar            
        android:numStars="1"
        android:rating="1"
        android:stepSize="0.01"
        android:isIndicator="true"
        android:layout_width="75dp"
        android:layout_height="77dp"
        android:layout_gravity="bottom"
        android:gravity="bottom"                                                          
        android:progressDrawable="@drawable/selector_a" />

    <RatingBar            
        android:numStars="1"
        android:rating="0.5"
        android:stepSize="0.01"
        android:isIndicator="true"
        android:layout_width="73dp"
        android:layout_height="114dp"
        android:layout_gravity="bottom"
        android:gravity="bottom"                                                          
        android:progressDrawable="@drawable/selector_b" />

    <RatingBar            
        android:numStars="1"
        android:rating="0.5"
        android:stepSize="0.01"
        android:isIndicator="true"
        android:layout_width="63dp"
        android:layout_height="132dp"
        android:layout_gravity="bottom"
        android:gravity="bottom"                                                          
        android:progressDrawable="@drawable/selector_c" />

    <RatingBar            
        android:numStars="1"
        android:rating="0.5"
        android:stepSize="0.01"
        android:isIndicator="true"
        android:layout_width="79dp"
        android:layout_height="145dp"
        android:layout_gravity="bottom"
        android:gravity="bottom"                                                          
        android:progressDrawable="@drawable/selector_d" />

    <RatingBar            
        android:numStars="1"
        android:rating="0.5"
        android:stepSize="0.01"
        android:isIndicator="true"
        android:layout_width="79dp"
        android:layout_height="161dp"
        android:layout_gravity="bottom"
        android:gravity="bottom"                                                          
        android:progressDrawable="@drawable/selector_e" />

    <RatingBar            
        android:numStars="1"
        android:rating="0.5"
        android:stepSize="0.01"
        android:isIndicator="true"
        android:layout_width="108dp"
        android:layout_height="168dp"
        android:layout_gravity="bottom"
        android:gravity="bottom"                                                          
        android:progressDrawable="@drawable/selector_f" />

    <RatingBar            
        android:numStars="1"
        android:rating="0.5"
        android:stepSize="0.01"
        android:isIndicator="true"
        android:layout_width="147dp"
        android:layout_height="189dp"
        android:layout_gravity="bottom"
        android:gravity="bottom"                                                          
        android:progressDrawable="@drawable/selector_g" />

</LinearLayout>

Would anyone be able to help me ensure that no matter what screen the Ratingbars are displayed on and no matter what density the device is, it will always look the same (the way it looks on the 10.1 mdpi screen which is displayed top left of the image showing all the different screens? Any help would be greatly appreciated.

Thanks very much for taking the time to read through this question. I have placed the code on Github at the following location: https://github.com/gbayo1/RatingBarScalingIssue.git

Community
  • 1
  • 1
Graham Baitson
  • 580
  • 1
  • 11
  • 29
  • Does it really have to be exactly the same size? Does it matter if the rating bar is smaller on smaller devices? It should be smaller in the first place because you have less space on smaller devices. And is there a reason why it shouldn't be bigger on larger devices? They have the screen real estate, is there a reason to waste it? The point I'm trying to make is: Don't use fixed sizes for your `Views`. Use `wrap_content` or `match_parent` instead. There are a lot of things you can do to make your layout scale with the device. I can help you if you tell me how it should look like. – Xaver Kapeller May 16 '14 at 13:11
  • Thanks so much for you comment. No it doesn't have to look identical, just once it's legible and readable across the different screens. They don't have to be the same size or take up the same space, once they look reasonable that's all I'm looking for. When I was trying wrap content, the ratingbar wasn't showing the image correctly, that's why I was using the fixed sizes – Graham Baitson May 16 '14 at 13:16
  • What do you want it to look like? I could for example show you how you can distribute all the `RatingBars` equally across the screen. – Xaver Kapeller May 16 '14 at 13:20
  • Well it's suppose to show a progression. So it's for a game where there are 100 levels. The 1st RatingBar (A in the case above) may represent the first 5 levels. So if you complete level 1, you get 20% across A, level 2, 40% across A, etc... Then the 2nd RatingBar may represent the next 10 levels, 3rd Ratingbar, the next 22 levels, etc... So each RatingBar is not directly proportional in terms of what they represent. Once they are close together, look ok and the user can basically see and understand their progress throughout the course of the game – Graham Baitson May 16 '14 at 13:35

1 Answers1

0

You can equally distribute Views across the Screen by using a LinearLayout and layout_weight. For layout_weight to work you have to set layout_width to 0dp. That is normal don't let yourself be confused by that. If you wanted to equally distribute vertically you would have to set layout_height to 0dp. The layout_weight tells the LinearLayout how big a View should be compared to the other ones. In your case we want all Views to be equally distributed so they all need to have the same width. That's the reason why we assign all Views the same layout_weight - 1. You can do a lot more with layout_weight, for example if you had two Views and you wanted the left view to take up two thirds of the screen and the right one to take up one third of the screen you would assign the left one a layout_weight of 2, and the right one a layout_weight of 1.

I have modified your layout accordingly:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent">


    <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true">

        <RatingBar
                android:numStars="1"
                android:rating="1"
                android:stepSize="0.01"
                android:isIndicator="true"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"
                android:progressDrawable="@drawable/selector_a"/>

        <RatingBar
                android:numStars="1"
                android:rating="0.5"
                android:stepSize="0.01"
                android:isIndicator="true"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"
                android:progressDrawable="@drawable/selector_b"/>

        <RatingBar
                android:numStars="1"
                android:rating="0.5"
                android:stepSize="0.01"
                android:isIndicator="true"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"
                android:progressDrawable="@drawable/selector_c"/>

        <RatingBar
                android:numStars="1"
                android:rating="0.5"
                android:stepSize="0.01"
                android:isIndicator="true"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"
                android:progressDrawable="@drawable/selector_d"/>

        <RatingBar
                android:numStars="1"
                android:rating="0.5"
                android:stepSize="0.01"
                android:isIndicator="true"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"
                android:progressDrawable="@drawable/selector_e"/>

        <RatingBar
                android:numStars="1"
                android:rating="0.5"
                android:stepSize="0.01"
                android:isIndicator="true"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"
                android:progressDrawable="@drawable/selector_f"/>

        <RatingBar
                android:numStars="1"
                android:rating="0.5"
                android:stepSize="0.01"
                android:isIndicator="true"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="wrap_content"
                android:progressDrawable="@drawable/selector_g"/>

    </LinearLayout>
</RelativeLayout>

I assume there will eventually be something else in the layout besides the Ratingbars, that's why I already wrapped everything in a RelativeLayout. Because of your custom progressDrawable you might have a few issues with the height of the rating bars since I changed layout_height to wrap_content. If it doesn't work or you want them to have different heights you can set a fixed height for the RatingBars but leave the layout_width as it is.

On a side note: All those layout_gravity and gravity elements you had in your original layout were essentially useless. To align all the RatingBars to the bottom you would only have needed android:gravity="bottom" on the LinearLayout itself.

Xaver Kapeller
  • 49,491
  • 11
  • 98
  • 86
  • Thanks @XaverKapeller. In implementing your code, although it distributes the ratingbars evenly, it doesn't however show the full ratingbar, which was the original problem. When using your code, all the ratingbar images are cropped on the bottom and right hand sides. This now shows the top left hand corner of each of the ratingbars but missing the bottom half and the right half of all them (the bigger the ratingbar image, the more of the right side and bottom of the image I'm missing). – Graham Baitson May 16 '14 at 14:57
  • That means that the `progressDrawables` are bigger than the actual `View`. It seems the `RatingBar` doesn't take into account the size of its `progressDrawable` when performing `onMeasure()`. I tested it with the default `RatingBar` and it works perfectly. You could either adjust the images or subclass the `RatingBar` and fix `onMeasure()`. But considering how much time you invested in all of this I think it might be easier to create a custom view. The `RatingBar` doesn't seem to be at all suitable for your purposes. – Xaver Kapeller May 16 '14 at 15:02
  • I think the easiest way to create a custom view for this would be to subclass `ImageView` and programmatically create a layer list drawable with the background and the progress drawable and use a padding in the layer list to get the progress effect. The `ImageView` already takes care of all the `onMeasure()` business which would be the most complicated part. – Xaver Kapeller May 16 '14 at 15:05
  • Yeah see that was the problem I was facing, I just couldn't get the image which represents the RatingBar to scale properly. I was actually thinking of what you suggested in terms of using weights and images and then working out the progress by padding/overlaying the full image over the default. I'll have a go at this over the weekend, and let you know how I get on. This is the last part of an app I'm working on, so hopefully I can get something working, otherwise I'll just have to use text instead. But some sort of a rating scale would be much more engaging. Thanks for all your help. – Graham Baitson May 16 '14 at 15:12
  • Maybe there is just something wrong with your `progressDrawable`, could you show it to me? – Xaver Kapeller May 16 '14 at 15:27
  • Of course, no problem at all. I've placed all the code on GitHub - https://github.com/gbayo1/RatingBarScalingIssue. So you can see the progressDrawable XML files in the Drawable folder, and then all the full and default image drawables in each of the drawable density folders – Graham Baitson May 16 '14 at 15:29
  • Hi @XaverKapeller, were you able to view the progressDrawable? – Graham Baitson May 18 '14 at 13:54
  • Sorry, but I haven't gotten around to take a look yet. The progress drawables seem fine, I will try to replicate the problem. – Xaver Kapeller May 19 '14 at 20:26
  • Hi, I've literally just solved it using a clipdrawable, will post tomorrow when I get a chance. I'll see can it start a bounty to give to you for all your help. Thanks again, I really appreciate it. I'll let you know when I have the final product in the store :-) – Graham Baitson May 19 '14 at 22:29
  • I'm glad I could help! So you are now using a `ClipDrawable` in an `ImageView`? – Xaver Kapeller May 20 '14 at 08:33
  • Exactly. So I have the default image (ImageView with drawable image) displayed in the background and I overlay that with the complete clipdrawable (ImageView with clip xml as drawable). I then calculate the percentage across the complete image that I need to clip at every single level in the game. Works quite well. And because I have both images the exact same size within the same layout, any scaling will happen on both which will keep the background and foreground image aligned – Graham Baitson May 20 '14 at 13:10