ionic怎么判断ionic侧滑菜单单滑出

3942人阅读
安装时总结
一、win系统下nodejs安装及环境配置
第一步:下载nodejs,官网:http://nodejs.org/download/
第二步:安装nodejs
下载完成之后,双击&node-v0.10.28-x86.msi&,开始安装nodejs,自定义安装在D:\dev\nodejs下面。
在cmd控制台输入:node -v,控制台将打印出:v0.10.28,出现版本提示表示安装成功。
该引导步骤会将node.exe文件安装到D:\dev\nodejs\目录下,并将该目录添加进PATH环境变量。
第三步:npm安装
由于新版的nodejs已经集成了npm,所以之前npm也一并安装好了。同样可以使用cmd命令行输入&npm -v&来测试是否成功安装。如下图,出现版本提示表示安装成功。
第四步:安装相关环境
npm install -g express-generator
npm install jade -g
npm install mysql -g
默认情况下上述组件都是安装在D:\dev\nodejs\node_modules文件夹下,这也是nodejs相关组件的自动查找路径。
第五步:安装CoffeeScript
npm install coffee-script -g
确认安装的命令:coffee -v,出现版本号表示成功安装。
补充说明:
所有命令都是-g进行全局安装的,这样安装的安装包都在当前用户下,在磁盘的所有其他地方都可以访问到,比较方面。否则安装在当前目录下,只能在当前目录下使用。
安装express问题
安装nodejs安装包后,通过npm安装express后,运行express提示& express&不是内部或外部命令,原因是版本问题,当前版本是4.0.0,改成3.5.0即可运行。
npm install -g express@3.5.0
注意:express 测试版本用大写V
npm命令集合:
1、npm install moduleNames:安装Node模块
npm install express
默认会安装express的最新版本,也可以通过在后面加版本号的方式安装指定版本,如npm install express@3.0.6
npm install &name& -g
将包安装到全局环境中
但是代码中,直接通过require()的方式是没有办法调用全局安装的包的。全局的安装是供命令行使用的,就好像全局安装了vmarket后,就可以在命令行中直接运行vm命令
npm install &name& --save
安装的同时,将信息写入package.json中项目路径中如果有package.json文件时,直接使用npm install方法就可以根据dependencies配置安装所有的依赖包,这样代码提交到github时,就不用提交node_modules这个文件夹了。
2、全局安装命令为npm install -g moduleName。
3、npm view moduleNames:查看node模块的package.json文件夹
4、npm list:查看当前目录下已安装的node包
5、npm help:查看帮助命令
6、npm view moudleName dependencies:查看包的依赖关系
7、npm view moduleName repository.url:查看包的源文件地址
8、npm view moduleName engines:查看包所依赖的Node的版本
9、npm help folders:查看npm使用的所有文件夹
10、npm rebuild moduleName:用于更改包内容后进行重建
11、npm outdated:检查包是否已经过时,此命令会列出所有已经过时的包,可以及时进行包的更新
12、npm update moduleName:更新node模块
13、npm uninstall moudleName:卸载node模块
14、一个npm包是包含了package.json的文件夹,package.json描述了这个文件夹的结构。访问npm的json文件夹的方法如下:
$ npm help json 此命令会以默认的方式打开一个网页,如果更改了默认打开程序则可能不会以网页的形式打开。
15、发布一个npm包的时候,需要检验某个包名是否已存在 $ npm search packageName
16、npm init:会引导你创建一个package.json文件,包括名称、版本、作者这些信息等
17、npm root:查看当前包的安装路径
npm root -g:查看全局的包的安装路径
18、npm -v:查看npm安装的版本
19、npm install --save moduleName 安装模块到本目录
更多命令请参看npm官方文档:https://www.npmjs.org/doc/
npm install -g cnpm --registry=https://registry.npm.taobao.org(npm镜像源指向淘宝)
cnpm install -g cordova ionic(安装cordova ionic)
cnpm update -g cordova ionic(更新cordova ionic)
ionic -help(查看帮助)
ionic -v(查看版本)
ionic start myApp blank(空项目)
ionic start myApp tabs(带导航条)
ionic start myApp sidemenu(带侧滑菜单)
ionic platform add android(添加android平台)
ionic platform remove android(移除android平台)
ionic build android(编译项目apk)ionic emulate android(运行项目apk 手机连接在手机运行 模拟器连接在模拟器运行)
ionic run android (相当于build + emulate)
ionic serve (开启服务调试)
ionic build ios(编译项目ipk)ionic emulate ios(运行项目ipk)
第六步:创建项目
1、express /tmp/foo && cd /tmp/foo
2、npm install //下载依赖包
3、npm start //启动项目
node_moduls
存放所有的项目依赖库
package.json
项目依赖配置及开发者信息
app.js 程序启动文件
public 静态文件(css js img)
routes 路由文件(MVC中的C,controller)
Viesws 页面文件(Ejs模板)
安装Android SDK
1、下载Android SDK,点击安装,直接默认路径即可!
下载地址:/sdk/index.html
2、默认路径安装后,安装完成,开始配置环境变量。
3、打开计算机属性——高级系统设置——环境变量(如上文)
4、新建一个环境变量,变量名:ANDROID_HOME,变量值:C:\Program Files (x86)\Android\android-sdk(以你安装目录为准,确认里面有tools和add-ons等多个文件夹),点击确认。
5、在变量PATH后面加上变量值%ANDROID_HOME%\点击确认即可。 如果没有这个变量,新建一个即可!新建方法见上文!
6、Android SDK配置完成,接下来验证配置是否成功。
7、点击运行——输入cmd——回车——输入android -h——回车
Android 环境搭建过程遇到的问题 http://bbs./read.php?tid=7&fid=4
第七步 安装Ionic
1、执行npm install -g ionic
2、创建一个Ionic APP 执行ionic start myapp[template]
Template 有如下三种 默认是tabs project:
tabs(默认)、sidemenu、blank
3、安装模板
ionic install --save 模板
4、cordova 是页面与设备桥接
5、npm install --save bower
第八步 安装gulp相关代码合并、混淆工具
npm install gulp
npm install gulp-concat:合并文件
npm install gulp-rename:重命名文件
npm install gulp-sass:支持sass
npm install gulp-minify-css:压缩css
npm install gulp-connect
配置一个web服务器
第九步 编译测试
1.android版本
ionic platform add android
ionic build android
ionic run android
如果要在虚拟机中测试,可以改用
ionic emulate android
$ ionic start myapp tabs
$ cd myapp
$ ionic platform add ios
$ ionic build ios
$ ionic emulate ios
3.浏览器同时修改端口号
ionic serve -p 8105
4.连接数据线直接测试
ionic run android
重点集合:
一、ionic安装失败或者cordova安装失败解决方法:
镜像使用方法(三种办法任意一种都能解决问题,建议使用第三种,将配置写死,下次用的时候配置还在):
1.通过config命令
npm config set registry https://registry.npm.taobao.org
npm info underscore (如果上面配置正确这个命令会有字符串response)
2.命令行指定
npm --registry https://registry.npm.taobao.org info underscore
3.编辑 ~/.npmrc 加入下面内容
registry = https://registry.npm.taobao.org
搜索镜像: https://npm.taobao.org
建立或使用镜像,参考: /cnpm/cnpmjs.org
4、使用cnpm(强烈建议)
npm install -g cnpm --registry=https://registry.npm.taobao.org
以后所有的npm可用cnpm代替,如:cnpm install ionic
5、如果还是一直失败,将ionic文件下载解压后放到C:\Users\Auser\AppData\Roaming\npm\node_modules中
6、1.配置环境变量node的npm命令
Path : C:\Documents and Settings\Administrator\Application Data\npm
2.补齐npm文件夹下有关于ionic的文件 http://download.csdn.net/detail/superjunjin/8417723
3.补齐ionic项目下node_modules等文件 http://download.csdn.net/detail/superjunjin/8417731
(具体文件见最后的压缩包)
ionic start myApp sidemenu
//创建带有左侧带有menu栏的示例项目
ionic start myApp blank
//创建空白项目
第二部分 开发过程总结
一、Angular 提供了 3 种方法来创建并注册我们自己的服务。1. Provider
2. Factory
3. Service
Providers 是唯一一种你可以传进 .config() 函数的 service。当你想要在 service对象启用之前,先进行模块范围的配置,那就应该用 provider。
二、bower安装restangular失败(报错:Bower : ENOGIT git is not installed or not in the PATH)
1.添加git路径到环境变量PATH中(命令:set PATH=%PATH%;D:\Program Files\Git\bin)
2.运行bower install restangular 即可安装成功
3.http://www./posts/restangular.html
4.restangular文档:/mgonto/restangular#element-methods
5.安装bower install underscore
三、restangular需要用到的js(js少引入报错:Uncaught ReferenceError: _ is not defined from restangular)
&script type=&text/javascript& src=&/ajax/libs/underscore.js/1.4.4/underscore-min.js&&&/script&
&script type=&text/javascript& src=&http://cdn.jsdelivr.net/restangular/latest/restangular.min.js&&&/script&
四、跨域错误解决办法(XMLHttpRequest cannot load
''. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin ' ' is therefore not allowed access. )
Java代码中返回结果集前增加response.setHeader(&Access-Control-Allow-Origin&, &*&);
五、国际化
/blog/3bba606c19e9338dca679.html
1.安装bower install angular-translate
2.安装bower install angular-translate-loader-static-files
3.angular国际化不存在乱码,如果出现乱码可能是中文json文件编码非UTF-8格式
4.国际化使用(移动端有问题
无法使用):
1. 需要引入JS
&script src=&lib/angular/angular.js&&&/script&
&script src=&lib/angular-translate/angular-translate.js&&&/script&
&script src=&lib/angular-translate-loader-static-files/angular-translate-loader-static-files.js&&&/script&
2.建立文件夹i18n存放cn.json /en.json
en.json:{&100001&:&Login&,&100002&:&Register&}
cn.json:{&100001&:&登录&,&100002&:&注册&}
3.在app.js中config里配置如下
//国际化配置
$translateProvider.preferredLanguage('cn');
$translateProvider.useStaticFilesLoader({
prefix: '/i18n/',
suffix: '.json'
4.html页面使用
{{'; | translate }}
5.按钮切换语种
.controller('LanguageSwitchingCtrl', ['$scope', '$translate', function (scope, $translate) {
scope.switching = function (lang) {
$translate.use(lang);
六、AngularJS iframe跨域打开内容时报错误的解决办法
&iframe id=&myFrame& ng-src=&{{url}}& width=&100%& height=&100%& seamless frameborder=&0& &&/iframe&
打开不同域的内容时报下面的错误:
Blocked loading resource from url not allowed by $sceDelegate policy
解决方案:
app.config(function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist([
// Allow same origin resource loads.
'self',
// Allow loading from our assets domain.
Notice the difference between * and **.
'http://media.w3.org/**']);
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:199990次
积分:2621
积分:2621
排名:第11470名
原创:55篇
转载:64篇
评论:20条
(1)(3)(3)(2)(2)(1)(3)(2)(1)(2)(3)(2)(4)(3)(4)(1)(2)(1)(2)(4)(4)(2)(1)(2)(10)(9)(12)(7)(7)(1)(5)(6)(2)(1)(1)(3)一,侧滑效果主要涉及到三个地方1,菜单页面主要分为主页面和侧滑页面两部分,又分别都包含顶部bar和主要内容部分注意:主页面内容部分,关键的菜单内容name要对应到是菜单内容的页面(在app.js中,下面会贴出)& & & & &&item href的构成:1,#本页面 &2,app本页面链接 & 3,todolist/{{menu.groupId}}列表页面链接(没有冒号)&ion-side-menus&
&!--主页面--&
&ion-side-menu-content class=&bar-positive&&
&!--主页面顶部bar--&
&ion-nav-bar class=&bar-positive&&
&ion-nav-back-button class=&button-clear&&&i class=&icon ion-chevron-left&&&/i&&/ion-nav-back-button&
&/ion-nav-bar&
&!--主页面内容 关键的菜单内容name要对应到是菜单内容的页面--&
&ion-nav-view name=&menuContent& animation=&slide-left-right&&&/ion-nav-view&
&/ion-side-menu-content&
&!--侧滑页面--&
&ion-side-menu side=&left&&
&!--侧滑页面顶部bar--&
&ion-header-bar align-title=&center& class=&bar-dark&&
&h1 class=&title&&需求&/h1&
&div class=&buttons pull-right& nav-clear menu-close ng-click=&settings()&&
&button class=&button button-icon icon ion-gear-b&&&/button&
&/ion-header-bar&
&!--侧滑页面主要内容--&
&ion-content class=&has-header&&
&ion-list&
&!--item href的构成:1,#本页面
2,app本页面链接
3,todolist/{{menu.groupId}}列表页面链接(没有冒号)--&
&ion-item nav-clear menu-close ng-repeat=&menu in menus& href=http://blog.csdn.net/superjunjin/article/details/"#/app/todolist/{{menu.groupId}}">
??{{menu.title}} 0">{{menu.badge}}
2,app.js在配置页面记得,通过菜单内容页面的name,把菜单和菜单内容页关联起来angular.module('todo.io', ['ionic', 'todo.io.directives', 'todo.io.filters', 'todo.io.services', 'todo.io.controllers', 'nsPopover','LocalStorageModule','ngCordova'])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
if(window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
if(window.StatusBar) {
StatusBar.styleDefault();
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('tutorial', {
url: '/',
templateUrl: 'templates/tutorial.html',
controller: 'TutorialCtrl'
.state('sign', {
url: '/sign',
templateUrl: 'templates/sign.html',
controller: 'SignCtrl'
.state('app', {
url: &/app&,
abstract: true,
templateUrl: &templates/menu.html&,
controller: 'AppCtrl'
//通过菜单内容页面的name,把菜单和菜单内容页关联起来
.state('app.todolist', {
url: &/todolist/:groupId&,
'menuContent' :{
templateUrl: &templates/todo_list.html&,
controller: 'TodolistsCtrl'
.state('app.todolistedit', {
url: &/todolist/edit/:groupId&,
'menuContent' :{
templateUrl: &templates/todo_list_edit.html&,
controller: 'TodolistsEditCtrl'
.state('app.todoinfo', {
url: &/todo/:todoId&,
'menuContent' :{
templateUrl: &templates/todo_info.html&,
controller: 'TodoCtrl'
.state('app.grouplist', {
url: &/group&,
'menuContent' :{
templateUrl: &templates/group_list.html&,
controller: 'GrouplistCtrl'
.state('app.groupinfo', {
url: &/group/:groupId&,
'menuContent' :{
templateUrl: &templates/group_info.html&,
controller: 'GroupCtrl'
.state('app.search', {
url: &/search&,
'menuContent' :{
templateUrl: &templates/search.html&,
controller: 'SearchCtrl'
.state('app.settings', {
url: &/settings&,
'menuContent' :{
templateUrl: &templates/settings.html&,
controller: 'SettingsCtrl'
$urlRouterProvider.otherwise('/');
3,菜单内容页面主要就是关联菜单的button,设置此属性就行menu-toggle&ion-nav-buttons side=&left&&
&button menu-toggle=&left& class=&button button-icon icon ion-navicon-round&&&/button&
&button class=&button button-icon icon ion-refresh& ng-show=&groupId==1& ng-click=&refresh()&&&/button&
&/ion-nav-buttons&(的,也一样)二,下拉刷新主要两部分控制部分和页面部分1,页面部分ion-refresher刷新控件,on-refresh设置刷新方法,还有下拉文字,刷新文字,下拉效果图&html ng-app=&ionicApp&&
&meta charset=&utf-8&&
&meta name=&viewport& content=&initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width&&
&title&Ionic Pull to Refresh&/title&
&link href=http://blog.csdn.net/superjunjin/article/details/"///nightly/css/ionic.css" rel="stylesheet">
Pull To Refresh
2,控制部分主要就是控制完成刷新后,通知关闭刷新效果angular.module('ionicApp', ['ionic'])
.controller('MyCtrl', function($scope, $timeout) {
$scope.items = ['Item 1', 'Item 2', 'Item 3'];
$scope.doRefresh = function() {
console.log('Refreshing!');
$timeout( function() {
//simulate async response
$scope.items.push('New Item ' + Math.floor(Math.random() * 1000) + 4);
//Stop the ion-refresher from spinning
$scope.$broadcast('scroll.refreshComplete');
});另外找到ionic的svg动画效果图,可是不知道怎么加入代码html&html ng-app=&ionicApp&&
&meta charset=&utf-8&&
&meta name=&viewport& content=&initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width&&
&title&Ionic Pull to Refresh&/title&
&link href=http://blog.csdn.net/superjunjin/article/details/"///nightly/css/ionic.css" rel="stylesheet">
Animated SVGs
cursor: url('/img/finger.png'),
text-align:
margin-bottom: 40px !
.spinner svg {
width: 19% !
height: 85px !
}例1代码下载&http://download.csdn.net/detail/superjunjin/8562153例2代码下载&http://download.csdn.net/detail/superjunjin/8562139超酷的Android 侧滑(双向滑动菜单)效果_android开发_ThinkSAAS
超酷的Android 侧滑(双向滑动菜单)效果
超酷的Android 侧滑(双向滑动菜单)效果
内容来源: 网络
下面看看我们如何使用它,达到我们想要的效果
public class MainActivity extends Activity {
private SliderMenu SliderM
private ListView contentL
private ArrayAdapter&String& contentListA
private String[] contentItems = { "Content Item 1", "Content Item 2", "Content Item 3",
"Content Item 4", "Content Item 5", "Content Item 6", "Content Item 7",
"Content Item 8", "Content Item 9", "Content Item 10", "Content Item 11",
"Content Item 12", "Content Item 13", "Content Item 14", "Content Item 15",
"Content Item 16" };
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SliderMenu = (SliderMenu) findViewById(R.id.bidir_sliding_layout);
contentList = (ListView) findViewById(R.id.contentList);
contentListAdapter = new ArrayAdapter&String&(this, android.R.layout.simple_list_item_1,
contentItems);
contentList.setAdapter(contentListAdapter);
SliderMenu.setScrollEvent(contentList);
这个类就是实现双向滑动菜单功能的核心类
public class SliderMenu extends RelativeLayout implements OnTouchListener {
public static final int SNAP_VELOCITY = 200;
public static final int DO_NOTHING = 0;
public static final int SHOW_LEFT_MENU = 1;
public static final int SHOW_RIGHT_MENU = 2;
public static final int HIDE_LEFT_MENU = 3;
public static final int HIDE_RIGHT_MENU = 4;
private int slideS
private int screenW
private int touchS
private float xD
private float yD
private float xM
private float yM
private float xUp;
private boolean isLeftMenuV
private boolean isRightMenuV
ThinkSAAS商业授权:
ThinkSAAS为用户提供有偿个性定制开发服务
ThinkSAAS将为商业授权用户提供二次开发指导和技术支持
让ThinkSAAS更好,把建议拿来。
开发客服微信Android侧滑编辑、删除效果_iOS开发_
Android侧滑编辑、删除效果
来源:人气:3
ListView侧滑删除操作已经成为了一种常见的交互,一般是从列表控件自身出发,重写ListView实现,比如网上使用频率较高的SweMenuListView。
其实还有一种更直观的做法,那就是把焦点放在滑动List Item本身这个操作上来,自定义可以支持侧滑操作的ItemView,即对ListView适配器中的convertView的RootView布局控件进行重写,这样更容易扩展,方便控制列表项哪些可以侧滑,哪些不能滑动,哪些使用左滑,哪些使用右滑等,还减少了代码量。
侧滑控件的实现
package com.john.
import android.animation.A
import android.animation.AnimatorListenerA
import android.animation.ValueA
import android.content.C
import android.content.res.TypedA
import android.graphics.PointF;
import android.util.AttributeS
import android.view.MotionE
import android.view.VelocityT
import android.view.V
import android.view.ViewC
import android.view.ViewG
import android.view.animation.AccelerateI
import android.view.animation.OvershootI
* 继承自ViewGroup,实现滑动出现删除等选项的效果
* Created by john on 16-11-15.
public class SwipeMenuViewGroup extends ViewGroup {
ivate int mScaleTouchS//为了处理单击事件的冲突
private int mMaxV//计算滑动速度用
private int mPointerId;//多点触摸只算第一根手指的速度
private int mH//自己的高度
private int mRightMenuW //右侧菜单宽度总和(最大滑动距离)
//滑动判定临界值(右侧菜单宽度的40%) 手指抬起时,超过了展开,没超过收起menu
private int mL
//存储contentView(第一个View)
private View mContentV
//上一次的xy
private PointF mLastP = new PointF();
private boolean isUnMoved =
//判断手指起始落点,如果距离属于滑动了,就屏蔽一切点击事件。
private PointF mFirstP = new PointF();
private boolean isUserS
//存储的是当前正在展开的View
private static SwipeMenuViewGroup mViewC
//防止多只手指一起滑我的flag 在每次down里判断, touch事件结束清空
private static boolean isT
private VelocityTracker mVelocityT//滑动速度变量
* 右滑删除功能的开关,默认开
private boolean isSwipeE
* IOS、式交互,默认开
private boolean isI
private boolean iosInterceptF//IOS类型下,是否拦截事件的flag
*左滑右滑的开关,默认左滑打开菜单
private boolean isLeftS
public SwipeMenuViewGroup(Context context) {
this(context, null);
public SwipeMenuViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
public SwipeMenuViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
public boolean isSwipeEnable() {
return isSwipeE
* 设置侧滑功能开关
* @param swipeEnable
public void setSwipeEnable(boolean swipeEnable) {
isSwipeEnable = swipeE
public boolean isIos() {
return isI
* 设置是否开启IOS阻塞式交互
* @param ios
public SwipeMenuViewGroup setIos(boolean ios) {
public boolean isLeftSwipe() {
return isLeftS
* 设置是否开启左滑出菜单,设置false 为右滑出菜单
* @param leftSwipe
public SwipeMenuViewGroup setLeftSwipe(boolean leftSwipe) {
isLeftSwipe = leftS
* 返回ViewCache
public static SwipeMenuViewGroup getViewCache() {
return mViewC
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
mScaleTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mMaxVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
//右滑删除功能的开关,默认开
isSwipeEnable =
//IOS、QQ式交互,默认开
//左滑右滑的开关,默认左滑打开菜单
isLeftSwipe =
TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SwipeMenuViewGroup, defStyleAttr, 0);
int count = ta.getIndexCount();
for (int i = 0; i & i++) {
int attr = ta.getIndex(i);
//如果引用成AndroidLib资源都不是常量,无法使用switch case
if (attr == R.styleable.SwipeMenuViewGroup_swipeEnable) {
isSwipeEnable = ta.getBoolean(attr, true);
} else if (attr == R.styleable.SwipeMenuViewGroup_ios) {
isIos = ta.getBoolean(attr, true);
} else if (attr == R.styleable.SwipeMenuViewGroup_leftSwipe) {
isLeftSwipe = ta.getBoolean(attr, true);
ta.recycle();
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setClickable(true);//令自己可点击,从而获取触摸事件
mRightMenuWidths = 0;//由于ViewHolder的复用机制,每次这里要手动恢复初始值
int contentWidth = 0;//适配GridLayoutManager,将以第一个子Item(即ContentItem)的宽度为控件宽度
int childCount = getChildCount();
//为了子View的高,可以matchParent(参考的FrameLayout 和LinearLayout的Horizontal)
final boolean measureMatchParentChildren = MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
boolean isNeedMeasureChildHeight =
for (int i = 0; i & childC i++) {
View childView = getChildAt(i);
//令每一个子View可点击,从而获取触摸事件
childView.setClickable(true);
if (childView.getVisibility() != GONE) {
//后续计划加入上滑、下滑,则将不再支持Item的margin
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
final MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams();
mHeight = Math.max(mHeight, childView.getMeasuredHeight());
if (measureMatchParentChildren && lp.height == LayoutParams.MATCH_PARENT) {
isNeedMeasureChildHeight =
if (i & 0) {//第一个布局是Left item,从第二个开始才是RightMenu
mRightMenuWidths += childView.getMeasuredWidth();
mContentView = childV
contentWidth = childView.getMeasuredWidth();
setMeasuredDimension(getPaddingLeft() + getPaddingRight() + contentWidth,
mHeight + getPaddingTop() + getPaddingBottom());//宽度取第一个Item(Content)的宽度
mLimit = mRightMenuWidths * 4 / 10;//滑动判断的临界值
if (isNeedMeasureChildHeight) {//如果子View的height有MatchParent属性的,设置子View高度
forceUniformHeight(childCount, widthMeasureSpec);
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
* 给MatchParent的子View设置高度
* @param count
* @param widthMeasureSpec
* @see android.widget.LinearLayout# 同名方法
private void forceUniformHeight(int count, int widthMeasureSpec) {
// Pretend that the linear layout has an exact size. This is the measured height of
// ourselves. The measured height should be the max height of the children, changed
// to accommodate the heightMeasureSpec from the parent
int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
MeasureSpec.EXACTLY);//以父布局高度构建一个Exactly的测量参数
for (int i = 0; i & ++i) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
if (lp.height == LayoutParams.MATCH_PARENT) {
int oldWidth = lp.//measureChildWithMargins 这个函数会用到宽,所以要保存一下
lp.width = child.getMeasuredWidth();
// Remeasure with new dimensions
measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0);
lp.width = oldW
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
int left = 0 + getPaddingLeft();
int right = 0 + getPaddingLeft();
for (int i = 0; i & childC i++) {
View childView = getChildAt(i);
if (childView.getVisibility() != GONE) {
if (i == 0) {//第一个子View是内容,宽度设置为全屏
childView.layout(left, getPaddingTop(), left + childView.getMeasuredWidth(), getPaddingTop() + childView.getMeasuredHeight());
left = left + childView.getMeasuredWidth();
if (isLeftSwipe) {
childView.layout(left, getPaddingTop(), left + childView.getMeasuredWidth(), getPaddingTop() + childView.getMeasuredHeight());
left = left + childView.getMeasuredWidth();
childView.layout(right - childView.getMeasuredWidth(), getPaddingTop(), right, getPaddingTop() + childView.getMeasuredHeight());
right = right - childView.getMeasuredWidth();
public boolean dispatchTouchEvent(MotionEvent ev) {
if (isSwipeEnable) {
acquireVelocityTracker(ev);
final VelocityTracker verTracker = mVelocityT
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
isUserSwiped =//判断手指起始落点,如果距离属于滑动了,就屏蔽一切点击事件
isUnMoved =
iosInterceptFlag =//每次DOWN时,默认是不拦截的
if (isTouching) {//如果有别的指头摸过了,那么就return false
isTouching =//第一个摸的指头,赶紧改变标志,宣誓主权
mLastP.set(ev.getRawX(), ev.getRawY());
mFirstP.set(ev.getRawX(), ev.getRawY());//判断手指起始落点,如果距离属于滑动了,就屏蔽一切点击事件
//如果down,view和cacheview不一样,则立马让它还原。且把它置为null
if (mViewCache != null) {
if (mViewCache != this) {
mViewCache.smoothClose();
iosInterceptFlag = isI//IOS模式开启的话,且当前有侧滑菜单的View,且不是自己的,就拦截该事件
//只要有一个侧滑菜单处于打开状态, 就不给外层布局上下滑动了
getParent().requestDisallowInterceptTouchEvent(true);
//求第一个触点的id, 此时可能有多个触点,但至少一个,计算滑动速率用
mPointerId = ev.getPointerId(0);
case MotionEvent.ACTION_MOVE:
//IOS模式开启的话,且当前有侧滑菜单的View,且不是自己的,就该拦截事件咯。滑动也不该出现
if (iosInterceptFlag) {
float gap = mLastP.x - ev.getRawX();
//为了在水平滑动中禁止父类ListView等再竖直滑动
if (Math.abs(gap) & 10 || Math.abs(getScrollX()) & 10) {//修改此处,使屏蔽父布局滑动更加灵敏
getParent().requestDisallowInterceptTouchEvent(true);
if (Math.abs(gap) & mScaleTouchSlop) {
isUnMoved =
scrollBy((int) (gap), 0);//滑动使用scrollBy
//越界修正
if (isLeftSwipe) {//左滑
if (getScrollX() & 0) {
scrollTo(0, 0);
if (getScrollX() & mRightMenuWidths) {
scrollTo(mRightMenuWidths, 0);
} else {//右滑
if (getScrollX() & -mRightMenuWidths) {
scrollTo(-mRightMenuWidths, 0);
if (getScrollX() & 0) {
scrollTo(0, 0);
mLastP.set(ev.getRawX(), ev.getRawY());
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (Math.abs(ev.getRawX() - mFirstP.x) & mScaleTouchSlop) {
isUserSwiped =
if (!iosInterceptFlag ) {//且滑动了 才判断是否要收起、展开menu
//求伪瞬时速度
puteCurrentVelocity(1000, mMaxVelocity);
final float velocityX = verTracker.getXVelocity(mPointerId);
if (Math.abs(velocityX) & 1000) {//滑动速度超过阈值
if (velocityX & -1000) {
if (isLeftSwipe) {//左滑
//平滑展开Menu
smoothExpand();
//平滑关闭Menu
smoothClose();
if (isLeftSwipe) {//左滑
// 平滑关闭Menu
smoothClose();
//平滑展开Menu
smoothExpand();
if (Math.abs(getScrollX()) & mLimit) {//否则就判断滑动距离
//平滑展开Menu
smoothExpand();
// 平滑关闭Menu
smoothClose();
releaseVelocityTracker();
isTouching =
return super.dispatchTouchEvent(ev);
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
// fix 长按事件和侧滑的冲突
case MotionEvent.ACTION_MOVE:
//屏蔽滑动时的事件
if (Math.abs(ev.getRawX() - mFirstP.x) & mScaleTouchSlop) {
case MotionEvent.ACTION_UP:
//为了在侧滑时,屏蔽子View的点击事件
if (isLeftSwipe) {
if (getScrollX() & mScaleTouchSlop) {
if (ev.getX() & getWidth() - getScrollX()) {
if (isUnMoved) {
smoothClose();
//true表示拦截
if (-getScrollX() & mScaleTouchSlop) {
if (ev.getX() & -getScrollX()) {//点击范围在菜单外则屏蔽
if (isUnMoved) {
smoothClose();
if (isUserSwiped) {
//模仿IOS 点击其他区域关闭
if (iosInterceptFlag) {
//IOS模式开启,且当前有菜单的View,且不是自己的 拦截点击事件给子View
return super.onInterceptTouchEvent(ev);
* 平滑展开
private ValueAnimator mExpandAnim, mCloseA
private boolean isE//代表当前是否是展开状态
public void smoothExpand() {
//展开就加入ViewCache:
mViewCache = SwipeMenuViewGroup.
//侧滑菜单展开,屏蔽content长按
if (null != mContentView) {
mContentView.setLongClickable(false);
if (mCloseAnim != null && mCloseAnim.isRunning()) {
mCloseAnim.cancel();
mExpandAnim = ValueAnimator.ofInt(getScrollX(), isLeftSwipe ? mRightMenuWidths : -mRightMenuWidths);
mExpandAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
scrollTo((Integer) animation.getAnimatedValue(), 0);
mExpandAnim.setInterpolator(new OvershootInterpolator());
mExpandAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
isExpand =
mExpandAnim.setDuration(300).start();
* 平滑关闭
public void smoothClose() {
mViewCache =
if (null != mContentView) {
mContentView.setLongClickable(true);
if (mExpandAnim != null && mExpandAnim.isRunning()) {
mExpandAnim.cancel();
mCloseAnim = ValueAnimator.ofInt(getScrollX(), 0);
mCloseAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
scrollTo((Integer) animation.getAnimatedValue(), 0);
mCloseAnim.setInterpolator(new AccelerateInterpolator());
mCloseAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
isExpand =
mCloseAnim.setDuration(300).start();
* @param event 向VelocityTracker添加MotionEvent
* @see VelocityTracker#obtain()
* @see VelocityTracker#addMovement(MotionEvent)
private void acquireVelocityTracker(final MotionEvent event) {
if (null == mVelocityTracker) {
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(event);
* * 释放VelocityTracker
* @see VelocityTracker#clear()
* @see VelocityTracker#recycle()
private void releaseVelocityTracker() {
if (null != mVelocityTracker) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker =
protected void onDetachedFromWindow() {
if (this == mViewCache) {
mViewCache.smoothClose();
mViewCache =
super.onDetachedFromWindow();
//展开时,禁止长按
public boolean performLongClick() {
if (Math.abs(getScrollX()) & mScaleTouchSlop) {
return super.performLongClick();
* 快速关闭
* 用于点击侧滑菜单上的选项,同时想让它快速关闭(删除 编辑)
* 这个方法在ListView里是必须调用的
* 在RecyclerView里视情况而定,如果是mAdapter.notifyItemRemoved(pos)方法则不用调用
public void quickClose() {
if (this == mViewCache) {
//先取消展开动画
if (null != mExpandAnim && mExpandAnim.isRunning()) {
mExpandAnim.cancel();
mViewCache.scrollTo(0, 0);//关闭
mViewCache =
控件的使用
布局listview item时引用这个自定义控件
&? version="1.0" encoding="utf-8"?&
&com.john.sideslipdemo.SwipeMenuViewGroup
xmlns:android="/apk/res/android"
xmlns:app="/apk/res-auto"
xmlns:tools="/tools"
android:layout_width="match_parent"
android:layout_height="64dp"
android:paddingBottom="1dp"
app:ios="false"
app:leftSwipe="true"
app:swipeEnable="true"
android:clickable="true"&
&LinearLayout
android:id="@+id/content_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp"&
&ImageView
android:id="@+id/content_iv"
android:layout_width="40dp"
android:layout_height="40dp"
tools:src="@drawable/ic_more"/&
&LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_weight="1"
android:orientation="vertical"&
android:id="@+id/content_tv"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:textColor="@android:color/black"
android:textSize="15sp"
tools:text="测试"/&
android:id="@+id/time_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:text="10:00"
android:textColor="@android:color/darker_gray"
android:textSize="13sp"/&
&/LinearLayout&
&/LinearLayout&
&LinearLayout
android:layout_width="160dp"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:background="#76828F"
android:orientation="horizontal"
android:clickable="true"&
&ImageView
android:id="@+id/iv_delete"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@drawable/swipe_more_delete" /&
&ImageView
android:id="@+id/iv_edit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_toRightOf="@+id/iv_delete"
android:layout_alignTop="@+id/iv_delete"
android:src="@drawable/swipe_more_edit" /&
&/LinearLayout&
&/com.john.sideslipdemo.SwipeMenuViewGroup&
控制列表中哪些Item可以滑动
使用setSwipeEnable方法
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewH
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = inflater.inflate(R.layout.list_item, parent, false);
viewHolder.rootView = (SwipeMenuViewGroup) convertV
viewHolder.rootView.setSwipeEnable(true);
viewHolder.contentView = (LinearLayout) convertView.findViewById(R.id.content_view);
viewHolder.contentTv = (TextView) convertView.findViewById(R.id.content_tv);
viewHolder.contentIv = (ImageView) convertView.findViewById(R.id.content_iv);
viewHolder.deleteIv = (ImageView) convertView.findViewById(R.id.iv_delete);
viewHolder.editIv = (ImageView) convertView.findViewById(R.id.iv_edit);
convertView.setTag(viewHolder);
viewHolder = (ViewHolder) convertView.getTag();
if(position % 2 == 0) {
viewHolder.rootView.setSwipeEnable(false);
viewHolder.contentTv.setText(mList.get(position));
viewHolder.contentIv.setImageResource(R.drawable.ic_more);
return convertV
作为第三方控件
因为这个侧滑效果思路的实现只需要编写一个类完成,所以没有把他作为开源库,使用时直接将这个类拷贝到自己的工程中即可。
Demo实际效果
/coderJohnZhang/SideSlipDemo
优质网站模板

我要回帖

更多关于 ionic2 侧滑 的文章

 

随机推荐