Android: Tôi không thể có ViewPager WRAP_CONTENT


258

Tôi đã thiết lập một ViewPager đơn giản có ImageView với chiều cao 200dp trên mỗi trang.

Đây là máy nhắn tin của tôi:

pager = new ViewPager(this);
pager.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
pager.setBackgroundColor(Color.WHITE);
pager.setOnPageChangeListener(listener);
layout.addView(pager);

Mặc dù chiều cao được đặt là quấn_content, máy nhắn tin luôn lấp đầy màn hình mặc dù số lần xem hình ảnh chỉ là 200dp. Tôi đã cố gắng thay thế chiều cao của máy nhắn tin bằng "200" nhưng điều đó mang lại cho tôi kết quả khác nhau với nhiều độ phân giải. Tôi không thể thêm "dp" vào giá trị đó. Làm cách nào để thêm 200dp vào bố cục của trình duyệt?


Câu trả lời:


408

Ghi đè lên Niềm vui của bạn ViewPagernhư sau sẽ khiến nó có được chiều cao của đứa trẻ lớn nhất hiện tại.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int height = 0;
    for(int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        int h = child.getMeasuredHeight();
        if(h > height) height = h;
    }

    if (height != 0) {
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    }

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

24
Điều này gần nhất với những gì tôi cần, nhưng có hai điều cần thêm: 1. ViewPager chỉ thay đổi kích thước lớn nhất của những đứa trẻ thực sự của nó, đó là, chỉ có mục hiện có thể nhìn thấy và những thứ liền kề trực tiếp. Gọi setPackscreenPageLimit (tổng số trẻ em) trên ViewPager sẽ giải quyết vấn đề này và kết quả là ViewPager có kích thước được đặt thành lớn nhất trong tất cả các mục của nó và không bao giờ thay đổi kích thước. 2. WebView có một số vấn đề lạ khi cố gắng đo lường chúng. Gọi requestLayout () trên WebView sau khi tải một cái gì đó giải quyết điều đó.
0101100101

3
Chỉ có một vấn đề nhỏ mà tôi sẽ khắc phục: nếu viewPager có khả năng hiển thị thành Gone và bạn đặt nó thành hiển thị, onMeasure được gọi trước khi đoạn của nó được tạo. Vì vậy, nó sẽ có chiều cao bằng 0. Nếu ai có ý tưởng, anh ấy hoan nghênh. Tôi nghĩ rằng tôi sẽ thực hiện một cuộc gọi lại khi đoạn được tạo
edoardotognoni

4
Điều này sẽ không hoạt động nếu bạn có chế độ xem trang trí cho trẻ em - điều này là do ViewPager.onMeasure () đo lường các chế độ xem trang trí và phân bổ không gian cho chúng trước, sau đó cung cấp phần còn lại của không gian cho trẻ em không trang trí. Tuy nhiên, đây là giải pháp ít chính xác nhất ở đây vì vậy tôi đã nâng cao;)
Benjamin Dobell

3
Tôi tiếp tục trở lại đây mỗi khi tôi sử dụng một ViewPager
ono

7
getChildCount () có thể trả về 0 trong khi bạn đã thực hiện setAd CHƯƠNG () trên ViewPager! Cuộc gọi dân cư thực tế () (tạo ra các khung nhìn) xảy ra bên trong super.onMeasure (widthMeasureSpec, heightMeasureSpec); gọi. Việc gọi thêm super.onMeasure () khi bắt đầu hàm này đã thực hiện thủ thuật. Đồng thời kiểm tra stackoverflow.com/questions/38492210/
Mạnh

106

Một giải pháp chung chung hơn là wrap_contentchỉ để làm việc.

Tôi đã mở rộng ViewPagerđể ghi đè onMeasure(). Chiều cao được bao bọc xung quanh khung nhìn con đầu tiên. Điều này có thể dẫn đến kết quả không mong muốn nếu trẻ em xem không chính xác cùng chiều cao. Vì vậy, lớp có thể dễ dàng được mở rộng để giả sử kích thước của chế độ xem / trang hiện tại. Nhưng tôi không cần điều đó.

Bạn có thể sử dụng ViewPager này trong các bố cục XML giống như ViewPager ban đầu:

<view
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    class="de.cybergen.ui.layout.WrapContentHeightViewPager"
    android:id="@+id/wrapContentHeightViewPager"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"/>

Ưu điểm: Cách tiếp cận này cho phép sử dụng ViewPager trong mọi bố cục bao gồm RelativeLayout để phủ các phần tử ui khác.

Một nhược điểm vẫn còn: Nếu bạn muốn sử dụng lề, bạn phải tạo hai bố cục lồng nhau và cung cấp cho bên trong một lề mong muốn.

Đây là mã:

public class WrapContentHeightViewPager extends ViewPager {

    /**
     * Constructor
     *
     * @param context the context
     */
    public WrapContentHeightViewPager(Context context) {
        super(context);
    }

    /**
     * Constructor
     *
     * @param context the context
     * @param attrs the attribute set
     */
    public WrapContentHeightViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // find the first child view
        View view = getChildAt(0);
        if (view != null) {
            // measure the first child view with the specified measure spec
            view.measure(widthMeasureSpec, heightMeasureSpec);
        }

        setMeasuredDimension(getMeasuredWidth(), measureHeight(heightMeasureSpec, view));
    }

    /**
     * Determines the height of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @param view the base view with already measured height
     *
     * @return The height of the view, honoring constraints from measureSpec
     */
    private int measureHeight(int measureSpec, View view) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            // set the height from the base view if available
            if (view != null) {
                result = view.getMeasuredHeight();
            }
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

}

34
Bất cứ ai khác có trang trống bên cạnh mục hiện tại khi trình xem bị hủy và mở lại?
Zyoo

1
Tôi cũng có những trang trống.
aeren

10
Bạn chỉ cần hợp nhất hai câu trả lời hàng đầu của câu hỏi này như được mô tả trong blog của tôi: pristalovpavel.wordpress.com/2014/12/26/iêu
anil

4
Chỉ cần thay thế mã của phương pháp 'onMeasure' bằng câu trả lời được đưa ra bởi 'Daniel López Lacalle'.
Yog Guru

1
Tuyệt quá..! Làm việc cho tôi .. @cybergen Cảm ơn rất nhiều bạn đã cứu ngày của tôi ..!
Dnyanesh M

59

Tôi dựa trên câu trả lời của mình về Daniel López Lacalle và bài đăng này http://www.henning.ms/2013/09/09/viewpager-that-simply-dont-measure-up/ . Vấn đề với câu trả lời của Daniel là trong một số trường hợp, các con tôi có chiều cao bằng không. Giải pháp là không may đo hai lần.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int mode = MeasureSpec.getMode(heightMeasureSpec);
    // Unspecified means that the ViewPager is in a ScrollView WRAP_CONTENT.
    // At Most means that the ViewPager is not in a ScrollView WRAP_CONTENT.
    if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
        // super has to be called in the beginning so the child views can be initialized.
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int height = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            int h = child.getMeasuredHeight();
            if (h > height) height = h;
        }
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    }
    // super has to be called again so the new specs are treated as exact measurements
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Điều này cũng cho phép bạn đặt chiều cao trên ViewPager nếu bạn muốn hoặc chỉ quấn_content.


