Search code examples
javaandroid-fragmentsandroid-imageviewlayout-inflaterfragmentpageradapter

FragmentPagerAdapter to ViewPager


I've started using Android Studio and learning Java not so long ago and now stucked on applying adapters to differen ViewPager. I don't have enough knowledge to solve such components' behaviour problem myself, and will be grateful for your help. (Sorry for my English)

The idea is:

1) A column of records (like in Instagram), which can be scrolled up-down;

2) Each record has block of images, which can be scrolled left-right, and a text block;

How it is organised (see screenshot also):

1) (main.xml) On the main layout (ConstraintLayout) there is nestedScrollView, in which I included LinearLayout (records.xml). This is the column of records.

2) LinearLayout is filled with records in cycle (record.xml). Each record is ConstraintLayout, which contains ViewPager and TextView.

3) In each new added record I try to find ViewPager, whom I set adapter to with setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager())).

4) MyFragmentPagerAdapter when comes to getItem calls RecordImageFragment.newInstance, creating new fragment (RecordImageFragment.java).

5) Each RecordImageFragment places FrameLayout (record_images_pager.xml), finds ImageView in it, and places picture there.

Screenshots:

app running

project structure

The problem is:

All works, but pictures are seen only in first record. When record is created, I also find TextView there (record.xml) and set text to it. Texts differs from record to record (that's good) , but only first ViewPager in first record has pictures. Though adapter (MyFragmentPagerAdapter) works in every ViewPager, OnPageChangeListener calls, tracing position: 0-2. After research it turned out that RecordImageFragment thinks that first record is his ViewGroup.getRootView().getRootView() (not the record to which adapter was set to). Till now I try to figure out, why the contents (record_images_pager.xml) of the fragment (RecordImageFragment.java) are added not to the ViewPager, to which adapter was set to, but to the first one.

MainActivity.java

package example;

import android.os.*;
import android.support.v4.app.*;
import android.support.v4.view.*;
import android.support.v7.app.AppCompatActivity;
import android.util.*;
import android.view.*;
import android.widget.*;

public class MainActivity extends AppCompatActivity {
	@Override
    protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		addRecords();
	}
	
	public void addRecords()
	{
		for (int j = 1; j <= 10; j++  ) 
		{
			addNewRecord(j);
		}
	}
	public void addNewRecord(int id)
	{
		LinearLayout records = (LinearLayout) findViewById(R.id.records);
		// Add new record
		LayoutInflater inflater = getLayoutInflater();
		View record = inflater.inflate(R.layout.record, null, false);
		records.addView(record);
		// Set text to text field
		TextView title_tv = (TextView) record.findViewById(R.id.title);
		title_tv.setText("Запись " + id);
		//Find ViewPager, add adapter and page change listener
		ViewPager pager = (ViewPager) record.findViewById(R.id.pager);
		pager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager()));
		pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener()
		{
			@Override
			public void onPageSelected(int position) { Log.d("example", "Картинка сменилась на: " + position);}
			@Override
			public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
			@Override
			public void onPageScrollStateChanged(int state) {}
		});
	}
	
	private class MyFragmentPagerAdapter extends FragmentPagerAdapter
	{
		int[] internalPicts = {R.drawable.pict1, R.drawable.pict2, R.drawable.pict3};
		public MyFragmentPagerAdapter(FragmentManager fm) { super(fm); }
		@Override
		public Fragment getItem(int pos) { return RecordImageFragment.newInstance(internalPicts[pos]); }
		@Override
		public int getCount() {
			return internalPicts.length;
		}
	}
}

RecordImageFragment.java

package example;

import android.os.Bundle;
import android.support.v4.content.*;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

public class RecordImageFragment extends android.support.v4.app.Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View layoutWithImage = inflater.inflate(R.layout.record_images_pager, container, false);
		//Find ImageView on layout and place drawable there
		ImageView image = (ImageView) layoutWithImage.findViewById(R.id.image);
		int imgId = getArguments().getInt("img");
		image.setImageDrawable(ContextCompat.getDrawable(getActivity(), imgId));
    	return layoutWithImage;
    }

    public static RecordImageFragment newInstance(int image) {
		RecordImageFragment f = new RecordImageFragment();
        Bundle b = new Bundle();
        b.putInt("img", image);
		f.setArguments(b);
        return f;
    }
}


Solution

  • Finally, after hours of searching and trying. I've found an answer. The problem is very subtle. The problem lies with your adapter, you're using a FragmentManager in your adapter to manage the fragments. In the subsequent (second or third rows) I think the FragmentPagerAdapter doesn't consider the items returned by getItem() method as new fragments. So you don't see them at all. This could probably be because you're using the same FragmentManager to manage the adapter.

    The solution would be to completely do away with the FragmentManager by using the basic PagerAdapter and implementing instantiateItem() and destroyItem() yourself. This eliminates the needs for Fragments to begin with and you can simply inflate the Image views yourself.

    Here's the new MyPagerAdapter you need to put in place of the MyFragmentPagerAdapter.

    private class MyPagerAdapter extends PagerAdapter {
            int[] internalPicts = {R.drawable.dashboard, R.drawable.icon, R.drawable.mockup};
    
            public MyPagerAdapter() {
                super();
            }
    
            @Override
            public int getCount() {
                return internalPicts.length;
            }
    
            @Override
            public boolean isViewFromObject(View view, Object object) {
                return view == object;
            }
    
            @Override
            public Object instantiateItem(ViewGroup collection, int position) {
    
                // Inflating layout
                LayoutInflater inflater = (LayoutInflater) collection.getContext()
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    
                View view = inflater.inflate(R.layout.record_images_pager, null);
    
                ImageView image = (ImageView) view.findViewById(R.id.image);
    
                image.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(), internalPicts[position]));
    
                collection.addView(view, 0);
                return view;
    
            }
    
            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                container.removeView((View) object);
            }
        }
    

    At the end make sure you replace pager.setAdapter(new MyFragmentPagerAdapter()); by pager.setAdapter(new MyPagerAdapter());

    You don't need RecordImageFragment too.

    Tested in Android Studio.

    I read this for reference.

    Tip - Use ListView with a custom adapter instead of a simple LinearLayout to place all your viewpagers for smooth scrolling.