i implemented a viewpager in my activity that on scroll, loads a set of data to an adapter and display them in a list view. everything is working fine, but i cant seem to find out how to make it more efficient because when scrolling, the view freezes but for example in the GMAIL app when scrolling between Emails the scrolling is so smooth.
Below is the code i am using:
Activity:
in oncreate
pageradapter adapter2 = new pageradapter(this, ArrayOfItems); //ArrayOfItems is an Array containing the data that will be displayed
ViewPager myPager = (ViewPager) findViewById(R.id.mypanelpager);
myPager.setAdapter(adapter2);
pagechangelistener pageListener = new pagechangelistener();
myPager.setOnPageChangeListener(pageListener);
pageradapter:
public pageradapter(Activity act, ArrayList <List<Item>>Arrayitems) {
this.Arrayitems = Arrayitems;
activity = act;
}
public int getCount() {
return Arrayitems.size();
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
List<Item> item = Arrayitems.get(position);
ListView view = new ListView(activity);
taskdetailsarrayadaptertest adapter = new taskdetailsarrayadaptertest(activity, item);
view.setAdapter(adapter);
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
((ViewPager) container).addView(view, 0);
return view;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((View) object);
}
#Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == ((View) arg1);
}
#Override
public Parcelable saveState() {
return null;
}
pagechangelistener:
#Override
public void onPageSelected(int position)
{
currentPage = position;
int positionTemp = position + 1;
}
public final int getCurrentPage() {
return currentPage;
}
First you need to use holders like this.
in your ViewPager:
class Holder{
ListView listView;
Adapter adapter;
}
private ArrayList<Holder> mHolders;
//init holders
#Override
public Object instantiateItem(ViewGroup collection, int position) {
Holder holder = mHolders.get(position);
holder.listView.setAdapter(holder.adapter);
collection.addView(holder.listView);
return holder.listView;
}
Second try async loading data in listview
set myPager.setOffscreenPageLimit(1);
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 am using viewpager with Pager adapter and passing an arraylist in the adapter. The arraylist has size "0" in the start but once it is filled, I am calling viewpager.notifydatasetchanged().After calling notifydatasetchanged the method CharSequence getPageTitle(int position) is not called and titles are not updated dynamically.
My adapter code:
public class MyViewPagerAdapter extends PagerAdapter {
private ArrayList<String> arrayList;
private Activity activity;
public ViewPagerAdapter(Activity activity, ArrayList<String> arrayList) {
this.activity = activity;
this.arrayList = arrayList;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public int getCount() {
return arrayList.size();
}
#Override
public boolean isViewFromObject(#NonNull View view, Object o) {
return o == view;
}
#NonNull
#Override
public Object instantiateItem(#NonNull ViewGroup container, int position) {
// Inflate a new layout from our resources
View view = activity.getLayoutInflater().inflate(R.layout.pager_item,
container, false);
// Add the newly created View to the ViewPager
container.addView(view);
// Retrieve a TextView from the inflated View, and update it's text
TextView tvWeatherDescription = view.findViewById(R.id.tv);
// Return the View
return view;
}
#Override
public CharSequence getPageTitle(int position) {
return arrayList.get(position);
}
#Override
public void destroyItem(#NonNull ViewGroup container, int position, #NonNull Object object) {
container.removeView((View) object);
}
}
My Activity code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<String> arrayList = new ArrayList<String>
ViewPager viewpager = findviewbyId(R.id.viewpager);
MyViewPagerAdapter adapter = new MyViewPagerAdapter(MainActivity.this, arraylist);
viewpager.setAdapter(adapter);
SlidingTabLayout slidingTabLayout = findViewById(R.id.sliding_tabs);
slidingTabLayout.setViewPager(viewpager);
}
Once the size of arraylist is changed I am calling it like this:
arraylist.add("Item1")
adapter.notifydatasetchanged();
but the title value is not changing.Is there any way that method CharSequence getPageTitle(int position) is called after notifydatasetchanged?
I have a regular ViewPager with an adapter:
public class MyDialogFragment extends DialogFragment {
private ViewPager viewPager;
private MyViewPagerAdapter myViewPagerAdapter;
private City city;
public static MyDialogFragment newInstance() {
MyDialogFragment fragment = new MyDialogFragment();
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_layout, container, false);
viewPager = (ViewPager) v.findViewById(R.id.viewPager);
city = Parcels.unwrap(getArguments().getParcelable("city"));
myViewPagerAdapter = new MyViewPagerAdapter(getContext());
viewPager.setAdapter(myViewPagerAdapter);
viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// Page changed
}
});
return v;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public class MyViewPagerAdapter extends PagerAdapter {
private Context context;
private LayoutInflater layoutInflater;
public MyViewPagerAdapter(Context context) {
this.context = context;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
layoutInflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = layoutInflater.inflate(R.layout.city_layout, container, false);
ImageView imageView = (ImageView) view.findViewById(R.id.cityImage);
Glide.with(context)
.load(city.getImage())
.into(imageView);
}
container.addView(view);
return view;
}
#Override
public int getCount() {
return city.size();
}
#Override
public boolean isViewFromObject(View view, Object obj) {
return view == ((View) obj);
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
}
As you can see, I have a page change listener set on my ViewPager. But I need to be able to notify the adapter when the page has changed so that I can modify the adapter's ImageView. How can I do this?
Add a method to your adapter class
public class MyViewPagerAdapter extends PagerAdapter {
...
public void pageChanged(int position) {
// Do something
}
...
}
After that in your OnPageChangeListener use that method
#Override
public void onPageSelected(int position) {
myViewPagerAdapter.pageChanged(position);
}
One approach is to create a data structure in your adapter to map the pager view to its position. A SparseArray is most efficient:
private SparseArray<View> pageMap = new SparseArray<>();
Then in the adapter's instantiateItem() method, add the view to the SparseArray:
public Object instantiateItem(ViewGroup container, int position) {
...
pageMap.put(position, view);
...
}
Now in your OnPageChangeListener you can do the following:
#Override
public void onPageSelected(int position) {
View view = adapter.getView(pos);
if (view != null) {
ImageView iv = (ImageView) view.findViewById(R.id.your_image_view);
// You now have the image view
}
}
Make sure you remove views from the SparseArray in the adapter's destroyItem() method:
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
pageMap.remove(position);
...
}
You can use the constructor with imagelist to pass and that automatically called on next page..In your code will be modified like :::
ArrayList imageList=new ArrayList<>();
imageList.add("ImagePath here");
myViewPagerAdapter = new MyViewPagerAdapter(getContext(),imageList);
viewPager.setAdapter(myViewPagerAdapter);
Then your adapter will be like ::
public class MyViewPagerAdapter extends PagerAdapter {
private Context context;
private ArrayList<String> imageList
private LayoutInflater layoutInflater;
public MyViewPagerAdapter(Context context,ArrayList<String> imageList) {
this.context = context;
this.imageList = imageList;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
layoutInflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = layoutInflater.inflate(R.layout.city_layout, container, false);
ImageView imageView = (ImageView) view.findViewById(R.id.cityImage);
Glide.with(context)
.load(imageList.get(position)) // This will be changes like
.into(imageView);
}
container.addView(view);
return view;
}
#Override
public int getCount() {
return city.size();
}
#Override
public boolean isViewFromObject(View view, Object obj) {
return view == ((View) obj);
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
That done ..
After this, there are some suggestion that
- please use arraylist while using the viewpager ..
Now you just taking the parceble object from bundle..
you can do the same for the arraylist,
just receive the arraylist from the same bundle by passing the whole arraylist at once.
so you dont need to find the page change event..viewpager will automatically handle that..
Here's How to send & receiving the parceable arraylist
// while sending the list
Bundle bundle = new Bundle();
bundle.putParcelableArrayList("citylist", new ArrayList<City>());
// while receiving the list
ArrayList<City> citylist = getIntent().getParcelableArrayListExtra("citylist");
After this just get the all image and add in the imagelist array
for (int i = 0; i < citylist.size(); i++) {
imageList.add(citylist.get(i).getImage());
}
And pass this imageList in
myViewPagerAdapter = new MyViewPagerAdapter(getContext(),imageList);
viewPager.setAdapter(myViewPagerAdapter);
Tell me if needed more assitance.
I have a ViewPager in my activity. The ViewPager shows different values on swiping the pager. But I get an IllegalStateException:
PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged!
Expected adapter item count: 0, found: 20
Pager id: com.exalt.vts:id/summarypager Pager class: class android.support.v4.view.ViewPager
Problematic adapter: class com.exalt.vts.DashBoard$MyPagerAdapter
This is my source code for PagerAdapter:
public class SamplePagerAdapter extends PagerAdapter{
List<View> pages = null;
public SamplePagerAdapter(List<View> pages){
this.pages = pages;
}
#Override
public Object instantiateItem(View collection, int position){
View v = pages.get(position);
((ViewPager) collection).addView(v, 0);
return v;
}
#Override
public void destroyItem(View collection, int position, Object view){
((ViewPager) collection).removeView((View) view);
}
#Override
public int getCount(){
return pages.size();
}
#Override
public boolean isViewFromObject(View view, Object object){
return view.equals(object);
}
#Override
public void finishUpdate(View arg0){
}
#Override
public void restoreState(Parcelable arg0, ClassLoader arg1){
}
#Override
public Parcelable saveState(){
return null;
}
#Override
public void startUpdate(View arg0){
}
}
This is the MainActivity:
public class MainActivity extends Activity {
ViewPager viewPager;
SamplePagerAdapter myPagerAdapter;
private TextView textView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
List<View> pages = new ArrayList<View>();
viewPager = (ViewPager)findViewById(R.id.myviewpager);
myPagerAdapter = new SamplePagerAdapter(pages);
viewPager.setAdapter(myPagerAdapter);
viewPager.setCurrentItem(1);
LayoutInflater inflater = LayoutInflater.from(this);
View page = inflater.inflate(R.layout.mylayout, null);
for (int i = 0; i < 20; i++) {
page = inflater.inflate(R.layout.mylayout, null);
textView = (TextView) page.findViewById(R.id.text_view);
textView.setText("Страница "+i);
pages.add(page);
}
}
I try to add 20 views in my ViewPager but I get an exception.
You either need to move the following code from where it is currently to after your for loop, so that the adapter is set after your data set is setup
myPagerAdapter = new SamplePagerAdapter(pages);
viewPager.setAdapter(myPagerAdapter);
viewPager.setCurrentItem(1);
or alternatively, you can add the following after your for loop to notify the adapter that the data set has changed since you set it up
myPagerAdapter.notifyDataSetChanged();
I have ViewPager with one page and Tab's. In AsyncTask i load data, and update ViewPager, but nothing change. In LogCat i checked, i have needed data.
MainClass.java
mViewPager = (ViewPager) findViewById(R.id.cinema_view_pager);
mTabPager = new TabPager("Загрузка");
mViewPager.setAdapter(mTabPager);
LoadCinema cinema = new LoadCinema(this,0);
cinema.execute();
TabPager(Adapter).class
public class TabPager extends PagerAdapter{
private String message = "";
public TabPager(String msg){
message = msg;
}
#Override
public int getCount() {
return 1;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view.equals(object);
}
public void update(String msg){
message = msg;
notifyDataSetChanged();
}
#Override
public View instantiateItem(View container, int position){
View v = getLayoutInflater().inflate(R.layout.cinema_layout, null);
TextView text = (TextView) v.findViewById(R.id.textView1);
text.setText(message+"Message" +"");
((ViewPager) container).addView(v);
return v;
}
public void destroyItem(View container, int position, Object object) {
((ViewPager) container).removeView((View) object);
}
}
LoadCinema.class
public void onPostExecute(List<CinemaCategory> result){
super.onPostExecute(result);
CinemaCategory cat = result.get(page);
cinema.mTabPager.update(cat.getDescription());
}
I tryed with three pages, and when i swype to last page, i haved data and then swype to first page view pager updated.
Now i have Answer, Thank's to this post
To adapter need add this code
public int getItemPosition(Object object) {
return POSITION_NONE;
}
Consider using FragmentStatePagerAdapter instead of PagerAdapter
public class TabPager extends FragmentStatePagerAdapter {
...
}