Tôi đã có cùng một vấn đề và giải quyết nó với câu trả lời của bạn, cảm ơn. Nhưng bất kỳ lời giải thích là tại sao?
Bart Burg

Tôi nghĩ rằng họ không có ý định bọc nội dung để được hỗ trợ vì tôi không nghĩ họ nghĩ đó là trường hợp sử dụng bình thường. Để hỗ trợ nó, chúng tôi phải đo lại bản thân sau khi con cái được đo để chúng tôi có thể bọc nội dung.
MinceMan

Tại sao hình ảnh trong ViewPager này thực tế ngắn hơn hình ảnh trong ImageView sử dụng giống scaleTypevà tương tự, layout_width=match_parentcũng như layout_height=wrap_content? Có 20dp mất tích ở đó.
Cá mập

Shark, tôi thực sự không chắc chắn. Điều đó có thể có liên quan đến những gì loại quy mô của bạn đang thực sự làm. Có thể muốn thử thiết lập một chiều cao.
MinceMan

1
TÔI KHÔNG THỂ NÓI ĐƯỢC! Tôi đã dành 2 ngày để dán lại trình xem tùy chỉnh của mình và gặp vấn đề, khi chế độ xem ban đầu của tôi không hiển thị và tôi không thể hiểu tại sao! // super has to be called in the beginning so the child views can be initialized.<----- ĐÓ là lý do, phải gọi nó khi bắt đầu và khi kết thúc chức năng onMeasure. Yippiii, ảo fives cao trên tôi ngày hôm nay!
Starwave

37

Tôi vừa trả lời một câu hỏi rất giống nhau về vấn đề này, và tình cờ tìm thấy điều này khi tìm kiếm một liên kết để sao lưu các yêu cầu của tôi, thật may mắn cho bạn :)

Câu trả lời khác của tôi:
ViewPager không hỗ trợ wrap_contentvì nó (thường) không bao giờ có tất cả các con của nó được tải cùng một lúc và do đó không thể có kích thước phù hợp (tùy chọn sẽ có một máy nhắn tin thay đổi kích thước mỗi khi bạn chuyển đổi trang).

Tuy nhiên, bạn có thể đặt kích thước chính xác (ví dụ 150dp) và cũng match_parenthoạt động.
Bạn cũng có thể sửa đổi kích thước động từ mã của mình bằng cách thay đổi height-attribution trong nó LayoutParams.

Đối với nhu cầu của bạn, bạn có thể tạo ViewPager trong tệp xml của riêng mình, với layout_height được đặt thành 200dp, sau đó trong mã của bạn, thay vì tạo ViewPager mới từ đầu, bạn có thể thổi phồng tệp xml đó:

LayoutInflater inflater = context.getLayoutInflater();
inflater.inflate(R.layout.viewpagerxml, layout, true);

3
Câu trả lời hay, loại khó chịu mà hành vi mặc định là "làm điều gì đó hơi khó hiểu". Cảm ơn đã giải thích.
Chris Vandevelde

8
@ChrisVandevelde đây dường như là một người thuê chung của một số thư viện Android. Ngay khi bạn học các nguyên tắc cơ bản, bạn nhận ra không có gì theo chúng
CQM

1
Nhưng @Jave, tại sao người xem không thể điều chỉnh chiều cao mỗi khi con của nó được tải?
Diffy

@CQM quả thật! Thư viện ViewPagerTheicator có cùng một vấn đề với layout_heightcài đặt wrap_content, nhưng thậm chí còn tệ hơn khi cách giải quyết đơn giản để thiết lập nó thành một lượng cố định không hoạt động.
Giulio Piancastelli

20

Sử dụng câu trả lời của Daniel López Localle , tôi đã tạo ra lớp này trong Kotlin. Hy vọng nó giúp bạn tiết kiệm nhiều thời gian hơn

class DynamicHeightViewPager @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : ViewPager(context, attrs) {

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    var heightMeasureSpec = heightMeasureSpec

    var height = 0
    for (i in 0 until childCount) {
        val child = getChildAt(i)
        child.measure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED))
        val h = child.measuredHeight
        if (h > height) height = h
    }

    if (height != 0) {
        heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
    }

    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}}

16

Tôi đã phải đối mặt với vấn đề này trong một số dự án và chưa bao giờ có một giải pháp hoàn chỉnh. Vì vậy, tôi đã tạo một dự án github WrapContentViewPager như một sự thay thế tại chỗ cho ViewPager.

https://github.com/rnevet/WCViewPager

Giải pháp được lấy cảm hứng từ một số câu trả lời ở đây, nhưng cải thiện về:

  • Tự động thay đổi chiều cao ViewPager theo Chế độ xem hiện tại bao gồm cả trong khi cuộn.
  • Xem xét chiều cao của các chế độ xem "trang trí" như PagerTabStrip.
  • Đưa vào xem xét tất cả các đệm.

Cập nhật cho thư viện hỗ trợ phiên bản 24 đã phá vỡ triển khai trước đó.


@mvai bạn có thể mở một vấn đề, hoặc rẽ nhánh và sửa đổi ứng dụng mẫu không?
Raanan

1
Tôi phát hiện ra RecyclerView cũng có một số vấn đề về bọc_content; nó hoạt động nếu bạn sử dụng một linearLayoutManager tùy chỉnh, như thế này . Vì vậy, không có gì sai với thư viện của bạn.
natario

1
Những gì vẫn phải sửa là việc sử dụng nó với FragmentStatePagerAdOG. Có vẻ như nó đang đo trẻ em trước khi các mảnh vỡ được đặt ra, do đó cho chiều cao nhỏ hơn. Điều làm việc cho tôi là câu trả lời của @logan, mặc dù tôi vẫn đang làm việc với nó. Bạn có thể muốn thử hợp nhất cách tiếp cận đó vào thư viện của bạn. Tôi không quen với github, xin lỗi.
natario

Cảm ơn, tôi sẽ xem xét điều đó.
Raanan

1
Đối với bất kỳ ai thắc mắc làm thế nào để làm cho điều này hoạt động với FragmentPagerAd CHƯƠNG, hãy làm cho Bộ điều hợp của bạn triển khai ObjectAtPocationInterface bằng cách giữ một danh sách các Đoạn trong nội bộ, để nó có thể trả về Đoạn tương ứng từ phương thức getObjectAtPocation.
Pablo

15

