项目已经告一段落,从中总结一下自己学习到的知识。一开始的项目ui框架采用的tabhost,在第一版完成时,老板提出加入侧滑菜单,其实就是将某一个tab页作为菜单滑出。现在网上有各种侧滑菜单的实现方法以及类库,但考虑到项目已经成型,实在不想改动ui框架,便在原来的基础上使用ValueAnimator来进行布局的移动来达到侧滑菜单的效果,如下截图。
图中的“tab3”原本是一个tab页,现作为右侧菜单的触发按钮,当"tab3"选中时,底部的tab栏向左滑动(中间的内容区域保持不变),同时右侧菜单滑出。
TabActivity的布局代码如下
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();
ValueAnimator
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()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);
}
}
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).
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. |