android 개발 여행기: 멋진 시작 페이지에서 두 ViewPager의 연결을 어떻게 실현합니까

16807 단어 android개발하다
효과도:
오픈소스 프로젝트에서 본 효과로 괜찮은 것 같아서 끼워 사용했는데 그중에 두 개의 ViewPager의 연동을 사용해 좋은 효과를 냈으니 공유해 보자.두 ViewPager를 연결하는 방법
여기는 소스 오픈 프로젝트의 다운로드 주소입니다.
글에서 분명히 말했는데, 내가 중점을 정리한 다음에 나는 원본 코드를 다운로드했고, 그것을 약간 수정하여 프로젝트에서 사용하기 편리하도록 봉인했다. 바로 컨트롤로 봉인된 Linked ViewPager이다
실현된 사고방식은 사실 매우 간단하다. ViewPager는 이동 거리를 통해 리셋되는 인터페이스가 없기 때문에 우리는 v4 패키지의 원본 코드를 수정할 수 밖에 없다. 원래의 ViewPager에 ViewPager를 하나 더 설치하고 ViewPager의 이벤트 처리 절차를 추적한다. 메인 ViewPager를 미끄러질 때도 내장된 ViewPager를 미끄러지게 한다.이렇게 하면 우리는 어떤 처리도 하지 않아도 우리의 목적에 도달할 수 있다.실현 과정은 매우 간단하다. 다음은 구체적인 수정이다.
ViewPager에 내장된 ViewPager를 추가하려면:
private ViewPager mFollowViewPager;
    public  void setFlolwViewPager(ViewPager page){
    mFollowViewPager = page;
}

ViewPager는 손가락이 풀리지 않을 때 performDrag를 사용하여 보기를 변환하고 performDrag의 페이지 Scrolled 코드 줄 뒤에 내장된 ViewPager 스크롤 코드를 삽입합니다.
final float pageOffset =  scrollX / width;
    if(mFollowViewPager!=null){
        mFollowViewPager.scrollTo( (int)(pageOffset*mFollowViewPager.getWidth()), mFollowViewPager.getScrollY());  
    }

손가락이 풀릴 때도 ViewPager는 거리를 굴리기 때문에 여기도 상응하는 처리를 해야 한다. Motion Event.ACTION_UP 이벤트의 setcurrentItemInternal 코드 행에 스크롤 이벤트 코드가 추가됩니다.
if(mFollowViewPager!=null){
     mFollowViewPager.setCurrentItemInternal(nextPage, true, true, initialVelocity);
}

여기서 ViewPager에 대한 개조가 완료되었습니다. 다음은 링크드 ViewPager를 사용하는 곳에서 ViewPager에 내부를 연결하는 ViewPager를 사용했습니다.
mPager.setFollowViewPager(mFollowViewPager);

이렇게 하면 연동된 ViewPager가 완성됩니다.
만약 당신이 사용한 LZ가 컨트롤 코드로 봉인되어 있다면, Activity에서 아무 일도 하지 말고, 상응하는 레이아웃 파일에서 컨트롤을 사용하면 됩니다. OK:
"http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".UserGuideActivity" >

    <com.jcodecraeer.linkedviewpager.LinkedViewPager.MyLinkViewPager
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" >
    com.jcodecraeer.linkedviewpager.LinkedViewPager.MyLinkViewPager>


아래
연동 ViewPager는 하나의viewpager가 미끄러질 때 다른 ViewPager도 따라서 미끄러지고 둘은 동기화된다는 뜻이다.
만약 ViewPager가 이동 거리에 대한 리셋 인터페이스가 있다면 이 일은 하기 쉬울 것입니다. 유감스럽게도 OnPageChangeListener가 하나 있습니다. OnPageChangeListener에서 onPageScrolled(int position, float position Offset, int position Offset Pixels)의 매개 변수에 따라 해 보았지만 실패했습니다.
그러면 ViewPager를 사용자 정의할 수밖에 없습니다.
나는 ViewPager의 원본 코드를 v4에서 직접 꺼내서 불필요한 것들을 제거하고 클래스를 찾을 수 없을 때까지
ViewPager를 꺼내야 하는 것 외에 관련 Pager Adapter 클래스도 꺼내야 한다. 그렇지 않으면 ViewPager는 자신의 것을 사용하고 adapter는 v4의 것을 사용해서 문제가 생길 수 있다.
연결을 위해 ViewPager에 private 변수 mFollow ViewPager를 추가합니다.
private ViewPager mFollowViewPager;
   public  void setFlolwViewPager(ViewPager page){
   mFollowViewPager = page;
}

