虽然QQ5.0已经过去很久了,但是有些特效还是值得学习的

效果:

 源码点我

导入的jar包,

一个是高版本的support.v4包,需要这个v4包中有ViewDragHelper.

我这里使用的是support-v4:24.1.1

还要添加一个nineoldandroids的jar包,这是一个开源的动画库,使用方便。

GitHub地址:https://github.com/JakeWharton/NineOldAndroids

项目源码里也有这些

布局主要分为菜单界面和主界面

layout_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/my_layout"android:background="#ffffff"android:orientation="vertical" ><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="50dp"android:background="#18B4ED" ><ImageViewandroid:layout_width="30dp"android:layout_marginLeft="15dp"android:id="@+id/iv_head"android:layout_height="30dp"android:layout_centerVertical="true"android:background="@drawable/head" /></RelativeLayout><ListView android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/main_listview"android:listSelector="@android:color/transparent"></ListView></LinearLayout>

预览效果:

layout/layout_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:paddingLeft="20dp"android:paddingTop="50dp"><ImageViewandroid:layout_width="50dp"android:layout_height="50dp"android:src="@drawable/head" /><ListViewandroid:id="@+id/menu_listview"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="5dp"android:listSelector="@android:color/transparent" ></ListView></LinearLayout>

预览

最后activity_main.xml

这里设置背景图片

<com.example.xw.qqslidemenu.SlideMenu xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/slideMenu"android:background="@drawable/bg"><!-- 菜单的布局 --><include layout="@layout/layout_menu"/><!-- 主界面的布局 --><include layout="@layout/layout_main"/></com.example.xw.qqslidemenu.SlideMenu>

接下来开始写我们的自定义SlideMenu

1.继承FrameLayout,这里利用FrameLayout的特性,方便

2.实现Drag拖拉跟随即动画,缩放等

3.onTouchEvent的事件处理

首先上代码,再来分析

