63

そのため、ビューの表示時に描画可能な背景を設定しようとすると、少し混乱します。コードはビューの高さを知ることに依存しているので、またはから呼び出すことはできません。0onCreate()を返すからです。しかし、私が得ることができる最も近いようです。ユーザーに表示したときに背景が変わるように、次のようなコードをどこに配置すればよいですか?onResume()getHeight()onResume()

    TextView tv = (TextView)findViewById(R.id.image_test);
    LayerDrawable ld = (LayerDrawable)tv.getBackground();
    int height = tv.getHeight(); //when to call this so as not to get 0?
    int topInset = height / 2;
    ld.setLayerInset(1, 0, topInset, 0, 0);
    tv.setBackgroundDrawable(ld);
4

9 に答える 9

71

知らなかったのでViewTreeObserver.addOnPreDrawListener()、テストプロジェクトで試してみました。

コードを使用すると、次のようになります。

public void onCreate() {
setContentView(R.layout.main);

final TextView tv = (TextView)findViewById(R.id.image_test);
final LayerDrawable ld = (LayerDrawable)tv.getBackground();
final ViewTreeObserver obs = tv.getViewTreeObserver();
obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw () {
        Log.d(TAG, "onPreDraw tv height is " + tv.getHeight()); // bad for performance, remove on production
        int height = tv.getHeight();
        int topInset = height / 2;
        ld.setLayerInset(1, 0, topInset, 0, 0);
        tv.setBackgroundDrawable(ld);

        return true;
   }
});
}

私のテストプロジェクトonPreDraw()では2回呼び出されましたが、あなたの場合は無限ループが発生する可能性があると思います。

setBackgroundDrawable()変更の高さが変更された場合にのみ呼び出しを試みることができTextViewます:

private int mLastTvHeight = 0;

public void onCreate() {
setContentView(R.layout.main);

final TextView tv = (TextView)findViewById(R.id.image_test);
final LayerDrawable ld = (LayerDrawable)tv.getBackground();
final ViewTreeObserver obs = mTv.getViewTreeObserver();
obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw () {
        Log.d(TAG, "onPreDraw tv height is " + tv.getHeight()); // bad for performance, remove on production
        int height = tv.getHeight();
        if (height != mLastTvHeight) {
            mLastTvHeight = height;
            int topInset = height / 2;
            ld.setLayerInset(1, 0, topInset, 0, 0);
            tv.setBackgroundDrawable(ld);
        }

        return true;
   }
});
}

しかし、それはあなたが達成しようとしていることには少し複雑に聞こえ、パフォーマンスにはあまり良くありません。

kcoppockによる編集

これが私がこのコードからやったことです。ゴーティエの答えは私をこの点に導いたので、私は自分で答えるよりも、修正を加えてこの答えを受け入れたいと思います。代わりに、ViewTreeObserverのaddOnGlobalLayoutListener()メソッドを使用することになりました(これはonCreate()にあります)。

final TextView tv = (TextView)findViewById(R.id.image_test);
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        LayerDrawable ld = (LayerDrawable)tv.getBackground();
        ld.setLayerInset(1, 0, tv.getHeight() / 2, 0, 0);
    }
});

完璧に機能しているようです。LogCatを確認しましたが、異常なアクティビティは見られませんでした。うまくいけば、これはそれです!ありがとう!

于 2010-12-09T13:13:14.193 に答える
64

ビューツリーオブザーバーについて:

返されたViewTreeObserverオブザーバーは、このビューの存続期間中有効であることが保証されていません。このメソッドの呼び出し元がViewTreeObserverへの長期間の参照を保持している場合は、isAlive()の戻り値を常にチェックする必要があります。

たとえそれが短命の参照であったとしても(ビューを測定し、あなたがしているのと同じ種類のことをするために一度だけ使用されます)、私はそれがもう「生きて」いないのを見て、例外を投げます。実際、ViewTreeObserverのすべての関数は、「アライブ」でなくなった場合にIllegalStateExceptionをスローするため、常に「isAlive」をチェックする必要があります。私はこれをしなければなりませんでした:

    if(viewToMeasure.getViewTreeObserver().isAlive()) {
        viewToMeasure.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if(viewToMeasure.getViewTreeObserver().isAlive()) {
                    // only need to calculate once, so remove listener
                    viewToMeasure.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                }

                // you can get the view's height and width here

                // the listener might not have been 'alive', and so might not have been removed....so you are only
                // 99% sure the code you put here will only run once.  So, you might also want to put some kind
                // of "only run the first time called" guard in here too                                        

            }
        });            
    }

これは私にはかなりもろいようです。まれではありますが、多くのことがうまくいかないようです。

そこで私はこれを始めました。ビューにメッセージを投稿すると、メッセージはビューが完全に初期化された後(測定を含む)にのみ配信されます。測定コードを1回だけ実行し、ビューの存続期間中はレイアウトの変更を実際に観察したくない場合(サイズ変更されていないため)、測定コードを次のような投稿に配置します。 :

 viewToMeasure.post(new Runnable() {

        @Override
        public void run() {
            // safe to get height and width here               
        }

    });

