Android的Launcher界面功能菜单是一个列表,当应用程序较多时,可以向下滑动查看其它,如下图。那能不能将功能菜单做成横向拖动,并且分屏的效果呢?


    本文对该问题进行研究,要达到的目标如下:
    1、可以实现应用程序的分屏显示,当一屏放不下时,放入另一个屏。
    2、屏与屏之间切换为横向。
    3、屏与屏之间切换时有动画效果,一个屏退出,一个屏出现。
    本文的方法暂时没有解决屏幕切换随着手的移动而逐渐切换的问题,但是本文的屏幕切换可以采用手势的方式。
    分屏和横向显示不是很难解决的问题,关键问题在于动画效果的实现。由于在屏幕切换时两个屏同时发生动画,一个退出,一个进入,因此至少要同时存在两个View。实际上Android已经为我们考虑了这种情况。ViewSwitcher就是专门针对这种情况而设计的。
    ViewSwitcher内部保存了两个View,通过我们的控制可以显示前一个和后一个,并且我们可以设置在切换中两个View的动画。View的生成为ViewFactory生成。该类还是比较简单的,需要详细研究参考google文档和源代码。
多的不说,上代码吧。
    首先我们模拟一下功能菜单的数据部分,也就是分几个屏,每个屏有哪些应用之类的东西。注释较多,不多解释。

  1. /** 
  2. * 该类模拟了功能菜单的数据部分 
  3. */ 
  4. public class MenuData { 
  5.     /**该常量代表每一屏能够容纳的应用程序数目*/ 
  6.     public static final int NUMBER_IN_ONE_SCREEN = 9
  7.      
  8.     /**该类代表每个应用程序的数据部分*/ 
  9.     public static class DataItem { 
  10.         public String dataName;   //应用程序名称 
  11.         public Drawable drawable;  //应用程序图标 
  12.     } 
  13.      
  14.     /**该类代表了一个屏的所有应用程序*/ 
  15.     public static class MenuDataOneScreen {  
  16.         ArrayList<DataItem> mDataItems = new ArrayList<DataItem>(); 
  17.     } 
  18.      
  19.     /**该数据时该类的主要部分,所有屏的列表,实际上该类就是代表了所有的屏*/ 
  20.     ArrayList<MenuDataOneScreen> mScreens = new ArrayList<MenuDataOneScreen>(); 
  21.      
  22.     /**对该类进行赋予数据*/ 
  23.     public void setMenuItems(ArrayList<DataItem> dataItems) { 
  24.         int screenNum = dataItems.size() / NUMBER_IN_ONE_SCREEN; 
  25.         int remain = dataItems.size() % NUMBER_IN_ONE_SCREEN; 
  26.         screenNum += remain == 0 ? 0 : 1
  27.          
  28.         int pos = 0
  29.         for (int i = 0; i < screenNum; i++) { 
  30.             MenuDataOneScreen screen = new MenuDataOneScreen(); 
  31.             for (int j = 0; j < NUMBER_IN_ONE_SCREEN; j++) { 
  32.                 if (pos <= dataItems.size() - 1) { 
  33.                     screen.mDataItems.add(dataItems.get(pos)); 
  34.                     pos++; 
  35.                 } 
  36.             } 
  37.             mScreens.add(screen); 
  38.         } 
  39.     } 
  40.      
  41.     /**获取屏的数目*/ 
  42.     public int getScreenNumber() { 
  43.         return mScreens.size(); 
  44.     } 
  45.      
  46.     /**根据屏的索引,获取某个屏的数据*/ 
  47.     public MenuDataOneScreen getScreen(int screenIndex) { 
  48.         return mScreens.get(screenIndex); 
  49.     } 

    然后,我们重载ViewFactory类,定义我们如何生成View,生成什么样的View。

  1. public class SlideViewFactory implements ViewFactory{ 
  2.     LayoutInflater mInflater; 
  3.     public SlideViewFactory(Context context) { 
  4.         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
  5.     } 
  6.      
  7.     /**这个函数就是得到我们要生成的View,这里实际上直接从布局得到, 
  8.     *我们定义的是一个GridView ,一个GridView用于显示一屏的应用程序*/  
  9.     public View makeView() { 
  10.         return mInflater.inflate(R.layout.slidelistview, null); 
  11.     } 

    从上面的代码我们可以看出,我们生成的View实际是一个GridView,GridView要想和数据关联,则需要一个Adapter,因此我们下面定义该Adapter:

  1. public class OneScreenListAdapter extends BaseAdapter{ 
  2.     private MenuDataOneScreen mScreen; 
  3.     private LayoutInflater mInflater; 
  4.      
  5.     public OneScreenListAdapter(Context context) { 
  6.         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
  7.     } 
  8.      
  9.     /**这里将数据赋予Adapter*/ 
  10.     public void setScreenData(MenuDataOneScreen screenData) { 
  11.         mScreen = screenData
  12.     } 
  13.  
  14.     public int getCount() { 
  15.         return mScreen.mDataItems.size(); 
  16.     } 
  17.  
  18.     public Object getItem(int position) { 
  19.         return mScreen.mDataItems.get(position); 
  20.     } 
  21.  
  22.     public long getItemId(int position) { 
  23.         return position; 
  24.     } 
  25.  
  26.     /**该函数中将数据和View进行关联*/ 
  27.     public View getView(int position, View convertView, ViewGroup parent) { 
  28.         View view = convertView
  29.         if (convertView == null) { 
  30.             view = mInflater.inflate(R.layout.labelicon, null); 
  31.         }  
  32.      
  33.         ImageView imageView = (ImageView) view.findViewById(R.id.imageview); 
  34.         TextView textView = (TextView) view.findViewById(R.id.textview); 
  35.         imageView.setImageDrawable(mScreen.mDataItems.get(position).drawable); 
  36.         textView.setText(mScreen.mDataItems.get(position).dataName); 
  37.          
  38.         return view; 
  39.     } 
  40.  

    下面是继承ViewSwitcher的部分,用来实现两个屏的切换。

 

  1. /**该部分是ViewSwitcher的重载,用该类实现两个屏的切换和切换的动画实现*/ 
  2. public class SlideMenuSwitcher extends ViewSwitcher{ 
  3.     private MenuData mMenuData; 
  4.     private int mCurrentScreen; 
  5.     private Context mContext; 
  6.      
  7.     public SlideMenuSwitcher(Context context, AttributeSet attrs) { 
  8.         super(context, attrs); 
  9.         setFactory(new SlideViewFactory(context)); 
  10. //      setAnimateFirstView(false); 
  11.         mContext = context
  12.     } 
  13.      
  14.     /**通过该方法将数据赋值进去,并且将初始的屏显示出来*/ 
  15.     public void setData(ArrayList<DataItem> dataItems) { 
  16.         mMenuData = new MenuData(); 
  17.         mMenuData.setMenuItems(dataItems); 
  18.         mCurrentScreen = mMenuData.getScreenNumber() / 2; 
  19.          
  20.         GridView listView = (GridView) getCurrentView(); 
  21.         OneScreenListAdapter adapter = new OneScreenListAdapter(mContext); 
  22.         adapter.setScreenData(mMenuData.getScreen(mCurrentScreen)); 
  23.         listView.setAdapter(adapter); 
  24.     } 
  25.      
  26.     /**该方法用于显示下一屏*/ 
  27.     public void showNextScreen() { 
  28.         if (mCurrentScreen < mMenuData.getScreenNumber() - 1) { 
  29.             mCurrentScreen++; 
  30.             setInAnimation(mContext, R.anim.slide_in_right); 
  31.             setOutAnimation(mContext, R.anim.slide_out_left); 
  32.         } else { 
  33.             return; 
  34.         } 
  35.          
  36.         setViewData(mCurrentScreen); 
  37.         showNext(); 
  38.     } 
  39.      
  40.     /**该方法用于显示上一屏*/ 
  41.     public void showPreviousScreen() { 
  42.         if (mCurrentScreen > 0) { 
  43.             mCurrentScreen--; 
  44.             setInAnimation(mContext, R.anim.slide_in_left); 
  45.             setOutAnimation(mContext, R.anim.slide_out_right); 
  46.         } else { 
  47.             return; 
  48.         } 
  49.          
  50.         setViewData(mCurrentScreen); 
  51.         showPrevious(); 
  52.     } 
  53.      
  54.     private void setViewData(int index) { 
  55.         GridView listView = (GridView) getNextView(); 
  56.         OneScreenListAdapter adapter = new OneScreenListAdapter(mContext); 
  57.         adapter.setScreenData(mMenuData.getScreen(index)); 
  58.         listView.setAdapter(adapter); 
  59.     } 
  60.  

    我们将在一个Activity中,将我们做好的东西实现。首先在布局中将我们写的SlideMenuSwitcher。

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  2.     android:layout_width="fill_parent" 
  3.     android:layout_height="fill_parent" 
  4.     > 
  5.     <com.winuxxan.SlideMenu.SlideMenuSwitcher 
  6.         android:id="@+id/slide_view" 
  7.         android:layout_width="fill_parent" 
  8.         android:layout_height="fill_parent" 
  9.     /> 
  10.      
  11.     <Button 
  12.         android:id="@+id/button_prev" 
  13.         android:layout_width="wrap_content" 
  14.         android:layout_height="wrap_content" 
  15.         android:layout_alignParentBottom="true" 
  16.         android:layout_alignParentLeft="true" 
  17.         android:text="&lt;--" 
  18.     /> 
  19.     <Button 
  20.         android:id="@+id/button_next" 
  21.         android:layout_width="wrap_content" 
  22.         android:layout_height="wrap_content" 
  23.         android:layout_alignParentBottom="true" 
  24.         android:layout_alignParentRight="true" 
  25.         android:text="--&gt;" 
  26.     /> 
  27. </RelativeLayout> 

    最后,实现我们的Activity:

  1. public class SlideMenuActivity extends Activity { 
  2.     /** Called when the activity is first created. */ 
  3.     SlideMenuSwitcher switcher; 
  4.     @Override 
  5.     public void onCreate(Bundle savedInstanceState) { 
  6.         super.onCreate(savedInstanceState); 
  7.         setContentView(R.layout.main); 
  8.         switcher = (SlideMenuSwitcher) findViewById(R.id.slide_view); 
  9.         switcher.setData(makeItems());    //将24个应用程序赋值到swithcer中。 
  10.          
  11.         findViewById(R.id.button_next).setOnClickListener(new OnClickListener() { 
  12.              
  13.             public void onClick(View v) { 
  14.                 switcher.showNextScreen();  //点击右边按钮,显示下一屏,当然可以采用手势 
  15.             } 
  16.         }); 
  17.          
  18.         findViewById(R.id.button_prev).setOnClickListener(new OnClickListener() { 
  19.              
  20.             public void onClick(View v) { 
  21.                 switcher.showPreviousScreen();  //点击左边按钮,显示上一屏,当然可以采用手势 
  22.  
  23.             } 
  24.         }); 
  25.     } 
  26.      
  27.     /**模拟24个应用程序*/ 
  28.     private ArrayList<DataItem> makeItems() { 
  29.         ArrayList<DataItem> items = new ArrayList<DataItem>(); 
  30.         for (int i = 0; i < 24; i++) { 
  31.             String label = "" + i; 
  32.             Drawable drawable = getResources().getDrawable(R.drawable.icon); 
  33.             DataItem item = new DataItem(); 
  34.             item.dataName = label; 
  35.             item.drawable = drawable; 
  36.             items.add(item); 
  37.         } 
  38.          
  39.         return items; 
  40.     } 

    好了,运行我们的应用程序,看下,效果是否是我们想要的?(动画效果没法抓图)