侧滑菜单 ——仿QQ实现动画效果

1、首先实现侧滑菜单,这里用HorizontalScrollView来进行实现

2、SideMenu为自定义view 继承horizontalScrollView

3、这个自定义命名空间 :xmlns:lyl="http://schemas.android.com/apk/res/com.example.horizontalscrollview_qq"

  中的res/下面为项目的包名 可以复制manifest.xml中的包名

4、<include />中包含的是侧滑菜单的布局   可以自行定义
5、内部的linearlayout 为内容布局  也可以自行定义

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:lyl="http://schemas.android.com/apk/res/com.example.horizontalscrollview_qq"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.horizontalscrollview_qq.MainActivity" >

    <com.example.sidemenu.SideMenu
        android:id="@+id/menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/img_frame_background"
        lyl:MenuPadding="50dp" >

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal" >

            <include layout="@layout/side_menu" />

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:background="@drawable/qq" >

                <Button
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@android:color/transparent"
                    android:onClick="toggleMenu"
                    android:text="@string/back"
                    android:textColor="@android:color/white"
                    android:textSize="30sp" />
            </LinearLayout>
        </LinearLayout>
    </com.example.sidemenu.SideMenu>

</RelativeLayout>

自定义View

1、构造方法

  一个参数 context

  两个参数 context attr

  三个参数  context attr defStyle

三个参数的构造方法中含有 自定义属性的值  所以 在一个参数的构造方法中使用this(context ,null)调用两个参数的,以此类推

2、复写 onMeasure()方法

  定义子视图 设置子视图的大小

3、复写 onLayout()方法

  设置子视图的位置

4、动画效果

  这里为更好的兼容3.0以下的版本 导入nineoldAndroid.jar包

  nineoldAndroid的API和Honeycomb API完全一样,只是改变了使用com.nineoldandroids.XXX的入口。
       

package com.example.sidemenu;

import com.example.horizontalscrollview_qq.R;
import com.nineoldandroids.view.ViewHelper;

import android.content.Context;
import android.content.res.TypedArray;
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.view.animation.AnimationSet;
import android.view.animation.TranslateAnimation;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

public class SideMenu extends HorizontalScrollView {
    private LinearLayout mWapper;
    private ViewGroup mMenu;
    private ViewGroup mContent;

    private int mScreenWidth;
    private int mMenuRightpadding = 50;
    private boolean once;

    private int mMenuWidth;
    private int mContentWidth;

    private boolean isOpen;

    public SideMenu(Context context) {
        // super(context);
        // 调用两个参数的构造方法
        this(context, null);

    }

    public SideMenu(Context context, AttributeSet attrs) {
        // super(context, attrs);
        // 调用含有自定义属性参数的构造方法
        this(context, attrs, 0);

    }

    public SideMenu(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // 读取所有自定义参数
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs,
                R.styleable.sidemenu, defStyleAttr, 0);
        // 获得属性数组的数量
        int n = array.getIndexCount();

