仿qq的侧拉菜单效果

自定义控件
import android.animation.ArgbEvaluator;
import android.animation.FloatEvaluator;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.ScaleAnimation;
import android.widget.FrameLayout;
import android.widget.Scroller;
/**
* 让SlideMen去继承系统已有的布局,目的是为了让他们帮我们实现onMeasure方法,
* 一般的话我们会选择继承FrameLayout,因为FrameLayout最轻量级
*/
public class SlideMenu extends FrameLayout {
ViewDragHelper dragHelper;
private View menu;
private View main;
int maxLeft;
ArgbEvaluator argbEval = new ArgbEvaluator();
FloatEvaluator floatEval = new FloatEvaluator();
public SlideMenu(Context context) {
this(context, null);
}
public SlideMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
dragHelper = ViewDragHelper.create(this,callback);
}
/**
* 该方法是当前view在布局文件中的xml结束标签读取完后执行,此时就知道
* 当前View有几个子View了,但是注意:此时还不能获取子View的宽高,因为
* 还木有测量呢!
*/
@Override
protected void onFinishInflate() {
super.onFinishInflate();
menu = getChildAt(0);
main = getChildAt(1);
}
/**
* 该方法是onMeasure执行之后执行,因此可获取宽高
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
maxLeft = (int) (getMeasuredWidth()*0.6f);
}
// /**
// * 用来测量自己和自己的子View的
// * @param widthMeasureSpec
// * @param heightMeasureSpec
// */
// @Override
// protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// //只需要测量子View即可
//
// for (int i = 0; i < getChildCount(); i++) {
// View child = getChildAt(i);
// measureChild(child,widthMeasureSpec,heightMeasureSpec);
// }
// }
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//让ViewDragHelper帮助我们判断是否应该拦截
boolean result = dragHelper.shouldInterceptTouchEvent(ev);
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//让ViewDragHelper帮我们处理触摸事件
dragHelper.processTouchEvent(event);
return true;
}
ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
/**
* 尝试监视View的触摸事件
* @param child 当前触摸的子View
* @param pointerId 手指多点触摸时的触摸点的索引
* @return true表示监视 ,false就是忽略不管
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child==main || child==menu;
}
/**
* 看起来是获取View水平的拖拽范围的,然而并不是这样,这是一个鸡肋的方法,目前它的作用是
* 用来判断你是否想强制水平滑动的,如果想强制水平滑动,则返回大于0的任意值
* @param child
* @return
*/
@Override
public int getViewHorizontalDragRange(View child) {
return 1;
}
/**
* 修正修改View水平方向的位置移动,控制水平移动的
* @param child 当前触摸的子View
* @param left ViewDragHelper认为我想让View的left变成的值,它是这样计算好的:child.getLeft+dx
* @param dx 本次手指移动的水平距离
* @return 最终返回的值表示我们真正想让child的left变成的值
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//只限制main
if(child==main){
left = clampLeft(left);
}
return left;
}
/**
* 修正修改View垂直方向的位置移动,控制垂直移动的
* @param child 当前触摸的子View
* @param top ViewDragHelper认为我想让View的top变成的值,它是这样计算好的:child.getTop+dy
* @param dy 本次手指移动的垂直距离
* @return 最终返回的值表示我们真正想让child的top变成的值
*/
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return 0;
}
/**
* 当View位置改变的时候执行
* @param changedView 当前位置改变的View
* @param left 当前VIew改变后最新的left
* @param top 当前VIew改变后最新的top
* @param dx 本次移动的水平距离
* @param dy 本次移动的垂直距离
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
// Log.e("tag","left: "+left + " dx: "+dx);
//根据dx,让main进行伴随的移动
if(changedView==menu){
//手动让menu固定在原点位置
menu.layout(0,0,menu.getMeasuredWidth(),menu.getBottom());
int newLeft = main.getLeft()+dx;
//对newLeft进行限制
newLeft = clampLeft(newLeft);
main.layout(newLeft,0,newLeft+main.getMeasuredWidth(),
main.getBottom());
}
//执行动画
//1.获取main拖动的百分比:0-1f
float fraction = main.getLeft()*1f / maxLeft;
//2.根据百分比去执行一些列的伴随的动画
execAnim(fraction);
//3.回调接口的方法
if(listener!=null){
listener.onSliding(fraction);
if(fraction==0f){
listener.onClose();
}else if(fraction==1f){
listener.onOpen();
}
}
}
/**
* 手指抬起的时候执行
* @param releasedChild 抬起的那个子View
* @param xvel x方向滑动的速度,单位是px/s
* @param yvel y方向滑动的速度
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
// Log.e("tag","xvel: "+xvel);
if(main.getLeft()>maxLeft/2){
//要open
openMenu();
}else {
//close
closeMenu();
}
}
};
/**
* 执行伴随动画
* @param fraction
*/
private void execAnim(float fraction) {
//fraction:0f - 1f
//main执行缩放动画
//scale: 1f - 0.8f
//算法: startVal + (endVal-startVal)*fraction
float scale = floatEval.evaluate(fraction,1f,0.8f);
main.setScaleY(scale);
main.setScaleX(scale);
//menu执行缩放
menu.setScaleX(floatEval.evaluate(fraction,0.3f,1f));
menu.setScaleY(floatEval.evaluate(fraction,0.3f,1f));
//menu执行平移
menu.setTranslationX(floatEval.evaluate(fraction,-menu.getMeasuredWidth()/2,0));
//立体3D效果
// main.setRotationY(floatEval.evaluate(fraction,0,90));
// menu.setRotationY(floatEval.evaluate(fraction,-90,0));
//给SlideMenu的背景图片添加阴影遮罩效果
if(getBackground()!=null){
int color = (int) argbEval.evaluate(fraction,Color.BLACK,Color.TRANSPARENT);
getBackground().setColorFilter(color,PorterDuff.Mode.SRC_OVER);
}
}
/**
* 关闭菜单
*/
public void closeMenu() {
dragHelper.smoothSlideViewTo(main,0,0);
ViewCompat.postInvalidateOnAnimation(SlideMenu.this);
}
/**
* 打开菜单
*/
public void openMenu() {
// scroller.startScroll();
// invalidate();
//ViewDragHelper的写法是这样滴:
dragHelper.smoothSlideViewTo(main,maxLeft,0);
ViewCompat.postInvalidateOnAnimation(SlideMenu.this);
}
// Scroller scroller = null;
@Override
public void computeScroll() {
super.computeScroll();
// if(scroller.computeScrollOffset()){
// //如果动画还没有结束
// scrollTo(scroller.getCurrX(),scroller.getCurrY());
// invalidate();
// }
if(dragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(SlideMenu.this);
}
}
/**
* 判断left的值
* @param left
* @return
*/
private int clampLeft(int left) {
if(left<0){
left = 0;
}else if(left>maxLeft){
left = maxLeft;
}
return left;
}
OnSlideListener listener;
public void setOnSlideListener(OnSlideListener listener){
this.listener = listener;
}
//定义回调接口
public interface OnSlideListener{
void onSliding(float fraction);
void onOpen();
void onClose();
}
}
import android.animation.ObjectAnimator;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.BounceInterpolator;
import android.view.animation.CycleInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import butterknife.Bind;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity {
@Bind(R.id.menu_listview)
ListView menuListview;
@Bind(R.id.iv_head)
ImageView ivHead;
@Bind(R.id.main_listview)
ListView mainListview;
@Bind(R.id.slidemenu)
SlideMenu slidemenu;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
//填充数据
mainListview.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1
, Constant.NAMES));
menuListview.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1
, Constant.sCheeseStrings) {
@NonNull
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView view = (TextView) super.getView(position, convertView, parent);
view.setTextColor(Color.WHITE);//偷梁换柱
return view;
}
});
//添加滑动监听器
slidemenu.setOnSlideListener(new SlideMenu.OnSlideListener() {
@Override
public void onSliding(float fraction) {
ivHead.setRotation(720*fraction);
}
@Override
public void onOpen() {
Toast.makeText(MainActivity.this, "芝麻开门!", Toast.LENGTH_SHORT).show();
}
@Override
public void onClose() {
Toast.makeText(MainActivity.this, "芝麻关门-----!", Toast.LENGTH_SHORT).show();
ViewCompat.animate(ivHead)
.translationX(60)
// .setInterpolator(new CycleInterpolator(4))//循环执行
// .setInterpolator(new OvershootInterpolator(4))//超过一点再回来
.setInterpolator(new BounceInterpolator())//当当当当~~~~
.setDuration(1000)
.start();
}
});
}
}
仿qq的侧拉菜单效果的更多相关文章
- Android仿QQ ios dialog,仿QQ退出向上菜单
Android仿QQ ios dialog,仿QQ退出向上菜单 EasyDialog两种模式 仿QQ退出向上菜单,自己定义向上菜单 github地址:https://gith ...
- 使用DrawerLayout实现QQ5.0侧拉菜单效果
在上一篇文章中,我们介绍了怎么使用DrawerLayout来实现一个简单的侧拉菜单(使用DrawerLayout实现侧拉菜单),也就是我们常说的抽屉效果,GitHub上类似效果的实现方式非常多,实现出 ...
- Android开发技巧——使用Dialog实现仿QQ的ActionSheet菜单
最近看到有人用Dialog来实现QQ的仿ActionSheet的自定义菜单,对于自己没实现过的一些控件,看着也想实现一下.于是动手了一下,发现也不难,和大家分享一下. 本文原创,转载请注明出处:htt ...
- 仿qq最新侧滑菜单
为了后续对这个项目进行优化,比如透明度动画.背景图的位移动画,以及性能上的优化. 我把这个项目上传到github上面,请大家随时关注. github地址https://github.com/sungu ...
- 仿QQ空间长图效果简易版--母亲节感恩
手机网站 母亲节最火的两件事 1.NBA 杜兰特在获MVP催泪致辞献给母亲:她才是真的MVP. 2.QQ空间长图 ------------------------------------------- ...
- Android自定义View实现仿QQ实现运动步数效果
效果图: 1.attrs.xml中 <declare-styleable name="QQStepView"> <attr name="outerCol ...
- android桌面悬浮窗仿QQ手机管家加速效果
主要还是用到了WindowManager对桌面悬浮进行管理. 需要一个火箭的悬浮窗 一个发射台悬浮窗 ,判断火箭是否放到了发射台,如果放上了,则使用AsyTask 慢慢将火箭的图片往上移.结束后., ...
- 史上最简单,一步集成侧滑(删除)菜单,高仿QQ、IOS。
重要的话 开头说,not for the RecyclerView or ListView, for the Any ViewGroup. 本控件不依赖任何父布局,不是针对 RecyclerView. ...
- android开发学习 ------- 仿QQ侧滑效果的实现
需要做一个仿QQ侧滑删除的一个效果: 一开始是毫无头绪,百度找思路,找到 https://blog.csdn.net/xiaxiazaizai01/article/details/53036994 ...
随机推荐
- 【原版的:参赛作品】窥秘懒---android打开下拉手势刷新时代
小飒的成长史原创作品:窥视懒人的秘密---android下拉刷新开启手势的新纪元转载请注明出处 **************************************************** ...
- centos6.4搭建apache+mysql+php环境
最近用php做的项目到了项目部署的时候,服务器为centos6.4系统,为了快捷部署,采用yum安装部署 大部分内容参考博客 http://blog.sina.com.cn/s/blog_c02ed ...
- Centos中如何配置Texlive2013中文字体的问题
Centos中如何配置Texlive2013中文字体的问题: 第一步是下载你需要的字体,我从windows/fonts中拷贝的比较多,你只要复制你需要的字体即可. 注意只要文件扩展名为ttf的文件,t ...
- NFC高级
高级 NFC 本文档介绍了高级的NFC主题,如各种标签技术,NFC标签写入和前台发布,它允许即使当其他应用程序过滤器相同的时候,应用程序在前台处理Intent. Tag技术支持工作 当使NFC Tag ...
- 《剑指Offer》面试题-从头到尾打印链表
题目描述: 输入一个链表,从尾到头打印链表每个节点的值. 输入: 每个输入文件仅包含一组测试样例.每一组测试案例包含多行,每行一个大于0的整数,代表一个链表的节点.第一行是链表第一个节点的值,依次类推 ...
- iOS基础 - CALayer
一.CALayer简介 Core Animation是跨平台的,支持iOS环境和Mac OS X环境 凡是支持跨平台的框架,都不能直接使用UIKit框架,因为UIKit框架只能应用在iOS而不能用于M ...
- 任务分配book
上次写某道findpath的时候,没有写个二分答案没有过,之后就发现自己貌似一开始学分治那一块的时候就没有把二分学好....就打算重新回顾一下 这道题还算水,就是太久没有写模拟wa了3次....对自己 ...
- net破解一(反编译,反混淆-剥壳,工具推荐)
net破解一(反编译,反混淆-剥壳,工具推荐) 大家好,前段时间做数据分析,需要解析对方数据,而数据文件是对方公司内部的生成方式,完全不知道它是怎么生成的. 不过还好能拿到客户端(正好是C#开发)所以 ...
- python学习之路三(文件读写)
# -*- coding: utf-8 -* ''' Created on 2013-7-29 @author: lixingle ''' import os #引入操作文件和目录的函数包 impor ...
- asp.net缓存(转载)
由于工作的需要,最近对.net缓存做了相关了解和学习.做以下分类: 客户端缓存 第三方的缓存 服务器缓存 客户端缓存 客户端缓存主要是指浏览器帮我们缓存一些页面组件包括脚本,样式,图片等等,由于客户端 ...