自定义Android侧滑菜单控件

package com.tenghu.sideslipmenu.view;

import android.content.Context;

import android.os.AsyncTask;

import android.util.AttributeSet;

import android.util.DisplayMetrics;

import android.view.MotionEvent;

import android.view.VelocityTracker;

import android.view.View;

import android.view.WindowManager;

import android.widget.LinearLayout;

/**

* Created by Arvin_Li on 2014/11/18.

*/

public class SideslipMenuView extends LinearLayout {

//滚动显示和隐藏menu时,手指滑动需要达到的速度

private static final int SNAP_VELOCITY = 200;

private View mMenu;//菜单布局

private View mContent;//主内容布局

private int mScreenWidth;//屏幕宽度

private int mMenuWidth;//菜单宽度

//menu最多可以滑动到的左边缘,值由menu布局的宽度来定

private int leftEdge;

//menu最多可以滑到的有边缘,值恒为0

private int rightEdge = 0;

//menu完全显示时,留给content的宽度值

private int toRightPaddingWidth = 50;

//menu布局的参数,通过此参数来更改leftMargin的值

private LinearLayout.LayoutParams menuParams;

//记录手指按下的横坐标

private float xDown;

//记录手指抬起的横坐标

private float xUp;

//记录手指移动的横坐标

private float xMove;

//当前menu是显示还是隐藏,只有menu完全显示和隐藏才会改变该值,滑动时不会改变

private boolean isMenuVisible;

//用于计算手指滑动的速度

private VelocityTracker mVelocityTracker;

private boolean once;

public SideslipMenuView(Context context) {

super(context);

init(context);

}

public SideslipMenuView(Context context, AttributeSet attrs) {

super(context, attrs);

init(context);

}

/**

* 初始化

*/

private void init(Context context) {

//获取WindowManager对象

WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

DisplayMetrics displayMetrics = new DisplayMetrics();

windowManager.getDefaultDisplay().getMetrics(displayMetrics);

mScreenWidth = displayMetrics.widthPixels;//获取屏幕宽度

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

if (!once) {

mMenu = getChildAt(0);//获取菜单布局

mContent = getChildAt(1);//获取内容布局

menuParams = (LayoutParams) mMenu.getLayoutParams();//获取菜单参数

mMenuWidth = menuParams.width = mScreenWidth - toRightPaddingWidth;//设置菜单宽度

//左边缘的值赋值为menu宽度的负数

leftEdge = -menuParams.width;

//menu的leftMargin设置为左边缘的值,默认菜单不可见

menuParams.leftMargin = leftEdge;

mContent.getLayoutParams().width = mScreenWidth;//设置内容宽度

once = true;

}

}

@Override

public boolean onTouchEvent(MotionEvent event) {

createVelocityTracker(event);//创建VelocityTracker对象

switch (event.getAction()) {

//手指按下

case MotionEvent.ACTION_DOWN:

xDown = event.getRawX();//记录横坐标

break;

//手指移动

case MotionEvent.ACTION_MOVE:

//手指移动时,对比按下的横坐标,计算出移动的距离来调整menu的leftMargin值,从而显示和隐藏menu

xMove = event.getRawX();

//计算获取到距离

int distanceX = (int) (xMove - xDown);

if (isMenuVisible) {

menuParams.leftMargin = distanceX;

} else {

menuParams.leftMargin = leftEdge + distanceX;

}

//如果菜单的左边界小于了可以滑动的左边界

if (menuParams.leftMargin <= leftEdge) {

//将可以滑动的左边界赋值给菜单左边界

menuParams.leftMargin = leftEdge;

} else if (menuParams.leftMargin >= rightEdge) {

menuParams.leftMargin = rightEdge;

}

mMenu.setLayoutParams(menuParams);//设置菜单参数

break;

//手指抬起

case MotionEvent.ACTION_UP:

//手指抬起时,进行判断当前手势的意图,从而决定是滚动到menu界面,还是滚动到content界面

xUp = event.getRawX();

if (wantToShowMenu()) {

if (shouldScrollToMenu()) {

scrollToMenu();

} else {

scrollToContent();

}

} else if (wantToShowContent()) {

if (shouldScrollToContent()) {

scrollToContent();

} else {

scrollToMenu();

}

}

//手指抬起是回收VelocityTracker对象

recycleVelocityTracker();

break;

}

return true;

}

/**

* 创建VelocityTracker对象,并将触摸content界面的滑动时间加入到VelocityTracker中

*

* @param event

*/

private void createVelocityTracker(MotionEvent event) {

if (null == mVelocityTracker) {

mVelocityTracker = VelocityTracker.obtain();//初始化VelocityTracker

}

mVelocityTracker.addMovement(event);//添加界面滑动事件

}

/**

* 判断当前手势的意图是不是想显示content,如果手指移动的距离是负数,且当前menu是可见的,则认为当前手势是想要显示content。

*

* @return

*/

private boolean wantToShowContent() {

return xUp - xDown < 0 && isMenuVisible;

}

/**

* 判断当前手势的意图是不是想显示menu,如果手指移动的距离是正数,且当前menu是不可见,则认为当前手势是显示menu

*

* @return

*/

private boolean wantToShowMenu() {

return xUp - xDown > 0 && !isMenuVisible;

}

/**

* 判断是否应该滚动将menu展示出来,如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,就认为应该滚动将menu展示出来。

*

* @return 如果应该滚动将menu展示出来返回true,否则返回false。

*/

private boolean shouldScrollToMenu() {

return xUp - xDown > mMenuWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;

}

/**

* 判断是否应该滚动将content展示出来,如果手指移动距离加上menuPadding大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY, 就认为应该滚动将content展示出来。

*

* @return 如果应该滚动将content展示出来返回true,否则返回false。

*/

private boolean shouldScrollToContent() {

//这里菜单状态为显示,如果要菜单隐藏,那么手势是从右到左滑动,这里手指按下的横坐标就会比抬起时的横坐标小

return xDown - xUp + toRightPaddingWidth > mMenuWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;

}

/**

* 获取手指在content上滑动的速度

*

* @return 滑动速度,以每秒钟移动了多少像素为单位

*/

private int getScrollVelocity() {

mVelocityTracker.computeCurrentVelocity(1000);

int velocity = (int) mVelocityTracker.getXVelocity();//获取x方向的速度值

return Math.abs(velocity);

}

/**

* 回收VelocityTracker对象

*/

private void recycleVelocityTracker() {

if (null != mVelocityTracker) {

mVelocityTracker.recycle();

mVelocityTracker = null;

}

}

/**

* 将屏幕滚动到menu界面,设置滚动速度为30

*/

private void scrollToMenu() {

isMenuVisible = true;

new ScrollTask().execute(30);

}

/**

* 将屏幕滚动到content界面,设置滚动速度为-30

*/

private void scrollToContent() {

isMenuVisible = false;

new ScrollTask().execute(-30);

}

/**

* 创建滚动任务类

*/

class ScrollTask extends AsyncTask<Integer, Integer, Integer> {

@Override

protected Integer doInBackground(Integer... speed) {

int leftMargin = menuParams.leftMargin;

//根据传入的速度来滚动界面,当滚动到达左边界或右边界时跳出循环

while (true) {

leftMargin = leftMargin + speed[0];

if (leftMargin > rightEdge) {

leftMargin = rightEdge;

break;

}

if (leftMargin < leftEdge) {

leftMargin = leftEdge;

break;

}

//更新任务进度,会把值传入到onProgressUpdate()方法中进行UI的更新

publishProgress(leftMargin);

//为了要有滚动效果产生,每次循环使线程睡眠20毫秒

sleep(20);

}

return leftMargin;

}

/**

* 这里的Intege参数对应AsyncTask中的第二个参数

* 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行

* onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作

*

* @param leftMargin

*/

@Override

protected void onProgressUpdate(Integer... leftMargin) {

menuParams.leftMargin = leftMargin[0];

mMenu.setLayoutParams(menuParams);

}

/**

* 执行异步结束,接收doInBackground()方法的返回值,接收到的返回值对应AsyncTask<Integer, Integer, Integer> 第3个参数

*

* @param leftMargin

*/

@Override

protected void onPostExecute(Integer leftMargin) {

menuParams.leftMargin = leftMargin;

mMenu.setLayoutParams(menuParams);

}

}

/**

* 是当前线程睡眠指定的毫秒数

*

* @param millis

*/

private void sleep(long millis) {

try {

Thread.sleep(millis);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

以上就是整个自定义侧滑菜单控件的代码,在布局文件直接使用即可如下:

<?xml version="1.0" encoding="utf-8"?>

<com.tenghu.sideslipmenu.view.SideslipMenuView xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@drawable/bg_01">

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

<LinearLayout

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@drawable/bg_02"

android:orientation="vertical">

</LinearLayout>

</com.tenghu.sideslipmenu.view.SideslipMenuView>

其中include引用的就是一个菜单布局文件,如下:

<?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"

android:gravity="center_vertical">

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_marginLeft="30dp"

android:orientation="vertical">

<RelativeLayout

android:layout_width="wrap_content"

android:layout_height="50dp"

android:layout_marginBottom="10dp">

<ImageView

android:id="@+id/iv_img_01"

android:layout_width="50dp"

android:layout_height="match_parent"

android:src="@drawable/app_01" />

<TextView

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_marginLeft="10dp"

android:layout_toRightOf="@id/iv_img_01"

android:gravity="center_vertical"

android:text="第一个Item" />

</RelativeLayout>

<RelativeLayout

android:layout_width="wrap_content"

android:layout_height="50dp"

android:layout_marginBottom="10dp">

<ImageView

android:id="@+id/iv_img_02"

android:layout_width="50dp"

android:layout_height="match_parent"

android:src="@drawable/app_02" />

<TextView

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_marginLeft="10dp"

android:layout_toRightOf="@id/iv_img_02"

android:gravity="center_vertical"

android:text="第二个Item" />

</RelativeLayout>

<RelativeLayout

android:layout_width="wrap_content"

android:layout_height="50dp"

android:layout_marginBottom="10dp">

<ImageView

android:id="@+id/iv_img_03"

android:layout_width="50dp"

android:layout_height="match_parent"

android:src="@drawable/app_03" />

<TextView

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_marginLeft="10dp"

android:layout_toRightOf="@id/iv_img_03"

android:gravity="center_vertical"

android:text="第三个Item" />

</RelativeLayout>

<RelativeLayout

android:layout_width="wrap_content"

android:layout_height="50dp"

android:layout_marginBottom="10dp">

<ImageView

android:id="@+id/iv_img_04"

android:layout_width="50dp"

android:layout_height="match_parent"

android:src="@drawable/app_04" />

<TextView

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_marginLeft="10dp"

android:layout_toRightOf="@id/iv_img_04"

android:gravity="center_vertical"

android:text="第四个Item" />

</RelativeLayout>

<RelativeLayout

android:layout_width="wrap_content"

android:layout_height="50dp"

android:layout_marginBottom="10dp">

<ImageView

android:id="@+id/iv_img_05"

android:layout_width="50dp"

android:layout_height="match_parent"

android:src="@drawable/app_05" />

<TextView

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_marginLeft="10dp"

android:layout_toRightOf="@id/iv_img_05"

android:gravity="center_vertical"

android:text="第五个Item" />

</RelativeLayout>

</LinearLayout>

</RelativeLayout>

其中的菜单可以使用ListView去布局,这里做测试就没有去使用了

时间: 2024-10-15 06:18:20

自定义Android侧滑菜单控件的相关文章

iOS开发系列之常用自定义控件开发集—自定义UITableViewCell侧滑菜单控件开发

在很多app中就有UITableViewCell左滑出现菜单如系统删除按钮,但是系统的只能有一个,有很多需求需要个性化不仅可以放文字还可以放按钮修改背景色创建多个菜单项,那么系统提供的肯定不适合,所以我们需要自己手工打造. 直接上代码如下: WHC_MenuCell.h 头文件如下: // // WHC_MenuCell.m // WHC_MenuCell // // Created by 吴海超 on 15/4/3. // Copyright (c) 2015年 Sinosun Technol

两种主流的实现侧滑菜单控件的学习总结

第一次开始研究侧滑菜单实现还是QQ加入这个功能之后吸引的我,当时就觉得这个侧滑菜单的想法简直独具匠心,使用侧滑菜单的好处必须明显,它可以无形的使我们的屏幕利用更大化,你可以假想自己的屏幕比现实还要大,那个侧滑菜单就藏在看不见的屏幕里,当我们用侧滑手势之后,就将它们从看不见的屏幕里拉入真实屏幕中,当我们使用完菜单后,又可以将它们收回到看不见的屏幕中...这让本来空间就有限的手机屏幕,屏幕利用率得到大大的提高.后来通过自己在网络博客论坛的寻觅,发现了两种比较便捷的能帮助我们实现侧滑菜单效果的控件:

自定义android侧滑菜单

这里实现两种侧滑菜单效果,第一种拖拽内容部分,菜单像是被拖出来的感觉的这种效果,第二种是拖拽内容部分,菜单在内容后面不动,感觉有一种层次感的效果,如下 第一种效果的代码实现如下: package com.tenghu.customsideslip.menu.view; import android.content.Context; import android.os.AsyncTask; import android.util.AttributeSet; import android.util.

Android 自定义View修炼-打造完美的自定义侧滑菜单/侧滑View控件(转)

一.概述 在App中,经常会出现侧滑菜单,侧滑滑出View等效果,虽然说Android有很多第三方开源库,但是实际上 咱们可以自己也写一个自定义的侧滑View控件,其实不难,主要涉及到以下几个要点: 1.对Android中Window类中的DecorView有所了解 2.对Scroller类实现平滑移动效果 3.自定义ViewGroup的实现 首先来看看效果图吧:    下面现在就来说说这里咱们实现侧滑View的基本思路吧,这里我采用的是自定义一个继承于RelativeLayout的控件叫做XC

Android侧滑菜单DrawerLayout

侧滑菜单控件DrawerLayout是Support Library包中实现了侧滑菜单效果的控件,也许是因为第三方控件如MenuDrawer等的出现之后,Google借鉴而出现的产物.DrawerLayout分为侧边菜单和主内容两部分,侧边菜单可以根据手势展开与隐藏(DrawerLayout自身特性),主内容区的内容可以随着菜单的点击而变化,内容就要自己去实现啦. 下面的例子主要是根据官方文档移植过来的,简单的改动: -----------------------界面布局------------

android - 自定义(组合)控件 + 自定义控件外观

转载:http://www.cnblogs.com/bill-joy/archive/2012/04/26/2471831.html android - 自定义(组合)控件 + 自定义控件外观 Android自定义View实现很简单 继承View,重写构造函数.onDraw,(onMeasure)等函数. 如果自定义的View需要有自定义的属性,需要在values下建立attrs.xml.在其中定义你的属性. 在使用到自定义View的xml布局文件中需要加入xmlns:前缀="http://sc

Android自定义View之组合控件 ---- LED数字时钟

先上图 LEDView效果如图所示. 之前看到一篇博客使用两个TextView实现了该效果,于是我想用自定义控件的方式实现一个LEDView,使用时即可直接使用该控件. 采用组合控件的方式,将两个TextView叠放在一起,再使用digital-7.ttf字体来显示数据,从而达到LED的效果.代码如下: LEDView.class package ione.zy.demo; import java.io.File; import java.util.Calendar; import java.u

Android 重写系统控件UI,自定义进度条

Android的系统控件没有对应的XML布局文件,界面的逻辑都是在onDraw(Canvas canvas)里面进行绘制 所以如果对控件的外观进行修改就需要重写onDraw(Canvas canvas)方法,控件的功能逻辑可以不变. 下面是继承ProgressBar重写了控件的界面,一个是加了文字的水平进度条,一个是圆圈的进度条 public class HorizontalProgressBarWithNumber extends ProgressBar { private static fi

Android 中常见控件的介绍和使用

1 TextView文本框 1.1 TextView类的结构 TextView 是用于显示字符串的组件,对于用户来说就是屏幕中一块用于显示文本的区域.TextView类的层次关系如下: java.lang.Object   ? android.view.View   ? android.widget.TextView 直接子类: Button, CheckedTextView, Chronometer, DigitalClock, EditText 间接子类: AutoCompleteTextV