转载请注明出处: http://blog.youkuaiyun.com/forwardyzk/article/details/42780427
我们看到的应用引导页有横向滑动和纵向滑动的,横向的我们很好实现,使用ViewPager即可,如果是纵向的,ViewPager设置纵向的功能,所以我们需要自定义纵向的ViewPager,下面为大家通过一款既可以横向也可以纵向的ViewPager。
使用了自定义属性:
自定义类ViewPager继承ViewGroup
<span style="font-family:SimSun;font-size:18px;">TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.ViewPager, defStyle, 0);
mOrientation = a.getInt(R.styleable.ViewPager_orientation,
ORIENTATION_HORIZONTAL);</span>
获取自定义方向属性对应的值,默认的为横向的。
<span style="font-family:SimSun;font-size:18px;">public boolean isOrientationHorizontal() {
return mOrientation == ORIENTATION_HORIZONTAL;
}</span>
判断是是否是横向的,否则就是纵向的。
在onMeasure方法中对其所有的子孩子进行测量长和宽,
通过循环遍历所有的子孩子
<span style="font-family:SimSun;font-size:18px;">final int widthSpec = MeasureSpec.makeMeasureSpec(
widthSize, widthMode);
final int heightSpec = MeasureSpec.makeMeasureSpec(
heightSize, heightMode);
child.measure(widthSpec, heightSpec);
if (consumeVertical) {
childHeightSize -= child.getMeasuredHeight();
} else if (consumeHorizontal) {
childWidthSize -= child.getMeasuredWidth();
}</span>
同时也要准备显示的item显示的滑动的方向
<span style="font-family:SimSun;font-size:18px;">int focusDirection = View.FOCUS_FORWARD;
if (mCurItem != newCurrentItem) {
if (isOrientationHorizontal()) {
focusDirection = mCurItem < newCurrentItem ? View.FOCUS_RIGHT
: View.FOCUS_LEFT;
} else {
focusDirection = mCurItem < newCurrentItem ? View.FOCUS_DOWN
: View.FOCUS_UP;
}
oldCurInfo = infoForPosition(mCurItem);
mCurItem = newCurrentItem;
}</span>
在onLayout显示的位置
<span style="font-family:SimSun;font-size:18px;">switch (hgrav) {
default:
childLeft = paddingLeft;
break;
case Gravity.LEFT:
childLeft = paddingLeft;
paddingLeft += child.getMeasuredWidth();
break;
case Gravity.CENTER_HORIZONTAL:
childLeft = Math.max(
(width - child.getMeasuredWidth()) / 2,
paddingLeft);
break;
case Gravity.RIGHT:
childLeft = width - paddingRight
- child.getMeasuredWidth();
paddingRight += child.getMeasuredWidth();
break;
}
switch (vgrav) {
default:
childTop = paddingTop;
break;
case Gravity.TOP:
childTop = paddingTop;
paddingTop += child.getMeasuredHeight();
break;
case Gravity.CENTER_VERTICAL:
childTop = Math.max(
(height - child.getMeasuredHeight()) / 2,
paddingTop);
break;
case Gravity.BOTTOM:
childTop = height - paddingBottom
- child.getMeasuredHeight();
paddingBottom += child.getMeasuredHeight();
break;
}
if (isOrientationHorizontal()) {
childLeft += scrollX;
} else {
childTop += scrollY;
}
child.layout(childLeft, childTop,
childLeft + child.getMeasuredWidth(), childTop
+ child.getMeasuredHeight());
decorCount++;
}
}
}</span>
根据设置的Gravity,要计算其当前显示的间距,根据设置的方向和滑动的距离(scrollX),计算其不断的更改其位置(child.layout)。
在onTouchEvent()会根据手势点击状态,向外传递当前的状态。
<span style="font-family:SimSun;font-size:18px;">case MotionEvent.ACTION_DOWN: {
mScroller.abortAnimation();
mPopulatePending = false;
populate();
mIsBeingDragged = true;
setScrollState(SCROLL_STATE_DRAGGING);
// Remember where the motion event started
mLastMotionX = mInitialMotionX = ev.getX();
mLastMotionY = mInitialMotionY = ev.getY();
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
break;
}
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
final int pointerIndex = MotionEventCompat.findPointerIndex(ev,
mActivePointerId);
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float xDiff = Math.abs(x - mLastMotionX);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = Math.abs(y - mLastMotionY);
if (DEBUG)
Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff
+ "," + yDiff);
if (isOrientationHorizontal()) {
if (xDiff > mTouchSlop && xDiff > yDiff) {
if (DEBUG)
Log.v(TAG, "Starting drag!");
mIsBeingDragged = true;
mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX
+ mTouchSlop
: mInitialMotionX - mTouchSlop;
mLastMotionY = y;
setScrollState(SCROLL_STATE_DRAGGING);
setScrollingCacheEnabled(true);
}
} else {
if (yDiff > mTouchSlop && yDiff > xDiff) {
if (DEBUG)
Log.v(TAG, "Starting drag!");
mIsBeingDragged = true;
mLastMotionY = y - mInitialMotionY > 0 ? mInitialMotionY
+ mTouchSlop
: mInitialMotionY - mTouchSlop;
mLastMotionX = x;
setScrollState(SCROLL_STATE_DRAGGING);
setScrollingCacheEnabled(true);
}
}
}
// Not else! Note that mIsBeingDragged can be set above.
if (mIsBeingDragged) {
// Scroll to follow the motion event
final int activePointerIndex = MotionEventCompat
.findPointerIndex(ev, mActivePointerId);
float x = 0;
if (isOrientationHorizontal()) {
x = MotionEventCompat.getX(ev, activePointerIndex);
} else {
x = MotionEventCompat.getY(ev, activePointerIndex);
}
needsInvalidate |= performDrag(x);
}
break;</span>
当离开屏幕时,MotionEvent.ACTION_UP,需要判断需要显示的item, setCurrentItemInternal(nextPage, true, true, initialVelocity);
会有这样的看出,进行位置的滑动,
<span style="font-family:SimSun;font-size:18px;">populate(item);
scrollToItem(item, smoothScroll, velocity, dispatchSelected);</span>
当屏幕改变的时,来保证当前显示的item始终正确,
<span style="font-family:SimSun;font-size:18px;">protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// Make sure scroll position is set correctly.
if (w != oldw) {
recomputeScrollPosition(w, oldw, h, oldh, mPageMargin, mPageMargin);
}
}</span>
在computeScroll()处理mScroller手势的滑动,来改变位置的移动当前ViewPager(即ViewGroup)的移动
<span style="font-family:SimSun;font-size:18px;">public void computeScroll() {
if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
int oldX = getScrollX();
int oldY = getScrollY();
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
scrollTo(x, y);
if (!pageScrolled(isOrientationHorizontal() ? x : y)) {
mScroller.abortAnimation();
if (isOrientationHorizontal()) {
scrollTo(0, y);
} else {
scrollTo(x, 0);
}
}
}
// Keep on drawing until the animation has finished.
ViewCompat.postInvalidateOnAnimation(this);
return;
}
// Done with scroll, clean up state.
completeScroll(true);
}</span>
设置选中的item
<span style="font-family:SimSun;font-size:18px;">protected void onPageScrolled(int position, float offset, int offsetPixels) {
// Offset any decor views if needed - keep them on-screen at all times.
if (mDecorChildCount > 0) {
// TODO This is where I start getting tired. Refactor this better
// later.
if (isOrientationHorizontal()) {
final int scrollX = getScrollX();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
final int width = getWidth();
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child
.getLayoutParams();
if (!lp.isDecor)
continue;
final int hgrav = lp.gravity
& Gravity.HORIZONTAL_GRAVITY_MASK;
int childLeft = 0;
switch (hgrav) {
default:
childLeft = paddingLeft;
break;
case Gravity.LEFT:
childLeft = paddingLeft;
paddingLeft += child.getWidth();
break;
case Gravity.CENTER_HORIZONTAL:
childLeft = Math.max(
(width - child.getMeasuredWidth()) / 2,
paddingLeft);
break;
case Gravity.RIGHT:
childLeft = width - paddingRight
- child.getMeasuredWidth();
paddingRight += child.getMeasuredWidth();
break;
}
childLeft += scrollX;
final int childOffset = childLeft - child.getLeft();
if (childOffset != 0) {
child.offsetLeftAndRight(childOffset);
}
}
} else {
final int scrollY = getScrollY();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
final int height = getHeight();
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child
.getLayoutParams();
if (!lp.isDecor)
continue;
final int vgrav = lp.gravity
& Gravity.VERTICAL_GRAVITY_MASK;
int childTop = 0;
switch (vgrav) {
default:
childTop = paddingTop;
break;
case Gravity.TOP:
childTop = paddingTop;
paddingTop += child.getHeight();
break;
case Gravity.CENTER_VERTICAL:
childTop = Math.max(
(height - child.getMeasuredHeight()) / 2,
paddingTop);
break;
case Gravity.BOTTOM:
childTop = height - paddingBottom
- child.getMeasuredHeight();
paddingBottom += child.getMeasuredHeight();
break;
}
childTop += scrollY;
final int childOffset = childTop - child.getTop();
if (childOffset != 0) {
child.offsetTopAndBottom(childOffset);
}
}
}
}
if (mOnPageChangeListener != null) {
mOnPageChangeListener
.onPageScrolled(position, offset, offsetPixels);
}
if (mInternalPageChangeListener != null) {
mInternalPageChangeListener.onPageScrolled(position, offset,
offsetPixels);
}
if (mPageTransformer != null) {
final boolean horizontal = isOrientationHorizontal();
final int scroll = horizontal ? getScrollX() : getScrollY();
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.isDecor)
continue;
float transformPos;
if (horizontal) {
transformPos = (float) (child.getLeft() - scroll)
/ getClientWidth();
} else {
transformPos = (float) (child.getTop() - scroll)
/ getClientHeight();
}
mPageTransformer.transformPage(child, transformPos);
}
}
mCalledSuper = true;
}</span>
使用步骤:
activity_main.xml
<span style="font-family:SimSun;font-size:18px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.ryanharter.viewpager.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:orientation="vertical" >
</com.ryanharter.viewpager.ViewPager>
</RelativeLayout></span>
因为使用了自定义属性,所以要在xml的根节点下添加 xmlns:app="http://schemas.android.com/apk/res-auto"
设置app:orientation="vertical"或者horizontal
MainActivity.java
<span style="font-family:SimSun;font-size:18px;">public class MainActivity extends Activity {
private ViewPager pager;
private List<View> pagerViews;
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = MainActivity.this;
initView();
initPagerViews();
initData();
}
private void initData() {
pager.setAdapter(new MyAdapter());
pager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
Toast.makeText(getApplicationContext(), "选中了" + position, 0)
.show();
}
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
private void initPagerViews() {
pagerViews = new ArrayList<View>();
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
TextView tv = null;
for (int i = 0; i < 4; i++) {
tv = new TextView(mContext);
tv.setText("View--" + i);
tv.setLayoutParams(lp);
tv.setGravity(Gravity.CENTER);
if (i == 0) {
tv.setBackgroundColor(mContext.getResources().getColor(
android.R.color.holo_blue_bright));
} else if (i == 1) {
tv.setBackgroundColor(mContext.getResources().getColor(
android.R.color.holo_green_dark));
} else if (i == 2) {
tv.setBackgroundColor(mContext.getResources().getColor(
android.R.color.holo_orange_light));
} else if (i == 3) {
tv.setBackgroundColor(mContext.getResources().getColor(
android.R.color.holo_red_dark));
}
pagerViews.add(tv);
}
}
private void initView() {
pager = (ViewPager) findViewById(R.id.pager);
}
class MyAdapter extends PagerAdapter {
@Override
public int getCount() {
if (pagerViews == null)
return 0;
else
return pagerViews.size();
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// super.destroyItem(container, position, object);
container.removeView((View) object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(pagerViews.get(position));
return pagerViews.get(position);
// return super.instantiateItem(container, position);
}
}
}
</span>
使用的Viewpager,PagerAdapter和OnPageChangeListener是
import com.ryanharter.viewpager.PagerAdapter;
import com.ryanharter.viewpager.ViewPager;
import com.ryanharter.viewpager.ViewPager.OnPageChangeListener;
包中的。
源码下载: http://download.youkuaiyun.com/detail/forwardyzk/8375173
效果图: