РЕДАКТИРОВАТЬ 3:
Я поддерживаю первоначальный вопрос ниже по историческим причинам. Однако я обнаружил, что проблема не изолирована до FrameLayout
. Следовательно, обновлен заголовок.
Вместо того, чтобы переполнять этот пост большим количеством кода, я создал пример проекта, который демонстрирует проблему; и загрузил в Google Project Hosting.
Краткое описание проблемы:
Рисование в портретной ориентации с определенными границами, на
изменение ориентации и возврат к портрету, не сохраняет
установить границы. Вместо этого он возвращается к своим первоначальным границам. Это в
несмотря на принудительную установку явных границ изменения ориентации.
Обратите внимание, что любые ограничения, которые вы позже установили для Click и т. Д., Выполняются.
Я также загрузил версию приложения , которая содержит 2 отдельных действия, иллюстрирующих проблему.
- Обычная ванильная активность, которая только иллюстрирует проблему.
- Активность, использующая пользовательский
BitmapDrawable
для ImageView
- этот выводится в журнал при каждом изменении границ.
Вторая версия ясно показывает, что даже если я установлю границы на Drawable, эти границы не будут соблюдаться в onBoundsChange()
.
Оригинальный вопрос:
Я использую FrameLayout
с 2 ImageView
s, расположенными один над другим для отображения графики «Состояние батареи». Это в портретном режиме. В ландшафтном режиме я отображаю другой макет (график).
Моя проблема заключается в следующем - предположим, отображается состояние батареи - скажем, 30%. Теперь я поворачиваю экран и отображаю график. Когда я возвращаюсь в книжную ориентацию, изображение батареи возвращается в исходное положение (что является "полным").
Я пробовал все виды вещей, чтобы попытаться выяснить, что происходит. Отладка показывает мне, что границы для «верхней» графики действительно устанавливаются в соответствии с ожиданиями. Так что, похоже, это проблема недействительности. Я представляю код для 2 классов и 2 макетов XML (все упрощенно), который помогает воспроизвести проблему. Также прикрепление заполнителей PNG, используемых для ImageViews.
Может кто-нибудь заметить ошибку? Чтобы воссоздать проблему, запустите приложение и нажмите кнопку «Обновить». Графика будет «заполнена» до определенного уровня. Затем переключитесь в альбомную ориентацию, а затем обратно в портрет. Графика не помнит свое предыдущее значение.
Активность:
public class RotateActivity extends Activity {
private View portraitView, landscapeView;
private LayoutInflater li;
private Configuration mConfig;
private ValueIndicator indicator;
private Button btn;
private Random random = new java.util.Random();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
li = LayoutInflater.from(this);
portraitView = li.inflate(R.layout.portrait, null);
landscapeView = li.inflate(R.layout.landscape, null);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mConfig = newConfig;
initialize();
}
@Override
protected void onResume() {
mConfig = getResources().getConfiguration();
initialize();
super.onResume();
}
private void initialize(){
if(mConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
displayLandscape();
} else {
displayPortrait();
}
}
private void displayLandscape() {
setContentView(landscapeView);
}
private void displayPortrait() {
setContentView(portraitView);
btn = (Button)portraitView.findViewById(R.id.button1);
indicator = (ValueIndicator)portraitView.findViewById(R.id.valueIndicator1);
/*
* Forcing the graphic to perform redraw to its known state when we return to portrait view.
*/
indicator.updateIndicatorUi();
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
updateIndicator(random.nextInt(100));
}
});
}
private void updateIndicator(int newValue){
indicator.setPercent(newValue);
}
}
portrait.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.gs.kiran.trial.inval.ValueIndicator
android:id="@+id/valueIndicator1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Update" />
</LinearLayout>
ValueIndicator.java
public class ValueIndicator extends FrameLayout {
private ImageView ivFrame, ivContent;
private Drawable drFrame, drContent;
private Rect mBounds;
private int currentTop;
private int mPercent;
public ValueIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View root = li.inflate(R.layout.indicator, this, true);
ivFrame = (ImageView) root.findViewById(R.id.ivFrame);
ivContent = (ImageView) root.findViewById(R.id.ivContent);
drFrame = ivFrame.getDrawable();
drContent = ivContent.getDrawable();
mBounds = drFrame.getBounds();
Log.d(Constants.LOG_TAG, "Constructor of ValueIndicator");
}
public void setPercent(int newPercent){
this.mPercent = newPercent;
updateIndicatorUi();
}
public void updateIndicatorUi(){
Rect newBounds = new Rect(mBounds);
newBounds.top = mBounds.bottom - (int)(this.mPercent * mBounds.height() / 100);
currentTop = newBounds.top;
Log.d(Constants.LOG_TAG, "currentTop = "+currentTop);
drContent.setBounds(newBounds);
//invalidateDrawable(drContent);
invalidate();
}
}
Indicator.xml (XML для FrameLayout, используемый в пользовательском представлении)
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/frameLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<ImageView
android:id="@+id/ivFrame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/frame" />
<ImageView
android:id="@+id/ivContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/content" />
</FrameLayout>
landscape.xml (фиктивный заполнитель - достаточно для воссоздания проблемы)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Landscape" />
</LinearLayout>
Фрагмент AndroidManifest.xml:
<activity
android:label="@string/app_name"
android:name=".RotateActivity"
android:configChanges="orientation|keyboardHidden">
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
EDIT
Я также пытался вызвать setPercent()
и передать сохраненное значение в displayPortraitView()
- в основном принудительно обновляя известное состояние всякий раз, когда мы возвращаемся в портретный режим. Все еще не повезло. Обратите внимание, что журналы говорят мне, что границы рисования правильны. Я не могу понять, почему недействительность не происходит.
РЕДАКТИРОВАТЬ 2:
- В ValueIndicator.java я ввел переменную-член
mPercent
который всегда хранит последнее известное процентное значение.
- Обновлен код
setPercent()
для обновления переменной-члена mPercent
; а затем вызовите метод updateIndicateUi()
.
updateIndicatorUi()
(который теперь является public
методом) теперь использует состояние (т.е. mPercent
) для своей работы.
- Всякий раз, когда мы возвращаемся в портретный режим, я звоню
updateIndicatorUi()
. Это заставляет само изображение батареи обновляться.
Я также обновил код, чтобы отразить эти изменения.
Идея состоит в том, чтобы принудительно перерисовывать и аннулировать всякий раз, когда мы возвращаемся из ландшафтного в портретный режим. Опять же - я вижу, что границы элемента «содержимого» батареи устанавливаются по желанию, но пользовательский интерфейс отказывается идти в ногу.