本文介绍Android侧边栏的自定义实现,要实现手指在手机上向左或向右移动时,能相应的移动左右两个视图。通过自定义来实现,不借助第三方插件。实现的思路很简单,通过判断手指滑动的距离和速度来决定是否要滚动显示菜单项.(左边图片)
先来看看效果:
目录:
一、实现思路
二、代码清单
三、效果与说明
本文源码下载
------------------------------------------分割线------------------------------------------
具体下载目录在 /2015年资料/2月/18日/Android侧边栏的自定义实现(附源码)/
------------------------------------------分割线------------------------------------------
下面,让我们开始吧:
一、实现思路
1.思路
菜单在左,内容在右,然后菜单显示时和手机右边框有一定的间隔,内容显示一小部分。内容全部显示时,菜单全部不可见。如下面两个图
显示内容
显示菜单
2.判断逻辑
这是判断手指按着屏幕和手指抬起时要不要显示还是隐藏菜单
二、代码清单
首先来看下布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity" >
<LinearLayout
android:id="@+id/menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="@drawable/pn" >
</LinearLayout>
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="@drawable/sn">
</LinearLayout>
</LinearLayout>
接下来看看代码,都有注释:
package com.example.learningjava;
import com.example.learningjava.R.string;
import android.R.integer;
import android.R.menu;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.widget.LinearLayout.LayoutParams;
import android.app.Activity;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.widget.LinearLayout;
public class MainActivity extends Activity implements OnTouchListener{
private LinearLayout menuLayout;//菜单项
private LinearLayout contentLayout;//内容项
private LayoutParams menuParams;//菜单项目的参数
private LayoutParams contentParams;//内容项目的参数contentLayout的宽度值
private int disPlayWidth;//手机屏幕分辨率
private float xDown;//手指点下去的横坐标
private float xMove;//手指移动的横坐标
private float xUp;//记录手指上抬后的横坐标
private VelocityTracker mVelocityTracker; // 用于计算手指滑动的速度。
float velocityX;//手指左右移动的速度
public static final int SNAP_VELOCITY = 400; //滚动显示和隐藏menu时,手指滑动需要达到的速度。
private boolean menuIsShow = false;//初始化菜单项不可翙
private static final int menuPadding=80;//menu完成显示,留给content的宽度
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
initLayoutParams();
}
/**
*初始化Layout并设置其相应的参数
*/
private void initLayoutParams()
{
//得到屏幕的大小
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
disPlayWidth =dm.widthPixels;
//获得控件
menuLayout = (LinearLayout) findViewById(R.id.menu);
contentLayout = (LinearLayout) findViewById(R.id.content);
findViewById(R.id.layout).setOnTouchListener(this);
//获得控件参数
menuParams=(LinearLayout.LayoutParams)menuLayout.getLayoutParams();
contentParams = (LinearLayout.LayoutParams) contentLayout.getLayoutParams();
//初始化菜单和内容的宽和边距
menuParams.width = disPlayWidth - menuPadding;
menuParams.leftMargin = 0 - menuParams.width;
contentParams.width = disPlayWidth;
contentParams.leftMargin=0;
//设置参数
menuLayout.setLayoutParams(menuParams);
contentLayout.setLayoutParams(contentParams);
}
@Override
public boolean onTouch(View v, MotionEvent event)
{
acquireVelocityTracker(event);
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
xDown=event.getRawX();
break;
case MotionEvent.ACTION_MOVE:
xMove=event.getRawX();
isScrollToShowMenu();
break;
case MotionEvent.ACTION_UP:
xUp=event.getRawX();
isShowMenu();
releaseVelocityTracker();
break;
case MotionEvent.ACTION_CANCEL:
releaseVelocityTracker();
break;
}
return true;
}
/**
* 根据手指按下的距离,判断是否滚动显示菜单
*/
private void isScrollToShowMenu()
{
int distanceX = (int) (xMove - xDown);
if (!menuIsShow) {
scrollToShowMenu(distanceX);
}else{
scrollToHideMenu(distanceX);
}
}
/**
* 手指抬起之后判断是否要显示菜单
*/
private void isShowMenu()
{
velocityX =getScrollVelocity();
if(wantToShowMenu()){
if(shouldShowMenu()){
showMenu();
}else{
hideMenu();
}
}
else if(wantToHideMenu()){
if(shouldHideMenu()){
hideMenu();
}else{
showMenu();
}
}
}
/**
*想要显示菜单,当向右移动距离大于0并且菜单不可见
*/
private boolean wantToShowMenu(){
return !menuIsShow&&xUp-xDown>0;
}
/**
*想要隐藏菜单,当向左移动距离大于0并且菜单可见
*/
private boolean wantToHideMenu(){
return menuIsShow&&xDown-xUp>0;
}
/**
*判断应该显示菜单,当向右移动的距离超过菜单的一半或者速度超过给定值
*/
private boolean shouldShowMenu(){
return xUp-xDown>menuParams.width/2||velocityX>SNAP_VELOCITY;
}
/**
*判断应该隐藏菜单,当向左移动的距离超过菜单的一半或者速度超过给定值
*/
private boolean shouldHideMenu(){
return xDown-xUp>menuParams.width/2||velocityX>SNAP_VELOCITY;
}
/**
* 显示菜单栏
*/
private void showMenu()
{
new showMenuAsyncTask().execute(50);
menuIsShow=true;
}
/**
* 隐藏菜单栏
*/
private void hideMenu()
{
new showMenuAsyncTask().execute(-50);
menuIsShow=false;
}
/**
*指针按着时,滚动将菜单慢慢显示出来
*@param scrollX 每次滚动移动的距离
*/
private void scrollToShowMenu(int scrollX)
{
if(scrollX>0&&scrollX<= menuParams.width)
menuParams.leftMargin =-menuParams.width+scrollX;
menuLayout.setLayoutParams(menuParams);
}
/**
*指针按着时,滚动将菜单慢慢隐藏出来
*@param scrollX 每次滚动移动的距离
*/
private void scrollToHideMenu(int scrollX)
{
if(scrollX>=-menuParams.width&&scrollX<0)
menuParams.leftMargin=scrollX;
menuLayout.setLayoutParams(menuParams);
}
/**
* 创建VelocityTracker对象,并将触摸content界面的滑动事件加入到VelocityTracker当中。
* @param event 向VelocityTracker添加MotionEvent
*/
private void acquireVelocityTracker(final MotionEvent event) {
if(null == mVelocityTracker) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}
/**
* 获取手指在content界面滑动的速度。
* @return 滑动速度,以每秒钟移动了多少像素值为单位。
*/
private int getScrollVelocity() {
mVelocityTracker.computeCurrentVelocity(1000);
int velocity = (int) mVelocityTracker.getXVelocity();
return Math.abs(velocity);
}
/**
* 释放VelocityTracker
*/
private void releaseVelocityTracker() {
if(null != mVelocityTracker) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
/**
*
*:模拟动画过程,让肉眼能看到滚动的效果
*
*/
class showMenuAsyncTask extends AsyncTask<Integer, Integer, Integer>
{
@Override
protected Integer doInBackground(Integer... params)
{
int leftMargin = menuParams.leftMargin;
while (true)
{// 根据传入的速度来滚动界面,当滚动到达左边界或右边界时,跳出循环。
leftMargin += params[0];
if (params[0] > 0 && leftMargin > 0)
{
leftMargin= 0;
break;
} else if (params[0] < 0 && leftMargin <-menuParams.width)
{
leftMargin=-menuParams.width;
break;
}
publishProgress(leftMargin);
try
{
Thread.sleep(40);//休眠一下,肉眼才能看到滚动效果
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
return leftMargin;
}
@Override
protected void onProgressUpdate(Integer... value)
{
menuParams.leftMargin = value[0];
menuLayout.setLayoutParams(menuParams);
}
@Override
protected void onPostExecute(Integer result)
{
menuParams.leftMargin = result;
menuLayout.setLayoutParams(menuParams);
}
}
}
三、效果与说明