ビューの投稿戦略に問題はなく、画面のちらつきやその他の問題が発生する可能性もありません(まだ...)

グローバルレイアウトオブザーバーが削除されなかったため、またはレイアウトオブザーバーにアクセスしようとしたときに「アライブ」されていなかったために、本番コードでクラッシュが発生しまし

于 2013-03-08T18:45:56.983 に答える
12

グローバルリスナーを使用すると、無限ループに陥る可能性があります。

私はこれを呼び出すことによって修正しました

whateverlayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);

リスナー内のonGlobalLayoutメソッドの内部。

于 2012-12-18T21:09:54.837 に答える
4

私のプロジェクトでは、次のスニペットを使用しています。

public static void postOnPreDraw(Runnable runnable, View view) {
    view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            try {
                runnable.run();
                return true;
            } finally {
                view.getViewTreeObserver().removeOnPreDrawListener(this);
            }
        }
    });
}

したがって、内部でそれが測定されたRunnable#runことを確認し、関連するコードを実行することができます。View

于 2014-06-03T14:11:34.880 に答える
3

TextViewのonMeasureをオーバーライドできます。

public MyTextView extends TextView {
   ...
   public void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
      super.onMeasure(widthMeasureSpec,heightMeasureSpec); // Important !!!
      final int width = getMeasuredHeight();
      final int height = getMeasuredHeight();
      // do you background stuff
   }
   ...
}

私はあなたがコードの行なしでもそれをすることができるとほぼ確信しています。レイヤーリストを作成するチャンスがあると思います。

于 2010-12-09T00:47:09.563 に答える
1

要約すると、ビューのサイズを処理する3つの方法....うーん、多分そうです!

1)@Gautier Hayounが述べたように、OnGlobalLayoutListenerリスナーを使用します。ただし、リスナーの最初のコードを呼び出すことを忘れないでくださいlistViewProduct.getViewTreeObserver().removeOnGlobalLayoutListener(this);。このリスナーが無限に呼び出されないようにするためです。そしてもう1つ、このリスナーがまったく呼び出さない場合があります。これは、ビューが知らない場所に描画されているためです。onCreate()したがって、安全のために、メソッド(またはonViewCreated()フラグメント)でビューを宣言した直後にこのリスナーを登録しましょう。

view = ([someView]) findViewById(R.id.[viewId]);

// Get width of view before it's drawn.
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {

        // Make this listener call once.
        view.getViewTreeObserver().removeOnGlobalLayoutListener(this);

        int width = listViewProduct.getWidth();
      });

2)ビューが描画されて表示された直後に何かを実行したい場合は、post()方法を使用します(もちろん、ビューは完全に描画されているため、ここでサイズを取得できます)。例えば、

listViewProduct.post(new Runnable() {
  @Override
  public void run() {
    // Do your stuff here.
  }
});

postDelay()さらに、少しの遅延時間を追加して、同じ目的で使用できます。自分で試してみましょう。あなたはいつかそれを必要とするでしょう。プロンプトの例として、アイテムを追加した後、スクロールビューを自動的にスクロールしたり、アニメーションscroll viewで消えた状態からさらにビューを表示したりする場合scroll view(つまり、このプロセスですべてが表示されるまでに少し時間がかかります)。scroll viewのサイズは間違いなく変更されるため、scroll view描画後、ビューが追加された後、正確なサイズを取得するために少し待つ必要があります。今こそ、postDelay()メソッドが救いの手を差し伸べる時です!

3)独自のビューを設定する場合は、クラスを作成し、ビューのサイズを定義するメソッドをextend持つ任意のビューから作成できます。onMeasure()

于 2017-06-20T04:54:27.780 に答える
1
textView.doOnPreDraw {
   //call textView.height
}

このビュー拡張機能を使用することもできます。

androidx.core.view (ViewKt.class)

public inline fun View.doOnPreDraw(
    crossinline action: (View) → Unit
): OneShotPreDrawListener

ビューツリーが描画されようとしているときに、指定されたアクションを実行します。アクションは、次の描画の前に1回だけ呼び出され、その後削除されます。

于 2022-02-04T12:30:55.093 に答える
0

onCreate()メソッドの下のクラスメインアクティビティのメソッドですべてのビューメジャーを取得できます。

@Override
protected void onCreate(Bundle savedInstanceState){}
@Override
public void onWindowFocusChanged(boolean hasFocus){
    int w = yourTextView.getWidth();
}

だから、あなたは再描画、ソート...あなたのビューをすることができます。:)

于 2015-01-31T08:40:36.770 に答える
0

以前に呼び出されたため、OnPreDrawListener() の代わりに使用します。addOnGlobalLayoutListener()

  tv.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
    {
        @Override
        public boolean onPreDraw()
        {
            tv.getViewTreeObserver().removeOnPreDrawListener(this);
            // put your measurement code here
            return false;
        }
    });
于 2017-03-22T09:47:58.107 に答える