Reputation: 75
I use a ViewPager with two Fragments: PhotoFragment and GalleryFragment.
If I swipe to the second fragment (GalleryFragment), I always want to recreate that fragment.
But when I swipe back to the first fragment (PhotoFragment), I want that to be pulled from memory.
I tried to implement the solution on this question Update ViewPager dynamically?
I utilize notifyDataSetChanged()
and have overridden the getItemPosition()
to return POSITION_NONE when swiping to the second fragment.
And when I swipe to the second fragment, this gets recreated correctly. But when I swipe back to the first fragment, I notice that this has also been recreated.
I guess returning POSITION_NONE results in the removal and recreating of all fragments in the adapter. I'm a beginner Android developer and have no clue how I could just recreate the second fragment only.
MainActivity with ViewPager:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ViewPager viewPager = findViewById(R.id.viewpager);
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
if (i == 1) {
viewPager.getAdapter().notifyDataSetChanged();
}
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
TabLayout tabLayout = findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
}
}
Adapter:
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return 2;
}
@Override
public Fragment getItem(int i) {
switch (i) {
case 0:
return new PhotoFragment();
case 1:
return new GalleryFragment();
default:
return null;
}
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Photo";
case 1:
return "Gallery";
default:
return null;
}
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
PhotoFragment:
public class PhotoFragment extends Fragment {
private static final int CAMERA_REQUEST_CODE = 0;
private static final int GALLERY_REQUEST_CODE = 1;
private ImageView mCapturedImageView;
private String mCacheFileLocation;
private File mCacheFolder;
private File mGalleryFolder;
private String mImageFileLocation;
private TextView mMonthText;
private FrameLayout stickerFrameLayout;
private static final String TAG = "PhotoFragment";
public PhotoFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_photo, container, false);
mMonthText = rootView.findViewById(R.id.monthText);
mCapturedImageView = rootView.findViewById(R.id.capturedImageView);
ImageButton mCameraButton = rootView.findViewById(R.id.cameraButton);
ImageButton mSaveButton = rootView.findViewById(R.id.saveButton);
stickerFrameLayout = rootView.findViewById(R.id.stickerFrameLayout);
ImageButton mRainbowButton = rootView.findViewById(R.id.buttonRainbow);
ImageButton mDancerButton = rootView.findViewById(R.id.buttonDancer);
ImageButton mGlassesButton = rootView.findViewById(R.id.buttonGlasses);
ImageButton mHeartButton = rootView.findViewById(R.id.buttonHeart);
ImageButton mCrownButton = rootView.findViewById(R.id.buttonCrown);
ImageButton mJorisButton = rootView.findViewById(R.id.buttonJoris);
mMonthText.setVisibility(View.INVISIBLE);
createImageFolders();
View.OnClickListener stickerListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
addSticker(v.getId());
}
};
mRainbowButton.setOnClickListener(stickerListener);
mDancerButton.setOnClickListener(stickerListener);
mGlassesButton.setOnClickListener(stickerListener);
mHeartButton.setOnClickListener(stickerListener);
mCrownButton.setOnClickListener(stickerListener);
mJorisButton.setOnClickListener(stickerListener);
mCameraButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showPictureDialog();
}
});
mSaveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
saveImage();
}
});
return rootView;
}
private void showPictureDialog() {
AlertDialog.Builder pictureDialog = new AlertDialog.Builder(getActivity());
pictureDialog.setTitle("Select Action");
String[] pictureDialogItems = {"Select photo from device", "Capture photo with camera"};
pictureDialog.setItems(pictureDialogItems, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
pickImage();
break;
case 1:
takePhoto();
break;
}
}
});
pictureDialog.show();
}
private void pickImage() {
Intent pickImageIntent = new Intent(Intent.ACTION_PICK);
pickImageIntent.setType("image/*");
String[] mimeTypes = {"image/jpeg", "image/png"};
pickImageIntent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
pickImageIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
startActivityForResult(pickImageIntent, GALLERY_REQUEST_CODE);
mMonthText.setVisibility(View.INVISIBLE);
}
private void takePhoto() {
Intent callCameraApplicationIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (callCameraApplicationIntent.resolveActivity(getActivity().getPackageManager()) != null) {
File file = null;
try {
file = createCacheFile();
} catch (IOException e) {
e.printStackTrace();
}
if (file != null) {
Uri photoURI = FileProvider.getUriForFile(getActivity(), "com.jorisvanlaar.employeeofthemonth.fileprovider", file);
callCameraApplicationIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(callCameraApplicationIntent, CAMERA_REQUEST_CODE);
mMonthText.setVisibility(View.INVISIBLE);
}
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == getActivity().RESULT_OK) {
switch (requestCode) {
case GALLERY_REQUEST_CODE:
Bitmap bitmap = null;
if (data.getData() != null) {
try {
bitmap = BitmapFactory.decodeStream(getActivity().getContentResolver().openInputStream(data.getData()));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
try {
writeBitmapToFile(bitmap, createCacheFile());
} catch (IOException e) {
e.printStackTrace();
}
rotateImage(reduceImageSize());
break;
case CAMERA_REQUEST_CODE:
rotateImage(reduceImageSize());
break;
}
}
}
private void createImageFolders() {
File storageDirectory = getActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
mCacheFolder = new File(storageDirectory, "cached files");
if (!mCacheFolder.exists()) {
mCacheFolder.mkdirs();
}
mGalleryFolder = new File(storageDirectory, "Image Gallery");
if (!mGalleryFolder.exists()) {
mGalleryFolder.mkdirs();
}
}
@SuppressLint("SimpleDateFormat")
private File createCacheFile() throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String cacheFileName = "IMG_" + timeStamp + "_";
File image = File.createTempFile(cacheFileName, ".jpg", mCacheFolder);
mCacheFileLocation = image.getAbsolutePath();
return image;
}
@SuppressLint("SimpleDateFormat")
private File createImageFile() {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "EMPL_" + timeStamp + "_.jpg";
File image = new File(mGalleryFolder, imageFileName);
mImageFileLocation = image.getAbsolutePath();
return image;
}
private void writeBitmapToFile(Bitmap bitmap, File destination) {
FileOutputStream out = null;
try {
out = new FileOutputStream(destination);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
} catch (Exception ex) {
Log.i(TAG, "Error writing bitmap to file");
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
private Bitmap reduceImageSize() {
int targetImageViewWidth = mCapturedImageView.getWidth();
int targetImageViewHeight = mCapturedImageView.getHeight();
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mCacheFileLocation, bmOptions);
int cameraImageWidth = bmOptions.outWidth;
int cameraImageHeight = bmOptions.outHeight;
int scaleFactor = Math.min(cameraImageWidth / targetImageViewWidth, cameraImageHeight / targetImageViewHeight);
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
Bitmap photoReducedSizeBitmap = BitmapFactory.decodeFile(mCacheFileLocation, bmOptions);
return photoReducedSizeBitmap;
}
private void rotateImage(Bitmap bitmap) {
ExifInterface exifInterface = null;
try {
exifInterface = new ExifInterface(mCacheFileLocation);
} catch (IOException e) {
e.printStackTrace();
}
int orientation = 0;
if (exifInterface != null) {
orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
}
Matrix matrix = new Matrix();
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.setRotate(90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.setRotate(180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.setRotate(270);
break;
default:
}
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
mCapturedImageView.setImageBitmap(rotatedBitmap);
}
@SuppressLint("SetTextI18n")
private void saveImage() {
Calendar calendar = Calendar.getInstance();
int currentMonth = calendar.get(Calendar.MONTH);
MonthCollection monthCollection = new MonthCollection();
mMonthText.setText("Employee of " + monthCollection.getMonth(currentMonth));
mMonthText.setVisibility(View.VISIBLE);
Bitmap bitmap = Bitmap.createBitmap(stickerFrameLayout.getWidth(), stickerFrameLayout.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
stickerFrameLayout.draw(canvas);
writeBitmapToFile(bitmap, createImageFile());
Toast.makeText(getActivity(), "Image saved!", Toast.LENGTH_SHORT).show();
}
private void addSticker(int id) {
StickerImageView sticker = new StickerImageView(getActivity());
switch (id) {
case R.id.buttonRainbow:
sticker.setImageDrawable(getResources().getDrawable(R.drawable.sticker_rainbow));
break;
case R.id.buttonDancer:
sticker.setImageDrawable(getResources().getDrawable(R.drawable.sticker_dancer));
break;
case R.id.buttonGlasses:
sticker.setImageDrawable(getResources().getDrawable(R.drawable.sticker_sunglasses));
break;
case R.id.buttonHeart:
sticker.setImageDrawable(getResources().getDrawable(R.drawable.sticker_heart));
break;
case R.id.buttonCrown:
sticker.setImageDrawable(getResources().getDrawable(R.drawable.sticker_crown));
break;
case R.id.buttonJoris:
sticker.setImageDrawable(getResources().getDrawable(R.drawable.sticker_joris));
break;
default:
throw new RuntimeException("Unknown button ID");
}
stickerFrameLayout.addView(sticker);
}
}
GalleryFragment:
public class GalleryFragment extends Fragment {
private RecyclerView mRecyclerView;
private File mGalleryFolder;
private static int mColumnCount = 3;
private static int mImageWidth;
private static int mImageHeight;
public GalleryFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_gallery, container, false);
mRecyclerView = rootView.findViewById(R.id.galleryRecyclerView);
mGalleryFolder = getActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES + "/Image Gallery");
DisplayMetrics displayMetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
mImageWidth = displayMetrics.widthPixels / mColumnCount;
mImageHeight = mImageWidth * 4 / 3;
GridLayoutManager layoutManager = new GridLayoutManager(getActivity(), mColumnCount);
mRecyclerView.setLayoutManager(layoutManager);
File[] sortedGalleryFolder = sortFilesToLatest(mGalleryFolder);
RecyclerView.Adapter imageAdapter = new ImageAdapter(sortedGalleryFolder, mImageWidth, mImageHeight);
mRecyclerView.setAdapter(imageAdapter);
return rootView;
}
private File[] sortFilesToLatest(File imageDirectory) {
File[] files = imageDirectory.listFiles();
Arrays.sort(files, new Comparator<File>() {
@Override
public int compare(File o1, File o2) {
return Long.valueOf(o2.lastModified()).compareTo(o1.lastModified());
}
});
return files;
}
}
Upvotes: 1
Views: 443
Reputation: 1377
The first fragment is recreated because when you call notifyDataSetChanged()
it affects both fragments, not only the i == 1
fragment
The solution based on your current state is this
@Override
public int getItemPosition(@NonNull Object object) {
if (object instanceof PhotoFragment) {
return POSITION_UNCHANGED;
}
return POSITION_NONE;
}
However this is not optimal as the scroll is sluggish, you could try to use a child fragment to achieve what you want
Upvotes: 1
Reputation: 577
Use viewpager.setOffscreenPageLimit(1);
This will set the number of pages that should be retained to side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed.
Upvotes: 0
Reputation: 637
You can store your first fragment in a variable and reuse it and create the second one each time. Something like :
@Override
public Fragment getItem(int i) {
switch (i) {
case 0:
if(photoFragment == null) {
photoFragment = new PhotoFragment();
}
return photoFragment;
case 1:
return new GalleryFragment();
default:
return null;
}
}
Upvotes: 0