Иногда необходимо узнать размеры элементов - ширину, высоту - во время выполнения. Понадобиться это может чтобы изменить их взаимное расположение - например, чтобы отцентровать по высоте.
Проблема заключается в том, что во время выполнения onResume, а тем более - onCreate, размеры элементов ещё не известны. При этом какого-либо специального метода жизненного цикла Activity, вызываемого после окончания построения layout, нет.
Есть несколько способов решить такую проблему:
1. упаковать нужный набор компонентов в какой-нибудь потомок View, переопределить для полученного потомка метод onLayout, в котором вначале вызвать super-метод, а потом уже взять готовые размеры элементов и сделать то, что требуется.
Проблема - ну не хочется на каждый набор элементов создавать отдельный класс, так и утонуть в них недолго.
2. есть решение, использующее ViewTreeObserver - создать слушатель на OnGlobalLayoutListener(), в котором реализовать метод onGlobalLayout. Такое решение можно посмотреть в этом топике. Это уже лучше, но работать с этим не очень удобно, так как нужно поразбираться в isAlive(), не забыть вызвать removeGlobalOnLayoutListener, то есть работать нужно аккуратно.
Мне больше понравилось следующее решение, которое сейчас представляется наиболее и универсальным, и наиболее простым:
1. Создаём интерфейс
public interface OnLayoutCompletedListener {
void layoutCompleted(ViewGroup layout);
}
2. Делаем производный класс от одного из "базовых" layout - например, MyLinearLayout, в котором переопределяем метод onLayout - и в переопределённом методе вызываем layoutCompleted.
public class MyLinearLayout extends LinearLayout {
private OnLayoutCompletedListener layoutCompletedListener;
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (layoutCompletedListener != null) {
layoutCompletedListener.layoutCompleted(this);
}
}
public void setOnLayoutCompletedListener(OnLayoutCompletedListener layoutCompletedListener) {
this.layoutCompletedListener = layoutCompletedListener;
}
}
3. В раскладке Activity (пусть activity_main.xml) заменяем "корневой" LineаrLayout на свежесозданный MyLinearLayout, при этом не забыть установить для него идентификатор:
<my.components.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
... тут обычный набор ...
</my.components.MyLinearLayout>
4. Допиливаем Activity (у меня используются AndroidAnnotations):
@EActivity(R.layout.activity_main)
public class MyActivity extends Activity implements OnLayoutCompletedListener {
@ViewById
MyLinearLayout layout;
@AfterViews
protected void initViews() {
layout.setOnLayoutCompletedListener(this);
...
}
@Override
public void layoutCompleted(ViewGroup layout) {
updateArrowsPosition();
}
}
Вот и всё. Мне нравится то, что получаемое решение интуитивно понятно и набор дополнительных действий - минимален. Всё что нужно сделать (после того, как уже созданы нужные потомки "базовый" раскладок):
1. изменить раскладку для этой активити:
1.1. изменить класс для root-layout
1.2. добавить идентификатор
2.1. добавить интерфейс в implements
2.2. получить экземпляр раскладки - в общем случае через findViewById
2.3. установить для экземпляра раскладки слушатель события
2.4. реализовать обработку события, генерируемого после завершения раскладки.
Вроде вполне несложно.
Проблема заключается в том, что во время выполнения onResume, а тем более - onCreate, размеры элементов ещё не известны. При этом какого-либо специального метода жизненного цикла Activity, вызываемого после окончания построения layout, нет.
Есть несколько способов решить такую проблему:
1. упаковать нужный набор компонентов в какой-нибудь потомок View, переопределить для полученного потомка метод onLayout, в котором вначале вызвать super-метод, а потом уже взять готовые размеры элементов и сделать то, что требуется.
Проблема - ну не хочется на каждый набор элементов создавать отдельный класс, так и утонуть в них недолго.
2. есть решение, использующее ViewTreeObserver - создать слушатель на OnGlobalLayoutListener(), в котором реализовать метод onGlobalLayout. Такое решение можно посмотреть в этом топике. Это уже лучше, но работать с этим не очень удобно, так как нужно поразбираться в isAlive(), не забыть вызвать removeGlobalOnLayoutListener, то есть работать нужно аккуратно.
Мне больше понравилось следующее решение, которое сейчас представляется наиболее и универсальным, и наиболее простым:
1. Создаём интерфейс
public interface OnLayoutCompletedListener {
void layoutCompleted(ViewGroup layout);
}
2. Делаем производный класс от одного из "базовых" layout - например, MyLinearLayout, в котором переопределяем метод onLayout - и в переопределённом методе вызываем layoutCompleted.
public class MyLinearLayout extends LinearLayout {
private OnLayoutCompletedListener layoutCompletedListener;
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (layoutCompletedListener != null) {
layoutCompletedListener.layoutCompleted(this);
}
}
public void setOnLayoutCompletedListener(OnLayoutCompletedListener layoutCompletedListener) {
this.layoutCompletedListener = layoutCompletedListener;
}
}
3. В раскладке Activity (пусть activity_main.xml) заменяем "корневой" LineаrLayout на свежесозданный MyLinearLayout, при этом не забыть установить для него идентификатор:
<my.components.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
... тут обычный набор ...
</my.components.MyLinearLayout>
4. Допиливаем Activity (у меня используются AndroidAnnotations):
@EActivity(R.layout.activity_main)
public class MyActivity extends Activity implements OnLayoutCompletedListener {
@ViewById
MyLinearLayout layout;
@AfterViews
protected void initViews() {
layout.setOnLayoutCompletedListener(this);
...
}
@Override
public void layoutCompleted(ViewGroup layout) {
updateArrowsPosition();
}
}
Вот и всё. Мне нравится то, что получаемое решение интуитивно понятно и набор дополнительных действий - минимален. Всё что нужно сделать (после того, как уже созданы нужные потомки "базовый" раскладок):
1. изменить раскладку для этой активити:
1.1. изменить класс для root-layout
1.2. добавить идентификатор
2. реализовать в активити интерфейс OnLayoutCompletedListener
2.1. добавить интерфейс в implements
2.2. получить экземпляр раскладки - в общем случае через findViewById
2.3. установить для экземпляра раскладки слушатель события
2.4. реализовать обработку события, генерируемого после завершения раскладки.
Вроде вполне несложно.
Комментариев нет:
Отправить комментарий