我正在创建一个应用程序,其中一个活动用于显示带有标题的图像。这些图像使用ViewPager(一页中的每个图像)显示。
创建图像并使用字幕显示它们没有问题。但是,当我旋转设备时,我遇到了问题。在设备旋转时,图像被删除,但字幕是保留的。我想我知道原因(正如下面的代码片段后所解释的那样),但我无法解决它,因此我问这个问题。
首先,这是我的gallery_item.xml
文件,用于显示每个图像:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView android:id="@+id/gallery_image"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:scaleType="fitCenter"
android:background="@color/black" />
<TextView android:id="@+id/gallery_caption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="right"
android:textSize="20sp"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:textColor="@color/white"
android:background="@color/transparent_black_percent_50" />
</FrameLayout>
然后活动代码GalleryItemPagerActivity.java
低于
public class GalleryItemPagerActivity extends Activity {
private ViewPager mViewPager;
private NewsItem mNewsItem;
private int mNewsItemId;
ImageDownloaderThread<ImageView> mImageDownloaderThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.galleryItemViewPager);
setContentView(mViewPager);
// Create a thread that will be run in the background to download images from the web for each URL
//
// IMPORTANT NOTE:
// We are downloading the image in the activity instead of the fragment because we are use a pager activity
// ... and pager activities must have the adapter in the activity rather than the fragment, which causes us to download the image here
// ... if we try to download in the fragment, then we will have a very complicated code that will cause too many memory leaks.
mImageDownloaderThread = new ImageDownloaderThread<ImageView>(new Handler());
// Prepare listener to update UI when an image is downloaded
mImageDownloaderThread.setListener(new ImageDownloaderThread.Listener() {
// This method is used to update the UI when the image ready
public void onImageDownloaded(Bitmap image, String url, int pos, int download_image_type) {
// First, get the fragment by position
GalleryItemFragment fragment = ((GalleryItemFragmentStatePagerAdapter)mViewPager.getAdapter()).getFragment(pos);
// If the fragment exists
if (fragment != null) {
// Update the image
ImageView imageView = (ImageView) fragment.getView().findViewById(R.id.gallery_image);
imageView.setImageBitmap(image);
}
}
});
// Start the background thread
mImageDownloaderThread.start();
// Prepare the looper for the background thread
mImageDownloaderThread.getLooper();
mNewsItemId = getIntent().getIntExtra(GalleryItemFragment.EXTRA_NEWS_ITEM_ID, 0);
mNewsItem = NewsItems.get(this).getNewsItem(mNewsItemId);
FragmentManager fm = getFragmentManager();
GalleryItemFragmentStatePagerAdapter adapter = new GalleryItemFragmentStatePagerAdapter(fm);
mViewPager.setAdapter(adapter);
}
// Destroy the background thread when we exit the app, otherwise it will stay forever
@Override
public void onDestroy() {
super.onDestroy();
mImageDownloaderThread.quit();
}
// I am subclassing FragmentStatePagerAdapter so I can add the method getFragment()
// ... This method allows me to get the displayed fragment by position, and if it does not exist, a null is returned.
private class GalleryItemFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
// A map to track current fragments.
private Map<Integer, GalleryItemFragment> mFragmentReferenceMap = new HashMap<Integer, GalleryItemFragment>();
// Constructor
public GalleryItemFragmentStatePagerAdapter(FragmentManager fm) {
super(fm);
}
// Implementation of get count.
@Override
public int getCount() {
// The number of images to download is the number of our fragments
int count = mNewsItem.getImages().size();
return count;
}
// Fragment to display, and download the main image and author avatar
@Override
public Fragment getItem(int pos) {
// Download this image
String imageUrl = mNewsItem.getImages().get(pos).getUrl();
// Initiate a request to download the image at the background thread
mImageDownloaderThread.queueImage(imageUrl, pos, ImageDownloaderThread.DOWNLOAD_IMAGE_TYPE_MAIN);
// Get the fragment
GalleryItemFragment fragment = GalleryItemFragment.newInstance(mNewsItemId, pos);
// Add the fragment to our map
mFragmentReferenceMap.put(Integer.valueOf(pos), fragment);
return fragment;
}
// When a fragment is deleted, we have to remove it from the map
@Override
public void destroyItem(ViewGroup container, int pos, Object object) {
mFragmentReferenceMap.remove(Integer.valueOf(pos));
// Remove the fragment from the map
mFragmentReferenceMap.remove(Integer.valueOf(pos));
}
// Get the fragment by position (returns null if fragment does not exist)
public GalleryItemFragment getFragment(int key) {
return mFragmentReferenceMap.get(Integer.valueOf(key));
}
}
}
该片段的代码正好在GalleryItemFragment.java
public class GalleryItemFragment extends Fragment {
public static final String EXTRA_NEWS_ITEM_ID = "com.myproject.android.news_item_id";
public static final String EXTRA_GALLERY_ITEM_POSITION = "com.myproject.android.gallery_item_position";
private int mNewsItemId;
private int mGalleryItemPosition;
private TextView mCaptionField;
// This is used to set attach arguments to the fragment
public static GalleryItemFragment newInstance (int news_item_id, int gallery_item_position) {
Bundle args = new Bundle();
args.putInt(EXTRA_NEWS_ITEM_ID, news_item_id);
args.putInt(EXTRA_GALLERY_ITEM_POSITION, gallery_item_position);
GalleryItemFragment fragment = new GalleryItemFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNewsItemId = getArguments().getInt(EXTRA_NEWS_ITEM_ID);
mGalleryItemPosition = getArguments().getInt(EXTRA_GALLERY_ITEM_POSITION);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
// inflate our xml file first
View v = inflater.inflate(R.layout.gallery_item, parent, false);
mCaptionField = (TextView)v.findViewById(R.id.gallery_caption);
mCaptionField.setText(NewsItems.get(getActivity()).getNewsItem(mNewsItemId).getImages().get(mGalleryItemPosition).getCaption());
return v;
}
}
现在问题。
如前所述,当我旋转设备时,活动被销毁,但片段被保留(如接受的答案here中所述)。
另外,您注意到我的案例中的图像是在活动而不是片段中下载的(由于使用了寻呼机活动,这要求其适配器位于活动而不是片段中。)
因此,当活动被破坏时,图像消失了(但字幕会被保留,因为它们是保留片段的一部分)。这导致黑色页面没有图像,但只有字幕。
现在,我阅读了不同的解决方案(例如this one),并尝试将以下行添加到AndroidManifest.xml
android:configChanges="orientation|screenSize"
但这并没有多大帮助,因为在旋转设备后左右滑动时图像看起来“不成比例”,因为screenSize
属性保留了屏幕尺寸,从而产生了有趣的图像。 / p>
现在,我想我应该尝试onSaveInstanceState()
,但我不知道该怎么做。如何保存图像并使用onSaveInstanceState()
保留图像?我的活动类中的其他变量(例如mViewPager
)如何保存呢?
感谢。
答案 0 :(得分:1)
请参阅,根据设计,每当设备配置发生变化时,活动或片段都会被破坏,但是如果你想自己处理它们,那么android会为你提供与APIS相同的设施,如
onSaveInstanceState() -- you can save and restore the instance stae
onConfigurationChanged() -- you can handle the configuration changes which you have declared to handle yourself, like in your case "orientation|screenSize"
除此之外,您可以在片段中使用setRetainInstance
API在活动被销毁时将对象保存在片段中,您可以阅读它here,但是这个api确实提到不要保留像Bitmaps这样的对象。
现在出现问题,这就是你应该做的。
您可以在我的guthub链接中查看。
答案 1 :(得分:0)
您的问题是在onconfigurationchanged上重新加载,在清单文件中给予该活动文件的权限,如下所示:
android:configChanges="orientation|keyboard"