        for (int i = 0; i < n; i++) {
            int attr = array.getIndex(i);
            switch (attr) {
            case R.styleable.sidemenu_MenuPadding:
                // 1 、设置自定义属性值
                // 2、设置默认值
                mMenuRightpadding = array.getDimensionPixelSize(attr,
                        (int) TypedValue.applyDimension(
                                TypedValue.COMPLEX_UNIT_DIP, 50, context
                                        .getResources().getDisplayMetrics()));
                break;
            default:
                break;
            }
        }
        // 回收
        array.recycle();
        WindowManager manager = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(outMetrics);
        mScreenWidth = outMetrics.widthPixels;

    }

    // 自定义视图时,需要复写这几个方法 onMeasure onLayout
    // onMeasure 计算子View的宽和高,以及设置自己的宽和高
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (!once) {
            // 先初始化linearLayout
            mWapper = (LinearLayout) getChildAt(0);
            // linearLayout内装入子视图
            mMenu = (ViewGroup) mWapper.getChildAt(0);
            // 在装入第二个子视图
            mContent = (ViewGroup) mWapper.getChildAt(1);
            // 设置 第一个 视图的 宽度
            mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth
                    - mMenuRightpadding;
            // 设置第二个视图的宽度
            mContentWidth = mContent.getLayoutParams().width = mScreenWidth;
            // 只计算一次
            once = true;
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    // onLayout 决定子View的布局的位置
    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);
            isOpen = false;
        }
    }

    // 触摸事件处理
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
        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);
    }

    // 打开菜单
    public void open() {
        this.smoothScrollTo(0, 0);
        isOpen = true;
    }

    // 关闭菜单
    public void close() {
        this.smoothScrollTo(mMenuWidth, 0);
        isOpen = false;
    }

    // 按钮开关
    public void toggle() {
        if (isOpen) {
            close();
        } else {
            open();
        }
    }

    // 实现侧滑的抽屉效果
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        // l: horizontalScroll起始的水平位置
        // t: 起始的垂直位置
        // 调用动画属性 设置translation
        // mMenu.setTranslationX(l);
        // 这里为更好的兼容3.0以下的版本 导入nineoldAndroid.jar包

        // 该API和Honeycomb API完全一样,只是改变了使用com.nineoldandroids.XXX的入口。
        ViewHelper.setTranslationX(mMenu, l*0.7f);
        float scale = l * 1.0f / mMenuWidth;
        // 实现内容区域的缩放
        // 设置缩放的中心点
        ViewHelper.setPivotX(mContent, 0);
        ViewHelper.setPivotY(mContent, mContent.getHeight() / 2);
        // 从1.0缩放到0.7
        ViewHelper.setScaleX(mContent, 0.7f + 0.3f * scale);
        ViewHelper.setScaleY(mContent, 0.7f + 0.3f * scale);
        // mMenu 的缩放从0.7到1.0
        ViewHelper.setScaleX(mMenu, 1.0f - 0.3f * scale);
        ViewHelper.setScaleY(mMenu, 1.0f - 0.3f * scale);
        // 设置mMenu 的透明度 0.6到1.0
        ViewHelper.setAlpha(mMenu, 1.0f - 0.6f * scale);

    }

}

 上面的自定义属性需要声明

  自定义属性(styleable)的名字为sidemenu 属性名为 MenuPadding

  在自定义view中使用 需要自定义命名空间 

    xmlns:lyl="http://schemas.android.com/apk/res/com.example.horizontalscrollview_qq"

  在res/下为项目的包名 可以在manifest.xml中复制
  在自定义视图中使用:lyl:MenuPadding="50dp"

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <attr name="MenuPadding" format="dimension"></attr>

    <declare-styleable name="sidemenu">
        <attr name="MenuPadding" ></attr>
    </declare-styleable>
</resources>

1、自定义view中 可以通过 TypedArray来获得自定义的所有属性

  TypedArray array = context.getTheme().obtainStyledAttributes(attrs,
                  R.styleable.sidemenu, defStyleAttr, 0);

2、 获得自定义属性可以通过 getDimensionPixelSize(index, defValue)来获得   并设置默认值

  当需要将参数值的单位进行转化时可以用 

  TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context
                                        .getResources().getDisplayMetrics());
      单位为dip  即返回的的结果为:50dp乘以显示密度density(dpi/160)。即为像素px

 

 

mian里面实现了菜单的开关按钮

package com.example.horizontalscrollview_qq;

import com.example.horizontalscrollview_qq.R.id;
import com.example.sidemenu.SideMenu;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;


public class MainActivity extends Activity {
private SideMenu menu;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.main_activity);    
        menu=(SideMenu) findViewById(id.menu);
        
    }

    public void toggleMenu(View view){
        menu.toggle();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

 

 

 

 

posted @ 2015-08-18 18:32  手酒刘  阅读(1879)  评论(0编辑  收藏  举报