Tôi chỉ va vào vấn đề tương tự. Tôi đã có ViewPager và tôi muốn hiển thị một quảng cáo tại nút của nó. Giải pháp tôi tìm thấy là đưa máy nhắn tin vào RelativeView và đặt layout_above thành id chế độ xem mà tôi muốn xem bên dưới nó. Điều đó đã làm việc cho tôi.

đây là XML bố trí của tôi:

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

    <LinearLayout
        android:id="@+id/AdLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical" >
    </LinearLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/mainpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/AdLayout" >
    </android.support.v4.view.ViewPager>
</RelativeLayout>

4
chỉ để tham khảo, bạn không cần xmlns: android = " schemas.android.com/apk/res/android " trong cả hai, chỉ trong cái đầu tiên.
Martin Marconcini

2
Vấn đề của bạn không giống nhau chút nào. Bố cục của bạn hoạt động tốt với ViewPager được đặt thành match_parent - OP có một tình huống mà anh ấy muốn ViewPager bao bọc nội dung của nó.
k2col

9

Tôi cũng gặp phải vấn đề này, nhưng trong trường hợp của tôi, tôi đã có một FragmentPagerAdaptertrang đang cung cấp các ViewPagertrang của nó. Vấn đề tôi đã là onMeasure()của ViewPagerđược gọi trước khi bất kỳ Fragmentsđã được tạo ra (và do đó không thể kích thước riêng của mình một cách chính xác).

Sau một chút thử nghiệm và lỗi, tôi thấy rằng finishUpdate()phương thức của FragmentPagerAd CHƯƠNG được gọi sau khi Fragmentsđã được khởi tạo (từ instantiateItem()trong FragmentPagerAdapter) và cả sau / trong quá trình cuộn trang. Tôi đã tạo một giao diện nhỏ:

public interface AdapterFinishUpdateCallbacks
{
    void onFinishUpdate();
}

mà tôi chuyển vào FragmentPagerAdaptervà gọi:

@Override
public void finishUpdate(ViewGroup container)
{
    super.finishUpdate(container);

    if (this.listener != null)
    {
        this.listener.onFinishUpdate();
    }
}

do đó cho phép tôi để gọi setVariableHeight()trên tôi CustomViewPagerthực hiện:

public void setVariableHeight()
{
    // super.measure() calls finishUpdate() in adapter, so need this to stop infinite loop
    if (!this.isSettingHeight)
    {
        this.isSettingHeight = true;

        int maxChildHeight = 0;
        int widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
        for (int i = 0; i < getChildCount(); i++)
        {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.UNSPECIFIED));
            maxChildHeight = child.getMeasuredHeight() > maxChildHeight ? child.getMeasuredHeight() : maxChildHeight;
        }

        int height = maxChildHeight + getPaddingTop() + getPaddingBottom();
        int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

        super.measure(widthMeasureSpec, heightMeasureSpec);
        requestLayout();

        this.isSettingHeight = false;
    }
}

Tôi không chắc đó là cách tiếp cận tốt nhất, sẽ thích bình luận nếu bạn nghĩ nó tốt / xấu / xấu, nhưng có vẻ như nó hoạt động khá tốt trong quá trình thực hiện của tôi :)

Hy vọng điều này sẽ giúp ai đó ngoài kia!

EDIT: Tôi đã quên thêm một requestLayout()sau khi gọi super.measure()(nếu không nó sẽ không vẽ lại khung nhìn).

Tôi cũng quên thêm phần đệm của cha mẹ vào chiều cao cuối cùng.

Tôi cũng bỏ việc giữ các biện pháp đo chiều rộng / chiều cao ban đầu để ủng hộ việc tạo một cái mới theo yêu cầu. Đã cập nhật mã cho phù hợp.

Một vấn đề khác mà tôi gặp phải là nó sẽ không có kích thước chính xác trong một ScrollViewvà tìm ra thủ phạm là đo đứa trẻ MeasureSpec.EXACTLYthay vì MeasureSpec.UNSPECIFIED. Cập nhật để phản ánh điều này.

Những thay đổi này đã được thêm vào mã. Bạn có thể kiểm tra lịch sử để xem các phiên bản cũ (không chính xác) nếu bạn muốn.


Tại sao bạn không thêm những người bạn quên mã.
hasan

@hasan Mình đã làm rồi, xin lỗi vì có nhầm lẫn! Sẽ cập nhật câu trả lời để nói điều đó
logan

Tuyệt vời! Vui vì nó đã giúp :)
logan

8

Một giải pháp khác là cập nhật ViewPagerchiều cao theo chiều cao trang hiện tại PagerAdapter. Giả sử rằng bạn đang tạo các ViewPagertrang của mình theo cách này:

@Override
public Object instantiateItem(ViewGroup container, int position) {
  PageInfo item = mPages.get(position);
  item.mImageView = new CustomImageView(container.getContext());
  item.mImageView.setImageDrawable(item.mDrawable);
  container.addView(item.mImageView, 0);
  return item;
}

Trường hợp mPagesdanh sách nội bộ của các PageInfocấu trúc được thêm động vào PagerAdapterCustomImageViewchỉ là thường xuyên ImageViewvới onMeasure()phương thức overriden đặt chiều cao của nó theo chiều rộng được chỉ định và giữ tỷ lệ khung hình ảnh.

Bạn có thể buộc ViewPagerchiều cao trong setPrimaryItem()phương pháp:

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
  super.setPrimaryItem(container, position, object);

  PageInfo item = (PageInfo) object;
  ViewPager pager = (ViewPager) container;
  int width = item.mImageView.getMeasuredWidth();
  int height = item.mImageView.getMeasuredHeight();
  pager.setLayoutParams(new FrameLayout.LayoutParams(width, Math.max(height, 1)));
}

Lưu ý Math.max(height, 1). Điều đó sửa lỗi gây phiền nhiễu ViewPagerkhông cập nhật trang được hiển thị (hiển thị trống), khi trang trước đó có chiều cao bằng không (nghĩa là không thể rút ra trong CustomImageView), mỗi lần vuốt qua lại giữa hai trang.


Dường như với tôi con đường đúng để đi theo nhưng tôi cần phải quảng cáo item.mImageView.measure(..)để có được kích thước chính xác trong getMeasuredXXX()các phương thức.
Gianluca P.

6

Khi sử dụng nội dung tĩnh bên trong chế độ xem và bạn không muốn hoạt hình ưa thích, bạn có thể sử dụng máy nhắn tin xem sau

public class HeightWrappingViewPager extends ViewPager {

  public HeightWrappingViewPager(Context context) {
    super(context);
  }

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

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)   {
      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
      View firstChild = getChildAt(0);
      firstChild.measure(widthMeasureSpec, heightMeasureSpec);
      super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(firstChild.getMeasuredHeight(), MeasureSpec.EXACTLY));
  }
}

Điều này hoạt động tốt. Tôi mở rộng nó bằng cách vòng qua những đứa trẻ và lấy cái có chiều cao tối đa.
Javier Mendonça

Hoạt động tốt ngay cả dưới chế độ xem tái chế
kanudo

