天天看點

HorizontalScrollView+viewpager實作仿天天動聽ios版和QQ側邊菜單效果

最近用HorizontalScrollView弄了個和slidingmenu差不多的效果的側邊菜單,順便在裡面嵌套viewpager,效果類似天天動聽ios版和仿QQ側邊菜單欄,界面略戳,看圖

HorizontalScrollView+viewpager實作仿天天動聽ios版和QQ側邊菜單效果

1.    結構

最外層是一個HorizontalScrollView ,重寫了它裡面的一些方法進而實作劃出和收起菜單的效果

裡面是viewpager,能左右滑動和點選上面的文字切換。2.主要代碼

</pre><pre name="code" class="html">首頁布局xml和代碼
           
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.have.ingeniousmusicplayer.business.workmain.view.SlidingMenu
        android:id="@+id/sm_workmain_slidingmenu"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:background="@drawable/workmain_background" >

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

            <LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:background="@color/white"
                android:orientation="vertical" >

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

                <com.have.ingeniousmusicplayer.business.workmain.view.ViewPager
                    android:id="@+id/vp_workmain_container"
                    android:layout_width="fill_parent"
                    android:layout_height="0dp"
                    android:layout_weight="1" >
                </com.have.ingeniousmusicplayer.business.workmain.view.ViewPager>
            </LinearLayout>
            
            <include layout="@layout/layout_workmain_menu" />

        </LinearLayout>
    </com.have.ingeniousmusicplayer.business.workmain.view.SlidingMenu>

</LinearLayout>
           
<strong>
</strong>
           
package com.have.ingeniousmusicplayer.business.workmain;

import java.util.ArrayList;
import java.util.List;

import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.have.baselib.client.common.base.BaseFragmentActivity;
import com.have.ingeniousmusicplayer.MyApplication;
import com.have.ingeniousmusicplayer.R;
import com.have.ingeniousmusicplayer.business.workmain.frag.EqualizerFrag;
import com.have.ingeniousmusicplayer.business.workmain.frag.MyMusicFrag;
import com.have.ingeniousmusicplayer.business.workmain.frag.SearchFrag;
import com.have.ingeniousmusicplayer.business.workmain.frag.TransportFrag;
import com.have.ingeniousmusicplayer.business.workmain.view.SlidingMenu;
import com.have.ingeniousmusicplayer.service.QuickMenuFlowService;

/**   
 * 首頁
 *
 * @author hongdameng
 * @version: [版本号, 2014-12-31] 
 * 
 */
public class WorkMainActivity extends BaseFragmentActivity implements OnClickListener {
	
	private ViewPager vp_workmain_container;
	
	private FragmentPagerAdapter fragmentPagerAdapter;
	
	private List<Fragment> fragments = new ArrayList<Fragment>();
	
	private MyMusicFrag myMusicFrag;
	
	private EqualizerFrag equalizerFrag;
	
	private SearchFrag searchFrag ;
	
	private TransportFrag transportFrag;
	
	private SlidingMenu slidingMenu;
	
	/**四個tab的容器 */
	private LinearLayout llTabContainer;
	
	private TextView tvMymusic;
	
	private TextView tvEq;
	
	private TextView tvSearch;
	
	private TextView tvTransport;
	
	private LinearLayout llMyMusic;
	
	private LinearLayout llEq;
	
	private LinearLayout llSearch;
	
	private LinearLayout llTransport;
	
	private ImageView ivMenu;
	
	private int currentIndex;

	private boolean isClickTab;

	private int tabContainerWidth;

	private View ivTabLine;
	
