安卓开发笔记——自定义HorizontalScrollView控件(实现QQ5.0侧滑效果)
对于滑动菜单栏SlidingMenu,大家应该都不陌生,在市场上的一些APP应用里经常可以见到,比如人人网,FaceBook等。
前段时间QQ5.0版本出来后也采用了这种设计风格:(下面是效果图)
之前在GitHub上看到过关于此设计风格的开源项目,它只需要引入对应的类库,就可以定制灵活、各种阴影和渐变以及动画的滑动效果的侧滑菜单。
这里是关于开源组件的介绍网址:https://github.com/jfeinstein10/SlidingMenu
但作为开发人员,在学习阶段还是建议尽可能的去自己实现,所以今天我不讲此开源项目的使用方式,我们用自定义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 <ImageView 100 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 <TextView 109 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 <RelativeLayout 123 android:layout_width="match_parent" 124 android:layout_height="wrap_content" 125 android:layout_centerInParent="true" > 126 127 <ImageView 128 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 <TextView 137 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 @Override 107 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 ~ 0 111 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 // 调用属性动画,设置TranslationX 126 ViewHelper.setTranslationX(myMenu, myMenuWidth * scale * 0.8f); 127 128 ViewHelper.setScaleX(myMenu, leftScale); 129 ViewHelper.setScaleY(myMenu, leftScale); 130 ViewHelper.setAlpha(myMenu, leftAlpha); 131 // 设置内容缩放的中心点 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); } }
图片素材:http://pan.baidu.com/s/1kTkjmAj
作者:Balla_兔子
出处:http://www.cnblogs.com/lichenwei/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!