onMeasure()をオーバーライドする信頼できる方法は?


88

onMeasure()をオーバーライドする正しい方法は何ですか?私はさまざまなアプローチを見てきました。たとえば、Professional Android Developmentは、MeasureSpecを使用してディメンションを計算し、setMeasuredDimension()の呼び出しで終了します。例えば:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
}

一方、この投稿によると、「正しい」方法は、MeasureSpecを使用し、setMeasuredDimensions()を呼び出し、続いてsetLayoutParams()を呼び出し、最後にsuper.onMeasure()を呼び出すことです。例えば:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

では、どちらが正しい方法ですか?どちらのアプローチも私にとって100%うまくいきませんでした。

私が本当に求めているのは、onMeasure()、レイアウト、子ビューの寸法などを説明するチュートリアルを知っている人はいると思いますか?


15
答えは役に立ちましたか/正しいと思いましたか?なぜとしてそれをマークしていない答えは?
superjos 2011

回答:


68

他のソリューションは包括的ではありません。それらは場合によっては機能する可能性があり、開始するのに適した場所ですが、機能することが保証されていない場合があります。

onMeasureが呼び出されると、サイズを変更する権限がある場合とない場合があります。onMeasure(widthMeasureSpecheightMeasureSpec)に渡される値には、子ビューで実行できることに関する情報が含まれています。現在、3つの値があります。

  1. MeasureSpec.UNSPECIFIED -あなたは好きなだけ大きくすることができます
  2. MeasureSpec.AT_MOST-必要なだけ大きく(スペックサイズまで)、これはparentWidthあなたの例です。
  3. MeasureSpec.EXACTLY- 選択の余地ない。親が選択しました。

これは、Androidが複数のパスを作成して各アイテムの適切なサイズを見つけることができるようにするためです。詳細については、こちらをご覧ください。

これらのルールに従わない場合、アプローチが機能することが保証されません。

たとえば、サイズの変更が許可されているかどうかを確認する場合は、次の操作を実行できます。

final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
boolean resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
boolean resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;

この情報を使用すると、コードのように値を変更できるかどうかがわかります。または、別のことをする必要がある場合。目的のサイズをすばやく簡単に解決するには、次のいずれかの方法を使用します。

int resolveSizeAndState(int size、int measureSpec、int childMeasuredState)

int resolveSize(int size、int measureSpec)

1つ目はHoneycombでのみ利用可能ですが、2つ目はすべてのバージョンで利用可能です。

注:あなたはそれを見つけるかもしれませんしresizeWidthresizeHeight常に間違っています。私が要求していた場合、これが当てはまることがわかりましたMATCH_PARENTWRAP_CONTENT親のレイアウトをリクエストし、UNSPECIFIEDフェーズでサイズをリクエストすることで、これを修正できましたInteger.MAX_VALUE。そうすることで、親がonMeasureの次のパスで許可する最大サイズが得られます。


39

ドキュメントはこの問題に関する権限です:http//developer.android.com/guide/topics/ui/how-android-draws.htmlおよびhttp://developer.android.com/guide/topics/ui/custom -components.html

要約すると、オーバーライドされたonMeasureメソッドの最後に、を呼び出す必要がありますsetMeasuredDimension

呼び出したsuper.onMeasure後に呼び出すべきではありませんsetMeasuredDimension。設定したものがすべて消去されます。状況によっては、super.onMeasure最初にを呼び出してから、を呼び出して結果を変更したい場合がありますsetMeasuredDimension

呼び出さないでくださいsetLayoutParamsの中でonMeasure。レイアウトは、測定後2回目のパスで行われます。


私の経験では、super.onMeasureを呼び出さずにonMeasureをオーバーライドすると、子ビューでOnLayoutが呼び出されません。
William Jockusch 2014年

2

オーバーライドしている親によって異なると思います。

たとえば、ViewGroup(FrameLayoutなど)を拡張している場合、サイズを測定したら、次のように呼び出す必要があります

super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));

ViewGroupで残りの作業を行うことができるため(子ビューでいくつかの作業を行う)

ビュー(ImageViewなど)を拡張する場合は、を呼び出すだけですthis.setMeasuredDimension(width, height);。これは、親クラスが通常と同じように実行するためです。

つまり、親クラスが無料で提供する機能が必要な場合は、呼び出す必要がありますsuper.onMeasure()(通常、MeasureSpec.EXACTLYモードのメジャースペックを渡します)。それ以外の場合this.setMeasuredDimension(width, height);は、呼び出すだけで十分です。


