7

In my application I have a scrolling view. During the life of my application I dynamically add new views to the scrolling view. I can add the views with little issue, however I cant get the dimensions right. Here is a sketch of my desired layout:

enter image description here

In the image I have a screen with dimensions Width by Height. It contains a HorizontalScrollView (or maybe a RecyclerView but I'm not sure how to use this one) which will grow when views are added to it. I want each item that gets added into the view to have a width equal to one fifth the screen-height and have the matching heights. I can make this work if I hardcode the width of the inflated view, however, my view wont be the same aspect ratio on all phones.

Note: I've tried using a PercentRelativeLayout, but I couldn't get it to work. Not sure how to solve this issue... any ideas?


Note on the bounty: I will accept answers that are in Java or C# (Xamarin)!! My project is in Xamarin so it would be nice if it works for that, or if it can be easily adapted to work in C#

flakes
  • 21,558
  • 8
  • 41
  • 88
  • It really depends on whether or not the sizes are dynamic or fixed. If they're fixed, you can code the dimensions in the layout and use just different dpi overrides to change the sizes of the dimensions. If you need the sizes to be dynamic, then I can answer that too, but see if you can manage it with the xml layout & dimensions first. Also, I would suggest that you look into using RecylerView. It's a little bit to wrap your head around, but offers a lot more flexibility and control. This is how you can do it horizontally: http://stackoverflow.com/a/28460399/1740059 – Justin Mitchell Nov 24 '16 at 03:31
  • @JustinMitchell the ratio will be constant, preferably defined in the xml as a percentage. I tried making a custom view that overrides the size, but I couldn't get it working without messing up the sizing for its child views. – flakes Nov 24 '16 at 03:36
  • Dont use percentages, use fixed dp values. However, you can still resize the view on the onCreateView method. Make sure you create an observer that listens for view tree modifications so that you can get the true view dimensions, otherwise the dimensions will be off. – Justin Mitchell Nov 24 '16 at 03:40
  • I would look into using `PercentRelativeLayout` or `PercentFrameLayout` from `android.support.percent`. `ConstraintLayout` with percent based guides may also be a possibility – Emmanuel Nov 27 '16 at 00:15
  • Are you using RecyclerView to load your child views? – Ajith Pandian Nov 29 '16 at 09:57
  • @AjithPandian I was not originally, but I'm starting to learn how to use them.. seems like the way to go. – flakes Nov 29 '16 at 17:35

3 Answers3

4

You don't want your view a fifth of the screen height, you want it to be a fifth of the parent view height. Or to be perfectly exact you want your width to be a fifth of the views height.

The difference is that using screen width / height might work on your device, but break on others. What would you do if someone opened your app with split screen?

However the definition of your view dimensions, the approach is the same: If you want a View that follows some rules you will have to create your own and measure your view accordingly.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
  int wantedWidth = availableHeight / 5;

  setMeasuredDimension(wantedWidth, availableHeight);
}

And that's basically it. You might want to read the proper documentation about MeasureSpec and measuring views in general. I also wrote a blog post about custom views covering some basics.

You can then just add your custom views to the LinearLayout backing the ScrollView and you're set.

Here's the full sample that will basically work, you might want to use something different than a frame layout.

public class FifthWidthView extends FrameLayout {
  public FifthWidthView(Context context) {
    super(context);
  }

  public FifthWidthView(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public FifthWidthView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
      int wantedWidth = availableHeight / 5;

      setMeasuredDimension(wantedWidth, availableHeight);
    }
}
David Medenjak
  • 33,993
  • 14
  • 106
  • 134
  • Will try this tomorrow and get back to you :) I'll probably make a layout attribute to set the exact percentage. Will this work with a nested view? – flakes Nov 27 '16 at 00:38
  • @flakes With this approach you should rather use view attributes to modify the ratio. Just wrap the views you add to the LinearLayout(in the scrollview) with the given layout. To use layout attributes you'd have to implement your own ViewGroup and do layouting as well for the attributes to take effect, which is another (also valid) approach, but a little more effort) – David Medenjak Nov 28 '16 at 08:27
2

let's suppose you can use a ViewPager. if so, you'll need to override the getPageWidth() method. This method tells the viewpager how wide each view inside it must be, from 0 (nothing) to 1 (use all).

We will calculate a dynamic size if possible, taking in mind we want to use just a bit more of a third of the screen. Let's suppose you want to use the whole screen for collections with only one element, half the screen for collections with two elements, and the dynamic calculation for every else.

@Override
public float getPageWidth(int position) {

        try{
            Paint paint = new Paint();
            return 0.3f+paint.measureText(data.get(position).getSize())/ DeviceFunctions.giveScreenResolution((Activity) context).x;
        }
        catch(Exception e){

            int size = data.getValues().size();
            switch(size){

                case 1: return 1.f;
                case 2: return 0.5f;
                case 3: return 0.33f;
                default: return 0.33f;
            }
        }
    }

we assume :

-the data collection allows you to detect the intended width of your view. You can change this with any reference you want. Or you can give a fixed size based on the view's position:

@Override
public float getPageWidth(int position) {

            int size = data.getValues().size();
                switch(size){

                    case 1: return 0.35f;
                    case 2: return 0.35f;
                    case 3: return 0.30f;
                    default: return 0.33f;
                }
        }

And for getting the screen resolution:

@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
    public static Point giveScreenResolution(Activity activity){

        Display display = activity.getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        return size;
    }
Fco P.
  • 2,486
  • 17
  • 20
1

In one of my application used this code and working perfectly fine.

 public static int getScreenHeight(Activity context)
        {
            Display display = context.getWindowManager().getDefaultDisplay();
            Point size = new Point();
            display.getSize(size);
            int height = size.y;
            return height;
        }

In my Activity i am using bellow code

//Container layout may be child of HorizontalScrollView
LinearLayout parent = (LinearLayout)findViewById(R.id.splash_screen_logo);
//Child layout to be added 
View mychildview = getLayoutInflater().inflate((R.layout.childView);
mychildview.getLayoutParams().width = Utils.getScreenHeight(this)/5;
mychildview.getLayoutParams().height = Utils.getScreenHeight(this); 
parent.addview(mychildview)
Swapnil
  • 2,409
  • 4
  • 26
  • 48