安卓端实现拉出式、抽屉式、仿QQ侧滑菜单效果

来源:互联网 发布:painter安装教程mac 编辑:程序博客网 时间:2024/04/30 13:26

一、概述

一般的侧滑实现:

ViewGroup Menu+Content

onTouchEvent监听器可以用来监听手指抬起,点击等事件

MOVE:不断监听用户的移动距离,改变ViewGroupleftMargin

UP:对用户的操作进行判断,判断是大于一半还是小于一半,若大于则显示菜单,若小于则隐藏菜单(即根据显示菜单的宽度,决定将其隐藏还是显示)

1、Scroller辅助类实现动画效果

2、LeftMargin+Thread

 

换个思路:继承HorizontalScrollerView(水平滚动条),好处:无需判断高度只需考虑水平宽度,可以省去MOVE冲突的处理。

 

二、创建安卓工程QQ50SlidingMenu

菜单布局文件:

新建布局文件:left_menu.xml

Strings.xml: 

 QQ侧滑菜单
    Settings
    Hello world!
    赵灵儿
    林月如
    阿奴
    韩菱纱
    唐雨柔
    切换菜单

  布局文件:

    android:layout_width="match_parent"

android:layout_height="match_parent" 

android:background="#0000" 

   

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_centerInParent="true"

        android:orientation="vertical">

        

        

            android:layout_width="fill_parent"

            android:layout_height="wrap_content">

            

                android:id="@+id/img1"

                android:layout_width="50dp"

                android:layout_height="50dp"

                android:layout_marginLeft="20dp"

                android:layout_marginTop="20dp"

                android:src="@drawable/img_1"

                android:layout_centerVertical="true"

                />

            

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:textSize="20sp"

                android:textColor="#ffffff"

                android:text="@string/txt1"

                android:layout_marginLeft="20dp"

                android:layout_marginTop="20dp"

                android:layout_toRightOf="@+id/img1"

                android:layout_centerVertical="true"

                />

        

        

         

            android:layout_width="fill_parent"

            android:layout_height="wrap_content">

            

                android:id="@+id/img2"

                android:layout_width="50dp"

                android:layout_height="50dp"

                android:layout_marginLeft="20dp"

                android:layout_marginTop="20dp"

                android:src="@drawable/img_2"

                android:layout_centerVertical="true"

                />

            

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:textSize="20sp"

                android:textColor="#ffffff"

                android:text="@string/txt2"

                android:layout_marginLeft="20dp"

                android:layout_marginTop="20dp"

                android:layout_toRightOf="@+id/img2"

                android:layout_centerVertical="true"

                />

        

        

         

            android:layout_width="fill_parent"

            android:layout_height="wrap_content">

            

                android:id="@+id/img3"

                android:layout_width="50dp"

                android:layout_height="50dp"

                android:layout_marginLeft="20dp"

                android:layout_marginTop="20dp"

                android:src="@drawable/img_3"

                android:layout_centerVertical="true"

                />

            

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:textSize="20sp"

                android:textColor="#ffffff"

                android:text="@string/txt3"

                android:layout_marginLeft="20dp"

                android:layout_marginTop="20dp"

                android:layout_toRightOf="@+id/img3"

                android:layout_centerVertical="true"

                />

        

        

          

            android:layout_width="fill_parent"

            android:layout_height="wrap_content">

            

                android:id="@+id/img4"

                android:layout_width="50dp"

                android:layout_height="50dp"

                android:layout_marginLeft="20dp"

                android:layout_marginTop="20dp"

                android:src="@drawable/img_4"

                android:layout_centerVertical="true"

                />

            

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:textSize="20sp"

                android:textColor="#ffffff"

                android:text="@string/txt4"

                android:layout_marginLeft="20dp"

                android:layout_marginTop="20dp"

                android:layout_toRightOf="@+id/img4"

                android:layout_centerVertical="true"

                />

        

        

           

            android:layout_width="fill_parent"

            android:layout_height="wrap_content">

            

                android:id="@+id/img5"

                android:layout_width="50dp"

                android:layout_height="50dp"

                android:layout_marginLeft="20dp"

                android:layout_marginTop="20dp"

                android:src="@drawable/img_5"

                android:layout_centerVertical="true"

                />

            

                android:layout_width="wrap_content"

                android:layout_height="wrap_content"

                android:textSize="20sp"

                android:textColor="#ffffff"

                android:text="@string/txt5"

                android:layout_marginLeft="20dp"

                android:layout_marginTop="20dp"

                android:layout_toRightOf="@+id/img5"

                android:layout_centerVertical="true"

                />

        

    

 

 

 

 

