Android tabhost+侧滑菜单

           项目已经告一段落,从中总结一下自己学习到的知识。一开始的项目ui框架采用的tabhost,在第一版完成时,老板提出加入侧滑菜单,其实就是将某一个tab页作为菜单滑出。现在网上有各种侧滑菜单的实现方法以及类库,但考虑到项目已经成型,实在不想改动ui框架,便在原来的基础上使用ValueAnimator来进行布局的移动来达到侧滑菜单的效果,如下截图。

   

    图中的“tab3”原本是一个tab页,现作为右侧菜单的触发按钮,当"tab3"选中时,底部的tab栏向左滑动(中间的内容区域保持不变),同时右侧菜单滑出。

TabActivity的布局代码如下

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        android:id="@+id/main">
        <LinearLayout
           android:layout_alignParentLeft="true"
             android:layout_width="match_parent"
    android:layout_height="match_parent"
   android:orientation="vertical" >
<TabHost
    android:id="@android:id/tabhost"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
   >
    <TabWidget
        android:id="@android:id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:visibility="gone" 
        android:background="@android:color/transparent"
       >
    </TabWidget>
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
  >
  <!-- 自定义的内容页,拦截右侧菜单事件的传递 -->
 <com.example.slidingmenu.ContentLayout
      android:layout_width="match_parent"
            android:layout_height="0dip"  
        android:layout_weight="1.0"
    android:orientation="vertical"
    android:id="@+id/content_layout">
        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="match_parent"
            android:layout_height="match_parent"       
>
        </FrameLayout>
  </com.example.slidingmenu.ContentLayout>
  <!--左布局-->   
        <LinearLayout
           android:id="@+id/left_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
           android:orientation="horizontal"
           android:layout_gravity="bottom"  
           >
  <RadioGroup
                android:id="@+id/radiogroupbutton"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="horizontal"
               >
                <RadioButton
                    android:id="@+id/tab1"
                    style="@style/maintabt"
                    android:text="tab1"
                   />
                <RadioButton
                    android:id="@+id/tab2"
                    style="@style/maintabt"
                     android:text="tab2"
                   />
                <CheckBox
                    android:id="@+id/tab3"
                    style="@style/maintabt"
                      android:text="tab3"
                   />
            </RadioGroup>           
        </LinearLayout>
      </LinearLayout> 
</TabHost>
</LinearLayout>
<!--右侧菜单-->
 <LinearLayout
            android:id="@+id/right_layout"
              android:layout_width="match_parent"
        android:layout_height="match_parent" 
        android:orientation="vertical"
       android:layout_alignParentRight="true"
      android:background="@android:color/black"
        android:visibility="gone">
        </LinearLayout>
        </RelativeLayout>

com.example.slidingmenu.ContentLayout   为自定义的tab内容页布局,由于当菜单滑出时,移动的只是底部的tab栏而不是整个tab页面的移动,即右侧菜单是覆盖在内容页上面,当右侧菜单上有点击事件时,事件会向下传递,会触发到内容页中相应的事件。故在右侧菜单滑出时,需要拦截右侧菜单事件的传递。

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
public class ContentLayout extends LinearLayout{
	private boolean isRight=false;//右侧菜单是否打开标识
	private float position;
	public float getPosition() {
		return position;
	}
	public void setPosition(float position) {
		this.position = position;
	}
	public boolean isRight() {
	return isRight;
	}
	public void setRight(boolean right) {
		this.isRight = right;
	}
	public ContentLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}
	public ContentLayout(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		if(isRight()){
			if(ev.getAction()== MotionEvent.ACTION_DOWN){
				  setPosition(ev.getX());//设置点击坐标,便于判断是点击的是否为非右侧菜单区域,若是,这将右侧菜单关闭
			}
			return true;//右菜单打开,拦截内容页上的点击事件
		}
		return false;
		
	}
}


在tabactivity中,使用ValueAnimator的子类ObjectAnimator,为需要移动的布局设置相应的动画属性

ValueAnimator bounceAnim = ObjectAnimator.ofFloat(rightLayout, "x",window_width, window_width/3).setDuration(300);//右侧菜单滑出
bounceAnim.setInterpolator(new LinearInterpolator());//匀速运动
bounceAnim.start();
右侧菜单关闭,只需调用
bounceAnim.reverse();