	private float screenWidth;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_workmain);
		initView();
		initTabLine();
		initData();
		
		initService();
	}


	private void initView() {
		llTabContainer = (LinearLayout) findViewById(R.id.ll_workmain_tab_container);
		vp_workmain_container = (ViewPager) findViewById(R.id.vp_workmain_container);
		tvMymusic = (TextView) findViewById(R.id.tv_workmain_head_tab_mymusic);
		tvEq = (TextView) findViewById(R.id.tv_workmain_head_tab_eq);
		tvSearch = (TextView) findViewById(R.id.tv_workmain_head_tab_search);
		tvTransport = (TextView) findViewById(R.id.tv_workmain_head_tab_transport);
		
		llMyMusic = (LinearLayout) findViewById(R.id.ll_workmain_head_tab_mymusic);
		llEq = (LinearLayout) findViewById(R.id.ll_workmain_head_tab_eq);
		llSearch = (LinearLayout) findViewById(R.id.ll_workmain_head_tab_search);
		llTransport = (LinearLayout) findViewById(R.id.ll_workmain_head_tab_transport);
		
		ivMenu = (ImageView) findViewById(R.id.iv_workmain_menu);
		ivMenu.setOnClickListener(this);
		llMyMusic.setOnClickListener(this);
		llEq.setOnClickListener(this);
		llSearch.setOnClickListener(this);
		llTransport.setOnClickListener(this);
		
		slidingMenu = (SlidingMenu) findViewById(R.id.sm_workmain_slidingmenu);
	}
	
	private void initData() {
		myMusicFrag = new MyMusicFrag();
		equalizerFrag = new EqualizerFrag();
		searchFrag = new SearchFrag();
		transportFrag = new TransportFrag();
		
		fragments.add(myMusicFrag);
		fragments.add(equalizerFrag);
		fragments.add(searchFrag);
		fragments.add(transportFrag);
		
		fragmentPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
			
			@Override
			public int getCount() {
				return fragments.size();
			}
			
			@Override
			public Fragment getItem(int arg0) {
				return fragments.get(arg0);
			}
		};
		
		vp_workmain_container.setAdapter(fragmentPagerAdapter);
		
		vp_workmain_container.setOnPageChangeListener(new MyOnPageChangeListener());
	}
	

	private void initService() {
//		Intent intent = new Intent(getApplicationContext(), QuickMenuFlowService.class);
//		startService(intent);
		
	}
	
	private class MyOnPageChangeListener implements OnPageChangeListener{


		@Override
		public void onPageSelected(int position) {
			resetTextColor();
			switch (position) {
			case 0:
				tvMymusic.setTextColor(getResources().getColor(
						R.color.blue_price));
				MyApplication.workmainCurrentPage = 0;
				MyApplication.isCanControlMenu = false;
				
				break;
			case 1:
				tvEq.setTextColor(getResources().getColor(
						R.color.blue_price));
				MyApplication.workmainCurrentPage = 1;
				MyApplication.isCanControlMenu = false;
				
				break;
			case 2:
				tvSearch.setTextColor(getResources().getColor(
						R.color.blue_price));
				MyApplication.workmainCurrentPage = 2;
				MyApplication.isCanControlMenu = false;
				
				break;
			case 3:
				tvTransport.setTextColor(getResources().getColor(
						R.color.blue_price));
				MyApplication.workmainCurrentPage = 3;
				
				break;
			}

			currentIndex = position;
		}

		@Override
		public void onPageScrolled(int position, float positionOffset,
				int positionOffsetPixels) {
			if (isClickTab) {
				isClickTab = false;
				return;
			}
			
			float trueOffset = positionOffset * (tabContainerWidth/screenWidth);
			
			LinearLayout.LayoutParams layoutParams = (android.widget.LinearLayout.LayoutParams) ivTabLine
					.getLayoutParams();
			if (currentIndex == 0 && position == 0) {
				layoutParams.leftMargin = (int) (trueOffset * (tabContainerWidth * 1.0 / 4) + currentIndex * (tabContainerWidth / 4)) + tabContainerWidth / 16;
				ivTabLine.setLayoutParams(layoutParams);

			} else if (currentIndex == 1 && position == 0) {  // 從位置1平移到位置0
				layoutParams.leftMargin = (int) (-(1 - trueOffset) * (tabContainerWidth * 1.0 / 4) + currentIndex * (tabContainerWidth / 4)) + tabContainerWidth / 16;
				ivTabLine.setLayoutParams(layoutParams);
			} else if(currentIndex == 1 && position == 1){
				layoutParams.leftMargin = (int) (trueOffset * (tabContainerWidth * 1.0 / 4) + currentIndex * (tabContainerWidth / 4)) + tabContainerWidth / 16;
				ivTabLine.setLayoutParams(layoutParams);
			} else if(currentIndex == 2 && position == 1){
				layoutParams.leftMargin = (int) (-(1 - trueOffset) * (tabContainerWidth * 1.0 / 4) + currentIndex * (tabContainerWidth / 4)) + tabContainerWidth / 16;
				ivTabLine.setLayoutParams(layoutParams);
			} else if(currentIndex == 2 && position == 2){
				layoutParams.leftMargin = (int) (trueOffset * (tabContainerWidth * 1.0 / 4) + currentIndex * (tabContainerWidth / 4)) + tabContainerWidth / 16;
				ivTabLine.setLayoutParams(layoutParams);
			} else if(currentIndex == 2 && position == 3){
				layoutParams.leftMargin = (int) (trueOffset * (tabContainerWidth * 1.0 / 4) + currentIndex * (tabContainerWidth / 4)) + tabContainerWidth / 16;
				ivTabLine.setLayoutParams(layoutParams);
			} else if(currentIndex == 3 && position == 2){
				layoutParams.leftMargin = (int)(-(1 - trueOffset) * (tabContainerWidth * 1.0 / 4) + currentIndex * (tabContainerWidth / 4)) + tabContainerWidth / 16;
				ivTabLine.setLayoutParams(layoutParams);
			} else if(currentIndex == 3 && position == 3){
				layoutParams.leftMargin = (int) (trueOffset * (tabContainerWidth * 1.0 / 4) + currentIndex * (tabContainerWidth / 4)) + tabContainerWidth / 16;
				ivTabLine.setLayoutParams(layoutParams);
			}
				
		}

		@Override
		public void onPageScrollStateChanged(int state) {
		}
	
	}
	
	
	
	private void initTabLine() {
		int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED); 
		int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED); 
		ivMenu.measure(w, h); 
		int menuWidth =ivMenu.getMeasuredWidth();
		
		ivTabLine = (ImageView) findViewById(R.id.iv_workmain_tabline);
		DisplayMetrics outMetrics = new DisplayMetrics();
		getWindow().getWindowManager().getDefaultDisplay()
				.getMetrics(outMetrics);
		screenWidth = outMetrics.widthPixels;
		tabContainerWidth = (int) (screenWidth  - menuWidth);
		LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) ivTabLine
				.getLayoutParams();
		lp.width = tabContainerWidth / 8;
		ivTabLine.setLayoutParams(lp);
	}
	
	/**
	 * 重置顔色
	 */
	protected void resetTextColor() {
		tvMymusic.setTextColor(getResources().getColor(R.color.gray_cccccc));
		tvEq.setTextColor(getResources().getColor(R.color.gray_cccccc));
		tvSearch.setTextColor(getResources().getColor(R.color.gray_cccccc));
		tvTransport.setTextColor(getResources().getColor(R.color.gray_cccccc));
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.iv_workmain_menu:
			slidingMenu.toggle();
			break;
		case R.id.ll_workmain_head_tab_mymusic:
			vp_workmain_container.setCurrentItem(0);
			break;
		case R.id.ll_workmain_head_tab_eq:
			vp_workmain_container.setCurrentItem(1);
			break;
		case R.id.ll_workmain_head_tab_search:
			vp_workmain_container.setCurrentItem(2);
			break;
		case R.id.ll_workmain_head_tab_transport:
			vp_workmain_container.setCurrentItem(3);
			break;
		default:
			break;
		}
	}
	
	@Override
	protected void onDestroy() {
		MyApplication.isCanControlMenu = false;
		super.onDestroy();
	}
}
           