Tôi nhận được ngoại lệ này- java.lang.NullPulumException: Cố gắng gọi phương thức ảo 'void android.view.View.measure (int, int)' trên một tham chiếu đối tượng null
PJ2104

Nhưng lấy yếu tố đầu tiên có thể là sai.
Tobias Reich

4
public CustomPager (Context context) {
    super(context);
}

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

int getMeasureExactly(View child, int widthMeasureSpec) {
    child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    int height = child.getMeasuredHeight();
    return MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;

    final View tab = getChildAt(0);
    if (tab == null) {
        return;
    }

    int width = getMeasuredWidth();
    if (wrapHeight) {
        // Keep the current measured width.
        widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
    }
    Fragment fragment = ((Fragment) getAdapter().instantiateItem(this, getCurrentItem()));
    heightMeasureSpec = getMeasureExactly(fragment.getView(), widthMeasureSpec);

    //Log.i(Constants.TAG, "item :" + getCurrentItem() + "|height" + heightMeasureSpec);
    // super has to be called again so the new specs are treated as
    // exact measurements.
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

4

Từ mã nguồn của ứng dụng Popcorn thời gian, tôi đã tìm thấy giải pháp này tự động điều chỉnh kích thước của trình xem với hình ảnh động đẹp tùy thuộc vào kích thước của đứa trẻ hiện tại.

https://git.popcorntime.io/popcorntime/android/blob/5934f8d0c8fed39af213af4512272d12d2efb6a6/mobile/src/main/java/pct/droid/wid/

public class WrappingViewPager extends ViewPager {

    private Boolean mAnimStarted = false;

    public WrappingViewPager(Context context) {
        super(context);
    }

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

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        if(!mAnimStarted && null != getAdapter()) {
            int height = 0;
            View child = ((FragmentPagerAdapter) getAdapter()).getItem(getCurrentItem()).getView();
            if (child != null) {
                child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                height = child.getMeasuredHeight();
                if (VersionUtils.isJellyBean() && height < getMinimumHeight()) {
                    height = getMinimumHeight();
                }
            }

            // Not the best place to put this animation, but it works pretty good.
            int newHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
            if (getLayoutParams().height != 0 && heightMeasureSpec != newHeight) {
                    final int targetHeight = height;
                    final int currentHeight = getLayoutParams().height;
                    final int heightChange = targetHeight - currentHeight;

                    Animation a = new Animation() {
                        @Override
                        protected void applyTransformation(float interpolatedTime, Transformation t) {
                            if (interpolatedTime >= 1) {
                                getLayoutParams().height = targetHeight;
                            } else {
                                int stepHeight = (int) (heightChange * interpolatedTime);
                                getLayoutParams().height = currentHeight + stepHeight;
                            }
                            requestLayout();
                        }

                        @Override
                        public boolean willChangeBounds() {
                            return true;
                        }
                    };

                    a.setAnimationListener(new Animation.AnimationListener() {
                        @Override
                        public void onAnimationStart(Animation animation) {
                            mAnimStarted = true;
                        }

                        @Override
                        public void onAnimationEnd(Animation animation) {
                            mAnimStarted = false;
                        }

                        @Override
                        public void onAnimationRepeat(Animation animation) {
                        }
                    });

                    a.setDuration(1000);
                    startAnimation(a);
                    mAnimStarted = true;
            } else {
                heightMeasureSpec = newHeight;
            }
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

4

Trong trường hợp bạn cần ViewPager điều chỉnh kích thước của nó cho mọi đứa trẻ , không chỉ lớn nhất, tôi đã viết một đoạn mã thực hiện nó. Lưu ý rằng không có hình ảnh động khi thay đổi đó (không cần thiết trong trường hợp của tôi)

android: cờ minHeight cũng được hỗ trợ.

public class ChildWrappingAdjustableViewPager extends ViewPager {
    List<Integer> childHeights = new ArrayList<>(getChildCount());
    int minHeight = 0;
    int currentPos = 0;

    public ChildWrappingAdjustableViewPager(@NonNull Context context) {
        super(context);
        setOnPageChangeListener();
    }

    public ChildWrappingAdjustableViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        obtainMinHeightAttribute(context, attrs);
        setOnPageChangeListener();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {            
        childHeights.clear();

        //calculate child views
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            int h = child.getMeasuredHeight();
            if (h < minHeight) {
                h = minHeight;
            }
            childHeights.add(i, h);
        }

        if (childHeights.size() - 1 >= currentPos) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeights.get(currentPos), MeasureSpec.EXACTLY);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    private void obtainMinHeightAttribute(@NonNull Context context, @Nullable AttributeSet attrs) {
        int[] heightAttr = new int[]{android.R.attr.minHeight};
        TypedArray typedArray = context.obtainStyledAttributes(attrs, heightAttr);
        minHeight = typedArray.getDimensionPixelOffset(0, -666);
        typedArray.recycle();
    }

    private void setOnPageChangeListener() {
        this.addOnPageChangeListener(new SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                currentPos = position;

                ViewGroup.LayoutParams layoutParams = ChildWrappingAdjustableViewPager.this.getLayoutParams();
                layoutParams.height = childHeights.get(position);
                ChildWrappingAdjustableViewPager.this.setLayoutParams(layoutParams);
                ChildWrappingAdjustableViewPager.this.invalidate();
            }
        });
    }
}

Vì vậy, bộ chuyển đổi này có một vấn đề lớn khi thay đổi các mục trong bộ chuyển đổi
jobbert

bạn có thể làm rõ tuyên bố của bạn?
Phatee P

Mã này có thể gây ra nullpoint vì không phải mọi đứa trẻ đều được tính khi bắt đầu. Hãy thử bố trí tab và cuộn từ 1 đến 5 hoặc mã thông minh và bạn sẽ thấy nó.
jobbert

4

Cải thiện câu trả lời của Daniel López Lacalle , được viết lại trong Kotlin :

class MyViewPager(context: Context, attrs: AttributeSet): ViewPager(context, attrs) {
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val zeroHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)

        val maxHeight = children
            .map { it.measure(widthMeasureSpec, zeroHeight); it.measuredHeight }
            .max() ?: 0

        if (maxHeight > 0) {
            val maxHeightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY)
            super.onMeasure(widthMeasureSpec, maxHeightSpec)
            return
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }
}

3

Tôi đã gặp phải vấn đề tương tự và tôi cũng phải làm cho ViewPager bao quanh nội dung của nó khi người dùng cuộn giữa các trang. Sử dụng câu trả lời trên của cybergen, tôi đã định nghĩa phương thức onMeasure như sau:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (getCurrentItem() < getChildCount()) {
        View child = getChildAt(getCurrentItem());
        if (child.getVisibility() != GONE) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
                    MeasureSpec.UNSPECIFIED);
            child.measure(widthMeasureSpec, heightMeasureSpec);
        }

        setMeasuredDimension(getMeasuredWidth(), measureHeight(heightMeasureSpec, getChildAt(getCurrentItem())));            
    }
}

