本文出自:http://blog.csdn.net/sodino/article/details/7211049
先上效果图:
实现“左右滑屏”核心类是Scroller,将View中的内容左右滚动从而实现滑屏效果。关键方法有:
scroller.scrollTo(x,y):
直接将View中的内容滚动到指定的(x,y)位置。
scroller.scrollTo(dx,dy):
直接将View中的内容滚动到相对当前状态的(dx,dy)位置。本例中用于实现手指拖拉移动View的效果。
scroller.startScroll(nowX, nowY, moveX, moveY, duration):
在duration的时间内完成move的位移。配合重写View.computeScroll()不断刷新界面从而实现滑屏动画。
如果当前点击拖拉的组件是按钮等自身可处理手势动作的组件,则重写ViewGroup.onInterceptTouchEvent(MotionEvent)可拦截此事件并将此事件传递至onTouchEvent(MotionEvent)进行处理。从而对如按钮等即可点击亦可拖拉。
左右滑屏的指示器位置为SlidingIndicator。在fadeOut()方法中为本组件的动画设置了延时,体验上更亲近:
- animFadeout.setStartTime(AnimationUtils.currentAnimationTimeMillis() + fadeDelay);
- setAnimation(animFadeout);
本文内容归CSDN博客博主Sodino 所有
转载请注明出处:http://blog.csdn.net/sodino/article/details/7211049
ActSlidingContainer.java
- package lab.sodino.sliding;
- import lab.sodino.sliding.SlidingContainer.OnSlidingListener;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- public class ActSlidingContainer extends Activity implements OnClickListener, OnSlidingListener {
- private SlidingContainer slidingContainer;
- private SlidingIndicator slidingIndicator;
- private Button btnLeft, btnRight, btnMid;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- btnLeft = (Button) findViewById(R.id.left);
- btnLeft.setOnClickListener(this);
- btnRight = (Button) findViewById(R.id.right);
- btnRight.setOnClickListener(this);
- btnMid = (Button) findViewById(R.id.mid);
- btnMid.setOnClickListener(this);
- slidingContainer = (SlidingContainer) findViewById(R.id.slidingContainer);
- slidingContainer.setOnSlidingListener(this);
- slidingIndicator = (SlidingIndicator) findViewById(R.id.slidingIndicator);
- slidingIndicator.setPageAmount(slidingContainer.getChildCount());
- }
- @Override
- public void onClick(View v) {
- if (v == btnLeft) {
- slidingContainer.scroll2page(slidingContainer.getCurrentPage() - 1);
- } else if (v == btnRight) {
- slidingContainer.scroll2page(slidingContainer.getCurrentPage() + 1);
- } else if (v == btnMid) {
- slidingContainer.scroll2page(slidingContainer.getChildCount() >> 1);
- }
- }
- @Override
- public void onSliding(int scrollX) {
- float scale = (float) (slidingContainer.getPageWidth() * slidingContainer.getChildCount())
- / (float) slidingIndicator.getWidth();
- slidingIndicator.setPosition((int) (scrollX / scale));
- }
- @Override
- public void onSlidingEnd(int pageIdx, int scrollX) {
- slidingIndicator.setCurrentPage(pageIdx);
- }
- }
- <pre name="code" class="java" style="background-color: rgb(255, 255, 255); "><pre>
SlidingContainer.java
- package lab.sodino.sliding;
- import java.util.ArrayList;
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Canvas;
- import android.graphics.Rect;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewConfiguration;
- import android.view.ViewGroup;
- import android.widget.Scroller;
- /**
- * @author Sodino E-mail:sodinoopen@hotmail.com
- * @version Time:2012-1-18 下午02:55:59
- */
- public class SlidingContainer extends ViewGroup {
- private static final int INVALID_SCREEN = -1;
- public static final int SCROLL_DURATION = 500;
- public static final int SPEC_UNDEFINED = ViewGroup.LayoutParams.FILL_PARENT;
- public static final int SNAP_VELOCITY = 500;
- private static final int STATE_STATIC = 0;
- private static final int STATE_SCROLLING = 1;
- private int pageWidth;
- /**
- * 标识是否是第一次布局。<br/>
- * 第一次布局需要将第一页调居中显示在屏幕上。<br/>
- */
- private boolean isFirstLayout;
- private int currentPage, nextPage;
- private Scroller scroller;
- /** 手指滑动过程中可理解为拖动的最小长度。 */
- private int distanceSlop;
- private int state = STATE_STATIC;
- private float lastMotionX;
- private OnSlidingListener slidingListener;
- public SlidingContainer(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- LogOut.out(this, "SlidingContainer() 3");
- initialization(context, attrs);
- }
- public SlidingContainer(Context context, AttributeSet attrs) {
- super(context, attrs);
- LogOut.out(this, "SlidingContainer() 2");
- initialization(context, attrs);
- }
- public SlidingContainer(Context context) {
- super(context);
- LogOut.out(this, "SlidingContainer() 1");
- initialization(context, null);
- }
- private void initialization(Context context, AttributeSet attrs) {
- if (attrs != null) {
- TypedArray typedArr = context.obtainStyledAttributes(attrs, R.styleable.sliding_SlidingContainer);
- pageWidth = typedArr.getDimensionPixelSize(R.styleable.sliding_SlidingContainer_pageWidth, SPEC_UNDEFINED);
- typedArr.recycle();
- }
- state = STATE_STATIC;
- isFirstLayout = true;
- currentPage = 0;
- nextPage = INVALID_SCREEN;
- scroller = new Scroller(context);
- final ViewConfiguration configuration = ViewConfiguration.get(context);
- distanceSlop = configuration.getScaledTouchSlop();
- }
- public int getCurrentPage() {
- return currentPage;
- }
- public int getScrollXByPage(int page) {
- return (page * pageWidth) - getPagePadding();
- }
- public int getPagePadding() {
- return (getMeasuredWidth() - pageWidth) >> 1;
- }
- public int getPageWidth() {
- return pageWidth;
- }
- public boolean scroll2page(int page) {
- if (page < 0) {
- return false;
- } else if (page >= getChildCount()) {
- return false;
- } else if (scroller.isFinished() == false) {
- return false;
- }
- enableChildrenCache(true);
- boolean changingPage = (page != currentPage);
- nextPage = page;
- View focusedChild = getFocusedChild();
- if (changingPage && focusedChild != null && focusedChild == getChildAt(currentPage)) {
- focusedChild.clearFocus();
- }
- final int nowX = getScrollX();
- final int newX = getScrollXByPage(nextPage);
- final int move = newX - nowX;
- final int absMove = Math.abs(move);
- int duration = SCROLL_DURATION;
- if (absMove < pageWidth) {
- duration = SCROLL_DURATION * absMove / pageWidth;
- }
- // 启动左右切屏动画
- scroller.startScroll(nowX, 0, move, 0, duration);
- invalidate();
- return true;
- }
- private void checkScrolling(float x) {
- float diff = Math.abs(x - lastMotionX);
- if (diff > distanceSlop) {
- state = STATE_SCROLLING;
- enableChildrenCache(true);
- }
- }
- /**
- * 开始滑动时设置允许使用缓存。<br/>
- * 结束滑动时设置取消缓存。<br/>
- */
- public void enableChildrenCache(boolean enable) {
- setChildrenDrawingCacheEnabled(enable);
- setChildrenDrawnWithCacheEnabled(enable);
- }
- /** 在正式显示之前设置才有效。 */
- public boolean setPageWidth(int width) {
- if (isFirstLayout) {
- pageWidth = width;
- return true;
- }
- return false;
- }
- public void setOnSlidingListener(OnSlidingListener listener) {
- slidingListener = listener;
- }
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- LogOut.out(this, "onMeasure()");
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- pageWidth = (pageWidth == SPEC_UNDEFINED) ? getMeasuredWidth() : pageWidth;
- pageWidth = Math.min(Math.max(0, pageWidth), getMeasuredWidth());
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- int childWidthSpec = MeasureSpec.makeMeasureSpec(pageWidth, MeasureSpec.EXACTLY);
- View view = getChildAt(i);
- view.measure(childWidthSpec, heightMeasureSpec);
- }
- }
- @Override
- protected void onLayout(boolean changing, int left, int top, int right, int bottom) {
- LogOut.out(this, "onLayout");
- int childLeft = 0;
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View view = getChildAt(i);
- if (view.getVisibility() != View.GONE) {
- int childWidth = view.getMeasuredWidth();
- view.layout(childLeft, 0, childLeft + childWidth, view.getMeasuredHeight());
- childLeft += childWidth;
- }
- }
- if (isFirstLayout) {
- scrollTo(getScrollXByPage(currentPage), 0);
- isFirstLayout = false;
- }
- }
- public boolean onInterceptTouchEvent(MotionEvent event) {
- LogOut.out(this, "onInterceptTouchEvent action=" + event.getAction());
- final int action = event.getAction();
- if (action == MotionEvent.ACTION_MOVE && state != STATE_STATIC) {
- // MOVE及非静止情况下,返回TRUE阻止将此事件传递给子组件,
- // 而是执行onTouchEvent()来实现滑动
- return true;
- }
- final float x = event.getX();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- lastMotionX = x;
- // 点击按钮时,此处设置状态为静止。
- state = scroller.isFinished() ? STATE_STATIC : STATE_SCROLLING;
- break;
- case MotionEvent.ACTION_MOVE:
- if (state == STATE_STATIC) {
- // 由于已静止,在点击按钮后进行拖拉,则根据拖拉位移大小决定是否需要改变状态进而进一步拦截此事件。
- checkScrolling(x);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- enableChildrenCache(false);
- state = STATE_STATIC;
- break;
- }
- // 非静止状态,将此事件交由onTouchEvent()处理。
- return state != STATE_STATIC;
- }
- public boolean onTouchEvent(MotionEvent event) {
- LogOut.out(this, "onTouchEvent");
- super.onTouchEvent(event);
- final int action = event.getAction();
- final float x = event.getX();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- lastMotionX = x;
- if (scroller.isFinished() == false) {
- scroller.abortAnimation();
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if (state == STATE_STATIC) {
- checkScrolling(x);
- } else if (state == STATE_SCROLLING) {
- int moveX = (int) (lastMotionX - x);
- lastMotionX = x;
- if (getScrollX() < 0 || getScrollX() > getChildAt(getChildCount() - 1).getLeft()) {
- // 对于越界的拖拉,则将位移减半。
- moveX = moveX >> 1;
- }
- scrollBy(moveX, 0);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (state == STATE_SCROLLING) {
- final int startX = getScrollXByPage(currentPage);
- // 默认选择回到手指滑动之前的当前页
- int whichPage = currentPage;
- int xSpace = getWidth() / 8;
- if (getScrollX() < startX - xSpace) {
- whichPage = Math.max(0, whichPage - 1);
- } else if (getScrollX() > startX + xSpace) {
- whichPage = Math.min(getChildCount() - 1, whichPage + 1);
- }
- scroll2page(whichPage);
- }
- state = STATE_STATIC;
- break;
- }
- return true;
- }
- /** 让拖拉、动画过程中界面过渡顺滑。 */
- protected void dispatchDraw(Canvas canvas) {
- final long drawingTime = getDrawingTime();
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- drawChild(canvas, getChildAt(i), drawingTime);
- }
- if (slidingListener != null) {
- int adjustedScrollX = getScrollX() + getPagePadding();
- slidingListener.onSliding(adjustedScrollX);
- if (adjustedScrollX % pageWidth == 0) {
- slidingListener.onSlidingEnd(adjustedScrollX / pageWidth, adjustedScrollX);
- }
- }
- }
- /** 与Scroller相匹配,实现动画效果中每一帧的界面更新。 */
- public void computeScroll() {
- if (scroller.computeScrollOffset()) {
- scrollTo(scroller.getCurrX(), scroller.getCurrY());
- postInvalidate();
- } else if (nextPage != INVALID_SCREEN) {
- currentPage = nextPage;
- nextPage = INVALID_SCREEN;
- enableChildrenCache(false);
- }
- }
- public static interface OnSlidingListener {
- public void onSliding(int scrollX);
- public void onSlidingEnd(int pageIdx, int scrollX);
- }
- }
SlidingIndicator.java
- package lab.sodino.sliding;
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.graphics.RectF;
- import android.util.AttributeSet;
- import android.view.View;
- import android.view.animation.AlphaAnimation;
- import android.view.animation.Animation;
- import android.view.animation.AnimationUtils;
- import android.view.animation.LinearInterpolator;
- /**
- * @author Sodino E-mail:sodinoopen@hotmail.com
- * @version Time:2012-1-18 下午03:31:08
- */
- public class SlidingIndicator extends View {
- public static final int BAR_COLOR = 0xaa777777;
- public static final int HIGHLIGHT_COLOR = 0xaa999999;
- public static final int FADE_DELAY = 2000;
- public static final int FADE_DURATION = 500;
- private int amount, currentPage, position;
- private Paint barPaint, highlightPaint;
- private int fadeDelay, fadeDuration;
- private float ovalRadius;
- private Animation animFadeout;
- /** RectF比Rect是精度上更精确。 */
- private RectF rectFBody, rectFIndicator;
- public SlidingIndicator(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- // 预设值。
- int barColor = BAR_COLOR, highlightColor = HIGHLIGHT_COLOR;
- fadeDelay = FADE_DELAY;
- fadeDuration = FADE_DURATION;
- if (attrs != null) {
- TypedArray typedArr = context.obtainStyledAttributes(attrs, R.styleable.sliding_SlidingIndicator);
- barColor = typedArr.getColor(R.styleable.sliding_SlidingIndicator_barColor, BAR_COLOR);
- highlightColor = typedArr.getColor(R.styleable.sliding_SlidingIndicator_highlightColor, HIGHLIGHT_COLOR);
- fadeDelay = typedArr.getInteger(R.styleable.sliding_SlidingIndicator_fadeDelay, FADE_DELAY);
- fadeDuration = typedArr.getInteger(R.styleable.sliding_SlidingIndicator_fadeDuration, FADE_DURATION);
- ovalRadius = typedArr.getDimension(R.styleable.sliding_SlidingIndicator_roundRectRadius, 0f);
- typedArr.recycle();
- }
- initialization(barColor, highlightColor, fadeDuration);
- }
- public SlidingIndicator(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public SlidingIndicator(Context context) {
- super(context);
- }
- private void initialization(int barColor, int highlightColor, int fadeDuration) {
- barPaint = new Paint();
- barPaint.setColor(barColor);
- highlightPaint = new Paint();
- highlightPaint.setColor(highlightColor);
- animFadeout = new AlphaAnimation(1f, 0f);
- animFadeout.setDuration(fadeDuration);
- animFadeout.setRepeatCount(0);
- animFadeout.setInterpolator(new LinearInterpolator());
- // 设置动画结束后,本组件保持动画结束时的最后状态,即全透明不可见。
- animFadeout.setFillEnabled(true);
- animFadeout.setFillAfter(true);
- rectFBody = new RectF();
- rectFIndicator = new RectF();
- }
- public void setPageAmount(int num) {
- if (num < 0) {
- throw new IllegalArgumentException("num must be positive.");
- }
- amount = num;
- invalidate();
- fadeOut();
- }
- private void fadeOut() {
- if (fadeDuration > 0) {
- clearAnimation();
- // 设置动画的延时时间,此时间段内正好指示当前页位置。
- animFadeout.setStartTime(AnimationUtils.currentAnimationTimeMillis() + fadeDelay);
- setAnimation(animFadeout);
- }
- }
- public int getCurrentPage() {
- return currentPage;
- }
- public void setCurrentPage(int idx) {
- if (currentPage < 0 || currentPage >= amount) {
- throw new IllegalArgumentException("currentPage parameter out of bounds");
- }
- if (this.currentPage != idx) {
- this.currentPage = idx;
- this.position = currentPage * getPageWidth();
- invalidate();
- fadeOut();
- }
- }
- public void setPosition(int position) {
- if (this.position != position) {
- this.position = position;
- invalidate();
- fadeOut();
- }
- }
- public int getPageWidth() {
- return getWidth() / amount;
- }
- protected void onDraw(Canvas canvas) {
- rectFBody.set(0, 0, getWidth(), getHeight());
- canvas.drawRoundRect(rectFBody, ovalRadius, ovalRadius, barPaint);
- rectFIndicator.set(position, 0, position + getPageWidth(), getHeight());
- canvas.drawRoundRect(rectFIndicator, ovalRadius, ovalRadius, highlightPaint);
- }
- }
末尾,自己推荐另一个左右滑屏实现方法:ViewPager
http://my.oschina.net/kzhou/blog/29157