import android.content.Context;
import android.support.v4.graphics.ColorUtils;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;import com.nineoldandroids.animation.FloatEvaluator;
import com.nineoldandroids.animation.IntEvaluator;
import com.nineoldandroids.view.ViewHelper;import java.net.InterfaceAddress;/*** Created by xw on 2016/8/21.*/
public class SlideMenu extends FrameLayout {private View menuView;//菜单的viewprivate View mainView;//主界面的viewprivate ViewDragHelper viewDragHelper;private int width;private float dragRange;//拖拽范围private FloatEvaluator floatEvaluator;//float的计算器private IntEvaluator intEvaluator;//int的计算器private OnDragStateChangeListener listener;//回调监听器//定义状态常量enum DragState{Open,Close;}private DragState currentState = DragState.Close;//当前SlideMenu的状态默认是关闭的public SlideMenu(Context context) {super(context);init();}public SlideMenu(Context context, AttributeSet attrs) {super(context, attrs);init();}public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {viewDragHelper=ViewDragHelper.create(this,callback);floatEvaluator = new FloatEvaluator();intEvaluator = new IntEvaluator();}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return viewDragHelper.shouldInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {viewDragHelper.processTouchEvent(event);return true;}private ViewDragHelper.Callback callback=new ViewDragHelper.Callback() {/***用于判断是否捕获当前child的触摸事件* chid:当前触摸的子View* return true:捕获并处理 false:不处理*/@Overridepublic boolean tryCaptureView(View child, int pointerId) {return child==menuView||child==mainView;}/*** 获取view水平方向的拖拽范围,但是目前不能限制边界* 返回值目前用在手指抬起的时候view缓慢移动的动画世界的计算上面* 最好不能返回0* @param child* @return*/@Overridepublic int getViewHorizontalDragRange(View child) {return (int) dragRange;}/*** 控制child在水平方向的移动 left:* @param child* @param left 表示你想让child的left改变的值,left=child.getLeft+dx* @param dx 本次移动距离* @return 表示你真正想让child的left变成的值*/@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) {if(child==mainView){if (left<0) left=0; //限制左边if(left>dragRange) left= (int) dragRange; //限制右边
           }return left;}@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {if(changedView==menuView){//固定住menuViewmenuView.layout(0,0,menuView.getMeasuredWidth(),menuView.getMeasuredHeight());//让mainView移动起来int newLeft=mainView.getLeft()+dx;if(newLeft<0) newLeft=0;if(newLeft>dragRange) newLeft= (int) dragRange;mainView.layout(newLeft,mainView.getTop()+dy,newLeft+mainView.getMeasuredWidth(),mainView.getBottom()+dy);}//1,计算滑动的百分比float fraction =mainView.getLeft()/dragRange;//2执行伴随动画
            executeAnim(fraction);if(fraction==0 && currentState!=DragState.Close){//更改状态为关闭,并回调关闭的方法currentState = DragState.Close;if(listener!=null)listener.onClose();}else if (fraction==1f && currentState!=DragState.Open) {//更改状态为打开,并回调打开的方法currentState = DragState.Open;if(listener!=null)listener.onOpen();}//将drag的fraction暴漏给外界if(listener!=null){listener.onDraging(fraction);}}@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) {if(mainView.getLeft()<dragRange/2){close();}else{open();}//处理用户的稍微滑动if(xvel>200 && currentState!=DragState.Open){open();}else if (xvel<-200 && currentState!=DragState.Close) {close();}}};private void open() {viewDragHelper.smoothSlideViewTo(mainView, (int) dragRange,0);ViewCompat.postInvalidateOnAnimation(SlideMenu.this);}private void close() {viewDragHelper.smoothSlideViewTo(mainView,0,0);ViewCompat.postInvalidateOnAnimation(SlideMenu.this);}public void executeAnim(float fraction) {//缩小mainViewViewHelper.setScaleX(mainView,floatEvaluator.evaluate(fraction,1f,0.8f));ViewHelper.setScaleY(mainView,floatEvaluator.evaluate(fraction,1f,0.8f));//移动menuViewViewHelper.setTranslationX(menuView,intEvaluator.evaluate(fraction,-menuView.getMeasuredWidth()/2,0));//放大menuViewViewHelper.setScaleX(menuView,floatEvaluator.evaluate(fraction,0.5f,1f));ViewHelper.setScaleY(menuView,floatEvaluator.evaluate(fraction,0.5f,1f));//改变menuView的透明度
ViewHelper.setAlpha(menuView,floatEvaluator.evaluate(fraction,0.3f,1f));}public  void computeScroll(){if(viewDragHelper.continueSettling(true)){ViewCompat.postInvalidateOnAnimation(SlideMenu.this);}}@Overrideprotected void onFinishInflate() {super.onFinishInflate();if(getChildCount()!=2){throw new IllegalArgumentException("Slide should have two children");}menuView=getChildAt(0);mainView=getChildAt(1);}/*** 该方法在onMeasure()方法执行完成之后完成,* 可以在该方法初始化自己的宽高和子View的宽高*/@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);width=getMeasuredWidth();dragRange=width*0.6f;}public void setOnDragStateChangeListener(OnDragStateChangeListener listener){this.listener = listener;}interface OnDragStateChangeListener{void onOpen();void onClose();void onDraging(Float fraction);}
}

第一步:

    

   @Overrideprotected void onFinishInflate() {super.onFinishInflate();if(getChildCount()!=2){throw new IllegalArgumentException("Slide should have two children");}menuView=getChildAt(0);mainView=getChildAt(1);}/*** 该方法在onMeasure()方法执行完成之后完成,* 可以在该方法初始化自己的宽高和子View的宽高*/@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);width=getMeasuredWidth();dragRange=width*0.6f;}

得到两个子View,和得到width和拖动范围dragRange

第二步

初始化ViewDragHelper

private void init(){viewDragHelper = ViewDragHelper.create(this, callback);floatEvaluator = new FloatEvaluator();intEvaluator = new IntEvaluator();}

写ViewDragHelper.Callback callback

按照顺序复写方法:

1.捕获menuView或者mainView

/*** 用于判断是否捕获当前child的触摸事件 * child: 当前触摸的子View * return: true:就捕获并解析 false:不处理*/@Overridepublic boolean tryCaptureView(View child, int pointerId) {return child==menuView || child==mainView;}

2.设置水平方向移动范围