Bằng cách này, phương thức onMeasure đặt chiều cao của trang hiện tại được hiển thị bởi ViewPager.


Chỉ có nội dung có chiều cao nhất mới xuất hiện cùng với câu trả lời của bạn, nội dung khác bị biến mất ...
Blaze Tama

2

Không có gì gợi ý ở trên làm việc cho tôi. Trường hợp sử dụng của tôi có 4 ViewPager tùy chỉnh ScrollView. Đầu của chúng được đo dựa trên tỷ lệ khung hình và phần còn lại chỉ có layout_height=wrap_content. Tôi đã thử các giải pháp của Cybergen , Daniel López Lacalle . Không ai trong số họ làm việc đầy đủ cho tôi.

Tôi đoán tại sao cybergen không hoạt động trên trang> 1 là vì nó tính toán chiều cao của máy nhắn tin dựa trên trang 1, nó bị ẩn nếu bạn cuộn thêm.

Cả hai đề nghị của cybergenDaniel López Lacalle đều có hành vi kỳ lạ trong trường hợp của tôi: 2 trong số 3 được tải ok và 1 chiều cao ngẫu nhiên là 0. Xuất hiện onMeasuretrước khi trẻ em được đưa vào. Vì vậy, tôi đã đưa ra một hỗn hợp của 2 câu trả lời + bản sửa lỗi của riêng tôi:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
        // find the first child view
        View view = getChildAt(0);
        if (view != null) {
            // measure the first child view with the specified measure spec
            view.measure(widthMeasureSpec, heightMeasureSpec);
            int h = view.getMeasuredHeight();
            setMeasuredDimension(getMeasuredWidth(), h);
            //do not recalculate height anymore
            getLayoutParams().height = h;
        }
    }
}

Ý tưởng là để cho phép ViewPagertính kích thước của trẻ em và lưu chiều cao tính toán của trang đầu tiên trong các thông số bố cục của ViewPager. Đừng quên đặt chiều cao bố cục của mảnh thành wrap_contentnếu không bạn có thể nhận được height = 0. Tôi đã sử dụng cái này:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="wrap_content">
        <!-- Childs are populated in fragment -->
</LinearLayout>

Xin lưu ý rằng giải pháp này hoạt động rất tốt nếu tất cả các trang của bạn có cùng chiều cao . Nếu không, bạn cần tính toán lại ViewPagerchiều cao dựa trên hoạt động hiện tại của trẻ. Tôi không cần nó, nhưng nếu bạn đề xuất giải pháp, tôi sẽ vui lòng cập nhật câu trả lời.


Bạn vẫn có thể cập nhật câu trả lời của bạn sau tất cả những năm đó? Sẽ giúp tôi rất nhiều
Denny

2

Đối với những người gặp vấn đề này và mã hóa cho Xamarin Android bằng C #, đây cũng có thể là một giải pháp nhanh chóng:

pager.ChildViewAdded += (sender, e) => {
    e.Child.Measure ((int)MeasureSpecMode.Unspecified, (int)MeasureSpecMode.Unspecified);
    e.Parent.LayoutParameters.Height = e.Child.MeasuredHeight;
};

Điều này chủ yếu hữu ích nếu quan điểm của con bạn có cùng chiều cao. Mặt khác, bạn sẽ được yêu cầu lưu trữ một số loại giá trị "tối thiểu" đối với tất cả trẻ em mà bạn kiểm tra và thậm chí sau đó bạn có thể không muốn có các khoảng trống hiển thị bên dưới chế độ xem con nhỏ hơn của mình.

Bản thân giải pháp này không đủ cho tôi, nhưng đó là bởi vì các mục con của tôi là listViews và biện pháp đo lường của chúng không được tính toán chính xác.


Điều này làm việc cho tôi. Tất cả các chế độ xem của con tôi trong chế độ xem đều có cùng chiều cao.
Dmitry

2

Tôi có một phiên bản WrapContentHeightViewPager hoạt động chính xác trước API 23 sẽ thay đổi kích thước chiều cao của khung nhìn cha mẹ trên chế độ xem con hiện tại được chọn.

Sau khi nâng cấp lên API 23, nó đã ngừng hoạt động. Hóa ra giải pháp cũ đã được sử dụng getChildAt(getCurrentItem())để có được chế độ xem con hiện tại để đo lường cái không hoạt động. Xem giải pháp tại đây: https://stackoverflow.com/a/16512217/1265583

Dưới đây hoạt động với API 23:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int height = 0;
    ViewPagerAdapter adapter = (ViewPagerAdapter)getAdapter();
    View child = adapter.getItem(getCurrentItem()).getView();
    if(child != null) {
        child.measure(widthMeasureSpec,  MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        height = child.getMeasuredHeight();
    }
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Cảm ơn bạn!! Đã cố gắng trả lời trong nhiều giờ và đây là người duy nhất hoàn toàn làm việc cho tôi. Nó cần được kết hợp với một bộ điều hợp tùy chỉnh trong đó 'setPrimaryItem () `gọi một hàm trong máy nhắn tin gọi requestLayout()chiều cao được điều chỉnh khi chúng ta đi từ tab này sang tab khác. Bạn có nhớ tại sao supercần phải được gọi hai lần? Tôi nhận thấy rằng nó sẽ không hoạt động khác.
M3RS

Hoạt động với API 28.
Khalid Lakhani

2

Mã dưới đây là điều duy nhất làm việc cho tôi

1. Sử dụng lớp này để khai báo một TallWrappingViewPager:

 public class HeightWrappingViewPager extends ViewPager {

        public HeightWrappingViewPager(Context context) {
            super(context);
        }

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

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int mode = MeasureSpec.getMode(heightMeasureSpec);
            // Unspecified means that the ViewPager is in a ScrollView WRAP_CONTENT.
            // At Most means that the ViewPager is not in a ScrollView WRAP_CONTENT.
            if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
                // super has to be called in the beginning so the child views can be initialized.
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                int height = 0;
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                    int h = child.getMeasuredHeight();
                    if (h > height) height = h;
                }
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
            }
            // super has to be called again so the new specs are treated as exact measurements
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

2. Chèn máy nhắn tin xem chiều cao gói vào tệp xml của bạn:

<com.project.test.HeightWrappingViewPager
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</com.project.test.HeightWrappingViewPager>

3. Khai báo máy nhắn tin xem của bạn:

HeightWrappingViewPager mViewPager;
mViewPager = (HeightWrappingViewPager) itemView.findViewById(R.id.pager);
CustomAdapter adapter = new CustomAdapter(context);
mViewPager.setAdapter(adapter);
mViewPager.measure(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);

Cảm ơn. Điều này đã làm việc. Nhưng tại sao đội Android không thể có cái này trong cơ sở mã của họ?
Mohanakrrishna

