Viewpager fragment selector state - android

I'm trying to create a viewpager where the user can tap a photo and the photo will take a 'highlighted' state.
I achieve the 'highlighted' state by setting a FrameLayout overlay above the image. The problem is when i swipe trough the pages the state is not only lost but it appears on non selected images.
This is my viewpager adapter:
public class ImageSliderAdapter extends FragmentPagerAdapter {
//Image ids from constants
private int[] Images = Constants.Images;
private int mCount = Images.length;
public ImageSliderAdapter$Adapter(FragmentManager fm) {
super(fm);
}
// The displayed fragment for each position
#Override
public FragmentImageSlider getItem(int position) {
return FragmentImageSlider.newInstance(Images[position];
}
#Override
public int getCount() {
return mCount;
}
}
And this is the fragment that the viewpager returns:
public class FragmentImageSlider extends Fragment {
//Image resource id to show
int imageResourceId;
//Image view of the pic displayed
ImageView image;
// View to indicate highlighted images
View highliteView;
public static FragmentImageSlider newInstance(int imageResourceId){
FragmentImageSlider f = new FragmentImageSlider();
Bundle b = new Bundle();
b.putInt("image", imageResourceId);
f.setArguments(b);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (getArguments() !=null) {
//get image resource id from extras
imageResourceId = getArguments().getInt("image");
}
View root = inflater.inflate(R.layout.displayLayout, null);
image = (ImageView) root.findViewById(R.id.imageView);
highliteView = root.findViewById(R.id.highliteView);
image.setImageResource(imageResourceId);
//When the image is clicked, toggle the highlighted state
image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//toggle state
//this view has a selector and changes when selected
highliteView.setSelected(!highliteView.isSelected());
}
});
return root;
}
}
So my question is, how can i keep the highlighted state?

You can change the visibility of your highlight view instead of selected state.
By default set the visibility of highlight view as GONE and when the image is selected change it to visible.
highliteView.setVisibility(View.GONE);
// when the image is clicked show the highlighted view
image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
highliteView.setVisibility(View.VISIBLE);
}
});

According to the [FragmentPagerAdapter document](view hierarchy may be destroyed when not visible),
The fragment of each page the user visits will be kept in memory, though its view hierarchy may be destroyed when not visible
The view maybe reloaded from default layout when it go out of the view, that's why the highlight state is lost.
You should keep the highlight state in your Fragment:
public MyFragment extends Fragment{
//variable to keep the state
private boolean highlight;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//rest of your other code
image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
highliteView.setSelected(!highliteView.isSelected());
//set the highlight state when clicked
highlight = !highliteView.isSelected();
}
});
return root;
}
//set the view state to the image view
public void onViewCreated(View view, Bundle savedInstanceState){
highliteView.setSelected(highlight);
}
}

Related

Set all SwipeCards in SwipeCardsView

