Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Inflate an expanding view with a fixed aspect ratio view

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#

like image 795
flakes Avatar asked Nov 24 '16 03:11

flakes


3 Answers

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);
    }
}
like image 121
David Medenjak Avatar answered Oct 13 '22 22:10

David Medenjak


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;
    }
like image 2
Fco P. Avatar answered Oct 14 '22 00:10

Fco P.


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)
like image 1
Swapnil Avatar answered Oct 13 '22 23:10

Swapnil