Đây là một trong những điều bạn phải tự tùy chỉnh tùy thuộc vào nhu cầu của mình, cũng được Google giới thiệu viewPager2 trong năm 2019 Google I / O này và là một thay thế của ViewPager cũ, được tạo ra vào năm 2011, triển khai 'androidx.viewpager2: viewpager2 : 1.0.0-alpha04 '
Hossam Hassan

2

Tôi chỉnh sửa câu trả lời của cybergen để làm cho viewpager thay đổi chiều cao tùy thuộc vào mục được chọn Lớp này giống với của cybergen, nhưng tôi đã thêm một Vector các số nguyên là tất cả các chiều cao lượt xem của người xem và chúng ta có thể truy cập nó khi thay đổi trang để cập nhật chiều cao

Đây là lớp học:

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.ViewPager;

import java.util.Vector;

public class WrapContentHeightViewPager extends ViewPager {
    private Vector<Integer> heights = new Vector<>();

    public WrapContentHeightViewPager(@NonNull Context context) {
        super(context);
    }

    public WrapContentHeightViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        for(int i=0;i<getChildCount();i++) {
            View view = getChildAt(i);
            if (view != null) {
                view.measure(widthMeasureSpec, heightMeasureSpec);
                heights.add(measureHeight(heightMeasureSpec, view));
            }
        }
        setMeasuredDimension(getMeasuredWidth(), measureHeight(heightMeasureSpec, getChildAt(0)));
    }

    public int getHeightAt(int position){
        return heights.get(position);
    }

    private int measureHeight(int measureSpec, View view) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            if (view != null) {
                result = view.getMeasuredHeight();
            }
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }
}

Sau đó, trong hoạt động của bạn, thêm một OnPageChangeListener

WrapContentHeightViewPager viewPager = findViewById(R.id.my_viewpager);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
     @Override
     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
     @Override
     public void onPageSelected(int position) {
         LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) viewPager.getLayoutParams();
         params.height = viewPager.getHeightAt(position);
         viewPager.setLayoutParams(params);
     }
     @Override
     public void onPageScrollStateChanged(int state) {}
});

Và đây là xml:

<com.example.example.WrapContentHeightViewPager
    android:id="@+id/my_viewpager"
    android:fillViewport="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

Vui lòng sửa tiếng Anh của tôi nếu cần thiết


Điều này có một số vấn đề. Các heightsdanh sách có thể tăng vô cùng.
rosuh

@rosuh Khi nào bạn gặp phải vấn đề này? Tôi chỉ sử dụng tính năng này trong TabLayout với ViewPager, vì vậy tôi không chắc nó có hoạt động tốt ở mọi nơi không
geggiamarti

@geggiamarti Vấn đề là một số trang sẽ được tái chế. Và được tạo lại khi người dùng vuốt vào chúng do đó measuresẽ được gọi nhiều lần. Nó có thể tăng danh sách chiều cao. Một tình huống khác là người dùng có thể gọi requestLayout(hoặc setLayoutParamsphương thức, giống như những gì bạn đã làm) cho viewPager này theo cách thủ công, cũng sẽ đo nhiều lần.
rosuh

1

Nếu ViewPagerbạn đang sử dụng là con của ScrollView ANDPagerTitleStripcon, bạn sẽ cần sử dụng một sửa đổi nhỏ cho các câu trả lời tuyệt vời đã được cung cấp. Để tham khảo XML của tôi trông như thế này:

<ScrollView
    android:id="@+id/match_scroll_view"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/white">

    <LinearLayout
        android:id="@+id/match_and_graphs_wrapper"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <view
            android:id="@+id/pager"
            class="com.printandpixel.lolhistory.util.WrapContentHeightViewPager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <android.support.v4.view.PagerTitleStrip
                android:id="@+id/pager_title_strip"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="top"
                android:background="#33b5e5"
                android:paddingBottom="4dp"
                android:paddingTop="4dp"
                android:textColor="#fff" />
        </view>
    </LinearLayout>
</ScrollView>

Trong bạn, onMeasurebạn phải THÊM số đo Độ cao của cái PagerTitleStripnếu tìm thấy. Mặt khác, chiều cao của nó sẽ không được coi là chiều cao lớn nhất của tất cả trẻ em mặc dù nó chiếm thêm không gian.

Hy vọng điều này sẽ giúp người khác. Xin lỗi vì đó là một chút hack ...

public class WrapContentHeightViewPager extends ViewPager {