I have set recycler view in one fragment which consist of 20 records.When I click on any one of cardview a swipeard is displayed in next Fragment.I am passing some data from cardview to swipecard.I need to set all 20 SwipeCards after moving to SwipeCard Fragment.But only one SwipeCard is displayed.
This is code for setting swipecard
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v=inflater.inflate(R.layout.temp_user_status_info, container, false);
swipeCardsView=(SwipeCardsView)v.findViewById(R.id.swipeCard);
infoAdapter=new CurrentInfoAdapter(info,getActivity());
swipeCardsView.retainLastCard(false);
swipeCardsView.enableSwipe(true);
person_name=getArguments().getString("name");
person_people=getArguments().getString("people");
person_estimate=getArguments().getString("estimate");
CurrentInfo currentInfo=new CurrentInfo();
currentInfo.setName(person_name);
currentInfo.setPeople(person_people);
currentInfo.setEstimate(person_estimate);
Log.i("name",person_name);
Log.i("people",person_people);
Log.i("estimate",person_estimate);
info.add(currentInfo);
swipeCardsView.setAdapter(infoAdapter);
return v;
This is code for RecyclerView Adapter from where I am passing data to SwipeCardView
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
final CurrentEntry current=filterList.get(position);
// holder.number.setText(current.getNo());
holder.Name.setText(current.getName());
holder.People.setText(current.getPeople());
holder.Estimate.setText(current.getEstimate());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String name=current.getName();
String people=current.getPeople();
String estimate=current.getEstimate();
Bundle args=new Bundle();
args.putString("name",name);
args.putString("people",people);
args.putString("estimate",estimate);
// UserSatusInfo satusInfo=new UserSatusInfo();
// satusInfo.setArguments(args);
TempUserStatusInfo info=new TempUserStatusInfo();
info.setArguments(args);
AppCompatActivity activity=(AppCompatActivity)v.getContext();
// activity.getSupportFragmentManager().beginTransaction().replace(R.id.frame,satusInfo).addToBackStack("info").commit();
activity.getSupportFragmentManager().beginTransaction().replace(R.id.frame,info).addToBackStack("info").commit();
Toast.makeText(v.getContext(),name,Toast.LENGTH_SHORT).show();
Log.i("name",name);
}
});
How to get all Cards at a same time after clicking any one card from Recycclerview
You can pass you complete arraylist
bundle.putSerializable("key", ArrayList<T extends Serializable> list);
and then in fragment recieve
ArrayList<T extends Serializable> transactionList = (ArrayList<T extends Serializable>)getArguments().getSerializable("key");
Hope this helps.

How can i change Image ImageButton from another activity with another Fragment

