ViewPager migrate to ViewPager2. Fixes #522.

Swiping between tabs work. Fixes #105.
This commit is contained in:
Román Martínez
2020-11-26 22:07:58 +01:00
parent e8017a4e9d
commit ae8833d21b
6 changed files with 87 additions and 31 deletions
@@ -22,11 +22,14 @@ import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewParent;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentPagerAdapter; import androidx.viewpager2.adapter.FragmentStateAdapter;
import com.google.android.material.tabs.TabLayoutMediator;
import de.dennisguse.opentracks.content.TrackDataHub; import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.data.Marker; import de.dennisguse.opentracks.content.data.Marker;
@@ -75,8 +78,10 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
trackDataHub = new TrackDataHub(this); trackDataHub = new TrackDataHub(this);
viewBinding.trackDetailActivityViewPager.setAdapter(new CustomFragmentPagerAdapter(getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)); CustomFragmentPagerAdapter pagerAdapter = new CustomFragmentPagerAdapter(this);
viewBinding.trackDetailActivityTablayout.setupWithViewPager(viewBinding.trackDetailActivityViewPager); viewBinding.trackDetailActivityViewPager.setAdapter(pagerAdapter);
new TabLayoutMediator(viewBinding.trackDetailActivityTablayout, viewBinding.trackDetailActivityViewPager,
(tab, position) -> tab.setText(pagerAdapter.getPageTitle(position))).attach();
if (savedInstanceState != null) { if (savedInstanceState != null) {
viewBinding.trackDetailActivityViewPager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY)); viewBinding.trackDetailActivityViewPager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
} }
@@ -220,20 +225,15 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
} }
} }
private class CustomFragmentPagerAdapter extends FragmentPagerAdapter { private class CustomFragmentPagerAdapter extends FragmentStateAdapter {
public CustomFragmentPagerAdapter(@NonNull FragmentManager fm, int behavior) { public CustomFragmentPagerAdapter(@NonNull FragmentActivity fa) {
super(fm, behavior); super(fa);
}
@Override
public int getCount() {
return 4;
} }
@NonNull @NonNull
@Override @Override
public Fragment getItem(int position) { public Fragment createFragment(int position) {
switch (position) { switch (position) {
case 0: case 0:
return StatisticsRecordedFragment.newInstance(trackId); return StatisticsRecordedFragment.newInstance(trackId);
@@ -249,6 +249,10 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
} }
@Override @Override
public int getItemCount() {
return 4;
}
public CharSequence getPageTitle(int position) { public CharSequence getPageTitle(int position) {
switch (position) { switch (position) {
case 0: case 0:
@@ -13,8 +13,10 @@ import android.view.WindowManager;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentPagerAdapter; import androidx.viewpager2.adapter.FragmentStateAdapter;
import com.google.android.material.tabs.TabLayoutMediator;
import de.dennisguse.opentracks.content.TrackDataHub; import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
@@ -148,8 +150,10 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback); trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
trackDataHub = new TrackDataHub(this); trackDataHub = new TrackDataHub(this);
viewBinding.trackDetailActivityViewPager.setAdapter(new CustomFragmentPagerAdapter(getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)); CustomFragmentPagerAdapter pagerAdapter = new CustomFragmentPagerAdapter(this);
viewBinding.trackDetailActivityTablayout.setupWithViewPager(viewBinding.trackDetailActivityViewPager); viewBinding.trackDetailActivityViewPager.setAdapter(pagerAdapter);
new TabLayoutMediator(viewBinding.trackDetailActivityTablayout, viewBinding.trackDetailActivityViewPager,
(tab, position) -> tab.setText(pagerAdapter.getPageTitle(position))).attach();
if (savedInstanceState != null) { if (savedInstanceState != null) {
viewBinding.trackDetailActivityViewPager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY)); viewBinding.trackDetailActivityViewPager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
} }
@@ -358,20 +362,15 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
finish(); finish();
} }
private class CustomFragmentPagerAdapter extends FragmentPagerAdapter { private class CustomFragmentPagerAdapter extends FragmentStateAdapter {
public CustomFragmentPagerAdapter(@NonNull FragmentManager fm, int behavior) { public CustomFragmentPagerAdapter(@NonNull FragmentActivity fa) {
super(fm, behavior); super(fa);
}
@Override
public int getCount() {
return 4;
} }
@NonNull @NonNull
@Override @Override
public Fragment getItem(int position) { public Fragment createFragment(int position) {
switch (position) { switch (position) {
case 0: case 0:
return StatisticsRecordingFragment.newInstance(); return StatisticsRecordingFragment.newInstance();
@@ -387,6 +386,10 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
} }
@Override @Override
public int getItemCount() {
return 4;
}
public CharSequence getPageTitle(int position) { public CharSequence getPageTitle(int position) {
switch (position) { switch (position) {
case 0: case 0:
@@ -30,6 +30,7 @@ import android.view.GestureDetector;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector;
import android.view.View; import android.view.View;
import android.view.ViewParent;
import android.widget.Scroller; import android.widget.Scroller;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -117,6 +118,14 @@ public class ChartView extends View {
private boolean reportSpeed = true; private boolean reportSpeed = true;
private boolean showPointer = false; private boolean showPointer = false;
// To handle disallow intercept touch event in parent.
private static final int SCROLL_X_NONE = 0;
private static final int SCROLL_X_LEFT = 1;
private static final int SCROLL_X_RIGHT = 2;
private boolean isOnLeftEdge = true;
private boolean isOnRightEdge = true;
private int scrollXDirection = SCROLL_X_NONE;
private final GestureDetectorCompat detectorScrollFlingTab = new GestureDetectorCompat(getContext(), new GestureDetector.SimpleOnGestureListener() { private final GestureDetectorCompat detectorScrollFlingTab = new GestureDetectorCompat(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override @Override
@@ -129,6 +138,14 @@ public class ChartView extends View {
@Override @Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (distanceX < 0) {
scrollXDirection = SCROLL_X_LEFT;
} else if (distanceX > 0) {
scrollXDirection = SCROLL_X_RIGHT;
} else {
scrollXDirection = SCROLL_X_NONE;
}
if (Math.abs(distanceX) > 0) { if (Math.abs(distanceX) > 0) {
int availableToScroll = effectiveWidth * (zoomLevel - 1) - getScrollX(); int availableToScroll = effectiveWidth * (zoomLevel - 1) - getScrollX();
if (availableToScroll > 0) { if (availableToScroll > 0) {
@@ -434,6 +451,9 @@ public class ChartView extends View {
chartPoints.clear(); chartPoints.clear();
xExtremityMonitor.reset(); xExtremityMonitor.reset();
zoomLevel = 1; zoomLevel = 1;
isOnLeftEdge = true;
isOnRightEdge = true;
scrollXDirection = SCROLL_X_NONE;
updateDimensions(); updateDimensions();
} }
} }
@@ -500,6 +520,18 @@ public class ChartView extends View {
invalidate(); invalidate();
} }
/**
* Handle parent's view disallow touch event.
*
* @param disallow Does disallow parent touch event?
*/
private void requestDisallowInterceptTouchEventInParent(boolean disallow) {
ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(disallow);
}
}
/** /**
* Scrolls the view horizontally by a given amount. * Scrolls the view horizontally by a given amount.
* *
@@ -507,13 +539,21 @@ public class ChartView extends View {
*/ */
private void scrollBy(int deltaX) { private void scrollBy(int deltaX) {
int scrollX = getScrollX() + deltaX; int scrollX = getScrollX() + deltaX;
if (scrollX < 0) { if (scrollX <= 0) {
scrollX = 0; scrollX = 0;
isOnLeftEdge = true;
} else {
isOnLeftEdge = false;
} }
int maxWidth = effectiveWidth * (zoomLevel - 1); int maxWidth = effectiveWidth * (zoomLevel - 1);
if (scrollX > maxWidth) { if (scrollX >= maxWidth) {
scrollX = maxWidth; scrollX = maxWidth;
isOnRightEdge = true;
} else {
isOnRightEdge = false;
} }
scrollTo(scrollX, 0); scrollTo(scrollX, 0);
} }
@@ -538,6 +578,15 @@ public class ChartView extends View {
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
boolean isZoom = detectorZoom.onTouchEvent(event); boolean isZoom = detectorZoom.onTouchEvent(event);
boolean isScrollTab = detectorScrollFlingTab.onTouchEvent(event); boolean isScrollTab = detectorScrollFlingTab.onTouchEvent(event);
if (event.getPointerCount() == 1 && ((isOnLeftEdge && scrollXDirection == SCROLL_X_LEFT) || (isOnRightEdge && scrollXDirection == SCROLL_X_RIGHT))) {
// Focus on view's parent.
requestDisallowInterceptTouchEventInParent(false);
} else {
// Focus on ChartView.
requestDisallowInterceptTouchEventInParent(true);
}
return isZoom || isScrollTab; return isZoom || isScrollTab;
} }
+1 -1
View File
@@ -25,7 +25,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<androidx.viewpager.widget.ViewPager <androidx.viewpager2.widget.ViewPager2
android:id="@+id/track_detail_activity_view_pager" android:id="@+id/track_detail_activity_view_pager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
+1 -1
View File
@@ -25,7 +25,7 @@ limitations under the License.
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<androidx.viewpager.widget.ViewPager <androidx.viewpager2.widget.ViewPager2
android:id="@+id/track_detail_activity_view_pager" android:id="@+id/track_detail_activity_view_pager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
+1 -1
View File
@@ -11,7 +11,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<androidx.viewpager.widget.ViewPager <androidx.viewpager2.widget.ViewPager2
android:id="@+id/track_detail_activity_view_pager" android:id="@+id/track_detail_activity_view_pager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"