    public WrapContentHeightViewPager(Context context) {
        super(context);
    }

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int pagerTitleStripHeight = 0;
        int height = 0;
        for(int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            int h = child.getMeasuredHeight();
            if (h > height) {
                // get the measuredHeight of the tallest fragment
                height = h;
            }
            if (child.getClass() == PagerTitleStrip.class) {
                // store the measured height of the pagerTitleStrip if one is found. This will only
                // happen if you have a android.support.v4.view.PagerTitleStrip as a direct child
                // of this class in your XML.
                pagerTitleStripHeight = h;
            }
        }

        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height+pagerTitleStripHeight, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

1

Hầu hết các giải pháp tôi thấy ở đây dường như đang thực hiện một phép đo kép: đầu tiên là đo các quan điểm con, sau đó gọi super.onMeasure()

Tôi đã đưa ra một tùy chỉnh WrapContentViewPagerhiệu quả hơn, hoạt động tốt với RecyclerView và Fragment

Bạn có thể kiểm tra bản demo tại đây:

github / ssynhtn / WrapContentViewPager

và mã của lớp ở đây: WrapContentViewPager.java


0

Tôi có một kịch bản tương tự (nhưng phức tạp hơn). Tôi có một hộp thoại chứa ViewPager.
Một trong những trang con ngắn, có chiều cao tĩnh.
Một trang con khác phải luôn cao nhất có thể.
Một trang con khác chứa ScrollView và trang (và do đó toàn bộ hộp thoại) sẽ WRAP_CONTENT nếu nội dung ScrollView không cần chiều cao đầy đủ có sẵn cho hộp thoại.

Không có câu trả lời hiện có nào hoạt động hoàn toàn cho kịch bản cụ thể này. Giữ lấy - đó là một chuyến đi gập ghềnh.

void setupView() {
    final ViewPager.SimpleOnPageChangeListener pageChangeListener = new ViewPager.SimpleOnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            currentPagePosition = position;

            // Update the viewPager height for the current view

            /*
            Borrowed from https://github.com/rnevet/WCViewPager/blob/master/wcviewpager/src/main/java/nevet/me/wcviewpager/WrapContentViewPager.java
            Gather the height of the "decor" views, since this height isn't included
            when measuring each page's view height.
             */
            int decorHeight = 0;
            for (int i = 0; i < viewPager.getChildCount(); i++) {
                View child = viewPager.getChildAt(i);
                ViewPager.LayoutParams lp = (ViewPager.LayoutParams) child.getLayoutParams();
                if (lp != null && lp.isDecor) {
                    int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
                    boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;
                    if (consumeVertical) {
                        decorHeight += child.getMeasuredHeight();
                    }
                }
            }

            int newHeight = decorHeight;

            switch (position) {
                case PAGE_WITH_SHORT_AND_STATIC_CONTENT:
                    newHeight += measureViewHeight(thePageView1);
                    break;
                case PAGE_TO_FILL_PARENT:
                    newHeight = ViewGroup.LayoutParams.MATCH_PARENT;
                    break;
                case PAGE_TO_WRAP_CONTENT:
//                  newHeight = ViewGroup.LayoutParams.WRAP_CONTENT; // Works same as MATCH_PARENT because...reasons...
//                  newHeight += measureViewHeight(thePageView2); // Doesn't allow scrolling when sideways and height is clipped

                    /*
                    Only option that allows the ScrollView content to scroll fully.
                    Just doing this might be way too tall, especially on tablets.
                    (Will shrink it down below)
                     */
                    newHeight = ViewGroup.LayoutParams.MATCH_PARENT;
                    break;
            }

            // Update the height
            ViewGroup.LayoutParams layoutParams = viewPager.getLayoutParams();
            layoutParams.height = newHeight;
            viewPager.setLayoutParams(layoutParams);

            if (position == PAGE_TO_WRAP_CONTENT) {
                // This page should wrap content

                // Measure height of the scrollview child
                View scrollViewChild = ...; // (generally this is a LinearLayout)
                int scrollViewChildHeight = scrollViewChild.getHeight(); // full height (even portion which can't be shown)
                // ^ doesn't need measureViewHeight() because... reasons...

                if (viewPager.getHeight() > scrollViewChildHeight) { // View pager too tall?
                    // Wrap view pager height down to child height
                    newHeight = scrollViewChildHeight + decorHeight;

                    ViewGroup.LayoutParams layoutParams2 = viewPager.getLayoutParams();
                    layoutParams2.height = newHeight;
                    viewPager.setLayoutParams(layoutParams2);
                }
            }

            // Bonus goodies :)
            // Show or hide the keyboard as appropriate. (Some pages have EditTexts, some don't)
            switch (position) {
                // This case takes a little bit more aggressive code than usual

                if (position needs keyboard shown){
                    showKeyboardForEditText();
                } else if {
                    hideKeyboard();
                }
            }
        }
    };

    viewPager.addOnPageChangeListener(pageChangeListener);

    viewPager.getViewTreeObserver().addOnGlobalLayoutListener(
            new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    // http://stackoverflow.com/a/4406090/4176104
                    // Do things which require the views to have their height populated here
                    pageChangeListener.onPageSelected(currentPagePosition); // fix the height of the first page

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                        viewPager.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    } else {
                        viewPager.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                    }

                }
            }
    );
}


...

private void showKeyboardForEditText() {
    // Make the keyboard appear.
    getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
    getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

    inputViewToFocus.requestFocus();

    // http://stackoverflow.com/a/5617130/4176104
    InputMethodManager inputMethodManager =
            (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
    inputMethodManager.toggleSoftInputFromWindow(
            inputViewToFocus.getApplicationWindowToken(),
            InputMethodManager.SHOW_IMPLICIT, 0);
}

...

/**
 * Hide the keyboard - http://stackoverflow.com/a/8785471
 */
private void hideKeyboard() {
    InputMethodManager inputManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);

    inputManager.hideSoftInputFromWindow(inputBibleBookStart.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}

...

//https://github.com/rnevet/WCViewPager/blob/master/wcviewpager/src/main/java/nevet/me/wcviewpager/WrapContentViewPager.java
private int measureViewHeight(View view) {
    view.measure(ViewGroup.getChildMeasureSpec(-1, -1, view.getLayoutParams().width), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
    return view.getMeasuredHeight();
}

Rất cám ơn @Raanan cho mã để đo lượt xem và đo chiều cao trang trí. Tôi gặp vấn đề với thư viện của anh ấy - hoạt hình bị vấp, và tôi nghĩ ScrollView của tôi sẽ không cuộn khi chiều cao của hộp thoại đủ ngắn để yêu cầu nó.


0

trong trường hợp của tôi thêm clipToPaddinggiải quyết vấn đề.

<android.support.v4.view.ViewPager
    ...
    android:clipToPadding="false"
    ...
    />

Chúc mừng!


0

Tôi trường hợp của tôi thêm android: fillViewport = "true" đã giải quyết vấn đề


0

Trong trường hợp của tôi, tôi cần một trình xem với một quấn_content cho phần tử và hình động hiện được chọn khi áp dụng kích thước. Dưới đây bạn có thể thấy việc thực hiện của tôi. Ai đó có thể có ích

package one.xcorp.widget

import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import one.xcorp.widget.R
import kotlin.properties.Delegates.observable

class ViewPager : android.support.v4.view.ViewPager {

    var enableAnimation by observable(false) { _, _, enable ->
        if (enable) {
            addOnPageChangeListener(onPageChangeListener)
        } else {
            removeOnPageChangeListener(onPageChangeListener)
        }
    }

    private var animationDuration = 0L
    private var animator: ValueAnimator? = null

    constructor (context: Context) : super(context) {
        init(context, null)
    }

    constructor (context: Context, attrs: AttributeSet?) : super(context, attrs) {
        init(context, attrs)
    }

    private fun init(context: Context, attrs: AttributeSet?) {
        context.theme.obtainStyledAttributes(
            attrs,
            R.styleable.ViewPager,
            0,
            0
        ).apply {
            try {
                enableAnimation = getBoolean(
                    R.styleable.ViewPager_enableAnimation,
                    enableAnimation
                )
                animationDuration = getInteger(
                    R.styleable.ViewPager_animationDuration,
                    resources.getInteger(android.R.integer.config_shortAnimTime)
                ).toLong()
            } finally {
                recycle()
            }
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)

        val measuredHeight = if (heightMode == MeasureSpec.EXACTLY) {
            MeasureSpec.getSize(heightMeasureSpec)
        } else {
            val currentViewHeight = findViewByPosition(currentItem)?.also {
                measureView(it)
            }?.measuredHeight ?: 0

            if (heightMode != MeasureSpec.AT_MOST) {
                currentViewHeight
            } else {
                Math.min(
                    currentViewHeight,
                    MeasureSpec.getSize(heightMeasureSpec)
                )
            }
        }

        super.onMeasure(
            widthMeasureSpec,
            MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY)
        )
    }

    private fun measureView(view: View) = with(view) {
        val horizontalMode: Int
        val horizontalSize: Int
        when (layoutParams.width) {
            MATCH_PARENT -> {
                horizontalMode = MeasureSpec.EXACTLY
                horizontalSize = this@ViewPager.measuredWidth
            }
            WRAP_CONTENT -> {
                horizontalMode = MeasureSpec.UNSPECIFIED
                horizontalSize = 0
            }
            else -> {
                horizontalMode = MeasureSpec.EXACTLY
                horizontalSize = layoutParams.width
            }
        }

        val verticalMode: Int
        val verticalSize: Int
        when (layoutParams.height) {
            MATCH_PARENT -> {
                verticalMode = MeasureSpec.EXACTLY
                verticalSize = this@ViewPager.measuredHeight
            }
            WRAP_CONTENT -> {
                verticalMode = MeasureSpec.UNSPECIFIED
                verticalSize = 0
            }
            else -> {
                verticalMode = MeasureSpec.EXACTLY
                verticalSize = layoutParams.height
            }
        }

        val horizontalMeasureSpec = MeasureSpec.makeMeasureSpec(horizontalSize, horizontalMode)
        val verticalMeasureSpec = MeasureSpec.makeMeasureSpec(verticalSize, verticalMode)

        measure(horizontalMeasureSpec, verticalMeasureSpec)
    }

    private fun findViewByPosition(position: Int): View? {
        for (i in 0 until childCount) {
            val childView = getChildAt(i)
            val childLayoutParams = childView.layoutParams as LayoutParams

            val childPosition by lazy {
                val field = childLayoutParams.javaClass.getDeclaredField("position")
                field.isAccessible = true
                field.get(childLayoutParams) as Int
            }

            if (!childLayoutParams.isDecor && position == childPosition) {
                return childView
            }
        }

        return null
    }

    private fun animateContentHeight(childView: View, fromHeight: Int, toHeight: Int) {
        animator?.cancel()

        if (fromHeight == toHeight) {
            return
        }

        animator = ValueAnimator.ofInt(fromHeight, toHeight).apply {
            addUpdateListener {
                measureView(childView)
                if (childView.measuredHeight != toHeight) {
                    animateContentHeight(childView, height, childView.measuredHeight)
                } else {
                    layoutParams.height = animatedValue as Int
                    requestLayout()
                }
            }
            duration = animationDuration
            start()
        }
    }

    private val onPageChangeListener = object : OnPageChangeListener {

        override fun onPageScrollStateChanged(state: Int) {
            /* do nothing */
        }

        override fun onPageScrolled(
            position: Int,
            positionOffset: Float,
            positionOffsetPixels: Int
        ) {
            /* do nothing */
        }

        override fun onPageSelected(position: Int) {
            if (!isAttachedToWindow) {
                return
            }

            findViewByPosition(position)?.let { childView ->
                measureView(childView)
                animateContentHeight(childView, height, childView.measuredHeight)
            }
        }
    }
}

Thêm attrs.xml trong dự án:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ViewPager">
        <attr name="enableAnimation" format="boolean" />
        <attr name="animationDuration" format="integer" />
    </declare-styleable>
</resources>

Và sử dụng:

<one.xcorp.widget.ViewPager
    android:id="@+id/wt_content"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:enableAnimation="true" />

0

ViewPager này chỉ thay đổi kích thước cho những đứa trẻ có thể nhìn thấy hiện tại (không phải là những đứa trẻ lớn nhất của nó)

Ý tưởng từ https://stackoverflow.com/a/56325869/4718406

public class DynamicHeightViewPager extends ViewPager {

public DynamicHeightViewPager (Context context) {
    super(context);
    initPageChangeListener();
}

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



private void initPageChangeListener() {
    addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            requestLayout();
        }
    });
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //View child = getChildAt(getCurrentItem());
    View child = getCurrentView(this);
    if (child != null) {
        child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, 
         MeasureSpec.UNSPECIFIED));
        int h = child.getMeasuredHeight();

        heightMeasureSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}


