Fixed aspect ratio View

后端 未结 9 1796
后悔当初
后悔当初 2020-12-04 10:50

How would I go implementing a fixed aspect ratio View? I\'d like to have items with 1:1 aspect ratio in a GridView. I think it\'s better to subclas

相关标签:
9条回答
  • 2020-12-04 11:31

    I recently made a helper class for this very problem and wrote a blog post about it.

    The meat of the code is as follows:

    /**
     * Measure with a specific aspect ratio<br />
     * <br />
     * @param widthMeasureSpec The width <tt>MeasureSpec</tt> passed in your <tt>View.onMeasure()</tt> method
     * @param heightMeasureSpec The height <tt>MeasureSpec</tt> passed in your <tt>View.onMeasure()</tt> method
     * @param aspectRatio The aspect ratio to calculate measurements in respect to 
     */
    public void measure(int widthMeasureSpec, int heightMeasureSpec, double aspectRatio) {
        int widthMode = MeasureSpec.getMode( widthMeasureSpec );
        int widthSize = widthMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : MeasureSpec.getSize( widthMeasureSpec );
        int heightMode = MeasureSpec.getMode( heightMeasureSpec );
        int heightSize = heightMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : MeasureSpec.getSize( heightMeasureSpec );
    
        if ( heightMode == MeasureSpec.EXACTLY && widthMode == MeasureSpec.EXACTLY ) {
            /* 
             * Possibility 1: Both width and height fixed
             */
            measuredWidth = widthSize;
            measuredHeight = heightSize;
    
        } else if ( heightMode == MeasureSpec.EXACTLY ) {
            /*
             * Possibility 2: Width dynamic, height fixed
             */
            measuredWidth = (int) Math.min( widthSize, heightSize * aspectRatio );
            measuredHeight = (int) (measuredWidth / aspectRatio);
    
        } else if ( widthMode == MeasureSpec.EXACTLY ) {
            /*
             * Possibility 3: Width fixed, height dynamic
             */
            measuredHeight = (int) Math.min( heightSize, widthSize / aspectRatio );
            measuredWidth = (int) (measuredHeight * aspectRatio);
    
        } else {
            /* 
             * Possibility 4: Both width and height dynamic
             */
            if ( widthSize > heightSize * aspectRatio ) {
                measuredHeight = heightSize;
                measuredWidth = (int)( measuredHeight * aspectRatio );
            } else {
                measuredWidth = widthSize;
                measuredHeight = (int) (measuredWidth / aspectRatio);
            }
    
        }
    }
    
    0 讨论(0)
  • 2020-12-04 11:33

    You may find third-party libraries. Instead of using them, use constraint layout. Below code sets the aspect ratio of ImageView as 16:9 regardless of the screen size and orientation.

    <androidx.constraintlayout.widget.ConstraintLayout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:scaleType="fitXY"
            android:src="@drawable/mat3"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.0"
            app:layout_constraintDimensionRatio="H,16:9"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    app:layout_constraintDimensionRatio="H,16:9". Here, height is set with respect to the width of the layout.

    For your question, set android:layout_width="match_parent" and use app:layout_constraintDimensionRatio="H,1:1" in your view.

    0 讨论(0)
  • 2020-12-04 11:35

    I implemented FixedAspectRatioFrameLayout, so I can reuse it and have any hosted view be with fixed aspect ratio:

    public class FixedAspectRatioFrameLayout extends FrameLayout
    {
        private int mAspectRatioWidth;
        private int mAspectRatioHeight;
    
        public FixedAspectRatioFrameLayout(Context context)
        {
            super(context);
        }
    
        public FixedAspectRatioFrameLayout(Context context, AttributeSet attrs)
        {
            super(context, attrs);
    
            init(context, attrs);
        }
    
        public FixedAspectRatioFrameLayout(Context context, AttributeSet attrs, int defStyle)
        {
            super(context, attrs, defStyle);
    
            init(context, attrs);
        }
    
        private void init(Context context, AttributeSet attrs)
        {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FixedAspectRatioFrameLayout);
    
            mAspectRatioWidth = a.getInt(R.styleable.FixedAspectRatioFrameLayout_aspectRatioWidth, 4);
            mAspectRatioHeight = a.getInt(R.styleable.FixedAspectRatioFrameLayout_aspectRatioHeight, 3);
    
            a.recycle();
        }
        // **overrides**
    
        @Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
        {
            int originalWidth = MeasureSpec.getSize(widthMeasureSpec);
    
            int originalHeight = MeasureSpec.getSize(heightMeasureSpec);
    
            int calculatedHeight = originalWidth * mAspectRatioHeight / mAspectRatioWidth;
    
            int finalWidth, finalHeight;
    
            if (calculatedHeight > originalHeight)
            {
                finalWidth = originalHeight * mAspectRatioWidth / mAspectRatioHeight; 
                finalHeight = originalHeight;
            }
            else
            {
                finalWidth = originalWidth;
                finalHeight = calculatedHeight;
            }
    
            super.onMeasure(
                    MeasureSpec.makeMeasureSpec(finalWidth, MeasureSpec.EXACTLY), 
                    MeasureSpec.makeMeasureSpec(finalHeight, MeasureSpec.EXACTLY));
        }
    }
    
    0 讨论(0)
  • 2020-12-04 11:35

    I've used and liked Jake Wharton's implementation of ImageView (should go similarly for other views), others might enjoy it too:

    AspectRatioImageView.java - ImageView that respects an aspect ratio applied to a specific measurement

    Nice thing it's styleable in xml already.

    0 讨论(0)
  • 2020-12-04 11:38

    Simply override onSizeChanged and calculate ratio there.

    Formula for aspect ratio is:

    newHeight =  original_height / original_width x new_width
    

    this would give you something like that:

     @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    
        //3:5 ratio
        float RATIO = 5/3;
        setLayoutParams(new LayoutParams((int)RATIO * w, w));
    
    }
    

    hope this helps!

    0 讨论(0)
  • 2020-12-04 11:42

    For new users, here's a better non-code solution :

    A new support library called Percent Support Library is available in Android SDK v22 (MinAPI is 7 me thinks, not sure) :

    src : android-developers.blogspot.in

    The Percent Support Library provides percentage based dimensions and margins and, new to this release, the ability to set a custom aspect ratio via app:aspectRatio. By setting only a single width or height and using aspectRatio, the PercentFrameLayout or PercentRelativeLayout will automatically adjust the other dimension so that the layout uses a set aspect ratio.

    To include add this to your build.gradle :

    compile 'com.android.support:percent:23.1.1'
    

    Now wrap your view (the one that needs to be square) with a PercentRelativeLayout / PercentFrameLayout :

    <android.support.percent.PercentRelativeLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content">
         <ImageView
             app:layout_aspectRatio="100%"
             app:layout_widthPercent="100%"/>
     </android.support.percent.PercentRelativeLayout>
    

    You can see an example here.

    0 讨论(0)
提交回复
热议问题