mFollow ViewPager는 현재 ViewPager를 따라 스크롤되는 다른 ViewPager를 나타냅니다.
내 생각은 현재 ViewPager 스크롤과 관련된 코드에서 mFollow ViewPager의scrollTo 방법을 호출하는 것이다.그러면 어디에 넣는 것이 좋을까요? ViewPager의 행동을 자세히 추적한 결과 손가락이 풀리지 않을 때performDrag 방법은 관련 이동을 처리하고 그는 자신의scrollTo를 호출하여 자신의 평이를 실현하는 것을 발견했습니다. 따라서 우리는performDrag 방법에 다음과 같은 코드를 추가하면 됩니다.
//add by jcodecraeer
final float pageOffset =  scrollX / width;
if(mFollowViewPager!=null){
mFollowViewPager.scrollTo( (int)(pageOffset*mFollowViewPager.getWidth()), >mFollowViewPager.getScrollY());
}

주의, 메인 ViewPager가 얼마나 멀리 이동한 것이 아니라, mFollow ViewPager가 얼마나 멀리 이동하는지, 두 ViewPager의 폭이 다를 수 있으므로 전환이 필요합니다. 위의 코드에서final float page Offset = scroll X/width;pageOffset에서 얻은 값만 변환됩니다.
덮어쓴 performDrag은 다음과 같습니다.
private boolean performDrag(float x) {
    boolean needsInvalidate = false;
    final float deltaX = mLastMotionX - x;
    mLastMotionX = x;

    float oldScrollX = getScrollX();
    float scrollX = oldScrollX + deltaX;
    final int width = getWidth();
    float leftBound = width * mFirstOffset;
    float rightBound = width * mLastOffset;
    boolean leftAbsolute = true;
    boolean rightAbsolute = true;
    final ItemInfo firstItem = mItems.get(0);
    final ItemInfo lastItem = mItems.get(mItems.size() - 1);
    if (firstItem.position != 0) {
        leftAbsolute = false;
        leftBound = firstItem.offset * width;
    }
    if (lastItem.position != mAdapter.getCount() - 1) {
        rightAbsolute = false;
        rightBound = lastItem.offset * width;
    }
    if (scrollX < leftBound) {
        if (leftAbsolute) {
            float over = leftBound - scrollX;
            needsInvalidate = mLeftEdge.onPull(Math.abs(over) / width);
        }
        scrollX = leftBound;
    } else if (scrollX > rightBound) {
        if (rightAbsolute) {
            float over = scrollX - rightBound;
            needsInvalidate = mRightEdge.onPull(Math.abs(over) / width);
        }
        scrollX = rightBound;
    }
    // Don't lose the rounded component
    mLastMotionX += scrollX - (int) scrollX;
    scrollTo((int) scrollX, getScrollY());
    pageScrolled((int) scrollX);
    //add by jcodecraeer
    final float pageOffset =  scrollX / width;
    if(mFollowViewPager!=null){
        mFollowViewPager.scrollTo( (int)(pageOffset*mFollowViewPager.getWidth()), mFollowViewPager.getScrollY());  
    }
    return needsInvalidate;
}

손가락이 화면에서 벗어나지 않는 단계만 처리하면 충분하지 않다. 손가락이 풀리면 ViewPager는 스스로 일정한 거리를 유지할 수 있기 때문에 mFollow ViewPager도 따라서 움직여야 한다. 손가락을 풀어서case Motion Event에 있어야 하는지 생각해 보자.ACTION_UP에서 처리되는 건요?
관련 코드를 찾았습니다.
case MotionEvent.ACTION_UP:
    if (mIsBeingDragged) {
        final VelocityTracker velocityTracker = mVelocityTracker;
        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
        int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
                velocityTracker, mActivePointerId);
        mPopulatePending = true;
        final int width = getWidth();
        final int scrollX = getScrollX();
        final ItemInfo ii = infoForCurrentScrollPosition();
        final int currentPage = ii.position;
        final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
        final int activePointerIndex =
                MotionEventCompat.findPointerIndex(ev, mActivePointerId);
        final float x = MotionEventCompat.getX(ev, activePointerIndex);
        final int totalDelta = (int) (x - mInitialMotionX);
        int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
                totalDelta);
        setCurrentItemInternal(nextPage, true, true, initialVelocity);

        mActivePointerId = INVALID_POINTER;
        endDrag();
        needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();
    }