View getCurrentView(ViewPager viewPager) {
    try {
        final int currentItem = viewPager.getCurrentItem();
        for (int i = 0; i < viewPager.getChildCount(); i++) {
            final View child = viewPager.getChildAt(i);
            final ViewPager.LayoutParams layoutParams = (ViewPager.LayoutParams) 
             child.getLayoutParams();

            Field f = layoutParams.getClass().getDeclaredField("position"); 
            //NoSuchFieldException
            f.setAccessible(true);
            int position = (Integer) f.get(layoutParams); //IllegalAccessException

            if (!layoutParams.isDecor && currentItem == position) {
                return child;
            }
        }
    } catch (NoSuchFieldException e) {
        e.fillInStackTrace();
    } catch (IllegalArgumentException e) {
        e.fillInStackTrace();
    } catch (IllegalAccessException e) {
        e.fillInStackTrace();
    }
    return null;
}

}


0

Đo chiều cao của ViewPager:

public class WrapViewPager extends ViewPager {
    View primaryView;

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (primaryView != null) {
            int height = 0;
            for (int i = 0; i < getChildCount(); i++) {
                if (primaryView == getChildAt(i)) {
                    int childHeightSpec = MeasureSpec.makeMeasureSpec(0x1 << 30 - 1, MeasureSpec.AT_MOST);
                    getChildAt(i).measure(widthMeasureSpec, childHeightSpec);
                    height = getChildAt(i).getMeasuredHeight();
                }

            }

            setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
        }
    }

    public void setPrimaryView(View view) {
        primaryView = view;
    }

}

gọi setPrimaryView (Xem)

public class ZGAdapter extends PagerAdapter {

    @Override
    public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        super.setPrimaryItem(container, position, object);
        ((WrapViewPager)container).setPrimaryView((View)object);
    }

}

0

Cung cấp bố cục cho ViewPager như NestedScrollView

   <androidx.core.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"
    android:fillViewport="true">
        <androidx.viewpager.widget.ViewPager
            android:id="@+id/viewPager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        </androidx.viewpager.widget.ViewPager>
    </androidx.core.widget.NestedScrollView>

Đừng quên thiết lập android:fillViewport="true"

Điều này sẽ kéo dài scrollview và nội dung của nó để lấp đầy khung nhìn.

https://developer.android.com/reference/android/widget/ScrollView.html#attr_android:fillViewport


0

Bạn có thể chuyển sang ViewPager2. Đây là phiên bản cập nhật của ViewPager. Nó thực hiện tương tự như ViewPager nhưng theo cách thông minh và hiệu quả hơn. ViewPager2 đi kèm với một loạt các tính năng mới. Tất nhiên, vấn đề Wrap Content đã được ViewPager2 giải quyết.

Từ các tài liệu Android: "ViewPager2 thay thế ViewPager, giải quyết hầu hết các điểm đau của người tiền nhiệm, bao gồm hỗ trợ bố cục từ phải sang trái, hướng dọc, bộ sưu tập Fragment có thể sửa đổi, v.v."

Tôi giới thiệu bài viết này cho người mới bắt đầu:

https://medium.com/google-developer-experts/exploring-the-view-pager-2-86dbce06ff71


Vấn đề này vẫn còn đó, hãy kiểm trasuetracker.google.com/u/0/issues/143095219
Somesh Kumar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.