• 安卓开发笔记——自定义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/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
    正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!

  • 相关阅读:
    PowerDesigner导出SQL脚本
    【Android进阶学习】shape和selector的结合使用(转)
    国内最新安卓渠道列表42个(转)
    安卓新框架
    通知和消息有什么区别?(转)
    XSS之xssprotect(转)
    为你的Android App实现自签名的 SSL 证书(转)
    HTTPS和HTTP的区别(转)
    IT软件技术人员的职位路线(从程序员到技术总监)
    听大神说:https和http有何区别?(转)
  • 原文地址:https://www.cnblogs.com/lichenwei/p/4111252.html
Copyright © 2020-2023  润新知