I have a Fragment (CreaStanzaFragment) with an ImageButton.
This ImageButton has a picture.
Now when I click on this imagebutton, he open a new class (Gallery, an activity with lot of images). When I click on one of this image I need that the ImageButton (In fragment) change and open this fragment with this new picture in imagebutton.
CreaStanzaFragment belongs to a navigationdrawer.
This is Fragment code:
public class CreaStanzaFragment extends Fragment {
public CreaStanzaFragment(){}
ImageButton icon;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activity_log_creastanza, container, false);
return rootView;
}
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
icon = (ImageButton) view.findViewById(R.id.newicon);
icon.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getContext(), Galleria.class));
}
});
}
here i post only important parts of Galleria
public class Galleria extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gallery_main); //where all the images are located
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
// ......
recyclerView.addOnItemTouchListener(new GalleryAdapter.RecyclerTouchListener(getApplicationContext(), recyclerView, new GalleryAdapter.ClickListener() {
#Override
public void onClick(View view, int position) {
Bundle bundle = new Bundle();
bundle.putSerializable("images", images);
bundle.putInt("position", position);
Image image = images.get(position);
String ilnome = inomi.get(position);
Log.i("ILNOME", ilnome);
// What i need to do here to change pictures of ImageButton? How can i open Fragment?
ImageButton icon = (ImageButton) findViewById(R.id.newicon); //loceted in Fragment (CreaStanzaFragment)
imageButton.setBackgroundResource(0);
Glide.with(getApplicationContext()).load(image.getMedium())
.thumbnail(0.5f)
.crossFade()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageButton);
fragmentTransaction.commit();
}
Whit this code:
setContentView(R.layout.activity_log_creastanza);
ImageButton imageButton = (ImageButton) findViewById(R.id.newicon);
imageButton.setBackgroundResource(0);
Glide.with(getApplicationContext()).load(image.getMedium())
.thumbnail(0.5f)
.crossFade()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageButton);
When i open CreaStanzaFragment:
When i click on blue picture (open Galleria activity)
When i click on 1 of pictures:
WHY?
Basic Problem is that you need to access your Fragment from the Galleria Instance. For that you can temporarily "pass" the current Fragment to the Galleria.
Galleria Class holding a reference to current CreaStanzaFragment:
public class Galleria extends Activity {
private static CreaStanzaFragment currentCreaStanzaFragment = null;
public static void setFragment(CreaStanzaFragment f) {currentCreaStanzaFragment = f;}
public static CreaStanzaFragment getFragment() {return currentCreaStanzaFragment;}
...
In CreaStamzaFragment you set the Fragment Reference before Intent Action is invoked:
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
icon = (ImageButton) view.findViewById(R.id.newicon);
icon.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
// pass Fragment Reference
Galleria.setFragment(CreaStamzaFragment .this)
startActivity(new Intent(getContext(), Galleria.class));
}
});
}
In Galleria on Adapter Action, access the fragment and modify the imageButton (implies that fragment is still active in background and is not reloaded):
recyclerView.addOnItemTouchListener(new GalleryAdapter.RecyclerTouchListener(getApplicationContext(), recyclerView, new GalleryAdapter.ClickListener() {
#Override
public void onClick(View view, int position) {
Bundle bundle = new Bundle();
bundle.putSerializable("images", images);
bundle.putInt("position", position);
Image image = images.get(position);
String ilnome = inomi.get(position);
Log.i("ILNOME", ilnome);
// get imagebutton on fragment
CreaStamzaFragment f = Galleria.getFragment()
ImageButton icon = (ImageButton) f. getView().findViewById(R.id.newicon); //loceted in Fragment (CreaStanzaFragment)
// load as icon
icon.setBackgroundResource(0);
Glide.with(getApplicationContext()).load(image.getMedium())
.thumbnail(0.5f)
.crossFade()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(icon);
// explicit release reference
Galleria.setFragment(null);
// close galliera activity
Galleria.this.finish();
}
...
Note: Be aware that this introduces coupling between the Fragment and Galleria, a cleaner but possibly more complex process would be for example to use a Notification (Observer Pattern) from "Notifier" Galleria to "Listener" Fragment that an Image was selected and the Fragment should load the image instead in the notification handler.
Yet it is fast to implement and sufficient if Galleria/CreaStanzaFragment are not to be reused.

Android memory leak issue when using ViewPagerAdapter with nested fragments

I have a fragment, fragment A, which holds a ViewPager. The ViewPager loads different fragments which the user can swipe through "indefinitely" (I use a really high number of pages/loops to emulate this). When a user clicks on the current ViewPager fragment, then fragment A with the ViewPager is replaced by fragment B in the fragment manager. When the user returns from fragment B, the backstack is popped using popBackStackImmediate(). If the user repeats this action several times, the heap begins to fill up by about 100kb at a time until the app starts to become sloppy and malfunction as the memory fills up. I'm unsure what exactly is causing this, can anyone help?
My fragment A with the ViewPager:
public class MainFragment extends Fragment {
private MainWearActivity mMainWearActivity;
View view;
private int currentPage;
private ViewPager pager;
private ViewPagerAdapter adapter;
private LinearLayout helpIcons;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMainWearActivity = (MainWearActivity) getActivity();
adapter = new ViewPagerAdapter(this.getChildFragmentManager());
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_main, container, false);
// Scrolling menu
pager = (ViewPager) view.findViewById(R.id.watchNavPager);
pager.setAdapter(adapter);
pager.addOnPageChangeListener(adapter);
// Set current item to the middle page
pager.setCurrentItem(Consts.FIRST_PAGE);
currentPage = Consts.FIRST_PAGE;
// Set number of pages
pager.setOffscreenPageLimit(4);
// Set no margin so other pages are hidden
pager.setPageMargin(0);
return view;
}
#Override
public void onDestroyView() {
pager = null;
super.onDestroyView();
}
}
My adapter class:
public class ViewPagerAdapter extends FragmentPagerAdapter implements
ViewPager.OnPageChangeListener {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position)
{
position = position % Consts.PAGES;
switch(position){
case Consts.AUDIO_POS:
return new AdapterAudioFragment();
case Consts.VOICE_POS:
return new AdapterVoiceFragment();
case Consts.MAIL_POS:
return new AdapterMailFragment();
case Consts.INFO_POS:
return new AdapterInfoFragment();
default:
return null;
}
}
#Override
public int getCount()
{
return Consts.PAGES * Consts.LOOPS; // (4 * 1000)
}
#Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {}
#Override
public void onPageSelected(int position) {}
#Override
public void onPageScrollStateChanged(int state) {}
}
One of my fragments that the adapter loads (they are all pretty much the same):
public class AdapterAudioFragment extends Fragment {
private ImageView menuImg;
private TextView menuText;
private LinearLayout rootView;
private MainWearActivity mMainWearActivity;
private View.OnClickListener imgClickListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMainWearActivity = (MainWearActivity) getActivity();
imgClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
mMainWearActivity.replaceFragment(mMainWearActivity.getFragment(Consts.FRAG_AUDIO), Consts.FRAG_AUDIO);
}
};
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// Get root view of the fragment layout
rootView = (LinearLayout) inflater.inflate(
R.layout.fragment_nav_object, container, false);
// Set the current menu image and text
menuImg = (ImageView) rootView.findViewById(R.id.fragment_image);
menuImg.setImageResource(R.mipmap.ic_audio);
menuImg.setOnClickListener(imgClickListener);
menuText = (TextView) rootView.findViewById(R.id.menuTxt);
menuText.setText(Consts.MENU_HEADER_AUDIO);
// Set the current menu selection
mMainWearActivity.setCurrentSelection(Consts.AUDIO_POS);
return rootView;
}
}
I have a feeling that the adapter's fragments are all being created but never destroyed and piling up in the heap but I can't figure out how to resolve this. Do I need to call destroyItem in the adapter and manually destroy them? Any help would be most appreciated, thanks.
Adding this to Fragment stopped leaks for me:
#Override
public void onDestroyView() {
super.onDestroyView();
viewPager.setAdapter(null);
}
Looking at the source code, the problem seems to be that when calling ViewPager#setAdapter the view will register itself as observer for the adapter. So each time onViewCreated is called your pager adapter instance will have reference of the newly created view.
There is a specific PagerAdapter for your needs - FragmentStatePagerAdapter
This version of the pager is more useful when there are a large number of pages, working more like a list view. When pages are not visible to the user, their entire fragment may be destroyed, only keeping the saved state of that fragment. This allows the pager to hold on to much less memory associated with each visited page as compared to FragmentPagerAdapter at the cost of potentially more overhead when switching between pages.

