首页 > 代码库 > 安卓开发笔记——自定义HorizontalScrollView控件(实现QQ5.0侧滑效果)

安卓开发笔记——自定义HorizontalScrollView控件(实现QQ5.0侧滑效果)

对于滑动菜单栏SlidingMenu,大家应该都不陌生,在市场上的一些APP应用里经常可以见到,比如人人网,FaceBook等。

前段时间QQ5.0版本出来后也采用了这种设计风格:(下面是效果图)

之前在GitHub上看到过关于此设计风格的开源项目,它只需要引入对应的类库,就可以定制灵活、各种阴影和渐变以及动画的滑动效果的侧滑菜单。

但作为开发人员,在学习阶段还是建议尽可能的去自己实现,所以今天我不讲此开源项目的使用方式,我们用自定义HorizontalScrollView来实现此效果。

下面先看下实现效果图:

     

 上图的效果是用自定义HorizontalScrollView来实现的,在HorizontalScrollView里潜入一个横向排列的线性布局,然后在线性布局里分别加入菜单布局和内容布局,在我们初始化的时候把HorizontalScrollView的滚动条向左拉至左边菜单距离即可实现菜单布局的隐藏,关于缩放,移动效果我们可以使用开源动画库nineoldandroids来实现,只需要几行代码。

 

好了,接着直接上代码吧:

首先,先看下布局文件:

 

1、菜单栏布局文件:

  1 <?xml version="1.0" encoding="utf-8"?>  2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  3     android:layout_width="match_parent"  4     android:layout_height="match_parent">  5   6     <LinearLayout  7         android:layout_width="match_parent"  8         android:layout_height="match_parent"  9         android:orientation="vertical" > 10  11         <RelativeLayout 12             android:layout_width="match_parent" 13             android:layout_height="wrap_content" 14             android:layout_centerInParent="true" > 15  16             <ImageView 17                 android:id="@+id/menuimage1" 18                 android:layout_width="50dp" 19                 android:layout_height="50dp" 20                 android:layout_centerVertical="true" 21                 android:layout_marginLeft="20dp" 22                 android:layout_marginTop="20dp" 23                 android:src="@drawable/img_1" /> 24  25             <TextView 26                 android:id="@+id/menutext1" 27                 android:layout_width="wrap_content" 28                 android:layout_height="wrap_content" 29                 android:layout_centerVertical="true" 30                 android:layout_marginLeft="20dp" 31                 android:layout_marginTop="20dp" 32                 android:layout_toRightOf="@id/menuimage1" 33                 android:text="菜单一" 34                 android:textColor="@android:color/white" 35                 android:textSize="20dp" /> 36         </RelativeLayout> 37          38          39                 <RelativeLayout 40             android:layout_width="match_parent" 41             android:layout_height="wrap_content" 42             android:layout_centerInParent="true" > 43  44             <ImageView 45                 android:id="@+id/menuimage2" 46                 android:layout_width="50dp" 47                 android:layout_height="50dp" 48                 android:layout_centerVertical="true" 49                 android:layout_marginLeft="20dp" 50                 android:layout_marginTop="20dp" 51                 android:src="@drawable/img_2" /> 52  53             <TextView 54                 android:id="@+id/menutext2" 55                 android:layout_width="wrap_content" 56                 android:layout_height="wrap_content" 57                 android:layout_centerVertical="true" 58                 android:layout_marginLeft="20dp" 59                 android:layout_marginTop="20dp" 60                 android:layout_toRightOf="@id/menuimage2" 61                 android:text="菜单二" 62                 android:textColor="@android:color/white" 63                 android:textSize="20dp" /> 64         </RelativeLayout> 65          66                         <RelativeLayout 67             android:layout_width="match_parent" 68             android:layout_height="wrap_content" 69             android:layout_centerInParent="true" > 70  71             <ImageView 72                 android:id="@+id/menuimage3" 73                 android:layout_width="50dp" 74                 android:layout_height="50dp" 75                 android:layout_centerVertical="true" 76                 android:layout_marginLeft="20dp" 77                 android:layout_marginTop="20dp" 78                 android:src="@drawable/img_3" /> 79  80             <TextView 81                 android:id="@+id/menutext3" 82                 android:layout_width="wrap_content" 83                 android:layout_height="wrap_content" 84                 android:layout_centerVertical="true" 85                 android:layout_marginLeft="20dp" 86                 android:layout_marginTop="20dp" 87                 android:layout_toRightOf="@id/menuimage3" 88                 android:text="菜单三" 89                 android:textColor="@android:color/white" 90                 android:textSize="20dp" /> 91         </RelativeLayout> 92          93                          94                                 <RelativeLayout 95             android:layout_width="match_parent" 96             android:layout_height="wrap_content" 97             android:layout_centerInParent="true" > 98  99             <ImageView100                 android:id="@+id/menuimage4"101                 android:layout_width="50dp"102                 android:layout_height="50dp"103                 android:layout_centerVertical="true"104                 android:layout_marginLeft="20dp"105                 android:layout_marginTop="20dp"106                 android:src="@drawable/img_4" />107 108             <TextView109                 android:id="@+id/menutext4"110                 android:layout_width="wrap_content"111                 android:layout_height="wrap_content"112                 android:layout_centerVertical="true"113                 android:layout_marginLeft="20dp"114                 android:layout_marginTop="20dp"115                 android:layout_toRightOf="@id/menuimage4"116                 android:text="菜单四"117                 android:textColor="@android:color/white"118                 android:textSize="20dp" />119         </RelativeLayout>120         121                                 122                                         <RelativeLayout123             android:layout_width="match_parent"124             android:layout_height="wrap_content"125             android:layout_centerInParent="true" >126 127             <ImageView128                 android:id="@+id/menuimage5"129                 android:layout_width="50dp"130                 android:layout_height="50dp"131                 android:layout_centerVertical="true"132                 android:layout_marginLeft="20dp"133                 android:layout_marginTop="20dp"134                 android:src="@drawable/img_5" />135 136             <TextView137                 android:id="@+id/menutext5"138                 android:layout_width="wrap_content"139                 android:layout_height="wrap_content"140                 android:layout_centerVertical="true"141                 android:layout_marginLeft="20dp"142                 android:layout_marginTop="20dp"143                 android:layout_toRightOf="@id/menuimage5"144                 android:text="菜单五"145                 android:textColor="@android:color/white"146                 android:textSize="20dp" />147         </RelativeLayout>148     </LinearLayout>149 150 </RelativeLayout>

