Reputation: 2806
I need to use same fragment for Photos and Videos tab and still have three tabs. I have two fragments in viewpager and 3 tabs in tablayout. I want to achieve viewpager changing effect like instagram. What I've done: I created custom viewpager below:
public class SwipableViewPager extends ViewPager {
private float downX;
private float downY;
private float mStartDragX;
private float upX;
private float upY;
private boolean dragDisabled;
private SwipeListener mListener;
public void setSwipeListener(SwipeListener listener) {
mListener = listener;
}
public SwipableViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
float x = ev.getX();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = ev.getX();
downY = ev.getY();
mStartDragX = x;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
upX = ev.getX();
upY = ev.getY();
float deltaX = downX - upX;
if(deltaX>-getWidth() && deltaX< -getWidth()/4 && getCurrentItem() == getAdapter().getCount() - 1){
mListener.setPageAndEnableDrag();
}
break;
case MotionEvent.ACTION_MOVE:
if (mStartDragX - 30 > x && getCurrentItem() == getAdapter().getCount() - 1) {
mListener.onSwipeOutAtEnd();
}
break;
}
if(dragDisabled) {
return false;
}
else{
return super.onInterceptTouchEvent(ev);
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if(dragDisabled) return false;
return super.onTouchEvent(ev);
}
public void disableDrag(boolean disabled) {
dragDisabled = disabled;
}
public interface SwipeListener {
void setPageAndEnableDrag();
void onSwipeOutAtEnd();
}
}
It will notify swipe right when current item is last item in adapter (this need for when you are in photo tab and you want to go video tab with swiping). In activity I set up viewpager like this:
private void setupViewPager() {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFragment(GalleryFragment.newInstance(), getResources().getString(R.string.gallery_toolbar_title));
TakePhotoFragment fragment = new TakePhotoFragment();
adapter.addFragment(fragment, getResources().getString(R.string.photo));
viewPager.setAdapter(adapter);
viewPager.setSwipeListener(this);
tabLayout.setupWithViewPager(viewPager);
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
switch (tab.getPosition()){
case 1: //selected photo tab
viewPager.setCurrentItem(1);
toolbar.setTitle(R.string.photo);
viewPager.disableDrag(false); //switch on default swipe
break;
case 2: // selected video tab
toolbar.setTitle(R.string.video);
viewPager.disableDrag(true); // switch off default swipe
break;
default: // selected gallery tab
viewPager.setCurrentItem(tab.getPosition());
toolbar.setTitle(R.string.gallery_toolbar_title);
viewPager.disableDrag(false); //switch on default swipe
break;
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
@Override
public void setPageAndEnableDrag() {
tabLayout.getTabAt(1).select();
viewPager.disableDrag(false);
}
public void setPagerEnabledDrag(boolean enabled) {
viewPager.disableDrag(!enabled);
}
@Override
public void onSwipeOutAtEnd() {
viewPager.disableDrag(true);
tabLayout.getTabAt(2).select();
}
Everything okay, but when you are in gallery tab and if you want to go video tab, you can not done it by clicking video tab. When you click video tab first it makes active photo tab and after that if you click video tab secondly it will active video tab. Also changing from photo tab and video tab by swiping working ugly. I need to achieve when click video tab it must go to video tab not to photo tab like now.
also see video: https://youtu.be/tbT7mzkmydQ
PLEASE SEE THE VIDEO: Video of my activity in action
Upvotes: 2
Views: 2855
Reputation: 10309
As I understand you need to use same fragment for Photos and Videos tab and still have three tabs.
Swiping from photo to video tab - Add a blank fragment at third tab keeping its width to zero in pager adapter. Photo fragment will keep displaying when swiping from photo to video tab.
private void setupViewPager(){
SectionsPagerAdapter adapter = new SectionsPagerAdapter(getSupportFragmentManager());
adapter.addFragment(new GalleryFragment());
adapter.addFragment(new PhotoFragment());
adapter.addFragment(new Fragment()); //blank fragment
mViewPager = (ViewPager) findViewById(R.id.viewpager_container);
mViewPager.setAdapter(adapter);
mTabLayout = (TabLayout) findViewById(R.id.tabsBottom);
mTabLayout.setupWithViewPager(mViewPager);
mTabLayout.getTabAt(0).setText(getString(R.string.gallery));
mTabLayout.getTabAt(1).setText(getString(R.string.photo));
mTabLayout.getTabAt(2).setText(getString(R.string.video));
...
}
Set width of blank tab to 0
public class SectionsPagerAdapter extends FragmentPagerAdapter {
...
@Override
public float getPageWidth(int position) {
if (position == 2) {
return 0;
}
return super.getPageWidth(position);
}
}
Swiping from video to photo tab - Increase fling distance for Video tab so that swipe stops at photo tab. Reset fling distance to default for other tabs. You'll need Java Reflection API to make fling distance field accessible as it is a private field in ViewPager.class
final float density = getResources().getDisplayMetrics().density;
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
Field mFlingDistance;
try {
mFlingDistance = ViewPager.class.getDeclaredField("mFlingDistance");
mFlingDistance.setAccessible(true);
switch (tab.getPosition()){
case 2: mFlingDistance.set(mViewPager, (int)(200 * density));
break;
default: mFlingDistance.set(mViewPager, (int)(25 * density));
break;
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {}
@Override
public void onTabReselected(TabLayout.Tab tab) {}
});
Link to video: https://i.sstatic.net/pxmXf.jpg
This is a quick-and-dirty solution in the direction. You might need to fine tune it.
Upvotes: 5
Reputation: 3832
It is thoroughly unclear what you want to achieve. Why do have two fragments only, but three tabs? Why do implement a swiper (swiping, or rather paging, is built into the viewpager by default). Why do have to deal with unabling drag? Is this, or rather will this be, a feature of the viewpager? Or will it be a feature of one of your fragments? (In the latter, you should leave it with that fragment.)
You seem to be wanting an infinite viewpager (swiping to the left (!) on the far right fragment). Is this correct? Or do you simply want to get from the right (third) fragment to the left (first) fragment? You can do so with a simple implementation of ViewPager
and TabLayout
: the pages (fragments) will be swipable/pagable (swipe twice for switching from the third to the first fragment) and the tabs will be clickable (click once for doing the same switch).
Since nowhere do you define the order of your fragments, please don't speak of "Video" or "Gallery", but of the first and second (and possibly third) fragments.
For the sake of clarity, I post an activity with a standard viewpager-tablayout-toolbar setup - no swipe detection necessary.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ArrayList<Fragment> fragments = new ArrayList<>();
// make sure to instantiate and add all your fragments
// your fragments will probably be of different types
fragments.add(YourFragment.newInstance("Gallery"));
fragments.add(YourFragment.newInstance("Video"));
fragments.add(YourFragment.newInstance("Photo"));
final Toolbar toolbar = findViewById(R.id.toolbar);
final ViewPager viewPager = findViewById(R.id.viewpager);
FragmentPagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public int getCount() {
return fragments.size();
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
public CharSequence getPageTitle(int position) {
return ((YourFragment) fragments.get(position)).getTitle();
}
};
viewPager.setAdapter(adapter);
TabLayout tabLayout = findViewById(R.id.sliding_tabs);
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
// tab.getPosition() returns index for viewPager.setCurrentItem()
viewPager.setCurrentItem(tab.getPosition());
toolbar.setTitle(((YourFragment) fragments.get(tab.getPosition())).getTitle());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
tabLayout.setupWithViewPager(viewPager);
}
// make your fragments implement this interface
interface FragmentTitle {
CharSequence getTitle();
}
}
Upvotes: 3