仿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 ...
随机推荐
- 将DataTable内容导出到Excel表格的两种方法
方法一:循环DataTable单元格内容拼接字符串,利用StreamWriter的Write方法将字符串写入Excel文件中 这种方法很实现很简单.拼接字符串时,每个单元格之间添加'\t'(表示一个占 ...
- beanutils中WrapDynaBean
public class Emp { private String firstName="李"; private String lastName; public ...
- iOS后向兼容:如何发现过期接口
以4.3以下兼容性为例,在项目预编译头文件(xx.pch)中加入如下代码: #import <Availability.h> #define __AVAILABILITY_INTERNAL ...
- Confluence, JIRA, Fisheye
[tools]迁移Confluence, JIRA, Fisheye [背景] 原先的Confluence, JIRA, Fisheye都部署在一台服务器(192.168.200.203)上,导致 ...
- Plan : 破晓
题记 : 不要因为走的太远而忘记自己为什么而出发. 1. 白书(算法竞赛入门经典)看完(每一句话都要读懂) 2. 每次听完课把当天内容复习完(自习室10点以后复习) 3. 微机实验要提前预习(把实验报 ...
- Glue4Net简单部署基于win服务的Socket程序
smark 专注于高并发网络和大型网站架规划设计,提供.NET平台下高吞吐的网络通讯应用技术咨询和支持 Glue4Net简单部署基于win服务的Socket程序 在写一些服务应用的时候经常把要它部署到 ...
- 通过反射生成SQL的例子
全文摘自http://www.cnblogs.com/g1mist/p/3227290.html,很好的一个实例. 反射提供了封装程序集.模块和类型的对象.您可以使用反射动态地创建类型的实例,将类型绑 ...
- elasticsearch文档-analysis
elasticsearch文档-analysis analysis 基本概念 全文搜索引擎会用某种算法对要建索引的文档进行分析, 从文档中提取出若干Token(词元), 这些算法称为Tokeniz ...
- 温故而知新之java的collection framwork
经常用到的List,Map等这些数据结构,都是来自于java的util包下,而java对于其整体设计,简称为collection framwork.(ps.其实,Map接口并不继承自collectio ...
- SharePoint SPHierarchyDataSourceControl+SPTreeView
今天使用SPHierarchyDataSourceControl和SPTreeView来显示SharePoint文档库层级结构的过程中,发现一直在报下面的错误: The target 'ctl00$c ...