2、主内容布局文件:

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2     xmlns:tools="http://schemas.android.com/tools" 3     android:layout_width="match_parent" 4     android:layout_height="match_parent" 5         android:background="@drawable/img_frame_background" > 6  7 <com.example.sidesliptest.MyHorizontalScrollView  8     android:layout_height="match_parent" 9     android:layout_width="match_parent"10     android:scrollbars="none"11     >12     <LinearLayout 13         android:layout_height="match_parent"14         android:layout_width="match_parent"15         android:orientation="horizontal"16         >17         <include layout="@layout/left_menu"/>18         <LinearLayout 19             android:layout_width="match_parent"20             android:layout_height="match_parent"21             android:background="@drawable/qq"22             ></LinearLayout>23         24     </LinearLayout>25     26 </com.example.sidesliptest.MyHorizontalScrollView>27 28 </RelativeLayout>

 

3、自定义View(HorizontalScrollView)类:

自定义Viewi实现步骤:

1、继承要自定义View的类,并实现带有参数的构造方法

2、重写onMeasure(确定自定义View的大小)和onLayout(确定自定义View的位置)方法

 

关于HorizontalScrollView的滑动,我们可以用onScrollChanged来监听参数L:

打印日志:可以发现,当滚动条向左(画面向右滑动)的时候,L的值是逐渐增大的,所以我们可以通过它来作为动画的变化梯度值。