여기서 setCurrentItemInternal(nextPage,true,true,initialVelocity)이 관건이고 그의 코드는 다음과 같다.
void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
    if (mAdapter == null || mAdapter.getCount() <= 0) {
        setScrollingCacheEnabled(false);
        return;
    }
    if (!always && mCurItem == item && mItems.size() != 0) {
        setScrollingCacheEnabled(false);
        return;
    }
    if (item < 0) {
        item = 0;
    } else if (item >= mAdapter.getCount()) {
        item = mAdapter.getCount() - 1;
    }
    final int pageLimit = mOffscreenPageLimit;
    if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
        // We are doing a jump by more than one page.  To avoid
        // glitches, we want to keep all current pages in the view
        // until the scroll ends.
        for (int i=0; itrue;
        }
    }
    final boolean dispatchSelected = mCurItem != item;
    populate(item);
    scrollToItem(item, smoothScroll, velocity, dispatchSelected);
}

setCurrentItem 인터넷에서 scrollToItem(item,smoothScroll,velocity,dispatchSelected)이 호출된 것을 볼 수 있습니다.손가락이 풀린 후에도 계속 평평하게 움직일 수 있도록 한다.즉 mFollow ViewPager에 대해 우리도 마찬가지로 setCurrent Item 인터넷을 호출하면 그도 따라서 이동할 수 있다는 것이다.이 사고방식에 따라 우리는case Motion Event를 개작한다.ACTION_UP의 코드 세그먼트:
case MotionEvent.ACTION_UP:
    if (mIsBeingDragged) {
        final VelocityTracker velocityTracker = mVelocityTracker;
        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
        int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
                velocityTracker, mActivePointerId);
        mPopulatePending = true;
        final int width = getWidth();
        final int scrollX = getScrollX();
        final ItemInfo ii = infoForCurrentScrollPosition();
        final int currentPage = ii.position;
        final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
        final int activePointerIndex =
                MotionEventCompat.findPointerIndex(ev, mActivePointerId);
        final float x = MotionEventCompat.getX(ev, activePointerIndex);
        final int totalDelta = (int) (x - mInitialMotionX);
        int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
                totalDelta);
        setCurrentItemInternal(nextPage, true, true, initialVelocity);
        //add by jcodecraeer
        if(mFollowViewPager!=null){
            mFollowViewPager.setCurrentItemInternal(nextPage, true, true, initialVelocity);
        }
        mActivePointerId = INVALID_POINTER;
        endDrag();
        needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();
    }

이로써 우리는 모든 수정을 완성했지만 사실 몇 줄도 고치지 않았다.
그렇다면 액티비티에서 개조된 ViewPager를 어떻게 사용해서 두 ViewPager를 연동시킬 수 있을까?가령 하나는 mViewPager이고 하나는 mFollowViewPager라고 가정한다면 나는 mFollowViewPager가 mViewPager를 따라 움직이게 하고 싶다.
mPager.setFollowViewPager(mFollowViewPager);

주의해야 할 것은 내가 다음에 제시한 데모에서 나는 팔로우 ViewPager의 모든 터치 이벤트를 차단하고 메인 ViewPager를 팔로우 ViewPager 위에 덮어씌웠다. 이것은 내가 실현하고자 하는 효과와 일치한다.만약 follow ViewPager도 반대로 메인 ViewPager도 따라서 이동할 수 있도록 하려면 반대로 호출해도 무방하다.
mFollowViewPager.setFollowViewPager(mPager);

그러나 나는 이런 양방향 호출에 문제가 생길지 확실하지 않다. 왜냐하면 나는 mFollow ViewPager 변수에서 이동한 후에 반드시 발생해야 할 상태 변화(예를 들어 관련 변수)를 엄격하게 고려하지 않았기 때문이다.독자는 한번 시험해 보고 나서 개선할 수 있다.
ViewPager가 개조된 곳은 add by jcodecraeer로 표시됩니다. (삭제하기 위한 불필요한 코드는 포함되지 않습니다.)

좋은 웹페이지 즐겨찾기