接下来完成主布局文件:mainAActivity.xml文件:

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

     >

 

  

      android:layout_width="match_parent"

      android:layout_height="match_parent"

      android:background="@drawable/girl">

     >

     

         android:layout_width="wrap_content"

         android:layout_height="wrap_content"

         android:orientation="horizontal"

         >

         

         

         

             android:layout_width="match_parent"

             android:layout_height="match_parent"

             android:background="@drawable/qq"

             >

     

     

    

 

 

 

自定义View:SlidingMenu.java,继承水平滚动控件类

1、首先实现其两个参数的构造方法

2、将自定义的HorizontalScrollView名字改成自定义的属性名 包名+类名

…………………………

>

3、自定义ViewGroup:

(1)、onMeasure:决定内部View(View)的宽和高以及自己的宽和高

(2)、onLayout:决定子View放置的位置

(3)、onTouchEvent:判断用户的手指状态

 

package org.view;

 

import android.content.Context;

import android.util.AttributeSet;

import android.util.DisplayMetrics;

import android.util.TypedValue;

import android.view.MotionEvent;

import android.view.ViewGroup;

import android.view.WindowManager;

import android.widget.HorizontalScrollView;

import android.widget.LinearLayout;

 

public class SlidingMenu extends HorizontalScrollView {

private LinearLayout mWrapper;//固定写法,水平区域内只存在一个LinearLayout

private ViewGroup mMenu;

private ViewGroup mContent;

private int mScreenWidth;//屏幕宽度

private int mMenuRightPadding=50;//定义菜单与屏幕右侧的距离

private boolean once;//onMeasure()方法可能不止一次被调用

private int mMenuWidth;//菜单界面的宽度

public SlidingMenu(Context context, AttributeSet attrs) {

super(context, attrs);

WindowManager wm=(WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);//屏幕宽度可以通过上下文对象进行获取

DisplayMetrics outMetrics=new DisplayMetrics();

wm.getDefaultDisplay().getMetrics(outMetrics);

mScreenWidth=outMetrics.widthPixels;

mMenuRightPadding=(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,50, context.getResources().getDisplayMetrics());

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

if(!once){

mWrapper=(LinearLayout) this.getChildAt(0);//使用静态方法getChildAt(0)获取线性布局的属性值

mMenu=(ViewGroup) mWrapper.getChildAt(0);//获取内部菜单

mContent=(ViewGroup) mWrapper.getChildAt(1);

mMenuWidth=mMenu.getLayoutParams().width=mScreenWidth-mMenuRightPadding;//菜单宽度等于屏幕宽度减去菜单距离屏幕右侧的宽度

mContent.getLayoutParams().width=mScreenWidth;

once=true;

}

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

@Override

protected void onLayout(boolean changed, int l, int t, int r, int b) {

super.onLayout(changed, l, t, r, b);

if(changed){

this.scrollTo(mMenuWidth,0);

}

}

@Override

public boolean onTouchEvent(MotionEvent ev) {

int action=ev.getAction();

switch (action) {

case MotionEvent.ACTION_UP:

int scrollX=getScrollX();//屏幕多出的部分(隐藏在左边的宽度)

if(scrollX>=mMenuWidth/2){

this.smoothScrollTo(mMenuWidth, 0);//慢慢移动,有动画感觉

}else{

this.smoothScrollTo(0, 0);

}

return true;

}

return super.onTouchEvent(ev);

}

}

 

 

 

运行效果:

 


但这里有一个小问题,上顶部的黑色内容区域没有隐藏:

在mainAActivity中加入:

requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏黑色信息区域

 

运行效果:


拖动后的效果:


三、用户自定义属性

至此,侧滑已经实现。接下来加入自定义属性,可以让用户去设置菜单离屏幕右边的边距。

(1)、在values文件夹中创建一个xml文件,叫做userattr.xml

(2)注意:在xml文件中编写自定义属性时是没有代码提示功能的,因此要注意代码拼写

(3)接下来要在mainAActivity.xml文件中命名空间进行声明:

xmlns:Alan="http://schemas.android.com/apk/res/com.example.qq50slidingmenu"

注意:这里的格式是xmlns:自己编写,可以是任何名字="http://schemas.android.com/apk/res/当前应用的包名,而不是View的包名"

(4)、接下来将自定义属性放到View中进行使用

Alan:rightPadding="100dp"

(5)、在刚才未使用自定义属性时,调用自身的构造方法,定义之后,需要生成另外两个构造方法:将原来两个参数构造方法中的方法拷贝到三个参数的构造方法之中,然后将两个参数构造方法的第三个参数设置为0,将一个参数的构造方法(传入上下文对象方法)的第二个参数设置为null,即调用两个参数的构造方法。

(6)、获取我们定义的属性:在三个参数的构造方法中获取:通过TypedArray这个类获得自定义属性,这里需要注意的是TypedArray这个类用完之后需要进行释放(recycled)。

 

 

源代码:

activityMain.xml:

    xmlns:tools="http://schemas.android.com/tools"

    xmlns:Alan="http://schemas.android.com/apk/res/com.example.qq50slidingmenu"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

     

 

  

      android:layout_width="match_parent" 

      android:layout_height="match_parent"

      Alan:rightPadding="200dp"

     >

 

View代码:

public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

//获取我们定义的属性

TypedArray array=context.getApplicationContext().getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyle, 0);

int n=array.getIndexCount();//获取自定义属性数量

for(int i=0;i

int attr=array.getIndex(i);

switch (attr)  //只有一个

case R.styleable.SlidingMenu_rightPadding:

mMenuRightPadding=array.getDimensionPixelSize(attr,(int) TypedValue

.applyDimension(TypedValue.COMPLEX_UNIT_DIP,50,

context.getResources().getDisplayMetrics()));

//若无自定义属性,则使用原始的50px

}

}

array.recycle();//释放操作

WindowManager wm=(WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);//屏幕宽度可以通过上下文对象进行获取

DisplayMetrics outMetrics=new DisplayMetrics();

wm.getDefaultDisplay().getMetrics(outMetrics);

mScreenWidth=outMetrics.widthPixels;

//mMenuRightPadding=(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,50, context.getResources().getDisplayMetrics());

}

 

public SlidingMenu(Context context) {

super(context,null);//调用两个参数的构造方法

}

 

四、为侧滑菜单添加一个按钮,当用户点击时,侧滑菜单自动出现,再点击时自动消失

(1)、首先定义一个bool类型的变量标识当前的状态:

Boolean isOpen;  //默认打开时为false

(2)、划开之后设置为true,未划开为flase

public boolean onTouchEvent(MotionEvent ev) {

int action=ev.getAction();

switch (action) {

case MotionEvent.ACTION_UP:

int scrollX=getScrollX();//屏幕多出的部分(隐藏在左边的宽度)

if(scrollX>=mMenuWidth/2){

this.smoothScrollTo(mMenuWidth, 0);//慢慢移动,有动画感觉

isOpen=false;

}else{

this.smoothScrollTo(0, 0);

isOpen=true;

}

return true;

}

return super.onTouchEvent(ev);

}

 

(3)、新增打开、关闭、切换菜单方法:

public void openMenu(){

if(isOpen) return;

this.smoothScrollTo(0, 0);

isOpen=true;

}

public void closeMenu(){

if(!isOpen) return;

this.smoothScrollTo(mMenuWidth, 0);

isOpen=false;

}

public void transform(){

if(isOpen){

closeMenu();

}else{

openMenu();

}

}

(4)、在主布局文件中添加一个button

切换菜单

 

 

             android:layout_width="match_parent"

             android:layout_height="match_parent"

             android:background="@drawable/qq"

             >

             

                 android:onClick="transformMenu"

                 android:layout_width="wrap_content"

                 android:layout_height="wrap_content"

                 android:text="@string/transform"

                 />

             

         

 

