← Back to list

View getWidth() возвращает ноль

История древняя, но не теряющая своей актуальности после onCreate() вид который уже вроде бы должен быть размечен возвращает нули по ширине

Виталий Шеянов · 2017-10-08 18:25 · 4 claps · 1.2 min read
#androiddev #kotlin
Open on Medium ↗
Wiki topics: 📱 · Mobile Development

View getWidth() возвращает ноль

История древняя, но не теряющая своей актуальности после onCreate() вид который уже вроде бы должен быть размечен возвращает нули по ширине и высоте.

Конечно есть всякие правильные и монструозные решения

view.getViewTreeObserver().addOnGlobalLayoutListener(
    new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            view.getViewTreeObserver()
                .removeOnGlobalLayoutListener(this);
            view.getHeight(); //height is ready
        }
    }
);

Но есть более элегантное:

view.post(new Runnable() {
    @Override
    public void run() {
        view.getHeight(); //height is ready
    }
});

все это подробно и почему работает и разные другие варианты показаны сдесь

[embed]getWidth() and getHeight() of View returns 0 I am creating all of the elements in my android project dynamically. I am trying to get the width and height of a…stackoverflow.com

От асинхронности тут к сожалению никак не избавиться и это очень печально.

Для меня один из частых юзер кейсов это задача получения размера вида в RecyclerView, пример картинки в ImageView которую не хотелось бы каждый раз уменьшать а сразу преобразовать в подходящий размер и потом кэшировать. Поэтому я для себя сделал маленький и симпатичный класс подходящий лично для моих нужд.

Котлинобразно, реактивно и молодежно:

class ViewSize {

    private val emitter = BehaviorSubject.create<Pair<Int, Int>>()
    private var isFirst = true

    fun addView(view: View) {
        view.also {
            if (isFirst) view.post {
                if (isFirst) emitter.onNext(
                    Pair(view.width, view.height))
                isFirst = false
            }
        }
    }

    fun asSingle(): Single<Pair<Int, Int>> = emitter.firstOrError()
}

Да не феншуйно из-за использования Subject зато дешево и практично.

Где то в адаптере это выглядит так

...
private val viewSize = ViewSize()
init {
...
    Observables.combineLatest(
        viewSize.asSingle().map { it.first }.asObservable(), 
        imageObservable)
    .observeOn(Schedulers.computation())
    .map { (width, image) -> //уменьшаем до нужной ширины }
    ...
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe {
        //говорим что картинка готова
    }.let(disposable::add)
}
override fun onCreateViewHolder(parent: ViewGroup, 
    viewType: Int): RecyclerView.ViewHolder {
        LayoutInflater.from(parent.context)
        .inflate(R.layout.calendar_item, parent, false)
        .also(viewSize.addView(it))
        .let(CalendarAdapter::ItemHolder)
}

Вот таким способом можно решить проблему более позднего определения ширины и высоты видов

PS: Почему View.getWidth() а неView.getMeasuredWidth() ? Потому что первое отдает реальную ширину, а второе то которую хочет данный вид.


메타데이터
post_id
7c6fc1feba8
slug
view-getwidth-возвращает-0-7c6fc1feba8
url
https://medium.com/@vit.onix/view-getwidth-%D0%B2%D0%BE%D0%B7%D0%B2%D1%80%D0%B0%D1%89%D0%B0%D0%B5%D1%82-0-7c6fc1feba8
canonical_url
https://medium.com/@vit.onix/view-getwidth-%D0%B2%D0%BE%D0%B7%D0%B2%D1%80%D0%B0%D1%89%D0%B0%D0%B5%D1%82-0-7c6fc1feba8
author_url
https://medium.com/@vit.onix
status
ok
fetched_at
2026-07-30 09:39:55