Cannot update views on left and right side of view pager

I have used viewpager which displays view with an image and radiobutton below. Each swipe screen is not a fragment, they are layouts which just swipe the same view with different imageview. The problem is that, I am unable to deselect the radio button which is in left and right side of the current view after i select the radio button of the current screen. If I return POSITION_NONE in getItemPosition(), it refresh the screen and radio button also deselected but the view is flickered. If i am able to call instantiateItem(), my problem is solved . But this method is called only when view is destroyed based on the setOffsetScreenPageLimit(). How can I achieved such requirements, if view pager cannot help?
Fragment:
public class AnswerImageDialogFragment extends DialogFragment implements ViewPager.OnPageChangeListener {
//member declaration
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String strQuestion = null;
View rootView = inflater.inflate(R.layout.activity_viewpager_demo, container,
false);
getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
intro_images = (ViewPager) rootView.findViewById(R.id.pager_introduction);
pager_indicator = (LinearLayout) rootView.findViewById(R.id.viewPagerCountDots);
//do some stuff
//set the adapter
mAdapter = new ViewPagerAdapter(getContext(), map);
intro_images.setAdapter(mAdapter);
intro_images.setCurrentItem(clickPosition);
intro_images.setOnPageChangeListener(this);
setUiPageViewController();
bus = EventBus.getDefault();
bus.register(this);
return rootView;
}
//other method
}
PagerAdapter:
public class ViewPagerAdapter extends PagerAdapter {
// member declaration
public ViewPagerAdapter(Context mContext, HashMap<String, Object> map) {
//other initialization
}
#Override
public int getCount() {
return optionImages.size();
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((LinearLayout) object);
}
#Override
public Object instantiateItem(ViewGroup container, final int position) {
View itemView = LayoutInflater.from(mContext).inflate(R.layout.pager_item, container, false);
final ImageView ivOption = (ImageView) itemView.findViewById(R.id.answerId); // this is used as radio button in this case
/*The code snippet is to select the answer option based on whether answer is choosen or not.
If choosen, select as default in the pop up answer.
*/
if (null != ans) {
Iterator iterator = ans.iterator();
if (ans.size() > 0) {
int value = (int) iterator.next();
if (value == position) {
ivOption.setImageResource(R.drawable.ic_radio_button_tick);
} else {
ivOption.setImageResource(R.drawable.ic_radio_button_nontick);
}
}
}
p = position;
/*The code snippet is to change the answer value based on the user selection.
This means if user choosen another answer then clear the earlier choosen answer
*/
ivOption.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//do stuff
}
});
txtQuestion.setText(question);
txtOption.setText(optionsList.get(position));
imageView.setParseFile(optionImages.get(position));
imageView.loadInBackground();
container.addView(itemView);
return itemView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
LinearLayout ll = (LinearLayout) object;
/*RelativeLayout ivOption = (RelativeLayout) ll.getChildAt(2);
ImageView iv = (ImageView) ivOption.getChildAt(1);
iv.setImageResource(R.drawable.ic_radio_button_tick);*/
container.removeView(ll);
}
#Override
public int getItemPosition(Object object) {
return super.getItemPosition(object);
}
public void refresh(ArrayList object) {
this.object = new ArrayList();
this.object.addAll(object);
this.notifyDataSetChanged();
}

