質問
私は子供を持つカスタムビューグループを持っています ViewPager
. 。 ViewPagerはaによって供給されます PagerAdapter
それはaを提供します LinearLayout
に ViewPager
持っています LayoutParams
の WRAP_CONTENT
高さと幅の両方で。
ビューは正しく表示されますが、 child.measure()
メソッドはViewPagerで呼び出されます。これは、LinearLayoutの実際の寸法を返さず、残りのすべてのスペースを埋めるようです。
なぜこれが起こっているのか、そしてそれを修正する方法はありますか?
解決
私は受け入れられた答えにあまり満足していませんでした(コメントではあらゆる視野前の解決策もありません)ので、私はまとめました ViewPager
それは最初の利用可能な子供からその高さを取ります。これは、2回目の測定パスを実行し、最初の子供の身長を盗むことができるようにします。
より良い解決策は、内部で新しいクラスを作ることです android.support.v4.view
より良いバージョンを実装するパッケージ onMeasure
(ようなパッケージ可視方法へのアクセスを備えています populate()
)
しかし、当分の間、以下の解決策は私にぴったりです。
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);
boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec)
== MeasureSpec.AT_MOST;
if(wrapHeight) {
/**
* The first super.onMeasure call made the pager take up all the
* available height. Since we really wanted to wrap it, we need
* to remeasure it. Luckily, after that call the first child is
* now available. So, we take the height from it.
*/
int width = getMeasuredWidth(), height = getMeasuredHeight();
// Use the previously measured width but simplify the calculations
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
/* If the pager actually has any children, take the first child's
* height and call that our own */
if(getChildCount() > 0) {
View firstChild = getChildAt(0);
/* The child was previously measured with exactly the full height.
* Allow it to wrap this time around. */
firstChild.measure(widthMeasureSpec,
MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
height = firstChild.getMeasuredHeight();
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
他のヒント
互換性の瓶の中のビューページクラスの内部を見る:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// For simple implementation, or internal size is always 0.
// We depend on the container to specify the layout size of
// our view. We can't really know what it is since we will be
// adding and removing different arbitrary views and do not
// want the layout to change as this happens.
setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec));
...
}
ViewPagerの実装は、子供のビューを測定するのではなく、親が渡されているものに基づいてViewPagerを1つの標準ビューに設定するだけです。wrap_contentを渡すと、View Pagerが実際に必要なコンテンツを測定しないため利用可能な完全なエリアを上。
私の推奨事項は、子供のビューのサイズに基づいて、ビューページに静的サイズを設定することです。これが不可能な場合(たとえば、子供のビューが異なる場合があります)、最大サイズを選択して、いくつかのビューで余分なスペースに対処するか、ビューページを拡張し、子供を測定する測定値を提供する必要があります。あなたが遭遇する問題の1つは、さまざまなビューが表示されるため、ビューページャーが幅を変えないように設計されたため、おそらくサイズを選んでそれにとどまることを余儀なくされることです。
pageadapterのインスタンスイテムに(位置)(位置)の場合:
@Override
public Object instantiateItem(ViewGroup collection, int page) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = (View) inflater.inflate(R.layout.page_item , null);
view.setTag(page);
次に、onpageChangElistenerでビュー(アダプターのページ)を取得し、測定して、ビューページをサイズ変更できます。
private ViewPager pager;
@Override
protected void onCreate(Bundle savedInstanceState) {
pager = findViewById(R.id.viewpager);
pager.setOnPageChangeListener(new SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
resizePager(position);
}
});
public void resizePager(int position) {
View view = pager.findViewWithTag(position);
if (view == null)
return;
view.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
//The layout params must match the parent of the ViewPager
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width , height);
pager.setLayoutParams(params);
}
}
上記の例に従って、子供のビューの高さを測定すると、必ずしも正確な結果が戻ってくるとは限らないことがわかりました。解決策は、静的ビューの高さ(XMLで定義)を測定し、下部に動的に作成されたフラグメントの高さを追加することです。私の場合、静的要素はPagertitLestripであり、ランドスケープモードの幅にmatch_parentを使用するためにもオーバーライドする必要がありました。
それで、ここにDelyanからのコードに対する私の見解があります:
public class WrappingViewPager extends ViewPager {
public WrappingViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super has to be called in the beginning so the child views can be
// initialized.
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (getChildCount() <= 0)
return;
// Check if the selected layout_height mode is set to wrap_content
// (represented by the AT_MOST constraint).
boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec)
== MeasureSpec.AT_MOST;
int width = getMeasuredWidth();
View firstChild = getChildAt(0);
// Initially set the height to that of the first child - the
// PagerTitleStrip (since we always know that it won't be 0).
int height = firstChild.getMeasuredHeight();
if (wrapHeight) {
// Keep the current measured width.
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
}
int fragmentHeight = 0;
fragmentHeight = measureFragment(((Fragment) getAdapter().instantiateItem(this, getCurrentItem())).getView());
// Just add the height of the fragment:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + fragmentHeight,
MeasureSpec.EXACTLY);
// super has to be called again so the new specs are treated as
// exact measurements.
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public int measureFragment(View view) {
if (view == null)
return 0;
view.measure(0, 0);
return view.getMeasuredHeight();
}}
カスタムPagertitLestrip:
public class MatchingPagerTitleStrip extends android.support.v4.view.PagerTitleStrip {
public MatchingPagerTitleStrip(Context arg0, AttributeSet arg1) {
super(arg0, arg1);
}
@Override
protected void onMeasure(int arg0, int arg1) {
int size = MeasureSpec.getSize(arg0);
int newWidthSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
super.onMeasure(newWidthSpec, arg1);
}}
乾杯!
上記のソリューションを参照して、ビューページャーチャイルドの最大の高さを得るために、さらにいくつかのステートメントを追加しました。
以下のコードを参照してください。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super has to be called in the beginning so the child views can be
// initialized.
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (getChildCount() <= 0)
return;
// Check if the selected layout_height mode is set to wrap_content
// (represented by the AT_MOST constraint).
boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;
int width = getMeasuredWidth();
int childCount = getChildCount();
int height = getChildAt(0).getMeasuredHeight();
int fragmentHeight = 0;
for (int index = 0; index < childCount; index++) {
View firstChild = getChildAt(index);
// Initially set the height to that of the first child - the
// PagerTitleStrip (since we always know that it won't be 0).
height = firstChild.getMeasuredHeight() > height ? firstChild.getMeasuredHeight() : height;
int fHeight = measureFragment(((Fragment) getAdapter().instantiateItem(this, index)).getView());
fragmentHeight = fHeight > fragmentHeight ? fHeight : fragmentHeight;
}
if (wrapHeight) {
// Keep the current measured width.
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
}
// Just add the height of the fragment:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + fragmentHeight, MeasureSpec.EXACTLY);
// super has to be called again so the new specs are treated as
// exact measurements.
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
より良い変化
height = firstChild.getMeasuredHeight();
に
height = firstChild.getMeasuredHeight() + getPaddingTop() + getPaddingBottom();