I have a Viewpager with two tabs inside. The tabs are Cards and Favorite cards. I am using same fragment inside both tabs. The fragment contains a RecyclerView which I update dynamically. I add elements to both tabs one by one. I first completely add elements to first tab(Cards tab), after finishing that I add elements to second tab(Favorite card tab). My problem is when I add elements to the second tab, my RecyclerView inside first tab also get updated. Now the elements in first tab RecyclerView is the sum of existing elements plus the elements I add in the second tab. I set two different ArrayList in both the ViewPager tab. But when getItemCount() called in first tab RecyclerView, the ArrayList has the element that I added in the second tab.
I am using the same fragment with a RecyclerView in both tabs. When I want to update a single tab, I call notifyDatasetChanged() on the ViewPager. So each time RecyclerView inside the fragment is reinitialized.
This is my Viewpager adapter
public class ViewPagerWebCardsAdapter extends FragmentStatePagerAdapter {
private ArrayList<WebCardBaseResponseModel> webCardDetailList;
private ArrayList<WebCardBaseResponseModel> followingWebCardList;
private Context context;
private boolean isEmptyFavoriteWebcardList;
private boolean isEmptyWebcardsList;
public ViewPagerWebCardsAdapter(FragmentManager fm, ArrayList<WebCardBaseResponseModel> webCardDetailList,
ArrayList<WebCardBaseResponseModel> followingWebCardList,
Context context) {
super(fm);
this.webCardDetailList = webCardDetailList;
this.followingWebCardList = followingWebCardList;
this.context = context;
}
public void updateWebCardLists( ArrayList<WebCardBaseResponseModel> webCardDetailList,
ArrayList<WebCardBaseResponseModel> followingWebCardList) {
this.webCardDetailList = webCardDetailList;
this.followingWebCardList = followingWebCardList;
notifyDataSetChanged();
}
public void setEmptyFollowingCardsFragment() {
isEmptyFavoriteWebcardList = true;
notifyDataSetChanged();
}
public void setEmptyWebCardsFragment() {
isEmptyWebcardsList = true;
notifyDataSetChanged();
}
#Override
public Fragment getItem(int position) {
if(position == 0) {
WebCardListFragment webCardListFragment = new WebCardListFragment();
webCardListFragment.setWebCardDetailList(webCardDetailList);
webCardListFragment.setEmptyListFragment(isEmptyWebcardsList);
return webCardListFragment;
} else {
WebCardListFragment webCardListFragment = new WebCardListFragment();
webCardListFragment.setWebCardDetailList(followingWebCardList);
webCardListFragment.setEmptyListFragment(isEmptyFavoriteWebcardList);
webCardListFragment.setAsFavoritesList();
return webCardListFragment;
}
}
#Override
public int getCount() {
return Constants.NUMBER_TWO;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public CharSequence getPageTitle(int position) {
if(position == 0) {
return context.getString(R.string.webcards);
} else {
return context.getString(R.string.favorite);
}
}
}
This is my Card fragment
public class WebCardListFragment extends Fragment {
#BindView(R.id.web_card_recycler_view)
RecyclerView mWebCardDetailRecyclerView;
#BindView(R.id.tv_description)
TextView tvDescription;
#BindView(R.id.progress_bar)
public ProgressBar progressBar;
private WebCardListAdapter mWebCardListAdapter;
ArrayList<WebCardBaseResponseModel> mWebCardDetailList;
WebCardActivity activity;
private boolean isFavorites;
private boolean isEmptyListFragment;
#Override
public void onAttach(Context context) {
super.onAttach(context);
activity = (WebCardActivity) context;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void setWebCardDetailList(ArrayList<WebCardBaseResponseModel> mWebCardDetailList) {
this.mWebCardDetailList = mWebCardDetailList;
}
public void setAsFavoritesList() {
this.isFavorites = true;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_webcard_list, container, false);
ButterKnife.bind(this, view);
initView();
return view;
}
private void initView() {
if(isFavorites) {
tvDescription.setText(getString(R.string.select_webcard_to_share));
}
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(activity);
mWebCardDetailRecyclerView.setLayoutManager(mLayoutManager);
mWebCardDetailRecyclerView.setHasFixedSize(true);
mWebCardDetailRecyclerView.setItemAnimator(new DefaultItemAnimator());
if(mWebCardDetailList != null && mWebCardDetailList.size() > 0) {
progressBar.setVisibility(View.GONE);
tvDescription.setVisibility(View.VISIBLE);
mWebCardListAdapter = new WebCardListAdapter(activity, mWebCardDetailList, isFavorites);
}
if(isEmptyListFragment) {
progressBar.setVisibility(View.GONE);
tvDescription.setVisibility(View.VISIBLE);
tvDescription.setText(getString(R.string.no_webcards_to_list));
}
mWebCardDetailRecyclerView.setAdapter(mWebCardListAdapter);
}
public void setEmptyListFragment(boolean isEmptyList) {
isEmptyListFragment = isEmptyList;
}
}
Finally this is my RecyclerViewadapter I use inside the fragment
public class WebCardListAdapter extends RecyclerView.Adapter<WebCardListAdapter.ViewHolder> {
private Context mContext;
private ArrayList<WebCardBaseResponseModel> mWebCardDetailList;
private boolean isFavorites;
public WebCardListAdapter(Context context, ArrayList<WebCardBaseResponseModel> webCardDetailList, boolean isFavorites) {
mContext = context;
mWebCardDetailList = webCardDetailList;
this.isFavorites = isFavorites;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_webcard_detail_layout, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
setListItemView(holder, position);
setListItemClickListener(holder);
}
#Override
public int getItemCount() {
return mWebCardDetailList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
class ViewHolder extends RecyclerView.ViewHolder {
private TextView mCardNameTextView;
private LinearLayout mWeCardLinearLayout;
#BindView(R.id.tv_phone)
TextView phone;
ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
mCardNameTextView = (TextView) itemView.findViewById(R.id.webcard_name_textview);
mWeCardLinearLayout = (LinearLayout) itemView.findViewById(R.id.wed_card_title_linear_layout);
}
}
}
I want the ListView inside both tabs to be independent. When I add elements to the second tab RecyclerView, that should not reflect in the first tab RecyclerView.
The Basic difference
FragmentPagerAdapter
Good for a limited (fixed) number of items (Fragments). Why? Because it never removes a fragment instance from FragmentManager once it’s created (unless that Activity is finished). It only detaches the Views from Fragments which are currently not visible. onDestroyView() will be called on your Fragment once it’s out of reach and later onCreateView() will be called once you go back to this Fragment.
FragmentStatePagerAdapter
A FragmentStatePagerAdapter is more memory savvy. It completely removes Fragment instances from the FragmentManager once they are out of reach. The state of the removed Fragments is stored inside the FragmentStatePagerAdapter. The Fragment instance is recreated once you return back to an existing item and the state is restored. This adapter is suitable for lists with an unknown count or for lists where the items change a lot.
So FragmentPagerAdapter will solve this
I call notifyDatasetChanged() on the view pager. So each time recycler view inside the fragment is reinitialised.
Remove the override function "getItemPosition" from vew pager adapter class. Hope it will help.
Thanks for the answers, anyway this issue has got nothing to do with Recyclerview or Viewpager, it happened because of my misunderstanding on the variable reference. I change the ArrayList somewhere else inside the activity. That affects the list in recyclerview.
Related
Good morning,
I have a Fragment which its layout has a ViewPager, I provide a custom PageAdapter to show two child layouts. One layout is to fill with personal data and another layout is to fill with Address data by the user. On my fragment I want to retrieve both layouts data and fill a object Pessoa(Person) with an attribute Endereco (Address). So far my ViewPager is working and both layout are displayed, however when I try to get the fields on both layouts, either on onCreateView or onResume fragment's method, using :
viewpager.getChildAt(0).findViewById(R.id.txt1)
I got an NullPointerException, but if I add an OnPageChangeListener this works, I not sure if it's the best approach. Is There a best place to retrieve a reference to the viewpager child's layout?
Ps. I don't want to use two other Fragments in order to display each of the layouts.
PageAdapter
public class ViewPagerCadastroPessoaAdapter extends PagerAdapter {
private Context context;
private int[] views = {R.layout.layout_cadastro_dados_pessoais,R.layout.layout_cadastro_endereco };
public ViewPagerCadastroPessoaAdapter(Context context) {
this.context = context;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater inflater = LayoutInflater.from(this.context);
View layout = inflater.inflate(views[position], container, false);
container.addView(layout, position);
return layout;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position){
case 0:
return "Dados Pessoais";
case 1:
return "Endereço";
}
return super.getPageTitle(position);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}}
ViewPager's holder Fragment
public class CadastroPessoaFragment extends Fragment {
private ViewPager viewPagerCadastroPessoa;
private TabLayout tabLayoutCadastroPessoa;
public CadastroPessoaFragment() {
}
public static CadastroPessoaFragment newInstance() {
return new CadastroPessoaFragment();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_cadastro_pessoa, container, false);
tabLayoutCadastroPessoa = root.findViewById(R.id.tabLayoutCadastroPessoa);
viewPagerCadastroPessoa = root.findViewById(R.id.viewPagerCadastroPessoa);
viewPagerCadastroPessoa.setOffscreenPageLimit(2);
viewPagerCadastroPessoa.setAdapter(new ViewPagerCadastroPessoaAdapter(getContext()));
tabLayoutCadastroPessoa.setupWithViewPager(viewPagerCadastroPessoa);
int cor = ContextCompat.getColor(getContext(), android.R.color.white);
tabLayoutCadastroPessoa.setTabTextColors(cor, cor);
//get a null pointer
viewPagerCadastroPessoa.getChildAt(0).findViewById(R.id.txt1);
viewPagerCadastroPessoa.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
//works fine
viewPagerCadastroPessoa.getChildAt(0).findViewById(R.id.txt1);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
return root;
}
#Override
public void onResume() {
super.onResume();
//get a null pointer
viewPagerCadastroPessoa.getChildAt(0).findViewById(R.id.txt1);
}
}
Thank you all in advance
UPDATE
According with
Anton Malyshev answer
The way Viewpager creates its pages is kind of "unsynchronized", so I used the following approach to solve my problem:
On my custom adapter I definied an interface with a single method onViewPagerReady(), as my Viewpager will always have only two pages, inside instantiateItem I check when position is 1 (once position starts with 0), if position == 1 I call the method onViewPagerReady that my fragment viewpager's holder implements, so inside this method I retrieve the layout of viewpager's children
public class ViewPagerCadastroPessoaAdapter extends PagerAdapter {
public interface ViewPagerObserver{
void onViewPagerReady();
}
private Context context;
private int[] views = {R.layout.layout_cadastro_dados_pessoais,R.layout.layout_cadastro_endereco };
private ViewPagerObserver observer;
public ViewPagerCadastroPessoaAdapter(Context context, ViewPagerObserver observer) {
this.context = context;
this.observer = observer;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater inflater = LayoutInflater.from(this.context);
View layout = inflater.inflate(views[position], container, false);
container.addView(layout, position);
if(position == 1){
observer.onViewPagerReady();
}
return layout;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position){
case 0:
return "Dados Pessoais";
case 1:
return "Endereço";
}
return super.getPageTitle(position);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
Fragment Viewpager's Holder
#Override
public void onViewPagerReady() {
View rootCadastroDadosPessoais = viewPagerCadastroPessoa.getChildAt(0);
View rootCadastroEnderecos =viewPagerCadastroPessoa.getChildAt(1);
}
Thank you all.
Just wait until viewPagerCadastroPessoa.getChildCount() will be greater than 0. The pages in ViewPager are not created yet in onResume (they are created asynchronously).
I receive a list (of Categories) from server and make tabs using SectionsPagerAdapter. I use one common Fragment within which I have a RecyclerView. After user logs in, I store Categories in TempData.productCategories and they are less than 15. When user selects a tab, I refresh the RecyclerView with the products of the selected Category. The issue is, currently I have 4 Categories. Only the first and the last ones have products (one product under each category). The first tab showing no product maybe because the second one is empty and Android automatically loads the second tab just after the first one. I want to see the products of the first category. Can anybody tell me what am I doing wrong?
The server gets my store ID and one store ID has less than 15 categories:
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
mViewPager.setCurrentItem(tab.getPosition());
//doing after a delay otherwise activity is null
final int position = tab.getPosition();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
String cat = TempData.productCategories.get(position).getName();
Log.i("Temp", "pos: " + position + ", cat: " + cat);
AnyItemFragment.updateList(cat, context);
}
}, 400);
}
Here are the other methods used in this functionality:
public static void setupListForCategory(final Activity context, final RecyclerView listView, final String category) {
List<ProductUserModel> prods = getProductsUser(category);
if(prods.isEmpty()) return;
setupList(context, listView, prods);
}
And here I update the adapter:
private static void setupList(final Activity context, RecyclerView listView, List<ProductUserModel> prods){
FastItemAdapter<ProductUserModel> p = new FastItemAdapter<>();
p.add(prods);
p.withSelectable(true);
p.withOnClickListener(new FastAdapter.OnClickListener<ProductUserModel>() {
#Override
public boolean onClick(final View v, final IAdapter<ProductUserModel> adapter, final ProductUserModel item, final int position) {
PopupUtils.getUserInputQuantity(context, item, v);
return false;
}
});
//fill the recycler view
Log.i("Temp", "updating list : " + prods.size());
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(context);
listView.setLayoutManager(layoutManager);
listView.setAdapter(p);
// p.notifyAdapterDataSetChanged();
// p.notifyDataSetChanged();
listView.invalidate();
}
private static List<ProductUserModel> getProductsUser(String category) {
List<ProductUserModel> pum = new ArrayList<>();
for(int i = 0; i < TempData.productsUser.size(); i++){
if(TempData.productsUser.get(i).getCategory().equals(category))
pum.add(TempData.productsUser.get(i));
}
return pum;
}
And the sections pager adapter in the activity:
private class SectionsPagerAdapter extends android.support.v13.app.FragmentPagerAdapter {
SectionsPagerAdapter(android.app.FragmentManager fm) {
super(fm);
}
#Override
public android.app.Fragment getItem(int position) {
return AnyItemFragment.newInstance(position-1);
}
#Override
public int getCount() {
// Show 3 total pages.
return TempData.productCategories.size();
}
#Override
public CharSequence getPageTitle(int position) {
// switch (position) {
return TempData.productCategories.get(position).getName();
// case 0:
// return getString(R.string.toys);
// case 1:
// return getString(R.string.stationaries);
// case 2:
// return getString(R.string.books);
// }
// return null;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
Update: My fragment:
public class AnyItemFragment extends Fragment {
static AnyItemFragment fragment;
public static Activity context;
private static String TAG = "AllItemsFragment";
int selectedPosition = 0;
#BindView(R.id.listAll)
RecyclerView listAll;
private OnFragmentInteractionListener mListener;
public AnyItemFragment() {
// Required empty public constructor
}
public static AnyItemFragment newInstance(int categoryID) {
fragment = new AnyItemFragment();
fragment.selectedPosition = categoryID;
Log.i(TAG, "sel pos: " + fragment.selectedPosition);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_inv_any_item, container, false);
ButterKnife.bind(this, view);
context = getActivity();
return view;
}
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(!isVisibleToUser) return;
String cat = TempData.productCategories.get(fragment.selectedPosition).getName();
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
public static void updateList(String cat, Activity context){
TempData.setupListForCategory(context, fragment.listAll, cat);
}
}
I think for starters, you should keep the flow simple and not have a static fragment instance on which you keep calling refresh.
Your code right now is difficult to read and understand.
One basic thing to note with fragments is since they are already re-usable in tandem with a FragmentStatePagerAdapter, you almost never have to deal with this hassle of having a single static instance doing all the work.
Few problems I spotted right now for example were -
You want to attach multiple fragments (as many as there are categories) so you make a new instance and update the static field
public static AnyItemFragment newInstance(int categoryID) {
fragment = new AnyItemFragment();
fragment.selectedPosition = categoryID;
Log.i(TAG, "sel pos: " + fragment.selectedPosition);
return fragment;
}
What is happening right now is since the default offscreenPageLimit on your ViewPager defaults to 1 all your updates are going to the second instance of the fragment which most probably is linked with category-2 and hence nothing renders in the first tab.
You can confirm this by adding debug break-points.
What you would ideally want to do is send the categories to your ViewPagerAdapter and based on the position set the product model list to the correct fragment itself so it knows how to render post creation.
private class SectionsPagerAdapter extends android.support.v13.app.FragmentPagerAdapter {
private final Map<String, List<ProductUserModel>> mapOfCategoryAndProductUsers;
SectionsPagerAdapter(FragmentManager fm, Map<String, List<ProductUserModel>> mapOfCategoryAndProductUsers) {
super(fm);
this.mapOfCategoryAndProductUsers = mapOfCategoryAndProductUsers;
}
#Override
public android.app.Fragment getItem(int position) {
AnyItemFragment fragment = AnyItemFragment.newInstance(position-1);
// Logic to map position to category...
String category = TempData.productCategories.get(position)
fragment.setProductUsers(mapOfCategoryAndProductUsers.get(category))
return fragment;
}
#Override
public int getCount() {
// Show 3 total pages.
return TempData.productCategories.size();
}
...
...
}
And then have the render logic inside the fragment -
private List<ProductUserModel> productUsers;
public void setProductUsers(List<ProductUserModel> productUsers) {
this.productUsers = productUsers;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setupList()
}
private void setupList() {
FastItemAdapter<ProductUserModel> p = new FastItemAdapter<>();
p.add(prods);
p.withSelectable(true);
p.withOnClickListener(new FastAdapter.OnClickListener<ProductUserModel>() {
#Override
public boolean onClick(final View v, final IAdapter<ProductUserModel> adapter, final ProductUserModel item, final int position) {
PopupUtils.getUserInputQuantity(context, item, v);
return false;
}
});
//fill the recycler view
Log.i("Temp", "updating list : " + prods.size());
LayoutManager layoutManager = new LinearLayoutManager(context);
listView.setLayoutManager(layoutManager);
listView.setAdapter(p);
}
PS: You should avoid as much as you can the use of static methods, since they make unit testing a nightmare. Also if you don't test your code before hand, I'd suggest checking out unit tests and Espresso for instrumentation test to have more re-assurance around the working of your app and be free of regression blues.
Let me know if this helped or if you'd need more explanation
In my main activity I have multiple Fragments that you can navigate through a bottom navigation (Replace a FrameLayout container in main activity XML)
One of those Fragments contains a ViewPager that should show multiple Fragments (MyFragment) using a FragmentStatePagerAdapter (MyAdapter).
First time I open that Fragment it works just fine but when I navigate to a different one and then come back the first two Fragments in the Pager are grey and unpopulated. Anyone got an idea what the problem might be?
The Adapter is set up like this:
This is called in onFragmentViewCreated
private void setUpHeaderPager() {
featuredAdapter = new MyAdapter(getActivity().getSupportFragmentManager());
myPager.setAdapter(myAdapter);
pageIndicatorView.setViewPager(myPager);
pageIndicatorView.setAnimationType(AnimationType.WORM);
compositeSubscription.add(apiService.getMyPOJOs()
.subscribeOn(networkScheduler)
.observeOn(uiScheduler)
.subscribe(new Action1<List<MyPOJO>>() {
#Override
public void call(List<MyPOJO> myPOJOs) {
myAdapter.setItems(myPOJOs);
pageIndicatorView.setCount(myPOJOs.size());
myPager.setCurrentItem(0);
pageIndicatorView.setSelection(0);
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
}));
}
And this is what the Adapter looks like:
private class MyAdapter extends FragmentStatePagerAdapter {
private List<SomePOJO> items = new ArrayList<>(0);
public void setItems(#Nonnull List<Collection> items) {
this.items = items;
notifyDataSetChanged();
}
FeaturedReleasesAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return MyFragment.newInstance(items.get(position));
}
#Override
public int getCount() {
return items.size();
}
}
Just in case the relevant code of MyFragment:
public class FeaturedReleaseFragment extends BaseFragment {
private MyPOJO someObject = null;
// Bind some views
public static MyFragment newInstance(MyPOJO someObject) {
MyFragment fragment = new MyFragment();
fragment.someObject = someObject;
return fragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_my_fragment, container, false);
ButterKnife.bind(this, rootView);
populate();
return rootView;
}
private void populate() {
if (MyPOJO != null) {
// set some text and load images
}
}
onFragmentViewCreated is called just fine, the adapters getItem is not...
It may be how you're navigating between the bottom navigation fragments and subsequently whats happening with their lifecycle.
If you are using .replace on the FragmentTransaction, try using .show and .hide instead.
Something like the following might do it:
public void navigateToFragment( Fragment frag ) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.hide(currentFragment);
if ( !fragment.isAdded() ) {
transaction.add(R.id.container,fragment)
} else if ( !fragment.isHidden() ) {
transaction.show(fragment);
}
transation.commit();
}
I have a ListView which contains product details. On Item click of listview I open new activity with particular product detail. I wanted to convert activity to ViewPager so that I can swipe to load next and previous records in same fragment. Fragment structure will be same for all records. I don't know from where should I start. Can you give me overview idea how to achieve this. Here is my model class.
Product.java
public class Product implements Serializable{
public int id;
public String Name;
public String description;
public String longDescription;
public String amount;
public String image;
}
Here is my FragmentPagerAdapter class
ProductPagerAdapter.java
public class ProductPagerAdapter extends FragmentPagerAdapter {
private Context context;
private ArrayList<Product> list;
public ProductPagerAdapter(FragmentManager fm, Context context, ArrayList<Product> list) {
super(fm);
this.context = context;
this.list = list;
}
#Override
public Fragment getItem(int position) {
return ProductFragment.newInstance(position);
}
#Override
public int getCount() {
return list.size();
}
}
And this is my Fragment
ProductFragment.java
public class ProductFragment extends Fragment {
public ProductFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_product, container, false);
//findViewById...
return v;
}
public static Fragment newInstance(int id) {
Bundle args = new Bundle();
args.putInt("Id", id);
ProductFragment fragment = new ProductFragment();
fragment.setArguments(args);
return fragment;
}
}
And now on list item Click I am opening new activity. And I am sending Product object to it.
lv_itemRateList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(getActivity(), DetailsActivity.class);
Product r = new Product();
r = rateListArrayList.get(i);
intent.putExtra("product",r);
startActivity(intent);
}
});
My DetailsActivity contains my viewpager. Now can someone tell me how to do this?
1-) Need a custom adapter for ViewPager.
I assumed that all the pages will have the same content, so all of them have the same layout so ProductPagerAdapter extends the PagerAdapter.
public class ProductPagerAdapter extends PagerAdapter
{
//variables
private Context context;
private ArrayList<Product> list;
private Product product
//views
TextView txtName ;
public ProductPagerAdapter(Context context, List<Product> list)
{
this.context = context;
this.list = list;
}
#Override
public int getCount()
{
return list.size();
}
#Override
public boolean isViewFromObject(View view, Object object)
{
return view == ( object);
}
#Override
public Object instantiateItem(ViewGroup container, int position)
{
product = list.get(position);
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View itemView = inflater.inflate(R.layout.fragment_product, container,false);
txtName = (TextView) itemView.findViewById(R.id.txtName);
txtName.setText(product.Name)
((ViewPager) container).addView(itemView);
return itemView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object)
{
((ViewPager) container).removeView((LinearLayout) object);
}
}
2-) Adapter is ready. Now let's prepare the activity that will show the details. First initialize ViewPager and ProductPagerAdapter. Then show the details of the item which is selected by clicking on the previous activity.
public class DetailsActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
...
//getting product object
Intent intent = getIntent();
Product product = (Product) getIntent().getSerializableExtra("product");
//get selected item id
int selectedItemID = 0;
for(int i = 0 ; i < list.size() ; i++)
{
if(list.get(i).id == product.id)
{
selectedItemID = i;
break;
}
}
//Init ViewPager and adapter
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
ProductPagerAdapter detailAdapter = new ProductPagerAdapter(DetailActivity.this, list);
// Binds the Adapter to the ViewPager
viewPager.setAdapter(detailAdapter);
viewPager.setCurrentItem(selectedItemID); // set selection
viewPager.setPageTransformer(true, new ForegroundToBackgroundTransformer()); //custom transformer
...
}
}
3-) We've done what we have to do so far. So it will work like this. But what I would like to point out is that there are a few things to make the ViewPager better.
Performance : Use Parcelable instead of Serializable. Parcelable takes more time to implement but it will perform 10 times faster and use less resources. Please check this SO answer
Animation : You may want to need animations for transforming ViewPager. I suggest you to use ViewPagerTransforms
Indicators : If you want to use the paging indicator, I recommend ViewPagerIndicator.
For more on ViewPager
I've used ViewPagerIndicator to get WalkThrough like most popular application. But I can't understand how to add Pictures in ViewPager which shows how to use the application.
What I want is like these Walk Through.
What i got till now.
I dunno
How to add Custom View like ImageView and TextView in ViewPager?
Any guidance would be most welcome.
Since the question really old, I'll just write a short general guideline:
To populate viewpager with content, you add fragments to an adapter and then you set the adapter to the viewpager. You can do pretty much any layout in the fragment, putting there images/text etc..
As Inteist suggest, one can put any layout in fragment and supply that fragment to adapter.
Fragment:
public final class SelectModelFragment extends Fragment {
private static final String KEY_CONTENT = "SelectModelFragment:Content";
private static String TAG = SelectModelFragment.class.getSimpleName();
private SelectModel mSelectModelObj;
private CircularImageView mImageView;
public static SelectModelFragment newInstance(SelectModel obj) {
SelectModelFragment fragment = new SelectModelFragment();
fragment.mSelectModelObj =obj;
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if ((savedInstanceState != null) && savedInstanceState.containsKey(KEY_CONTENT)) {
mSelectModelObj = savedInstanceState.getParcelable(KEY_CONTENT);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_select_model, container, false);
mImageView = (CircularImageView)view.findViewById(R.id.fragment_select_model_iv);
return view;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(KEY_CONTENT, mSelectModelObj);
}
}
Fragment Adapter :
public class SelectModelAdapter extends FragmentPagerAdapter {
ArrayList<SelectModel> mList;
private int mCount;
private static final String TAG = SelectModelAdapter.class.getSimpleName();
public SelectModelAdapter(FragmentManager fm, ArrayList<SelectModel> mList) {
super(fm);
this.mList = mList;
mCount = mList.size();
}
#Override
public Fragment getItem(int position) {
return SelectModelFragment.newInstance(mList.get(position));
}
#Override
public int getCount() {
return mCount;
}
#Override
public CharSequence getPageTitle(int position) {
return TAG;
}
public void setCount(int count) {
if (count > 0 && count <= 10) {
mCount = count;
notifyDataSetChanged();
}
}
}
Activity: Where ViewPager has Fragment Adapter which feeds Fragment.
public class SelectModelActivity extends BaseSliderActivity {
private ViewPager mPager;
private SelectModelAdapter mAdapter;
private ArrayList<SelectModel> mList;
private void setAdapter() {
mAdapter = new SelectModelAdapter(getSupportFragmentManager(), mList);
mPager.setAdapter(mAdapter);
mIndicator.setViewPager(mPager);
}
}