注释很全,具体看注释吧。

  1 package com.example.sidesliptest;  2   3 import android.content.Context;  4 import android.util.AttributeSet;  5 import android.util.DisplayMetrics;  6 import android.util.Log;  7 import android.util.TypedValue;  8 import android.view.MotionEvent;  9 import android.view.ViewGroup; 10 import android.view.WindowManager; 11 import android.widget.HorizontalScrollView; 12 import android.widget.LinearLayout; 13  14 import com.nineoldandroids.view.ViewHelper; 15  16 public class MyHorizontalScrollView extends HorizontalScrollView { 17  18     // 在HorizontalScrollView有个LinearLayout 19     private LinearLayout linearLayout; 20     // 菜单,内容页 21     private ViewGroup myMenu; 22     private ViewGroup myContent; 23     //菜单宽度 24     private int myMenuWidth; 25  26     // 屏幕宽度 27     private int screenWidth; 28     // 菜单与屏幕右侧的距离(dp) 29     private int myMenuPaddingRight = 50; 30  31     // 避免多次调用onMeasure的标志 32     private boolean once = false; 33  34     /** 35      * 自定义View需要实现带有Context、AttributeSet这2个参数的构造方法,否则自定义参数会出错 36      * 当使用了自定义属性时,会调用此构造方法 37      *  38      * @param context 39      * @param attrs 40      */ 41     public MyHorizontalScrollView(Context context, AttributeSet attrs) { 42         super(context, attrs); 43         // 获取屏幕宽度 44         WindowManager windowManager = (WindowManager) context 45                 .getSystemService(Context.WINDOW_SERVICE); 46         DisplayMetrics outMetrics = new DisplayMetrics(); 47         windowManager.getDefaultDisplay().getMetrics(outMetrics); 48         screenWidth = outMetrics.widthPixels;// 屏幕宽度 49  50         // 将dp转换px 51         myMenuPaddingRight = (int) TypedValue.applyDimension( 52                 TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources() 53                         .getDisplayMetrics()); 54  55     } 56  57     /** 58      * 设置子View的宽高,决定自身View的宽高,每次启动都会调用此方法 59      */ 60     @Override 61     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 62         super.onMeasure(widthMeasureSpec, heightMeasureSpec); 63         if (!once) {//使其只调用一次 64             // this指的是HorizontalScrollView,获取各个元素 65             linearLayout = (LinearLayout) this.getChildAt(0);// 第一个子元素 66             myMenu = (ViewGroup) linearLayout.getChildAt(0);// HorizontalScrollView下LinearLayout的第一个子元素 67             myContent = (ViewGroup) linearLayout.getChildAt(1);// HorizontalScrollView下LinearLayout的第二个子元素 68  69             // 设置子View的宽高,高于屏幕一致 70             myMenuWidth=myMenu.getLayoutParams().width = screenWidth - myMenuPaddingRight;// 菜单的宽度=屏幕宽度-右边距 71             myContent.getLayoutParams().width = screenWidth;// 内容宽度=屏幕宽度 72             // 决定自身View的宽高,高于屏幕一致 73             // 由于这里的LinearLayout里只包含了Menu和Content所以就不需要额外的去指定自身的宽 74             once = true; 75         } 76     } 77  78     //设置View的位置,首先,先将Menu隐藏(在eclipse中ScrollView的画面内容(非滚动条)正数表示向左移,向上移) 79     @Override 80     protected void onLayout(boolean changed, int l, int t, int r, int b) { 81         super.onLayout(changed, l, t, r, b); 82         //刚载入界面的时候隐藏Menu菜单也就是ScrollView向左滑动菜单自身的大小 83         if(changed){ 84             this.scrollTo(myMenuWidth, 0);//向左滑动,相当于把右边的内容页拖到正中央,菜单隐藏     85         } 86      87     } 88      89     @Override 90     public boolean onTouchEvent(MotionEvent ev) { 91         int action=ev.getAction(); 92         switch (action) { 93         case MotionEvent.ACTION_UP: 94             int scrollX=this.getScrollX();//滑动的距离scrollTo方法里,也就是onMeasure方法里的向左滑动那部分 95             if(scrollX>=myMenuWidth/2){ 96                 this.smoothScrollTo(myMenuWidth,0);//向左滑动展示内容 97             }else{ 98                 this.smoothScrollTo(0, 0); 99             }100             return true;101         }102         return super.onTouchEvent(ev);103     }104     105     106     @Override107     protected void onScrollChanged(int l, int t, int oldl, int oldt) {108         super.onScrollChanged(l, t, oldl, oldt);109         Log.i("tuzi",l+"");110         float scale = l * 1.0f / myMenuWidth; // 1 ~ 0111 112         /**113          * 区别1:内容区域1.0~0.7 缩放的效果 scale : 1.0~0.0 0.7 + 0.3 * scale114          * 115          * 区别2:菜单的偏移量需要修改116          * 117          * 区别3:菜单的显示时有缩放以及透明度变化 缩放:0.7 ~1.0 1.0 - scale * 0.3 透明度 0.6 ~ 1.0 118          * 0.6+ 0.4 * (1- scale) ;119          * 120          */121         float rightScale = 0.7f + 0.3f * scale;122         float leftScale = 1.0f - scale * 0.3f;123         float leftAlpha = 0.6f + 0.4f * (1 - scale);124 125         // 调用属性动画,设置TranslationX126         ViewHelper.setTranslationX(myMenu, myMenuWidth * scale * 0.8f);127         128         ViewHelper.setScaleX(myMenu, leftScale);129         ViewHelper.setScaleY(myMenu, leftScale);130         ViewHelper.setAlpha(myMenu, leftAlpha);131         // 设置content的缩放的中心点132         ViewHelper.setPivotX(myContent, 0);133         ViewHelper.setPivotY(myContent, myContent.getHeight() / 2);134         ViewHelper.setScaleX(myContent, rightScale);135         ViewHelper.setScaleY(myContent, rightScale);136     }137 138 } 

4、主程序类:

package com.example.sidesliptest;import android.app.Activity;import android.os.Bundle;public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //requestWindowFeature(Window.FEATURE_NO_TITLE);        setContentView(R.layout.activity_main);    }}

 

安卓开发笔记——自定义HorizontalScrollView控件(实现QQ5.0侧滑效果)