ViewPager - Events in all page occurs only in last page

I have a viewPager with say 4 pages. All 4 pages uses same Xml. When i do an event in 1st page somehow it always triggers in the last page.
Here is my PagerAdapter
#Override
public Object instantiateItem(ViewGroup container, int pos) {
View desktopView;
OnTouchListener tl = null;
desktopView = act.getLayoutInflater().inflate(
act.getViewPagerLayout(groupName), null);
RelativeLayout rr_appContainer, rr_dialogContainer;
ImageView rr_home_container = (ImageView) desktopView
.findViewById(R.id.imageView_forClick);
Button buttonChange = (Button)desktopView.findViewById(R.id.B1);
Button buttonDelete = (Button)desktopView.findViewById(R.id.B2);
rr_appContainer = (RelativeLayout) desktopView
.findViewById(R.id.rr_home_container);
rr_dialogContainer = (RelativeLayout) desktopView
.findViewById(R.id.rr_dialogView);
..........
buttonDelete.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
deletestuff();
}
buttonChange.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
changeColorOfStuff();
}
.....
return desktopView;
}
What is happening is, When i click on buttonChange from 1st page it supposed to change the color of text on 1st page, but actually it is changing color of the last page. Similarly buttonDelete is deleting color from last page.
Regardless of what page i am in, its reflecting those changes on last page.
Any help would be appreciated.
From the context given here, the deleteStuff() and changeColorOfStuff() can only be members of the Fragment/Activity that owns the adapter, or the adapter itself. So these methods can only act on members of those classes. ViewPager asks the adapter for the fragments it is going to display. However, the text in the fragment being shown by the ViewPager belong to the that fragment. To act on that text, you need a method that's a member of that fragment. The usual way to do this is to use a custom fragment. For example:
Custom Fragment (inner class):
public static class CustomFragment extends Fragment {
//members of the fragment
TextView yourTextView;
...
public static CustomFragment newInstance(int pos) {
CustomFragment fragment = new CustomFragment();
//get whatever info you need for this page
Bundle args = getInfoSomehow(pos);
fragment.setArguments(args)
return fragment;
}
#Override
public View onCreateView(Layout inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(....
yourTextView = root.findViewById(...) //this is the text view you want to change things in
//all the stuff you're currently doing in instantiateItem()
return root;
}
private void deleteStuff() {
//whatever you need to do. But notice that here it's acting on the TextView that belongs to this particular fragment
}
private void changeColorOfStuff() {...}
...
}
Then in your instantiateItem(...)
#Override
public Object instantiateItem(ViewGroup container, int pos) {
return CustomFragment.newInstance(pos);
}

Categories

Resources