标签:
一般的侧滑的实现
ViewGroup Menu + Content
onTouchEvent
MOVE:ViewGroup的leftMargin
UP:根据显示菜单的高度,决定将其隐藏或者显示
1、Scroller
2、LeftMargin + Thread
彷QQ5.0侧滑菜单的实现,使用另外的一种方法,继承HorizontalScrollView
一、自定义ViewGroup
1、构造方法的选择,获得一些需要用到的值
2、onMeasure 计算子View的宽和高,以及设置自己的宽和高
3、onLayout 决定子View的布局的位置
[4、onTouchEvent 相应手指触摸屏幕按下、抬起等事件响应 可以不实现]
----------------------------------------------------------------------
对于构造方法来说,共有三个构造方法要实现
1、context new CustomViewGroup(context)
this(Context, null);
2、context, attr 布局文件中声明(没有自定义属性)
this(context,attr,0);
3、context, attr, defStyle(有自定义属性)
super(context, attr, defStyle);
-------------------------------------------------
自定义属性
1、attr.xml
2、布局文件中 xmlns=
3、在三个参数的构造方法中,获得我们自定义的属性值
---------------------------------------------------
属性动画:
Android 3.0之后加入的
若想要在更低的版本使用需导入nineoldanimation.jar包
该包的下载路径:http://mahuiying.github.io/nineoldandroids-2.4.0.jar
第一步实现普通的侧滑菜单:
代码:
菜单(Menu)的布局文件代码 left_menu.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#0000" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:orientation="vertical" > <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <ImageView android:id="@+id/id_img1" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:src="@mipmap/img_1" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_toRightOf="@id/id_img1" android:text="第一个Item" android:textColor="#ffffff" android:textSize="20sp" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <ImageView android:id="@+id/id_img2" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:src="@mipmap/img_2" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_toRightOf="@id/id_img2" android:text="第二个Item" android:textColor="#ffffff" android:textSize="20sp" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <ImageView android:id="@+id/id_img3" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:src="@mipmap/img_3" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_toRightOf="@id/id_img3" android:text="第三个Item" android:textColor="#ffffff" android:textSize="20sp" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <ImageView android:id="@+id/id_img4" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:src="@mipmap/img_4" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_toRightOf="@id/id_img4" android:text="第四个Item" android:textColor="#ffffff" android:textSize="20sp" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <ImageView android:id="@+id/id_img5" android:layout_width="50dp" android:layout_height="50dp" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:src="@mipmap/img_5" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:layout_toRightOf="@id/id_img5" android:text="第五个Item" android:textColor="#ffffff" android:textSize="20sp" /> </RelativeLayout> </LinearLayout> </RelativeLayout>
主界面的布局代码activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:mhy="http://schemas.android.com/apk/res/com.example.mhy.qq50slidemenu" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/img_frame_background" tools:context="com.example.mhy.qq50slidemenu.MainActivity" > <com.example.mhy.qq50slidemenu.view.SlidingMenu android:id="@+id/sliding_menu" android:layout_width="wrap_content" android:layout_height="match_parent" mhy:rightPadding="100dp" > <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent"> <include layout="@layout/left_menu" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/qq" > <Button android:onClick="toggleMenu" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="切换菜单" /> </LinearLayout> </LinearLayout> </com.example.mhy.qq50slidemenu.view.SlidingMenu> </RelativeLayout>
package com.example.mhy.qq50slidemenu.view; 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.widget.HorizontalScrollView; import android.widget.LinearLayout; import com.example.mhy.qq50slidemenu.R; import com.nineoldandroids.view.ViewHelper; /** * Created by mhy on 2016/6/9. */ public class SlidingMenu extends HorizontalScrollView { private LinearLayout mWapper; private ViewGroup mMenu; private ViewGroup mContent; private int mScreenWidth; private int mMenuWidth; //dp private int mMenuRightPadding = 50; private boolean once; private boolean isOpen; /** * 未使用自定义属性时,调用 * @param context * @param attrs */ public SlidingMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } /** * 当使用了自定义属性时,会调用此构造方法 * @param context * @param attrs * @param defStyle */ public SlidingMenu(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); //获取我们定义的属性 TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyle, 0); int n = a.getIndexCount(); for(int i=0; i<n; i++){ int attr = a.getIndex(i); switch(attr){ case R.styleable.SlidingMenu_rightPadding: mMenuRightPadding = a.getDimensionPixelSize(attr, (int)TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 50, context .getResources().getDisplayMetrics())); break; } } a.recycle(); WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); mScreenWidth = outMetrics.widthPixels; } public SlidingMenu(Context context) { this(context, null); } /** * 设置子View的宽和高 设置自己的宽和高 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if(!once) { mWapper = (LinearLayout) getChildAt(0); mMenu = (ViewGroup) mWapper.getChildAt(0); mContent = (ViewGroup) mWapper.getChildAt(1); mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth - mMenuRightPadding; mContent.getLayoutParams().width = mScreenWidth; once = true; } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } /** * 通过设置偏移量,将menu隐藏 * 该方法在View重绘时调用 * 通过设置偏移量,将menu隐藏 * @param changed view是否改变 * @param l 相对于父布局左边的距离 * @param t 相对于父布局顶部的距离 * @param r 相对于父布局右边的距离 * @param b 相对于父布局底部的距离 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if(changed) { //是View滚动到指定位置,第一个参数为所要到达的x坐标,第二个参数为所要到达的y坐标 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); isOpen = false; } else { this.smoothScrollTo(0,0); isOpen = true; } return true; } return super.onTouchEvent(ev); } /** * 打开菜单 */ public void openMenu() { if(isOpen) { return; } this.smoothScrollTo(0, 0); isOpen = true; } public void closeMenu() { if(!isOpen) return; this.smoothScrollTo(mMenuWidth, 0); } /** * 切换菜单 */ public void toggle() { if(isOpen) { closeMenu(); } else { openMenu(); } } }
package com.example.mhy.qq50slidemenu; import android.app.Activity; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.Window; import com.example.mhy.qq50slidemenu.view.SlidingMenu; public class MainActivity extends Activity { private SlidingMenu mSlidingMenu; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题拦 setContentView(R.layout.activity_main); mSlidingMenu = (SlidingMenu) findViewById(R.id.sliding_menu); } public void toggleMenu(View view) { mSlidingMenu.toggle(); } }
实现抽屉式的菜单
在普通菜单的基础上修改SlidingMenu.java代码,修改后如下:
package com.example.mhy.qq50slidemenu.view; 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.widget.HorizontalScrollView; import android.widget.LinearLayout; import com.example.mhy.qq50slidemenu.R; import com.nineoldandroids.view.ViewHelper; /** * Created by mhy on 2016/6/9. */ public class SlidingMenu extends HorizontalScrollView { private LinearLayout mWapper; private ViewGroup mMenu; private ViewGroup mContent; private int mScreenWidth; private int mMenuWidth; //dp private int mMenuRightPadding = 50; private boolean once; private boolean isOpen; /** * 未使用自定义属性时,调用 * @param context * @param attrs */ public SlidingMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } /** * 当使用了自定义属性时,会调用此构造方法 * @param context * @param attrs * @param defStyle */ public SlidingMenu(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); //获取我们定义的属性 TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyle, 0); int n = a.getIndexCount(); for(int i=0; i<n; i++){ int attr = a.getIndex(i); switch(attr){ case R.styleable.SlidingMenu_rightPadding: mMenuRightPadding = a.getDimensionPixelSize(attr, (int)TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 50, context .getResources().getDisplayMetrics())); break; } } a.recycle(); WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); mScreenWidth = outMetrics.widthPixels; } public SlidingMenu(Context context) { this(context, null); } /** * 设置子View的宽和高 设置自己的宽和高 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if(!once) { mWapper = (LinearLayout) getChildAt(0); mMenu = (ViewGroup) mWapper.getChildAt(0); mContent = (ViewGroup) mWapper.getChildAt(1); mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth - mMenuRightPadding; mContent.getLayoutParams().width = mScreenWidth; once = true; } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } /** * 通过设置偏移量,将menu隐藏 * 该方法在View重绘时调用 * 通过设置偏移量,将menu隐藏 * @param changed view是否改变 * @param l 相对于父布局左边的距离 * @param t 相对于父布局顶部的距离 * @param r 相对于父布局右边的距离 * @param b 相对于父布局底部的距离 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if(changed) { //是View滚动到指定位置,第一个参数为所要到达的x坐标,第二个参数为所要到达的y坐标 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); isOpen = false; } else { this.smoothScrollTo(0,0); isOpen = true; } return true; } return super.onTouchEvent(ev); } /** * 打开菜单 */ public void openMenu() { if(isOpen) { return; } this.smoothScrollTo(0, 0); isOpen = true; } public void closeMenu() { if(!isOpen) return; this.smoothScrollTo(mMenuWidth, 0); } /** * 切换菜单 */ public void toggle() { if(isOpen) { closeMenu(); } else { openMenu(); } } /** * 滚动发生时 */ @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); float scale = l*1.0f/mMenuWidth; //调用属性动画,设置TranslationX ViewHelper.setTranslationX(mMenu, mMenuWidth*scale); } }
修改第二步中代码,即修改SlidingMenu.java代码,修改后如下:
package com.example.mhy.qq50slidemenu.view; 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.widget.HorizontalScrollView; import android.widget.LinearLayout; import com.example.mhy.qq50slidemenu.R; import com.nineoldandroids.view.ViewHelper; /** * Created by mhy on 2016/6/9. */ public class SlidingMenu extends HorizontalScrollView { private LinearLayout mWapper; private ViewGroup mMenu; private ViewGroup mContent; private int mScreenWidth; private int mMenuWidth; //dp private int mMenuRightPadding = 50; private boolean once; private boolean isOpen; /** * 未使用自定义属性时,调用 * @param context * @param attrs */ public SlidingMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } /** * 当使用了自定义属性时,会调用此构造方法 * @param context * @param attrs * @param defStyle */ public SlidingMenu(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); //获取我们定义的属性 TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyle, 0); int n = a.getIndexCount(); for(int i=0; i<n; i++){ int attr = a.getIndex(i); switch(attr){ case R.styleable.SlidingMenu_rightPadding: mMenuRightPadding = a.getDimensionPixelSize(attr, (int)TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 50, context .getResources().getDisplayMetrics())); break; } } a.recycle(); WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); mScreenWidth = outMetrics.widthPixels; } public SlidingMenu(Context context) { this(context, null); } /** * 设置子View的宽和高 设置自己的宽和高 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if(!once) { mWapper = (LinearLayout) getChildAt(0); mMenu = (ViewGroup) mWapper.getChildAt(0); mContent = (ViewGroup) mWapper.getChildAt(1); mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth - mMenuRightPadding; mContent.getLayoutParams().width = mScreenWidth; once = true; } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } /** * 通过设置偏移量,将menu隐藏 * 该方法在View重绘时调用 * 通过设置偏移量,将menu隐藏 * @param changed view是否改变 * @param l 相对于父布局左边的距离 * @param t 相对于父布局顶部的距离 * @param r 相对于父布局右边的距离 * @param b 相对于父布局底部的距离 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if(changed) { //是View滚动到指定位置,第一个参数为所要到达的x坐标,第二个参数为所要到达的y坐标 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); isOpen = false; } else { this.smoothScrollTo(0,0); isOpen = true; } return true; } return super.onTouchEvent(ev); } /** * 打开菜单 */ public void openMenu() { if(isOpen) { return; } this.smoothScrollTo(0, 0); isOpen = true; } public void closeMenu() { if(!isOpen) return; this.smoothScrollTo(mMenuWidth, 0); } /** * 切换菜单 */ public void toggle() { if(isOpen) { closeMenu(); } else { openMenu(); } } /** * 滚动发生时 */ @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:内容区域1.0 ~ 0.7 缩放效果scale : 1.0 ~ 0.0 0.7 + 0.3 * scale * 区别2:菜单的偏移量需要修改 * 区别3:菜单的显示时有缩放以及透明度变化,缩放: 0.7 ~ 1.0 1.0 - scale * 0.3 透明度 0.6 ~ 1.0 * 0.6 + 0.4 *(1-scale) */ 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); //设置content的缩放的中心点 ViewHelper.setPivotX(mContent, 0); ViewHelper.setPivotY(mContent, mContent.getHeight() / 2); ViewHelper.setScaleX(mContent, rightScale); ViewHelper.setScaleY(mContent, rightScale); } }
彷QQ5.0侧滑菜单(自定义控件--SlideMenu的实现)
标签:
原文地址:http://blog.csdn.net/ydxzmhy/article/details/51627637