Android 页面滑动切换(类Launcher滑动屏幕实现)
jopen
13年前
<p> <span style="font-size:16px;">下面的这个例子也是从网上找来的,不是自己写的,一直想学习下,但是一直没有写,以前也研究研究的是launcher的页面跳转,</span><a style="text-align:left;line-height:30px;font-family:'Microsoft YaHei';color:#666666;text-decoration:none;" title="launcher修改--左右滑动屏幕切换源码追踪" href="/misc/goto?guid=4959500739954799649"><span style="font-size:16px;">launcher修改--左右滑动屏幕切换源码追踪</span></a><span style="font-size:16px;">说实话,那个代码有点复杂,所以理解的也不是很透彻。看到这个例子,比较简单些,再这里学习下:</span></p> <p><span style="font-size:16px;"> 首先,看下效果图吧:虽然很花哨,都是背景图片。</span></p> <p><span style="font-size:16px;"><img title="Android 页面滑动切换(类Launcher滑动屏幕实现)" border="0" alt="Android 页面滑动切换(类Launcher滑动屏幕实现)" src="https://simg.open-open.com/show/22ef892b42e3f3a0a78ff1250feadd81.jpg" width="485" height="710" /><br /> </span></p> <p><span style="font-size:16px;"> 看下他的布局文件:</span></p> <p></p> <pre class="brush:xml; toolbar: true; auto-links: false;"><?xml version="1.0" encoding="utf-8"?> <RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android"> <com.genius.scroll.MyScrollLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ScrollLayout" android:layout_width="fill_parent" android:layout_height="fill_parent"> <FrameLayout android:background="@drawable/guide01" android:layout_width="fill_parent" android:layout_height="fill_parent"> </FrameLayout> <FrameLayout android:background="@drawable/guide02" android:layout_width="fill_parent" android:layout_height="fill_parent"> </FrameLayout> <FrameLayout android:background="@drawable/guide03" android:layout_width="fill_parent" android:layout_height="fill_parent"> </FrameLayout> <FrameLayout android:background="@drawable/guide04" android:layout_width="fill_parent" android:layout_height="fill_parent"> </FrameLayout> <FrameLayout android:background="@drawable/guide05" android:layout_width="fill_parent" android:layout_height="fill_parent"> </FrameLayout> </com.genius.scroll.MyScrollLayout> <LinearLayout android:orientation="horizontal" android:id="@+id/llayout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="24.0dip" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true"> <ImageView android:clickable="true" android:padding="15.0dip" android:layout_gravity="center_vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/guide_round" /> <ImageView android:clickable="true" android:padding="15.0dip" android:layout_gravity="center_vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/guide_round" /> <ImageView android:clickable="true" android:padding="15.0dip" android:layout_gravity="center_vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/guide_round" /> <ImageView android:clickable="true" android:padding="15.0dip" android:layout_gravity="center_vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/guide_round" /> <ImageView android:clickable="true" android:padding="15.0dip" android:layout_gravity="center_vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/guide_round" /> </LinearLayout> </RelativeLayout></pre> <br /> <span style="font-size:16px;"> 底部的LinearLayout是放了5个按钮,上面使用到了一个自定义的控件:MyScrollLayout下面再看下这个自定义控件:里面使用frameLayout放了5张图片。</span> <p></p> <p></p> <pre class="brush:java; toolbar: true; auto-links: false;">public class MyScrollLayout extends ViewGroup{ private static final String TAG = "ScrollLayout"; private VelocityTracker mVelocityTracker; // 用于判断甩动手势 private static final int SNAP_VELOCITY = 600; private Scroller mScroller; // 滑动控制器 private int mCurScreen; private int mDefaultScreen = 0; private float mLastMotionX; private OnViewChangeListener mOnViewChangeListener; public MyScrollLayout(Context context) { super(context); init(context); } public MyScrollLayout(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public MyScrollLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { mCurScreen = mDefaultScreen; mScroller = new Scroller(context); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // TODO Auto-generated method stub if (changed) { int childLeft = 0; final int childCount = getChildCount(); for (int i=0; i<childCount; i++) { final View childView = getChildAt(i); if (childView.getVisibility() != View.GONE) { final int childWidth = childView.getMeasuredWidth(); childView.layout(childLeft, 0, childLeft+childWidth, childView.getMeasuredHeight()); childLeft += childWidth; } } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int width = MeasureSpec.getSize(widthMeasureSpec); final int widthMode = MeasureSpec.getMode(widthMeasureSpec); final int count = getChildCount(); for (int i = 0; i < count; i++) { getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); } scrollTo(mCurScreen * width, 0); } public void snapToDestination() { final int screenWidth = getWidth(); final int destScreen = (getScrollX()+ screenWidth/2)/screenWidth; snapToScreen(destScreen); } public void snapToScreen(int whichScreen) { // get the valid layout page whichScreen = Math.max(0, Math.min(whichScreen, getChildCount()-1)); if (getScrollX() != (whichScreen*getWidth())) { final int delta = whichScreen*getWidth()-getScrollX(); mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta)*2); mCurScreen = whichScreen; invalidate(); // Redraw the layout if (mOnViewChangeListener != null) { mOnViewChangeListener.OnViewChange(mCurScreen); } } } @Override public void computeScroll() { // TODO Auto-generated method stub if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub final int action = event.getAction(); final float x = event.getX(); final float y = event.getY(); switch (action) { case MotionEvent.ACTION_DOWN: Log.i("", "onTouchEvent ACTION_DOWN"); if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(event); } if (!mScroller.isFinished()){ mScroller.abortAnimation(); } mLastMotionX = x; break; case MotionEvent.ACTION_MOVE: int deltaX = (int)(mLastMotionX - x); if (IsCanMove(deltaX)){ if (mVelocityTracker != null){ mVelocityTracker.addMovement(event); } mLastMotionX = x; scrollBy(deltaX, 0); } break; case MotionEvent.ACTION_UP: int velocityX = 0; if (mVelocityTracker != null){ mVelocityTracker.addMovement(event); mVelocityTracker.computeCurrentVelocity(1000); velocityX = (int) mVelocityTracker.getXVelocity(); } if (velocityX > SNAP_VELOCITY && mCurScreen > 0) { Log.e(TAG, "snap left"); snapToScreen(mCurScreen - 1); } else if (velocityX < -SNAP_VELOCITY && mCurScreen < getChildCount() - 1) { Log.e(TAG, "snap right"); snapToScreen(mCurScreen + 1); } else { snapToDestination(); } if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } break; } return true; } private boolean IsCanMove(int deltaX) { if (getScrollX() <= 0 && deltaX < 0 ){ return false; } if (getScrollX() >= (getChildCount() - 1) * getWidth() && deltaX > 0){ return false; } return true; } public void SetOnViewChangeListener(OnViewChangeListener listener) { mOnViewChangeListener = listener; } }</pre> <br /> <span style="font-size:16px;">首先注意,他继承了ViewGroup类,在这里面主要重写了onMeasure()、onTouchEvent()等方法,在这里使用了一个自定义的接口private OnViewChangeListener mOnViewChangeListener。看下它的定义:</span> <p></p> <p></p> <pre class="brush:java; toolbar: true; auto-links: false;">public interface OnViewChangeListener { public void OnViewChange(int view); }</pre> <br /> <span style="font-size:16px;">这个接口里之定义了一个回调方法:OnViewChange()这个方法的具体实现,是在主Activity中:</span> <p></p> <p></p> <pre class="brush:java; toolbar: true; auto-links: false;">public class SwitchViewDemoActivity extends Activity implements OnViewChangeListener, OnClickListener{ /** Called when the activity is first created. */ private MyScrollLayout mScrollLayout; private ImageView[] mImageViews; private int mViewCount; private int mCurSel; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); init(); } private void init() { mScrollLayout = (MyScrollLayout) findViewById(R.id.ScrollLayout); LinearLayout linearLayout = (LinearLayout) findViewById(R.id.llayout); mViewCount = mScrollLayout.getChildCount(); mImageViews = new ImageView[mViewCount]; for(int i = 0; i < mViewCount; i++) { mImageViews[i] = (ImageView) linearLayout.getChildAt(i); mImageViews[i].setEnabled(true); mImageViews[i].setOnClickListener(this); mImageViews[i].setTag(i); } mCurSel = 0; mImageViews[mCurSel].setEnabled(false); mScrollLayout.SetOnViewChangeListener(this); } private void setCurPoint(int index) { if (index < 0 || index > mViewCount - 1 || mCurSel == index) { return ; } mImageViews[mCurSel].setEnabled(true); mImageViews[index].setEnabled(false); mCurSel = index; } @Override public void OnViewChange(int view) { // TODO Auto-generated method stub setCurPoint(view); } @Override public void onClick(View v) { // TODO Auto-generated method stub int pos = (Integer)(v.getTag()); setCurPoint(pos); mScrollLayout.snapToScreen(pos); } }</pre> <br /> <span style="font-size:16px;">这个OnViewChange()方法,主要调用了setCurPoint()方法,就是完成界面的跳转。在MyScrollLayout中的snapToScreen()方法中就是典型的回调方法:</span> <p></p> <p></p> <pre class="brush:java; toolbar: true; auto-links: false;">public void snapToScreen(int whichScreen) { // get the valid layout page whichScreen = Math.max(0, Math.min(whichScreen, getChildCount()-1)); if (getScrollX() != (whichScreen*getWidth())) { final int delta = whichScreen*getWidth()-getScrollX(); mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta)*2); mCurScreen = whichScreen; invalidate(); // Redraw the layout if (mOnViewChangeListener != null) { mOnViewChangeListener.OnViewChange(mCurScreen); } } } </pre> <br /> <pre class="brush:java; toolbar: true; auto-links: false;">mOnViewChangeListener.OnViewChange(mCurScreen);</pre> <br /> <p><span style="font-size:16px;">这句代码就是典型的回调。</span></p> <p><span style="font-size:16px;">在MyScrollLayout的onTouchEvent()的方法里面是对触屏事件做出的响应:</span></p> <p></p> <pre class="brush:java; toolbar: true; auto-links: false;">final int action = event.getAction(); final float x = event.getX(); final float y = event.getY(); switch (action) { case MotionEvent.ACTION_DOWN: Log.i("", "onTouchEvent ACTION_DOWN"); if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(event); } if (!mScroller.isFinished()){ mScroller.abortAnimation(); } mLastMotionX = x; break; case MotionEvent.ACTION_MOVE: int deltaX = (int)(mLastMotionX - x); if (IsCanMove(deltaX)){ if (mVelocityTracker != null){ mVelocityTracker.addMovement(event); } mLastMotionX = x; scrollBy(deltaX, 0); } break; case MotionEvent.ACTION_UP: int velocityX = 0; if (mVelocityTracker != null){ mVelocityTracker.addMovement(event); mVelocityTracker.computeCurrentVelocity(1000); velocityX = (int) mVelocityTracker.getXVelocity(); } if (velocityX > SNAP_VELOCITY && mCurScreen > 0) { Log.e(TAG, "snap left"); snapToScreen(mCurScreen - 1); } else if (velocityX < -SNAP_VELOCITY && mCurScreen < getChildCount() - 1) { Log.e(TAG, "snap right"); snapToScreen(mCurScreen + 1); } else { snapToDestination(); } if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } break; } </pre> <br /> 通过判断移动距离和移动方向做出不同的响应。 <p></p> <p></p> <p><span style="font-size:16px;"><br /> </span></p> <p><span style="font-size:16px;">其他的代码都比较好懂了,有什么问题欢迎大家讨论,下面是代码的下载地址:</span></p> <p><span style="font-size:16px;"><a href="/misc/goto?guid=4959500740040691286">http://download.csdn.net/detail/aomandeshangxiao/4017928</a><br /> </span></p>