注意: ValueAnimator是在api 11才添加,低版本的api无法使用,对于低版本采用设置view在屏幕上的位置来实现,但在本例中未加入移动动画效果。
ValueAnimator
tabactivity的全部代码:
public class MainActivity extends TabActivity {
	    private final static int MOVE_DISTANCE = 80;

	    private float mPositionX;
		private RadioGroup group;
	
		private TabHost host;
	private ContentLayout contentLayout;//内容页即tab切换页
	    private Context  mContext;
	    private RadioButton tab1,tab2;
	    private CheckBox tab3;
	    private TabSpec spec1,spec2, spec3;
	    private RelativeLayout main;
	    private LinearLayout rightLayout;
	    private LinearLayout leftLayout;/*左边底部tab*/
	    private ValueAnimator bounceAnim = null,bouAnimator1=null;//api 11以上才能使用
		  private boolean isRight=false;//右边菜单是否滑出标志
			private int window_width;// 屏幕的宽度	
		@SuppressWarnings("deprecation")
		@Override
		protected void onCreate(Bundle savedInstanceState) {
			super.onCreate(savedInstanceState);
			setContentView(R.layout.activity_main);
			mContext=this;
			window_width=getResources().getDisplayMetrics().widthPixels;//获取手机屏幕宽
			main=(RelativeLayout) findViewById(R.id.main);
			rightLayout=(LinearLayout) findViewById(R.id.right_layout);
			leftLayout=(LinearLayout) findViewById(R.id.left_layout);
		
		        rightLayout.setOnTouchListener(mOnTouchListener);
		        main.setOnTouchListener(mOnTouchListener);
	
			host = getTabHost();
			tab1=(RadioButton) findViewById(R.id.tab1);
			tab2=(RadioButton) findViewById(R.id.tab2);
			tab3=(CheckBox) findViewById(R.id.tab3);
			tab3.setChecked(isRight);
		
			
			tab3.setOnClickListener(new OnClickListener() {
				public void onClick(View v) {
					initMoveLayout();
				}
			});
		contentLayout=(ContentLayout) findViewById(R.id.content_layout);
			group = (RadioGroup) findViewById(R.id.radiogroupbutton);
			spec1 =host.newTabSpec("tab1").setIndicator("tab1").setContent(
					new Intent(this, ContentActivity.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
		host.addTab(spec1);
		     host.setCurrentTab(0);
			group.setOnCheckedChangeListener(new OnCheckedChangeListener() {
				@SuppressLint("NewApi")
				@Override
				public void onCheckedChanged(RadioGroup group, int checkedId) {
					switch (checkedId) {
					case R.id.tab1://	
					host.setCurrentTabByTag("tab1");
						break;
					case R.id.tab2 :
						host.setCurrentTabByTag("tab2");
						break;
					}	
				}
				});
		}
		/**
		 * 右边菜单滑出
		 */
		@TargetApi(Build.VERSION_CODES.HONEYCOMB)
		@SuppressLint("NewApi")
		private void initMoveLayout() {
			//API level 11
			if(Build.VERSION.SDK_INT >= 11){
				if(!isRight){
				 	rightLayout.setVisibility(View.VISIBLE);
					 moveLayout();
				}else{
					isRight = false;
					bounceAnim.reverse();
					bouAnimator1.reverse();
				}
	        }else{
	        	if(!isRight){
	        	rightLayout.setVisibility(View.VISIBLE);
	       
	        	RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) leftLayout
						.getLayoutParams();
	        	layoutParams.leftMargin= -window_width+window_width/3;
				leftLayout.setLayoutParams(layoutParams);
				RelativeLayout.LayoutParams rightLayoutParams = (RelativeLayout.LayoutParams) rightLayout
						.getLayoutParams();
				rightLayoutParams.leftMargin=window_width/3;
				rightLayout.setLayoutParams(rightLayoutParams);
			
				  isRight = true;
				}else{
					
					RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) leftLayout
							.getLayoutParams();
		        	layoutParams.leftMargin=0;
					leftLayout.setLayoutParams(layoutParams);
					RelativeLayout.LayoutParams rightLayoutParams = (RelativeLayout.LayoutParams) rightLayout
							.getLayoutParams();
					rightLayoutParams.leftMargin= window_width;
					
					rightLayout.setLayoutParams(rightLayoutParams);
				
					  isRight = false;
				}    
	        }
			contentLayout.setRight(isRight);
		}
		@TargetApi(Build.VERSION_CODES.HONEYCOMB)
		@SuppressLint("NewApi")
		private void moveLayout() {
			if (bounceAnim == null) {
			bounceAnim = ObjectAnimator.ofFloat(rightLayout, "x",window_width, window_width/3).setDuration(300);
			bounceAnim.setInterpolator(new LinearInterpolator());//匀速运动
		}if(bouAnimator1==null){
			bouAnimator1 = ObjectAnimator.ofFloat(leftLayout, "x",0,-window_width+window_width/3).setDuration(300);
			bouAnimator1.setInterpolator(new LinearInterpolator());//匀速运动
		}
		bouAnimator1.start();
		bounceAnim.start();
		isRight = true;
		}
	    @Override
	    protected void onResume() {
	        super.onResume();
	    }
	    @Override
	    protected void onPause() {
	        super.onPause();
	    }
	    /**
	     * 滑动监听
	     * 若是右侧菜单上的touch事件,当手指移动距离在一定范围内,则关闭菜单
	     * 若是非右侧菜单上的touch事件,则关闭菜单
	     */
	    private OnTouchListener mOnTouchListener = new OnTouchListener() {
	        @Override
	        public boolean onTouch(View v, MotionEvent event) {
	        	
	        	  if(v.getId()==R.id.main){
	        		   if(event.getAction()==MotionEvent.ACTION_DOWN&&isRight&&(contentLayout.getPosition()<window_width/3)){
	        		   touchmove();
	        		   }	 
	        	   }
	            if (v.getId() == R.id.right_layout) {
	                int action = event.getAction();
	                switch (action) {
	                    case MotionEvent.ACTION_DOWN :
	                        mPositionX = event.getX();   
	                        break;
	                    case MotionEvent.ACTION_MOVE :
	                        final float currentX = event.getX();
	                        int des=(int) Math.abs(currentX - mPositionX);
	                        if(des>MOVE_DISTANCE&&isRight){
	                        	touchmove();
	                        }

	                     
	                        break;
	                }
	                return true;
	            } 
	            return false;
	        }
	    };
	    /**
	     * 隐藏右边菜单
	     */
		@SuppressLint("NewApi")
		@TargetApi(Build.VERSION_CODES.HONEYCOMB)
		private void touchmove() {
			//ValueAnimator  是在api 11才添加,低版本的api暂通过设置布局view在屏幕上的位置来实现,可自行加入动画
			if(Build.VERSION.SDK_INT >= 11){
			bounceAnim.reverse();
			bouAnimator1.reverse();
	  			}else{

				RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) leftLayout
						.getLayoutParams();
				layoutParams.leftMargin=0;
				leftLayout.setLayoutParams(layoutParams);
				RelativeLayout.LayoutParams rightLayoutParams = (RelativeLayout.LayoutParams) rightLayout
						.getLayoutParams();
				rightLayoutParams.leftMargin= window_width;
				
				rightLayout.setLayoutParams(rightLayoutParams);
				
			}
			isRight = false;
		tab3.setChecked(isRight);
			contentLayout.setRight(isRight);
		}
	  
	}


官网api说明:
public static ObjectAnimator ofFloat (Object target, String propertyName, float... values)
Added in  API level 11

Constructs and returns an ObjectAnimator that animates between float values. A single value implies that that value is the one being animated to. Two values imply a starting and ending values. More than two values imply a starting value, values to animate through along the way, and an ending value (these values will be distributed evenly across the duration of the animation).

Parameters
target The object whose property is to be animated. This object should have a public method on it called setName(), where name is the value of thepropertyName parameter.
propertyName The name of the property being animated.
values A set of values that the animation will animate between over time.
Returns
  • An ObjectAnimator object that is set up to animate between the given values.







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值