(5)、在MainActivity.java中添加onClick方法,并为自定义菜单设置一个id用来获取并使用它:

      android:id="@+id/id_menu"

      android:layout_width="match_parent" 

      android:layout_height="match_parent"

       Alan:rightPadding="1000dp"

     >

 

 

public class MainActivity extends Activity {

private SlidingMenu mLeftMenu;

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏黑色信息区域

        setContentView(R.layout.activity_main);

        mLeftMenu=(SlidingMenu) findViewById(R.id.id_menu);

    }

    

    public void transformMenu(View view){

     mLeftMenu.transform();

}

 

运行效果:


点击后:


-------------------------------------------------------------------------------------------------------

五、抽屉式菜单:

效果:菜单在内容区的下方

mMenuWidth

100px  mMenuWidth-100

200px  mMeunWidth-200

 

在这里需要用到属性动画相关知识:

TranslationX

getScrollX:mMenuWidth~0

 

设置调用动画时机

ACTION_MOVE

 

由于属性动画是从Android 3.0开始才引入进来,则我们需要导入一个兼容性jarnineoldandroids.jar

 

@Override

protected void onScrollChanged(int l, int t, int oldl, int oldt) {

super.onScrollChanged(l, t, oldl, oldt);

float scale=l*1.0f/mMenuWidth;//1~0

//调用属性动画,设置TranslationX

ViewHelper.setTranslationX(mMenu, mMenuWidth*scale);

}

 

 

六、实现QQ5.0侧滑立体效果

区别1:内容区域范围:1.0~0.7缩放的效果

区别2:菜单的偏移量需要修改

区别3:菜单的显示时有缩放以及透明度的变化

 

Scale:1.0~0

0.7+0.3*scale   缩放比例

 

菜单显示缩放:0.7~1.0

1.0-scale*0.3

透明度:0.6~1.0

0.6+0.4*(1-scale)

 

protected void onScrollChanged(int l, int t, int oldl, int oldt) {

super.onScrollChanged(l, t, oldl, oldt);

float scale=l*1.0f/mMenuWidth;//1~0

float rightScale=0.7f+0.3f*scale ;

//调用属性动画,设置TranslationX

    ViewHelper.setTranslationX(mMenu, mMenuWidth*scale);

    

    ViewHelper.setPivotX(mContent, 0);//设置中心点

    ViewHelper.setPivotY(mContent, mContent.getHeight()/2);

ViewHelper.setScaleX(mContent, rightScale);

ViewHelper.setScaleY(mContent, rightScale);

}

 

 

float rightScale=0.7f+0.3f*scale ;

float leftScale=1.0f-scale*0.3f;//左部菜单缩放

float leftAlpha=0.6f+0.4f*(1-scale);//左侧透明度变化

//调用属性动画,设置TranslationX

    ViewHelper.setTranslationX(mMenu, mMenuWidth*scale);

    

    ViewHelper.setScaleX(mMenu, leftScale);

    ViewHelper.setScaleY(mMenu, leftScale);

    ViewHelper.setAlpha(mMenu, leftAlpha);

    ViewHelper.setPivotX(mContent, 0);//设置中心点

    ViewHelper.setPivotY(mContent, mContent.getHeight()/2);

ViewHelper.setScaleX(mContent, rightScale);

ViewHelper.setScaleY(mContent, rightScale);


但此时发现一个问题,当刚开始拖动时,有一部分内容隐藏在屏幕左侧,没有立即显示出来,给人感觉不像是直接在内容界面的下面。

//调用属性动画,设置TranslationX

    ViewHelper.setTranslationX(mMenu, mMenuWidth*scale*0.7f);

 

运行结果:

滑动前:



七、总结

1、自定义ViewGroup:

构造方法的选择,获得一些需要用到的值

onMeasure计算子View的宽和高,以及设置自己的宽和高

onLayout决定子View的布局的位置

onTouchEvent可选

2、构造方法

Context   new  SustomViewGroup(context)

This(context,null)

 

Context ,attr  布局文件中声明(没有自定义的属性)

This(context,attr,0)

 

Context ,attr,defStyle(有自定义的属性)

 

 

(3)、自定义属性

Attr.xml

布局文件中 xmlns=

3个参数的构造方法中,获得自定义属性的值

 

属性动画:

nineold


0 0
原创粉丝点击