1

これが私が問題を解決した方法です:

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

        ....

        setMeasuredDimension( measuredWidth, measuredHeight );

        widthMeasureSpec = MeasureSpec.makeMeasureSpec( measuredWidth, MeasureSpec.EXACTLY );
        heightMeasureSpec = MeasureSpec.makeMeasureSpec( measuredHeight, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

さらに、ViewPagerコンポーネントが必要でした


3
これは適切な解決策ではありません。super.onMeasureを使用して設定された寸法をクリアしsetMeasuredDimensionます。
tomrozb 2015

@tomrozbそれはhumptydevelopers.com/2013/05/…ではありませんそしてあなたはアンドロイドソースをチェックすることができます
Yuli

@YuliReiri:完璧な解決策!
ShayanTabatabaee19年

0

onMeasure必要なすべての内部のビューサイズを変更する場合は、setMeasuredDimension呼び出しです。外でサイズを変更する場合は、onMeasureを呼び出す必要がありますsetLayoutParams。たとえば、テキストが変更されたときにテキストビューのサイズを変更します。


setMinWidth()とsetMaxWidth()を呼び出して、標準のonMeasureで処理することもできます。カスタムViewクラス内からsetLayoutParamsを呼び出さない方がよいことがわかりました。これは、ViewGroupsまたはActivitiesが子ビューの動作をオーバーライドするために実際に使用されます。
ドリン2014年

0

使用しているコントロールによって異なります。ドキュメントの説明は、一部のコントロール(TextView、Button、...)では機能しますが、他のコントロール(LinearLayout、...)では機能しません。私にとって非常にうまくいった方法は、私が終わったらスーパーを呼ぶことでした。以下のリンクの記事に基づいています。

http://humptydevelopers.blogspot.in/2013/05/android-view-overriding-onmeasure.html


-1

setLayoutParamsと測定値の再計算は、通常、派生クラスのonMeasureで行われるため、子ビューのサイズを正しく変更するための回避策だと思います。

ただし、これが正しく機能することはめったにありません(何らかの理由で...)、measureChildrenを呼び出す(ViewGroupを派生させる場合)か、必要に応じて同様のことを試してください。


-5

このコードをonMeasure()の例として取り上げることができます::

public class MyLayerLayout extends RelativeLayout {

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);

        int currentChildCount = getChildCount();
        for (int i = 0; i < currentChildCount; i++) {
            View currentChild = getChildAt(i);

            //code to find information

            int widthPercent = currentChildInfo.getWidth();
            int heightPercent = currentChildInfo.getHeight();

//considering we will pass height & width as percentage

            int myWidth = (int) Math.round(parentWidth * (widthPercent / 100.0));
            int myHeight = (int) Math.round(parentHeight * (heightPercent / 100.0));

//Considering we need to set horizontal & vertical position of the view in parent

            AlignmentTraitValue vAlign = currentChildInfo.getVerticalLocation() != null ? currentChildlayerInfo.getVerticalLocation() : currentChildAlignmentTraitValue.TOP;
            AlignmentTraitValue hAlign = currentChildInfo.getHorizontalLocation() != null ? currentChildlayerInfo.getHorizontalLocation() : currentChildAlignmentTraitValue.LEFT;
            int topPadding = 0;
            int leftPadding = 0;

            if (vAlign.equals(currentChildAlignmentTraitValue.CENTER)) {
                topPadding = (parentHeight - myHeight) / 2;
            } else if (vAlign.equals(currentChildAlignmentTraitValue.BOTTOM)) {
                topPadding = parentHeight - myHeight;
            }

            if (hAlign.equals(currentChildAlignmentTraitValue.CENTER)) {
                leftPadding = (parentWidth - myWidth) / 2;
            } else if (hAlign.equals(currentChildAlignmentTraitValue.RIGHT)) {
                leftPadding = parentWidth - myWidth;
            }
            LayoutParams myLayoutParams = new LayoutParams(myWidth, myHeight);
            currentChildLayoutParams.setMargins(leftPadding, topPadding, 0, 0);
            currentChild.setLayoutParams(myLayoutParams);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

6
このコードは根本的に間違っています。まず、レイアウトモードが考慮されていません(Grimmaceの回答を参照)。それは悪いことですが、最後にsuper.onMeasure()も呼び出すため、その効果はわかりません。これは、設定した値をオーバーライドし(satur9nineの回答を参照)、onMeasure()をオーバーライドする目的を完全に無効にします。
spaaarky21 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.