Android 高仿 QQ5.0 侧滑菜单效果 自定义控件来袭【学习鸿洋_视频博客笔记总结】

学习鸿洋博客:http://blog.csdn.net/lmj623565791/article/details/39257409

学习鸿洋视频:慕课网视频

看看Android 高仿 QQ5.0 侧滑菜单效果 自定义控件实现效果:


技术分享


技术上,继承HorizontalScrollView 加上自定义ViewGroup来实现:

1.onMeasure:决定内部View(子View)的宽和高,以及自己的宽和高
2.onLayout:决定子View的放置位置
3.onTouchEvent【监听动作】


自定义属性:
允许用户设置菜单离屏幕右边的边距
1.书写xml文件  values/attr.xml                2.在布局文件中进行使用,特别注意xmlns              3.在构造方法中(3个参数的构造方法)获得我们设置的值

效果实现步骤:用的自定义控件继承HorizontalScrollView  ,里面放了两个ViewGroup,分别放的是menu菜单和content内容,初始状态时候隐藏menu,左右滑动的时候设置隐藏的动画和透明度就行了。其中还要设置中心点。

下面看看自定义的空控件:

<span style="font-size:18px;">public class SlidingMenu extends HorizontalScrollView {
	//SlidingMenu里面肯定有个LinearLayout
	private LinearLayout mWapper;
	//菜单栏
	private ViewGroup mMenu;
	//内容区域
	private ViewGroup mContent;
	//获取屏幕宽度
	private int mScreenWidth;
	private int mMenuWidth;
	//dp
	private int mMenuRightPadding=50;
	private boolean once=false;
	private boolean isOpen;
	/**未使用定义的属性时,调用*/
	public SlidingMenu(Context context, AttributeSet attrs) {
		this(context, attrs,-1);
	}
	
	/**
	 * 当自定义了属性时会调用此构造方法
	 * */
	public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		//获取自定义的属性
  		TypedArray a=context.getTheme().obtainStyledAttributes(attrs,
 				R.styleable.SlidingMenu, defStyle, 0); //R.styleable.SlidingMenu
  		
  		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;
  			}
  		}
  		//TypedArray类用完后一定要recycle();
  		a.recycle();
		//获得屏幕宽度
		WindowManager vm=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
		DisplayMetrics outMetrics=new DisplayMetrics();
		vm.getDefaultDisplay().getMetrics(outMetrics);
		mScreenWidth=outMetrics.widthPixels;
		//把dp转化为px
	//	mMenuRightPadding=(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics());
		//把px转化为dp
		//mMenuRightPadding=(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 50, context.getResources().getDisplayMetrics());
	}
	
	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隐藏
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		if(changed){
			//这行代码就将Menu隐藏了
			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);
		isOpen=false;
	}
	/*
	 * 切换菜单
	 */
     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);
     
     /*
      * 区别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*0.7f);
        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);
        
     }
}
</span>


MainActivity:

<span style="font-size:18px;">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);
		//toggleMenu
		mLeftMenu=(SlidingMenu)findViewById(R.id.id_menu);
	}
	public void toggleMenu(View view){
		mLeftMenu.toggle();
	}
}
</span>

下面看看两个配置文件:

<span style="font-size:18px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:wfc="http://schemas.android.com/apk/res/com.imooc.slidignmenu"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/img_frame_background"
    >
    <com.imooc.slidignmenu.view.SlidingMenu 
        android:id="@+id/id_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        wfc:rightPadding="100dp"
        >
        <LinearLayout
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:orientation="horizontal"
             >	`
             <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.imooc.slidignmenu.view.SlidingMenu>

</RelativeLayout>
</span>


left_menu.xml

<span style="font-size:18px;"><?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"
     >
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:orientation="vertical"
    >
    <RelativeLayout
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
        >
        <ImageView 
            android:id="@+id/id_img1"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginLeft="20dp"
            android:layout_marginTop="20dp"
            android:layout_centerVertical="true"
            android:src="@drawable/img_1"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:textSize="20sp"
            android:layout_toRightOf="@id/id_img1"
            android:layout_centerVertical="true"
            android:text="第一个Item"
            android:textColor="#ffffff"
            
            /> 
    </RelativeLayout>
        <RelativeLayout
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
        >
        <ImageView 
            android:id="@+id/id_img2"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginLeft="20dp"
            android:layout_marginTop="20dp"
            android:layout_centerVertical="true"
            android:src="@drawable/img_2"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:textSize="20sp"
            android:layout_toRightOf="@id/id_img2"
            android:layout_centerVertical="true"
            android:text="第二个Item"
            android:textColor="#ffffff"
            
            /> 
    </RelativeLayout>
    <RelativeLayout
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
        >
        <ImageView 
            android:id="@+id/id_img3"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginLeft="20dp"
            android:layout_marginTop="20dp"
            android:layout_centerVertical="true"
            android:src="@drawable/img_3"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:textSize="20sp"
            android:layout_toRightOf="@id/id_img3"
            android:layout_centerVertical="true"
            android:text="第三个Item"
            android:textColor="#ffffff"
            /> 
    </RelativeLayout>
      <RelativeLayout
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
        >
        <ImageView 
            android:id="@+id/id_img4"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginLeft="20dp"
            android:layout_marginTop="20dp"
            android:layout_centerVertical="true"
            android:src="@drawable/img_4"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:textSize="20sp"
            android:layout_toRightOf="@id/id_img4"
            android:layout_centerVertical="true"
            android:text="第四个Item"
            android:textColor="#ffffff"
            
            /> 
    </RelativeLayout>
      <RelativeLayout
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
        >
        <ImageView 
            android:id="@+id/id_img5"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginLeft="20dp"
            android:layout_marginTop="20dp"
            android:layout_centerVertical="true"
            android:src="@drawable/img_5"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:textSize="20sp"
            android:layout_toRightOf="@id/id_img5"
            android:layout_centerVertical="true"
            android:text="第五个Item"
            android:textColor="#ffffff"
            /> 
    </RelativeLayout>
</LinearLayout>
</RelativeLayout></span>

attr.xml自定义属性:

<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="rightPadding" format="dimension"></attr>
    
    <declare-styleable name="SlidingMenu">
        <attr name="rightPadding"></attr>
    </declare-styleable>
</resources></span>



项目中学到的知识点:

1.去掉标题栏:

<span style="font-size:18px;">requestWindowFeature(Window.FEATURE_NO_TITLE);</span>


2.布局文件中自定义控件的名称:包名+类名:

<span style="font-size:18px;"><com.imooc.slidignmenu.view.SlidingMenu </span>

3.一个布局文件中引入另外一个布局文件:用<include   layout="">

<span style="font-size:18px;"><include layout="@layout/left_menu"/></span>

4.使用HorizontalScrollView  一定要记住四个方法: 三个构造方法      onMeasure方法      onLayout方法     onTouchEvent方法

    构造方法:

        1)主要获得自定义属性: 

<span style="font-size:18px;">              //获取自定义的属性
  		TypedArray a=context.getTheme().obtainStyledAttributes(attrs,
 				R.styleable.SlidingMenu, defStyle, 0); //R.styleable.SlidingMenu</span>
<span style="font-size:18px;">
</span>
<span style="font-size:18px;">               //TypedArray类用完后一定要recycle();
  <span style="white-space:pre">		</span>a.recycle();
</span>

       小知识点:获得屏幕宽度

<span style="font-size:18px;">               //获得屏幕宽度
		WindowManager vm=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
		DisplayMetrics outMetrics=new DisplayMetrics();
		vm.getDefaultDisplay().getMetrics(outMetrics);
		mScreenWidth=outMetrics.widthPixels;</span>

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

               一般只需要加载一次,测量一次,这个时候注意用一个变量来设置


         onLayout:决定子View的放置位置

<span style="font-size:18px;">/*
	 * 通过偏移量,将menu隐藏
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		if(changed){
			//这行代码就将Menu隐藏了
			this.scrollTo(mMenuWidth, 0);
		}
	}</span>

         onTouchEvent【监听动作】

<span style="font-size:18px;">@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);
	}</span>

这里使用了HorizonScollerView 的smoothScrollTo方法来设置隐藏或者显示。


5.  HorizontalScrollView 里面还有个onScrollChanged方法,这个方法监控了该空间活动时候的动作:可以在这个步骤里面实现动画与透明度改变

<span style="font-size:18px;">/*
      * 滚动发生时
      */
     @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);
     
     /*
      * 区别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*0.7f);
        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);
        
     }</span>

最后总结如下:

总结:一、自定义ViewGroup
1.构造方法的选择,获得一些需要用到的值
2.onMeasure 计算子View的宽和搞,以及设置自己的宽和搞
3.onLayout  决定子View的布局的位置
4.onTouchEvent【监控事件】
二、构造方法
1.context  new CustomViewGroup(context)
2.context、attr布局文件中声明(没有布局文件中的声明)
3.context,attr,defStyle(有自定义的属性)

三、自定义属性
1.attr.xml
2.布局文件中  xmlns
3.在三个参数的构造方法里面获得我们自定义的属性值

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。