Обновите изображения на FragmentStatePagerAdapter при возобновлении активности
Я создал операцию, которая использует FragmentStatePagerAdapter
для создания небольшой галереи. Тем не менее, я не могу заставить его обновляться, когда активность возобновляется (например, после возвращения из другой активности). Каждый раз, когда первые два снимка будут пустыми, и только после того, как я проведу два снимка в сторону, они будут обновлены. Ни один из ответов, которые я нашел (особенно переопределение getItemPosition()
)
Я установил его так:
mPagerAdapter = new PhotosPagerAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.photosViewPager);
mPager.setAdapter(mPagerAdapter);
Затем у меня есть класс FragmentStatePagerAdapter:
private class PhotosPagerAdapter extends FragmentStatePagerAdapter{
public PhotosPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return photos.size();
}
@Override
public Fragment getItem(int position) {
ImageFragment f = new ImageFragment(position);
return f;
}
@Override
public int getItemPosition(Object object) {
throw new RuntimeException();
//return POSITION_NONE;
}
}
Как вы, вероятно, заметили, я выбрал RuntimeException в getItemPosition, потому что я хотел проверить, когда он вызвал. И он не вызывается, пока я не добавлю что-то в список, содержащий мои фотографии. Затем класс ImageFragment:
public class ImageFragment extends Fragment{
int position;
Bitmap mBitmap;
int width;
int height;
ImageView img;
public ImageFragment(){
}
public ImageFragment(int position){
this.position = position;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
img = new ImageView(container.getContext());
img.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
width = container.getWidth();
height = container.getHeight();
loadBitmap();
return img;
}
public void loadBitmap(){
if (img == null){
return;
}
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(photos.get(position), options);
options.inSampleSize = calculateInSampleSize(options, width/2, height/2);
options.inJustDecodeBounds = false;
mBitmap = BitmapFactory
.decodeFile(photos.get(position), options);
img.setImageBitmap(mBitmap);
}
@Override
public void onDestroyView() {
mBitmap.recycle();
super.onDestroyView();
}
}
Код выглядит бесполезным после того, как я попытался его исправить... Но: удаление onDestroyView()
не работает. Я поместил mPagerAdapter.notifyDataSetChanged()
в несколько мест, которые должны быть вызваны (например, onResume()
), без результата. Я получаю от этого отчаяние.
Ответы
Ответ 1
Работа с адаптерами пейджера фрагментов может быть PITA.
Вот несколько полезных советов:
ViewPager PagerAdapter не обновляет представление
Динамическое обновление ViewPager?
Вообще говоря, это работает 99% времени...
Переопределите getItemPosition в вашем PagerAdapter следующим образом:
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
Иногда даже те не работают, я должен использовать метод "грубой силы" и снова воссоздавать весь вид (начиная с onCreate)...
Ответ 2
У меня была аналогичная проблема, подобная вашей, после некоторых исследований я создал приятный script, который позволяет избежать повторного создания фрагментов, которые уже существуют, но в то же время позволяют обновлять все видимые/инициированные фрагменты.
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.view.ViewGroup;
import java.util.WeakHashMap;
/**
* Created by TheCobra on 9/17/15.
*/
public abstract class FragmentAdvanceStatePagerAdapter extends FragmentStatePagerAdapter
{
private WeakHashMap<Integer, Fragment> mFragments;
public FragmentAdvanceStatePagerAdapter(FragmentManager fm)
{
super(fm);
mFragments = new WeakHashMap<Integer, Fragment>();
}
@Override
public Fragment getItem(int position)
{
Fragment item = getFragmentItem(position);
mFragments.put(Integer.valueOf(position), item);
return item;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object)
{
super.destroyItem(container, position, object);
Integer key = Integer.valueOf(position);
if (mFragments.containsKey(key))
{
mFragments.remove(key);
}
}
@Override
public void notifyDataSetChanged()
{
super.notifyDataSetChanged();
for (Integer position : mFragments.keySet())
{
//Make sure we only update fragments that should be seen
if (position != null && mFragments.get(position) != null && position.intValue() < getCount())
{
updateFragmentItem(position, mFragments.get(position));
}
}
}
@Override
public int getItemPosition(Object object)
{
//If the object is a fragment, check to see if we have it in the hashmap
if (object instanceof Fragment)
{
int position = findFragmentPositionHashMap((Fragment) object);
//If fragment found in the hashmap check if it should be shown
if (position >= 0)
{
//Return POSITION_NONE if it shouldn't be display
return (position >= getCount()? POSITION_NONE : position);
}
}
return super.getItemPosition(object);
}
/**
* Find the location of a fragment in the hashmap if it being view
* @param object the Fragment we want to check for
* @return the position if found else -1
*/
protected int findFragmentPositionHashMap(Fragment object)
{
for (Integer position : mFragments.keySet())
{
if (position != null &&
mFragments.get(position) != null &&
mFragments.get(position) == object)
{
return position;
}
}
return -1;
}
public abstract Fragment getFragmentItem(int position);
public abstract void updateFragmentItem(int position, Fragment fragment);
}
Скопируйте этот код в имя файла "FragmentAdvanceStatePagerAdapter.java". Теперь в вашем адаптере, простирайтесь от этого и переопределите "getFragmentItem()" и "updateFragmentItem()". Всякий раз, когда вы вызываете notifydatachange(), updateFragmentItem() будет вызываться со всеми уже созданными фрагментами. Когда адаптеру необходимо создать новый фрагмент, вызывается getFragmentItem().
Надеюсь, что сэкономить и помочь многим людям:)
Удачи и счастливого программирования!!!
P.S. ваш адаптер "getItem()" должен быть "getFragmentItem()", если вы используете этот класс.
Ответ 3
Вместо того, чтобы возвращать POSITION_NONE
и снова создавать все фрагменты, вы можете сделать так, как я предложил здесь: Динамически обновлять ViewPager?
Ответ 4
Никогда не используйте POSITION_NONE
if(fragmentManager.getFragments().contains(object))
return POSITION_NONE;
else
return POSITION_UNCHANGED;
чтобы избежать Fatal Exception: java.lang.IllegalStateException
Фрагмент {} в настоящее время отсутствует в FragmentManager
Ответ 5
У меня были проблемы с FragmentStatePagerAdapter, который не обновлялся даже с помощью notifyDataSetChanged(), проблема в том, что фрагменты хранятся в backstack и используются повторно. так что давайте просто удалим их перед применением новых данных и затем вызовем notifyDataSetChanged
Котлин версия:
val backStackEntry = supportFragmentManager.backStackEntryCount
if (backStackEntry > 0) {
for (i in 0 until backStackEntry) {
supportFragmentManager.popBackStackImmediate()
}
}