Когда я могу впервые измерить представление?
так что у меня есть немного путаницы с попыткой установить фон drawable вида, как он отображается. Код полагается на знание высоты представления, поэтому я не могу вызвать его из onCreate() или onResume(), потому что getHeight() возвращает 0. 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);
8 ответов:
Я не знаю, о
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()был вызван дважды, и я думаю, что в вашем случае это может привести к бесконечному циклу.вы можете попробовать позвонить
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; } }); }но это звучит немного сложно за то, что вы пытаетесь достичь, и не очень хорошо для спектакль.
EDIT by kcoppock
вот что я в конечном итоге делает из этого кода. Ответ Готье привел меня к этому моменту, поэтому я предпочел бы принять этот ответ с модификацией, чем ответить на него сам. Вместо этого я использовал метод addOnGlobalLayoutListener () ViewTreeObserver, например (это в 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 и не видел никакой необычной активности. Надеюсь, что это он! Спасибо!
относительно наблюдателя дерева представлений:
возвращенный наблюдатель 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 } }); }Это кажется довольно хрупким. Многие вещи - хотя и редко-похоже, могут пойти не так.
Итак, я начал делать это: когда вы отправляете сообщение в представление, сообщения будут доставлены только после того, как представление будет полностью инициализировано (включая измерение). Если вы только хотите, чтобы ваш измерительный код выполнялся один раз, и вы на самом деле не хотите наблюдать за изменениями макета для жизни представления (потому что он не изменяется), тогда я просто помещаю свой измерительный код в сообщение следующим образом:
viewToMeasure.post(new Runnable() { @Override public void run() { // safe to get height and width here } });у меня не было никаких проблем со стратегией публикации представления, и я не видел никаких мерцаний экрана или других вещей, которые вы ожидали бы, могут пойти не так (пока...)
Я есть были сбои в производственном коде, потому что мой глобальный макет наблюдатель не был удален или потому, что наблюдатель макета не был "живым", когда я попытался получить к нему доступ
используя глобальный прослушиватель, вы можете запустить бесконечный цикл.
я исправил это путем вызова
whateverlayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);внутри метода onGlobalLayout в прослушивателе.
в моих проектах я использую следующий фрагмент:
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был измерен и выполнить соответствующий код.
вы можете переопределить onMeasure для TextView:
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 } ... }я почти уверен, что вы можете сделать это без каких-либо строка кода. Я думаю, что есть шанс создать layer-list.
вы можете получить все меры представления в методе класса main activity ниже метода onCreate ():
@Override protected void onCreate(Bundle savedInstanceState){} @Override public void onWindowFocusChanged(boolean hasFocus){ int w = yourTextView.getWidth(); }Так что вы можете перерисовка, сортировка... свои взгляды. :)
использовать
OnPreDrawListener()вместоaddOnGlobalLayoutListener(), потому что он звонил ранее.tv.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { tv.getViewTreeObserver().removeOnPreDrawListener(this); // put your measurement code here return false; } });
Итак, вкратце, 3 способа обработки размера представления.... хммм, может и так!
1) Как упоминал @Gautier Hayoun, используя ongloballayoutlistener listener. Однако, пожалуйста, не забудьте позвонить в первый код в слушателя:
listViewProduct.getViewTreeObserver().removeOnGlobalLayoutListener(this);чтобы убедиться, что этот слушатель не бесконечно называть. И еще одна вещь, когда-нибудь этот слушатель вообще не будет вызывать, потому что ваш взгляд был нарисован где-то, чего вы не знаете. Итак, чтобы быть в безопасности, давайте зарегистрируем этот прослушиватель сразу после объявления представления в метод 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 с той же целью, добавив немного времени задержки. Давай попробуем сами. Когда-нибудь он тебе понадобится. Для пример подсказки, когда вы хотите прокрутить представление прокрутки автоматически после добавления дополнительных элементов в представление прокрутки или развернуть или сделать еще один вид видимым из состояния gone в представлении прокрутки с анимацией (это означает, что этот процесс займет немного времени, чтобы показать все). Он определенно изменит размер прокрутки, поэтому после того, как прокрутка будет нарисована, нам также нужно немного подождать, чтобы получить точный размер после того, как он добавит в него больше просмотра. Это самое время для метода postDelay, чтобы прийти спасите!
3) и если вы хотите настроить свой собственный вид, вы можете создать класс и расширить его из любого вида, который вам нравится, у которого есть метод onMeasure () для определения размера представления.
надеюсь, это поможет,
Mttdat.
Comments