重寫HorizontalScrollView代碼

package com.have.ingeniousmusicplayer.business.workmain.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

import com.have.ingeniousmusicplayer.MyApplication;
import com.have.ingeniousmusicplayer.common.utils.ScreenUtils;
import com.nineoldandroids.view.ViewHelper;

/**
 * <菜單側邊欄>
 * 
 * @author hongdameng
 * @version: [版本号, 2015-1-4]
 * 
 */
public class SlidingMenu extends HorizontalScrollView {

	/**
	 * 螢幕寬度
	 */
	private int mScreenWidth;
	/**
	 * dp
	 */
	private int mMenuRightPadding;
	/**
	 * 菜單的寬度
	 */
	private int mMenuWidth;
	private int mHalfMenuWidth;

	private boolean isOpen;
	
	private boolean once;

	private ViewGroup mMenu;
	private ViewGroup mContent;
	
	public SlidingMenu(Context context, AttributeSet attrs) {
		this(context, attrs, 0);

	}

	public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		mScreenWidth = ScreenUtils.getScreenWidth(context);

		mMenuRightPadding = mScreenWidth / 3;
	}

	public SlidingMenu(Context context) {
		this(context, null, 0);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		/**
		 * 顯示的設定一個寬度
		 */
		if (!once) {
			LinearLayout wrapper = (LinearLayout) getChildAt(0);
			mContent = (ViewGroup) wrapper.getChildAt(0);
			mMenu = (ViewGroup) wrapper.getChildAt(1);

			mMenuWidth = mScreenWidth - mMenuRightPadding;
			mHalfMenuWidth = mMenuWidth / 2;
			mMenu.getLayoutParams().width = mMenuWidth;
			mContent.getLayoutParams().width = mScreenWidth;

		}
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		if (changed) {
			// 将菜單隐藏
			this.scrollTo(0, 0);
			once = true;
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		int action = ev.getAction();
		switch (action) {
		case MotionEvent.ACTION_UP:
			int scrollX = getScrollX();
			if (scrollX > mHalfMenuWidth) {
				this.smoothScrollTo(mMenuWidth, 0);
				isOpen = true;
			} else {
				this.smoothScrollTo(0, 0);
				 MyApplication.isOpenByClick = false;
				isOpen = false;
			}
			return true;
		}
		return super.onTouchEvent(ev);
	}

	/**
	 * 打開菜單
	 */
	public void openMenu() {
		MyApplication.isOpenByClick = true;
		if (isOpen)
			return;
		this.smoothScrollTo(mMenuWidth, 0);
		isOpen = true;
	}

	/**
	 * 關閉菜單
	 */
	public void closeMenu() {
		if (isOpen) {
			this.smoothScrollTo(0, 0);
			MyApplication.isOpenByClick = false;
			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;
		float leftScale = 1 - 0.3f * scale;
		float rightScale = 0.8f + scale * 0.2f;

		ViewHelper.setScaleX(mContent, leftScale);
		ViewHelper.setScaleY(mContent, leftScale);
		ViewHelper.setTranslationX(mContent, -mContent.getWidth() * scale * 0.01f);

		ViewHelper.setAlpha(mMenu, 0.6f + 0.4f * scale);
		ViewHelper.setPivotX(mMenu, 0);
		ViewHelper.setPivotY(mMenu, mMenu.getHeight() / 2);
		ViewHelper.setScaleX(mMenu, rightScale);
		ViewHelper.setScaleY(mMenu, rightScale);

	}
	
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		int action = ev.getAction();
		switch (action) {
		case MotionEvent.ACTION_DOWN:
			MyApplication.downXPosition = ev.getX();
			
		case MotionEvent.ACTION_MOVE:
			if(MyApplication.workmainCurrentPage == 3 && ev.getX() > MyApplication.downXPosition && ! isOpen){
				MyApplication.isCanControlMenu = false;
			}
		}
		
		return super.onInterceptTouchEvent(ev) && (MyApplication.isCanControlMenu || MyApplication.isOpenByClick);
	}
	
}
           