public int getViewHorizontalDragRange(View child) {return (int) dragRange;}

3.设置移动方向和限制边界

/*** 控制child在水平方向的移动 left:* 表示ViewDragHelper认为你想让当前child的left改变的值,left=chile.getLeft()+dx dx:* 本次child水平方向移动的距离 return: 表示你真正想让child的left变成的值*/public int clampViewPositionHorizontal(View child, int left, int dx) {if(child==mainView){if(left<0)left=0;//限制mainView的左边if(left>dragRange)left=(int) dragRange;//限制mainView的右边
            }return left;}

4,实现伴随移动和根据移动百分比执行动画,关于接口回调方法和执行动画代码参考一开始的全代码

 @Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {if(changedView==menuView){//固定住menuViewmenuView.layout(0,0,menuView.getMeasuredWidth(),menuView.getMeasuredHeight());//让mainView移动起来int newLeft=mainView.getLeft()+dx;if(newLeft<0) newLeft=0;if(newLeft>dragRange) newLeft= (int) dragRange;mainView.layout(newLeft,mainView.getTop()+dy,newLeft+mainView.getMeasuredWidth(),mainView.getBottom()+dy);}//1,计算滑动的百分比float fraction =mainView.getLeft()/dragRange;//2执行伴随动画
            executeAnim(fraction);if(fraction==0 && currentState!=DragState.Close){//更改状态为关闭,并回调关闭的方法currentState = DragState.Close;if(listener!=null)listener.onClose();}else if (fraction==1f && currentState!=DragState.Open) {//更改状态为打开,并回调打开的方法currentState = DragState.Open;if(listener!=null)listener.onOpen();}//将drag的fraction暴漏给外界if(listener!=null){listener.onDraging(fraction);}}

5.释放时根据位置或者滑动加速度控制菜单是打开还是关闭

 @Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) {if(mainView.getLeft()<dragRange/2){close();}else{open();}//处理用户的稍微滑动if(xvel>200 && currentState!=DragState.Open){open();}else if (xvel<-200 && currentState!=DragState.Close) {close();}}

6最后别忘了拦截Touch事件

 @Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return viewDragHelper.shouldInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {viewDragHelper.processTouchEvent(event);return true;}

7.

public  void computeScroll(){if(viewDragHelper.continueSettling(true)){ViewCompat.postInvalidateOnAnimation(SlideMenu.this);}}

最后加上回调接口,用枚举表示状态

最后给listview填充数据,及实现imageView的一些特效

MainActivity

import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.CycleInterpolator;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;import com.nineoldandroids.view.ViewHelper;
import com.nineoldandroids.view.ViewPropertyAnimator;import java.util.Random;public class MainActivity extends AppCompatActivity {private ListView mainlv;private  ListView menulv;private SlideMenu slideMenu;private ImageView iv_head;public static final String[] sCheeseStrings = {"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi","Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale","Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese","Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell","Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc","Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese","Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza","Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley","Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano"};public static final String[] NAMES = new String[] { "宋江", "卢俊义", "吴用","公孙胜", "关胜", "林冲", "秦明", "呼延灼", "花荣", "柴进", "李应", "朱仝", "鲁智深","武松", "董平", "张清", "杨志", "徐宁", "索超", "戴宗", "刘唐", "李逵", "史进", "穆弘","雷横", "李俊", "阮小二", "张横", "阮小五", " 张顺", "阮小七", "杨雄", "石秀", "解珍"," 解宝", "燕青", "朱武", "黄信", "孙立", "宣赞", "郝思文", "韩滔", "彭玘", "单廷珪","魏定国", "萧让", "裴宣", "欧鹏", "邓飞", " 燕顺", "杨林", "凌振", "蒋敬", "吕方","郭 盛", "安道全", "皇甫端", "王英", "扈三娘", "鲍旭", "樊瑞", "孔明", "孔亮", "项充","李衮", "金大坚", "马麟", "童威", "童猛", "孟康", "侯健", "陈达", "杨春", "郑天寿","陶宗旺", "宋清", "乐和", "龚旺", "丁得孙", "穆春", "曹正", "宋万", "杜迁", "薛永", "施恩",};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv_head= (ImageView) findViewById(R.id.iv_head);mainlv= (ListView) findViewById(R.id.main_listview);menulv= (ListView) findViewById(R.id.menu_listview);mainlv.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,NAMES));menulv.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,sCheeseStrings){@Override//改变textView颜色public View getView(int position, View convertView, ViewGroup parent) {TextView textView = (TextView) super.getView(position, convertView, parent);textView.setTextColor(Color.WHITE);return textView;}});slideMenu= (SlideMenu) findViewById(R.id.slideMenu);slideMenu.setOnDragStateChangeListener(new SlideMenu.OnDragStateChangeListener() {@Overridepublic void onOpen() {menulv.smoothScrollToPosition(new Random().nextInt(menulv.getCount()));}@Overridepublic void onClose() {ViewPropertyAnimator.animate(iv_head).translationXBy(15).setInterpolator(new CycleInterpolator(4)).setDuration(500).start();}@Overridepublic void onDraging(Float fraction) {ViewHelper.setAlpha(iv_head,1-fraction);}});}
}

效果

观察效果的同时,我们也发现了BUG,放我们切换到菜单界面时,我们main_view的listview还是可以滑动的,这肯定是不符合期望的,

我们应该在菜单打开时,禁止main_view的listview滑动。

如何去禁止listviiew滑动?listview的父亲LinearLayout拦截并消费滑动事件!

根据什么判断是否拦截?根据SlideMenu的状态,是打开还是关闭。

所以要实现一个自定义的LinearLayout

在这之前,需要SlideMenu提供一个暴露状态的方法

添加方法

 /*** 获取当前的状态* @return*/public DragState getCurrentState(){return currentState;}

MyLinearLayout

package com.example.xw.qqslidemenu;import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout;/*** 当slideMenu打开的时候,拦截并消费掉触摸事件* */
public class MyLinearLayout extends LinearLayout {public MyLinearLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public MyLinearLayout(Context context, AttributeSet attrs) {super(context, attrs);}public MyLinearLayout(Context context) {super(context);}private SlideMenu slideMenu;public void setSlideMenu(SlideMenu slideMenu){this.slideMenu = slideMenu;}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {if(slideMenu!=null && slideMenu.getCurrentState()== SlideMenu.DragState.Open){//如果slideMenu打开则应该拦截并消费掉事件return true;}return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {if(slideMenu!=null && slideMenu.getCurrentState()== SlideMenu.DragState.Open){//如果slideMenu打开则应该拦截并消费掉事件return true;}return super.onTouchEvent(event);}
}

最后将layout_main的最外层修改为我们的自定义LinearLayout

在MianActivity中根据id找到MyLinearLayout,设置setSlideMenu传入我们的SlideMenu当参数。

完成。

转载于:https://www.cnblogs.com/xurui1995/p/5798631.html

QQ 5.0的一些特效学习 一相关推荐

  1. OAuth2.0授权码模式学习

    OAuth2.0授权码模式学习 四种授权方式 1,授权码模式 2,简化模式 3,密码模式 4,客户端模式 授权码模式 四种授权模式中最完成,最严密的授权. (1)用户访问客户端,后者将前者导入认证服务 ...

  2. 三郎前端特效学习源代码:图片主页轮播组件

    三郎前端特效学习源代码:图片主页轮播组件 简单介绍 效果图 源代码 html部分 js部分 css部分 简单介绍 各大网站都比较常用的主页轮播组件 可以自己改改就能用 效果图 源代码 html部分 & ...

  3. Qt 实现 QQ 9.0版 自定义菜单控件

    #简述 重新最近开始了QQ最新版 9.0 界面的模仿,前几天搞了一个QQ登陆界面的动画效果详情见 QQ 9.0 新版登录窗口登录特效 ,今晚Qt技术学习班分享了QQ 9.0版本的自定义菜单控件,通过Q ...

  4. houdini特效学习总结

    houdini特效学习总结 这绝对会是一篇长长长长长长长长长长长长长长长长长长长长--到废骚话满篇的博客         自进入houdini的坑,不知不觉已经一年了,这一年里,跌跌撞撞遇到了不少的困 ...

  5. 三郎前端特效学习源代码:魔法旋转粒子动态渐变特效

    三郎前端特效学习源代码:魔法旋转粒子动态渐变特效 简单介绍 效果图 源代码 html部分 js部分 第二个js部分 简单介绍 类似电影里的魔法效果 轨迹次数速度都可以自己改改 效果图 源代码 html ...

  6. html5 特效 背景 腾讯,html5腾讯QQ登录界面背景动画特效

    特效描述:html5 腾讯QQ 登录界面 背景动画特效.腾讯QQ登陆界面动态背景,直接从腾讯网站获取,js代码有加密,做了个简单地示例 代码结构 1. 引入JS 2. HTML代码 *{margin: ...

  7. Qt 实现 QQ 9.0 新版登录窗口

    简述 QQ的界面又进行了一波更新,登录界面的样式换了一种全新的效果.一直很喜欢QQ的界面,所以进行了一波模仿. 点击登录,登录成功之后,右侧会出现一条伸出的竖线,然后窗口会自动向右移动直到窗口完全隐藏 ...

  8. Blender3.0动画制作入门学习教程 Learn Animation with Blender (2021)

    要求 下载并安装Blender.免费下载和免费用于任何目的. 描述 加入我的动画课程. 在本课程中,我将从头开始讲述在Blender中创建动画场景的过程. 从第一步到最终渲染.在这个课程中,我们将使用 ...

  9. Unity粒子系统创建VFX游戏特效学习教程 Visual Effects in Unity Particle Systems [Beginner’s Guide]

    在Unity中学习高级粒子系统和视觉效果创建.初级到中级 你会学到: 游戏的视觉效果 Unity粒子系统 Unity中的Vfx 创建Unity视觉效果的初级到中级指南 课程获取:Unity粒子系统创建 ...

  10. UE5虚幻引擎5中的实时特效学习 Introduction to real time FX in Unreal Engine 5

    MP4 |视频:h264,1280×720 |音频:AAC,44.1 KHz,2 Ch 语言:英语+中英文字幕(根据原英文字幕机译更准确) |时长:40节课(3h 36m) |大小解压后:2.65 G ...

最新文章

  1. 大型AI已有自主意识了?LeCun开喷Open AI首席科学家
  2. velocimeter-view android测速仪效果
  3. 超简易复制Model对象(为后续备忘录设计模式博文做铺垫)
  4. docker-macvlan网络
  5. 32位python和64位python区别_python32位和64位有什么区别
  6. [51nod1514] 美妙的序列
  7. 故障:OfficeScan Server 中的 DbServer.exe 占用 CPU 过高
  8. 计算机专业建设会议纪要,智能控制教研室会议纪要6号
  9. linux ansys172 卸载,基于ANSYS 经典界面的密封垫片的加载-卸载仿真
  10. cisco服务器桌面命令行窗口,WLC调试和显示命令
  11. 火车头采集器计划任务设置时间间隔无效问题解决
  12. .frm mysql_mysqlfrm使用
  13. 奈奎斯特定理和香农定理
  14. 编程实现Z=5X+3Y+10,设已知变量和结果均放在数据段
  15. 《Windows 8 权威指南》——2.7 降低功耗,延长续航时间才是王道
  16. 微信小程序开发——上课摇号系统的开发
  17. 用 LaTeX 写漂亮学位论文(from wloo)
  18. String index out of range: -824264796 不明的原因导致驱动程序造成失败,请回报这个例外。
  19. 武汉纺织大学计算机科学校区在哪,武汉纺织大学是一本吗 重点专业是什么 有几个校区及校区地址...
  20. VUE课堂笔记1-课前准备

热门文章

  1. MacBook Air 过热降温技巧
  2. mac电脑上的效率工具:alfred 4
  3. 强大的代码编辑工具:Nova for mac v7.3中文版
  4. 如何在Mac上禁用iCloud驱动器?
  5. mac苹果电脑如何查看mac地址
  6. 如何批量更改Mac视频帧速率
  7. [Jobdu] 题目1037:Powerful Calculator
  8. 举个栗子看如何做MySQL 内核深度优化 1
  9. 学习笔记之正则表达式
  10. MVC中的service controller 有状态,无状态Bean线程安全