基础吧,和viewpager绑定一下,就可以滑动了。因为viewpager里面处理了touch事件,会进行move事件的滑动处理。
看下例子:github地址点击打开链接 (https://github.com/shixin398/FragmentScroll)
这里用两个fragment,一个viewpager;
fragment布局里面就放一个textview:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_red_light"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="Fragment One" /> </LinearLayout>
第二个fragment的布局一样,只是textview的文字不一样;
fragment代码,仅解析下:
public class FragmentOne extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_one, container, false); } }
以上是fragment部分,下面来看viewpager的:
也就是在activity的布局中:
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.spreadtrumshitaoli.fragmentscroll.MainActivity"> <android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_height="match_parent" android:layout_width="match_parent"/> </android.support.constraint.ConstraintLayout>
就一个viewpager,没有别的。
这里有一个建议:在抓某个知识点的时候,不要care过多的东西,demo越简单越好,不好搞复杂,加什么textview作为tab,和fragment滑动时,变换textview的显示效果,没有必要。就简单的只有fragment,看看怎么实现滑动,基础都get了,花哨的也就能玩了。
看下activity:
public class MainActivity extends AppCompatActivity { private FragmentOne fragmentOne; private FragmentTwo fragmentTwo; private ViewPager viewPager; private ArrayList<Fragment> mFragmentList = new ArrayList <Fragment>(); private FragmentPagerAdapter fragmentPagerAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init() { viewPager = (ViewPager) findViewById(R.id.view_pager); fragmentOne = new FragmentOne(); fragmentTwo = new FragmentTwo(); mFragmentList.add(fragmentOne); mFragmentList.add(fragmentTwo); fragmentPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) { @Override public Fragment getItem(int i) { return mFragmentList != null ? mFragmentList.get(i) : null; } @Override public int getCount() { return mFragmentList != null ? mFragmentList.size() : 0; } }; viewPager.setAdapter(fragmentPagerAdapter); viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int i, float v, int i1) { } @Override public void onPageSelected(int i) { //TODO: } @Override public void onPageScrollStateChanged(int i) { } }); } }
1.首先把fragment,viewpager都实例化;
2.把fragment添加到泛型arraylist里,
3.需要把带有fragment的arraylist和adapter绑定,这里绑定注意了(后面会有code讲解)
fragmentPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) { @Override public Fragment getItem(int i) { return mFragmentList != null ? mFragmentList.get(i) : null; } @Override public int getCount() { return mFragmentList != null ? mFragmentList.size() : 0; } };
4.viewpager和adapter进行绑定;
完了,这样就可以滑动了。
具体的带有fragment的arraylist如何绑定给viewpager来操作的?touch来了,fragment怎么就可以滑动?
都在viewpager里实现的。
从上面的代码注意到,adapter和fragment的有关系的地方只有一句:就是getItem的地方
fragmentPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) { @Override public Fragment getItem(int i) { return mFragmentList != null ? mFragmentList.get(i) : null; }
那么这句话如何起到作用的呢?
先看adpater和viewpager:viewpager中源码:
public void setAdapter(PagerAdapter adapter) {
....
final PagerAdapter oldAdapter = mAdapter;
mAdapter = adapter;
然后看,viewpager如何和fragment挂钩的:viewpager使用如下变量进行管理fragment(这里使用viewpager和fragment,所以是和fragment挂钩,如果是别的,则和其他的挂钩,所以这里的泛型是ItemInfo而不是fragment)
private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
既然是arraylist,则必然是add的地方添加的fragment,源码:
ItemInfo addNewItem(int position, int index) {
ItemInfo ii = new ItemInfo();
ii.position = position;
ii.object = mAdapter.instantiateItem(this, position);
ii.widthFactor = mAdapter.getPageWidth(position);
if (index < 0 || index >= mItems.size()) {
mItems.add(ii);
} else {
mItems.add(index, ii);
}
return ii;
}
可以看到,这里会把viewpager挂钩的内容添加进来进行管理。
注意到函数:
ii.object = mAdapter.instantiateItem(this, position);
具体实现:
86 @SuppressWarnings("ReferenceEquality")
87 @Override
88 public Object instantiateItem(ViewGroup container, int position) {
89 if (mCurTransaction == null) {
90 mCurTransaction = mFragmentManager.beginTransaction();
91 }
92
93 final long itemId = getItemId(position);
94
95 // Do we already have this fragment?
96 String name = makeFragmentName(container.getId(), itemId);
97 Fragment fragment = mFragmentManager.findFragmentByTag(name);
98 if (fragment != null) {
99 if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
100 mCurTransaction.attach(fragment);
101 } else {
102 fragment = getItem(position);
103 if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
104 mCurTransaction.add(container.getId(), fragment,
105 makeFragmentName(container.getId(), itemId));
106 }
107 if (fragment != mCurrentPrimaryItem) {
108 fragment.setMenuVisibility(false);
109 fragment.setUserVisibleHint(false);
110 }
111
112 return fragment;
113 }
里面有, fragment = getItem(position);
这个函数我们复写了,并且我们复写的函数里面和fragment是挂钩的。
@Override public Fragment getItem(int i) { return mFragmentList != null ? mFragmentList.get(i) : null; }
好,现在知道是通过viewpager的addNewItem和相应的内容(fragment)挂钩的。
那么addNewItem,我们并没有显示调用啊,viewpager又是哪些逻辑,调用到这里的呢?
void populate(int newCurrentItem) {
...
if (curItem == null && N > 0) {
curItem = addNewItem(mCurItem, curIndex);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
...
// Make sure we have created all fragments that we need to have shown.
mInLayout = true;
populate();
mInLayout = false;
onMeasure的时候,调用。
至此,fragment和viewpager挂钩的逻辑,就清楚了。
下面看下,滑动的时候,我们也没有写逻辑,viewpager自己实现的滑动处理:
@Override
public boolean onTouchEvent(MotionEvent ev) {
...
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
mScroller.abortAnimation();
mPopulatePending = false;
populate();
// Remember where the motion event started
mLastMotionX = mInitialMotionX = ev.getX();
mLastMotionY = mInitialMotionY = ev.getY();
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
// A child has consumed some touch events and put us into an inconsistent
// state.
needsInvalidate = resetTouch();
break;
}
final float x = ev.getX(pointerIndex);
final float xDiff = Math.abs(x - mLastMotionX);
final float y = ev.getY(pointerIndex);
final float yDiff = Math.abs(y - mLastMotionY);
if (DEBUG) {
Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
}
if (xDiff > mTouchSlop && xDiff > yDiff) {
if (DEBUG) Log.v(TAG, "Starting drag!");
mIsBeingDragged = true;
requestParentDisallowInterceptTouchEvent(true);
mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :
mInitialMotionX - mTouchSlop;
mLastMotionY = y;
setScrollState(SCROLL_STATE_DRAGGING);
setScrollingCacheEnabled(true);
// Disallow Parent Intercept, just in case
ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
}
// Not else! Note that mIsBeingDragged can be set above.
if (mIsBeingDragged) {
// Scroll to follow the motion event
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(activePointerIndex);
needsInvalidate |= performDrag(x);
}
break;
代码简而概括:down的时候记录手指的信息,主要是(xy),当move的时候好计算划过的距离。
move事件的时候:1.看到有计算xy的滑动距离:
final float x = ev.getX(pointerIndex);
final float xDiff = Math.abs(x - mLastMotionX);
final float y = ev.getY(pointerIndex);
final float yDiff = Math.abs(y - mLastMotionY);
2.然后会调用:
performDrag(x);
这里(及子函数)会执行具体的滑动处理。详细需要看代码了。
总体上,viewpager和fragment的绑定,及滑动处理的逻辑概括就是这些了。