`
李楚男
  • 浏览: 118508 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

仿照Launcher的Workspace实现左右滑动切换

 
阅读更多

   对于Launcher的桌面滑动大家应该都比较熟悉了,最好的体验应该是可以随着手指的滑动而显示不同位置的桌面。
    昨天公司要我实现桌面Launcher这种效果,就是顶部布局不变,中间是一个可以滑动类似桌面Launcher的布局,而底部也是不动的布局。难点主要是中间那个可以实现图片滑动功能的ScrollLayout布局,下面我把实现ScrollLayout布局的代码贴出来,代码也是别人的,我只是注释了一下罢了:

package cn.flyaudio.android.lichunan;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
/**
 * 仿Launcher中的WorkSapce,可以左右滑动切换屏幕的类 
 * @author 李楚男
 * 该类继承ViewGroup容器类以实现自己需要的布局显示
 */
public class ScrollLayout extends ViewGroup{
	
	private static final String TAG = "ScrollLayout"; 
	
	/**用来平滑过渡各个页面之间的切换*/
    private Scroller mScroller;      
    /**用来跟踪触摸速度的类*/
    private VelocityTracker mVelocityTracker;       
           
    private int mCurScreen;       
    private int mDefaultScreen = 0;       
           
    private static final int TOUCH_STATE_REST = 0;//手指离开屏幕状态       
    private static final int TOUCH_STATE_SCROLLING = 1;       
           
    private static final int SNAP_VELOCITY = 600;       
           
    private int mTouchState = TOUCH_STATE_REST;//手指与屏幕是否接触状态       
    private int mTouchSlop;   
    /**横坐标的最终位置*/
    private float mLastMotionX;     
    /**纵坐标的最终位置*/
   private float mLastMotionY;    
    
    public ScrollLayout(Context context, AttributeSet attrs) {       
        this(context, attrs, 0);            
    }   
       
    public ScrollLayout(Context context) {       
        super(context);             
    }       
       
    public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {       
        super(context, attrs, defStyle);  
        
        /**使用缺省的持续时间和动画插入器创建一个Scroller*/
        mScroller = new Scroller(context);       
               
        mCurScreen = mDefaultScreen;       
        
        /**是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件*/
        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();    
        
    }       
    /**
     * 为每一个子view指定size和position
     */
    @Override       
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {              
        if (changed) { 
        	/**子view离父view左边的距离*/
            int childLeft = 0;    
            /**获取子view数目*/
            final int childCount = getChildCount();       
                   
            for (int i=0; i<childCount; i++) {   
            	/**获取子view*/
                final View childView = getChildAt(i);    
                
                if (childView.getVisibility() != View.GONE) {//如果子view可见的话     
                	/**获取子view的宽度*/
                    final int childWidth = childView.getMeasuredWidth();  
                    /**为子view设置大小和位置*/
                    childView.layout(childLeft, 0,        
                            childLeft+childWidth, childView.getMeasuredHeight());   
                    /**左边距自加子view宽度,从而得到下一个子view的x坐标*/
                    childLeft += childWidth;                 
                }       
            }       
        }       
    } 
     
    /**
     * 指明控件可获得的空间以及关于这个空间描述的元数据
     */
    @Override         
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {          
        Log.e(TAG, "onMeasure");       
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);          
         /**int specMode = MeasureSpec.getMode(measureSpec);
		  *	int specSize = MeasureSpec.getSize(measureSpec);
          * 依据specMode的值,如果是AT_MOST,specSize 代表的是最大可获得的空间;
          * 如果是EXACTLY,specSize 代表的是精确的尺寸;
          * 如果是UNSPECIFIED,对于控件尺寸来说,没有任何参考意义。
          */
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);    
        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);          
        if (widthMode != MeasureSpec.EXACTLY) {          
            throw new IllegalStateException("ScrollLayout only canmCurScreen run at EXACTLY mode!");        
        }          
         
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);          
        if (heightMode != MeasureSpec.EXACTLY) {          
            throw new IllegalStateException("ScrollLayout only can run at EXACTLY mode!");       
        }          
         
        //给每一个子view给予相同的空间          
        final int count = getChildCount();          
        for (int i = 0; i < count; i++) {          
            getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);          
        }          
        Log.e(TAG, "moving to screen "+mCurScreen);     
        /**滚动到目标坐标*/
        scrollTo(mCurScreen * widthSize, 0);                
    }         
           
    /**    
     * 根据当前布局的位置,滚动到目的页面    
     */       
    public void snapToDestination() {    
    	/**获取view的宽度*/
        final int screenWidth = getWidth();  
        Log.i(TAG, "screenWidth: " + screenWidth + " screenWidth/2: " + screenWidth/2);
        Log.i(TAG, "getScrollX():" + getScrollX());
        /**
         * getScrollX():获得滚动后view的横坐标
         */
        final int destScreen = (getScrollX()+ screenWidth/2)/screenWidth;       
        snapToScreen(destScreen);       
    }       
           
    public void snapToScreen(int whichScreen) {       
        // 获取有效页面       
        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();       // 使view重画       
        }       
    }       
           
    public void setToScreen(int whichScreen) {       
        whichScreen = Math.max(0, Math.min(whichScreen, getChildCount()-1));       
        mCurScreen = whichScreen;       
        scrollTo(whichScreen*getWidth(), 0);       
    }       
           
    public int getCurScreen() {       
        return mCurScreen;       
    }       
    /**
     * 由父视图调用,用于通知子视图在必要时更新 mScrollX 和 mScrollY 的值
     * 该操作主要用于子视图使用 Scroller 进行动画滚动时。       
     */
    @Override       
    public void computeScroll() {            
        if (mScroller.computeScrollOffset()) {//返回true,表示动画仍在进行,还没有停止       
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());// 滚动到目标坐标       
            postInvalidate();  //使view重画     
        }       
    }       
    
    /**
     * 触摸监听事件
     */
    @Override       
    public boolean onTouchEvent(MotionEvent event) {       
             
        if (mVelocityTracker == null) {       
            mVelocityTracker = VelocityTracker.obtain();//获取mVelocityTracker实例对象       
        }      
        
        /**将当前的移动事件传递给mVelocityTracker对象*/
        mVelocityTracker.addMovement(event);  
        
        /**获取当前触摸动作*/
        final int action = event.getAction();       
        final float x = event.getX();       
        final float y = event.getY();       
               
        switch (action) {       
        case MotionEvent.ACTION_DOWN://当向下按时     
            Log.e(TAG, "event down!");       
            if (!mScroller.isFinished()){       
                mScroller.abortAnimation();//Scrooller停止动画行为       
            }       
            mLastMotionX = x;       
            break;       
                   
        case MotionEvent.ACTION_MOVE://当手指滑动时    
            int deltaX = (int)(mLastMotionX - x);       
            mLastMotionX = x;       
            /**
             * scrollBy是将view的内容移动多大的距离
             * deltaX:指移动的距离
             */
            scrollBy(deltaX, 0);       
            break;       
                   
        case MotionEvent.ACTION_UP:       
            Log.e(TAG, "event : up");          
            // if (mTouchState == TOUCH_STATE_SCROLLING) {          
            final VelocityTracker velocityTracker = mVelocityTracker; 
            /**计算当前速度*/
            velocityTracker.computeCurrentVelocity(1000);     
            /**获取当前x方向的速度*/
            int velocityX = (int) velocityTracker.getXVelocity();          
            Log.e(TAG, "velocityX:"+velocityX);        
                   
            if (velocityX > SNAP_VELOCITY && mCurScreen > 0) { //向右滑动并且手指滑动速度大于指定的速度(此时速度的方向为正)         
                // Fling enough to move left          
                Log.e(TAG, "snap left");       
                snapToScreen(mCurScreen - 1);//滑到前一个页面      
            } else if (velocityX < -SNAP_VELOCITY          
                    && mCurScreen < getChildCount() - 1) {//向左滑动时并且手指滑动的速度也大于指定的速度(此时速度方向为负)          
                // Fling enough to move right          
                Log.e(TAG, "snap right");       
                snapToScreen(mCurScreen + 1);//滑到后一个页面          
            } else {          
                snapToDestination();          
            }          
            if (mVelocityTracker != null) {//释放VelocityTracker对象        
                mVelocityTracker.recycle();          
                mVelocityTracker = null;          
            }          
            // }          
            mTouchState = TOUCH_STATE_REST;          
            break;       
        case MotionEvent.ACTION_CANCEL:       
            mTouchState = TOUCH_STATE_REST;       
            break;       
        }       
               
        return true;       
    }       
    /**
     * 该方法是用于拦截手势事件的,每个手势事件都会先调用
     * 此方法返回false,则手势事件会向子控件传递
     * 返回true,则调用onTouchEvent方法
     */
    @Override       
    public boolean onInterceptTouchEvent(MotionEvent ev) {              
        Log.e(TAG, "onInterceptTouchEvent-slop:"+mTouchSlop);       
               
        final int action = ev.getAction();       
        if ((action == MotionEvent.ACTION_MOVE) &&        
                (mTouchState != TOUCH_STATE_REST)) {       
            return true;       
        }       
               
        final float x = ev.getX();       
        final float y = ev.getY();       
               
        switch (action) {       
        case MotionEvent.ACTION_MOVE:       
            final int xDiff = (int)Math.abs(mLastMotionX-x);       
            if (xDiff>mTouchSlop) {       
                mTouchState = TOUCH_STATE_SCROLLING; //视图还在移动状态      
                       
            }       
            break;       
                   
        case MotionEvent.ACTION_DOWN:       
            mLastMotionX = x;       
            mLastMotionY = y;       
            mTouchState = mScroller.isFinished()? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;       
            break;       
                   
        case MotionEvent.ACTION_CANCEL:       
        case MotionEvent.ACTION_UP:       
            mTouchState = TOUCH_STATE_REST;       
            break;       
        }       
               
        return mTouchState != TOUCH_STATE_REST;       
    }       

	
	
}

                                          
          

 布局文件:main.xml

              

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:background="@drawable/bg"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<cn.flyaudio.android.lichunan.ScrollLayout       
  android:id="@+id/ScrollLayout"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <include layout="@layout/menu_center1"/>
  <include layout="@layout/menu_center2"/>
  <include layout="@layout/menu_center3"/>
</cn.flyaudio.android.lichunan.ScrollLayout>
<ImageButton android:id="@+id/menu_tracklist"
			 android:layout_width="wrap_content"
			 android:layout_height="wrap_content"
			 android:layout_centerVertical="true"
			 android:layout_alignParentRight="true"
			 android:background="@drawable/menu_tracklist_imagebutton"/>
<include layout="@layout/menu_bottom"/>
</RelativeLayout>

 

           

  • 大小: 446.6 KB
分享到:
评论
1 楼 Will.Du 2012-04-13  
xml不全么,要给还不给个完整的demo~~ - -!

相关推荐

    拆解Launcher中的WorkSpace实现左右滑动效果

    工作区的左右滑动效果是Android Launcher的一个核心功能,让用户能够轻松地在不同的主屏幕之间切换,访问更多的应用程序和小部件。本文将深入解析如何在自定义的Launcher应用中实现这一特性。 首先,我们要了解...

    Launcher修改(实现左右循环滑动、壁纸的平滑过渡,带文档)

    本教程将详细讲解如何修改Launcher,实现左右循环滑动的效果,以及壁纸平滑过渡的功能。 首先,我们关注的是"循环滑动"这一特性。在原生的Android Launcher中,屏幕通常是线性的,用户只能前后滑动,无法实现循环。...

    Laucher 上下左右循环滑动workspace代码修改(新)

    功能是循环滑动界面,同时实现左右循环和上下循环。 关键技术是理解清楚“onInterceptTouchEvent和onTouchEvent”的关系,以及computeScroll和dispatchDraw等。 可以在百度中搜索关键词获取答案。

    提取Launcher中的WorkSapce,可以左右滑动切换屏幕页面的类

    提取Launcher中的WorkSapce,可以左右滑动切换屏幕页面的类,相应的源码说明可以参考文章:http://blog.csdn.net/Yao_GUET/archive/2011/05/04/6393962.aspx

    Android 界面禁止左右滑动切换

    首先,Android界面的左右滑动切换通常是通过`ViewPager`组件实现的,它允许用户通过横向滑动来浏览多个页面。如果我们想要禁止这种行为,我们需要对`ViewPager`进行定制。以下是一种可能的方法: 1. **自定义...

    Android 类似launcher左右滑动(实例二)

    在Android开发中,实现类似Launcher的左右滑动效果是一项常见的需求,这通常涉及到视图的切换、手势识别以及页面的滚动管理。以下我们将详细探讨如何实现这样的功能。 首先,理解"Android 类似launcher左右滑动...

    Laucher 循环滑动workspace代码修改

    根据gmailToyou大虾的指点,我修改了workspace的代码,已经成功实现了Launcher的循环滑动,这里将代码共享,以供参考,特别感谢gmailToyou的帮助!

    launcher中workspace代码

    android源代码 http://blog.csdn.net/aomandeshangxiao/article/details/6940670#reply讲解所用部分

    Android 类似launcher左右滑动(实例一)

    在这个实例中,我们可能会用到ViewPager来实现左右滑动的效果。但是,由于题目提到的是“实例一”,可能意味着还有其他非ViewPager的方法,所以我们也需要考虑自定义布局的可能性。 1. 自定义ViewGroup:如果不想...

    MT8788-Android12 Launcher3去掉抽屉实现左右滑动

    "MT8788-Android12 Launcher3去掉抽屉实现左右滑动"这个项目就是针对这种需求进行的定制。 首先,我们要理解“去掉抽屉”的含义。在原生Android中,应用抽屉是用户存放和访问所有应用的地方,通常可以通过滑动主...

    Launcher4.0界面循环滑动

    本文将深入探讨如何实现Android 4.0(ICS,Ice Cream Sandwich)中的Launcher界面循环滑动功能,通过代码分享和解析,帮助开发者更好地理解和实现这一特性。 首先,理解Launcher的工作原理至关重要。Launcher是一个...

    Launcher Workspace Demo

    搭建步骤: 1 下载代码,导入到myEclipse项目里,发现很多错误。 ...为了访问因此成员,需要改变类搜索顺序,选择项目属性-&gt;Java Build Path-&gt;Order and Export,把所建立的User Libraries移到Android SDK的上面。...

    launcher3切屏动态效果

    "launcher3切屏动态效果"是Android系统启动器的核心组件之一,主要负责用户界面的切换与动画呈现。在Android系统中,launcher3是默认的主屏幕应用,它提供了桌面图标、小部件以及应用抽屉等功能。这个源码着重关注的...

    Android 页面滑动切换(类Launcher滑动屏幕实现)

    下面的这个例子也是从网上找来的,不是自己写的,一直想学习下,但是一直没有写,以前也研究研究的是launcher的页面跳转,launcher修改--左右滑动屏幕切换源码追踪说实话,那个代码有点复杂,所以理解的也不是很透彻...

    Android 6.0 Launcher3 增加屏幕切换动画

    在Launcher3中,屏幕切换动画可能涉及到View的TranslationX和TranslationY属性的改变,以实现左右滑动切换屏幕的效果。此外,可能还会用到Alpha属性来控制视图的透明度,增加动画的层次感。 接下来,我们关注源码包...

    android4.4 launcher3 循环滑动

    安卓4.4 launcher3源码桌面无法循环滚动,修改pagedView实现

    viewPager实现左右滑动

    在Android开发中,ViewPager是一个非常重要的组件,常用于实现页面间的滑动切换效果,比如在应用启动器(launcher)中,用户通常会通过左右滑动来浏览不同的屏幕。本篇文章将详细探讨如何利用ViewPager实现类似...

    Android 9.0 Launcher Workspace 加载

    加载Workspace入口在/packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java,想了解Launcher app的启动流程,可以先看看这篇文章,https://www.jianshu.com/p/0b273112cd7e 1、Workspace加载...

    7.1 Launcher3 去掉应用抽屉

    为了将所有应用摆在 launcher 第一层,我们需要在 Workspace.java 文件中进行修改。具体来说,我们需要在 removeAllWorkspaceScreens 方法中清空所有工作台屏幕,并将应用程序图标摆放在 launcher 的第一层。 4. ...

Global site tag (gtag.js) - Google Analytics