重寫 viewpager 代碼

/*  
 * 檔案名:     ViewPager.java 
 * 檔案描述: ()
 * 修改人:     [hongdameng] 
 * 生成時間:  2015-1-5 下午1:56:59 
 * 修改記錄:
 */
package com.have.ingeniousmusicplayer.business.workmain.view;

import com.have.ingeniousmusicplayer.MyApplication;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;

/**
 * <首頁viewpager>
 * 
 * @author hongdameng
 * @version: [版本号, 2015-1-5]
 * 
 */
public class ViewPager extends android.support.v4.view.ViewPager {

	public ViewPager(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			MyApplication.downXPosition = ev.getX();
			return super.onTouchEvent(ev);
		case MotionEvent.ACTION_MOVE:
			if (this.getCurrentItem() == 3 && MyApplication.downXPosition > ev.getX()) {
				MyApplication.isCanControlMenu = true;
				return super.onTouchEvent(ev);
			} else {
				return super.onTouchEvent(ev);
			}

		default:
			return super.onTouchEvent(ev);
		}
	}

}
           
</pre><pre name="code" class="html">
           

為了在頁面切換過程中判斷是否要打開菜單,我在application儲存了一些狀态

public class MyApplication extends BaseApplication {
	public static int workmainCurrentPage = 0;
	
	public static boolean isCanControlMenu = false;
	
	public static float downXPosition = 0;
	
	public static boolean isOpenByClick = false;
	
	@Override
	public void onCreate() {
		super.onCreate();
		
		initService();
	}

	/** 
	 * 初始化服務
	 */
	private void initService() {
		//TODO
		workmainCurrentPage = 0; //可以通過讀取 SharedPreferences 設定目前頁
	}
}
           

1. 原理

重寫HorizontalScrollView ,實作劃出和收起菜單和QQ類似的效果,在使用者滑動再擡起手的時候判斷露出菜單的偏移量來打開或者關閉菜單,在viewpager滑動過程中,如果是滑到最後一頁,再向左滑動機打開菜單,裡面的狀态都是通過application儲存着。