保存到桌面加入收藏设为首页
IOS开发
当前位置:首页 > IOS开发

DragLayout: QQ5.0侧拉菜单的新特效-安度博客

时间:2019-02-04 14:58:00   作者:   来源:   阅读:66   评论:0
内容摘要:一、项目概要  1.1 项目效果如图:      1.2 需要使用到的技术    ViewDragHelper:要实现和QQ5.0侧滑的nk" target="_blank">特效,需要借助谷歌在2013年I/O大会上发布的ViewDragHe......
  • 一、项目概要

      1.1 项目效果如图:

      gif

      

      1.2 需要使用到的技术

         ViewDragHelper: 要实现和QQ5.0侧滑的nk" target="_blank">特效,需要借助谷歌在2013年I/O大会上发布的ViewDragHelper类,提供这个类目的就是为了解决拖拽滑动问题

      1.3 侧滑菜单的实现方式

        1. SlidingMenu 第三方库

        2. DrawerLayout v4包中的类

        3. 自定义控件 

      1.4 一些回调方法 

        - tryCaptureView: 用来决定是否可以拖动
        - clampViewPositionHorizontal: 用来设置子控件将要显示的位置 [限制子控件拖动的范围]
        - getViewHorizontalDragRange:返回水平方向拖动的最大范围返回大于0的值才可以拖动
        - onViewPositionchanged: 位置改变时调用 [关联菜单与主界面的滑动,监听拖动状态,伴随动画]
        - onViewReleased: 拖动结束后,松开手时调用 [平滑地打开或关闭侧滑菜单]

    二、项目实现

      2.1 创建DragLayout

    1 public class DragLayout extends frameLayout {2 public DragLayout(Context context) {3 super(context);4 }5 public DragLayout(Context context AttributeSet attrs) {6 super(context attrs);7 }8 }

        2.2 创建侧滑面板布局

     1 <com.xiaowu.draglayout.view.DragLayout 2 xmlns:android=&#39;http://schemas.android.com/apk/res/android&#39; 3 xmlns:tools=&#39;http://schemas.android.com/tools&#39; 4 android:id=&#39;@+id/drag_layout&#39; 5 android:layout_width=&#39;match_parent&#39; 6 android:layout_height=&#39;match_parent&#39; 7 android:background=&#39;@drawable/bg&#39; > 8 9 <!-- 侧滑菜单布局 -->10 <LinearLayout11 android:layout_width=&#39;match_parent&#39;12 android:layout_height=&#39;match_parent&#39;13 android:background=&#39;#33ff0000&#39; />14 15 <!-- 主界面布局 -->16 <LinearLayout17 android:layout_width=&#39;match_parent&#39;18 android:layout_height=&#39;match_parent&#39;19 android:background=&#39;#3300ff00&#39; />20 21 </com.xiaowu.draglayout.view.DragLayout>

          2.3 DragLayout的主程序代码,下面代码中有详细的讲解,我就不多分步骤实现了

     1 package com.xiaowu.draglayout.view; 2 3 import android.content.Context; 4 import android.graphics.Color; 5 import android.graphics.PorterDuff; 6 import android.graphics.drawable.Drawable; 7 import android.support.v4.view.ViewCompat; 8 import android.support.v4.widget.ViewDragHelper; 9 import android.util.AttributeSet; 10 import android.view.MotionEvent; 11 import android.view.View; 12 import android.widget.frameLayout; 13 14 /** 15 * Created by ${VINCENT} on 2016/11/8. 16 */ 17 18 public class DragLayout extends frameLayout { 19 20 private ViewDragHelper mViewDragHelper; 21 private View mMenuView; 22 private View mMainView; 23 private int mRange; 24 private int mWidth; 25 private int mHeight; 26 27 public DragLayout(Context context) { 28 super(context); 29 init(); 30 } 31 32 public DragLayout(Context context AttributeSet attrs) { 33 super(context attrs); 34 init(); 35 } 36 37 /** 填充完成后调用此方法 */ 38 @Override 39 protected void onfinishInflate() { 40 super.onfinishInflate(); 41 // 健壮性判断 42 if (getChildCount() < 2) { 43 throw new IllegalStateException(&#39;DrawLayout至少要有两个子控件&#39;); 44 } 45 mMenuView = getChildAt(0); 46 mMainView = getChildAt(1); 47 } 48 49 // step1:创建ViewDragHelper对象 50 private void init() { 51 float sensitivity = 1.0f; //值越大,灵敏度越高 52 mViewDragHelper = ViewDragHelper.create(this sensitivity mCallBack); 53 } 54 55 // step2:由ViewDragHelper决定是否拦截事件 56 @Override 57 public boolean onInterceptTouchEvent(MotionEvent ev) { 58 return mViewDragHelper.shouldInterceptTouchEvent(ev); 59 } 60 61 // step3:把触摸事件交给ViewDragHelper处理 62 @Override 63 public boolean onTouchEvent(MotionEvent event) { 64 mViewDragHelper.processTouchEvent(event); 65 return true; //让mViewDragHelper持续接收到触摸事件 66 } 67 68 // step4:处理ViewDragHelper的Callback方法 69 ViewDragHelper.Callback mCallBack = new ViewDragHelper.Callback() { 70 71 // (1)捕获子控件,返回true表示子控件可以拖动 72 @Override 73 public boolean tryCaptureView(View child int pointerId) { 74 return true; 75 } 76 77 // (2)子控件显示的方向(horizontal vertical) 78 // left: 被拖动控件的将要显示的位置 79 // dx: 位置的偏移量 = left - 当前的left 80 @Override 81 public int clampViewPositionHorizontal(View child int left int dx) { 82 if (child == mMainView) { 83 left = reviseLeft(left); 84 } 85 return left; 86 } 87 88 // (3)返回水平方向拖动的最大范围mRange内部会根据返回值计算动画执行的时间 89 @Override 90 public int getViewHorizontalDragRange(View child) { 91 return mRange; 92 } 93 94 // (4)位置发生改变的回调 95 @Override 96 public void onViewPositionchanged(View changedView int left int top int dx int dy) { 97 // (a) 关联子控件的滑动 98 if (changedView == mMenuView) { 99 // 侧拉菜单界面不变时100 mMenuView.layout(0 0 mWidth mHeight);101 // 主菜单界面的新位置102 int newLeft = mMenuView.getLeft() + dx;103 newLeft = reviseLeft(newLeft);104 mMainView.layout(newLeft 0 mWidth + newLeft mHeight);105 }106 // (b) 事件的监听(打开,拖动,关闭)107 listenDragStatus();108 // (c) 事件伴随的动画109 animateChildren();110 }111 112 // (5) 拖动结束时回调的方法113 // xvel:释放时的回调速度,在这里向右为正114 @Override115 public void onViewReleased(View releasedChild float xvel float yvel) {116 if (xvel > 0) {117 open();118 } else if (xvel == 0 && mMainView.getLeft() > mRange / 2) {119 open();120 } else {121 close();122 }123 }124 };125 126 //============================动画的定义=====================================127 /** 估值器:变化值 = 开始值 + (结束值 - 开始值) * 百分比 */128 public float evaluate(float start float end float percent) {129 return start + (end - start) * percent;130 }131 132 protected void animateChildren() {133 float percent = ((float) mMainView.getLeft()) / mRange;134 135 // 1.主界面的缩放136 mMainView.setScaleX(evaluate(1f 0.8f percent));137 mMainView.setScaleY(evaluate(1f 0.8f percent));138 // 2.侧拉菜单的缩放139 mMenuView.setTranslationX((int) evaluate(-mRange 0 percent)); // 平移140 mMenuView.setScaleX(evaluate(0.5f 1.0f percent));141 mMenuView.setScaleY(evaluate(0.5f 1.0f percent));142 mMenuView.setAlpha(evaluate(0.5f 1.0f percent));143 // 3.背景图片:亮度的变化144 Drawable background = getBackground();145 if (background != null) {146 // 过渡的颜色147 int color = (int)evaluate2(percent Color.BLACK Color.TRANSPARENT);148 background.setColorFilter(color PorterDuff.Mode.SRC_OVER);149 }150 }151 152 /** 处理颜色渐变的兼容性问题 */153 public object evaluate2(float fraction object startValue object endValue) {154 int startInt = (Integer) startValue;155 int startA = (startInt >> 24) & 0xff;156 int startR = (startInt >> 16) & 0xff;157 int startG = (startInt >> 8) & 0xff;158 int startB = startInt & 0xff;159 160 int endInt = (Integer) endValue;161 int endA = (endInt >> 24) & 0xff;162 int endR = (endInt >> 16) & 0xff;163 int endG = (endInt >> 8) & 0xff;164 int endB = endInt & 0xff;165 166 return ((startA + (int)(fraction * (endA - startA))) << 24) |167 ((startR + (int)(fraction * (endR - startR))) << 16) |168 ((startG + (int)(fraction * (endG - startG))) << 8) |169 ((startB + (int)(fraction * (endB - startB))));170 }171 172 //============================状态的监听begin================================173 /** 事件的监听 */174 protected void listenDragStatus() {175 int left = mMainView.getLeft();176 if (left == 0) {177 mCurrentStatus = DragStatus.CLOSE;178 } else if (left == mRange) {179 mCurrentStatus = DragStatus.OPEN;180 } else {181 mCurrentStatus = DragStatus.DRAGGING;182 }183 184 //当事件发生时,调用监听器中的方法185 if (mondragListener != null) {186 if (mCurrentStatus == DragStatus.OPEN) {187 mondragListener.onOpen();188 } else if (mCurrentStatus == DragStatus.CLOSE) {189 mondragListener.onClose();190 } else {191 float percent = ((float) mMainView.getLeft()) / mRange;192 mondragListener.ondragging(percent);193 }194 }195 }196 197 /** 状态的定义 */198 public enum DragStatus {199 OPEN CLOSE DRAGGING200 }201 202 /** 当前的状态 */203 private DragStatus mCurrentStatus = DragStatus.CLOSE;204 205 public DragStatus getCurrentStatus() {206 return mCurrentStatus;207 }208 209 /** 定义接口 */210 public interface ondragListener {211 void onOpen();212 void onClose();213 void ondragging(float percent);214 }215 216 private ondragListener mondragListener;217 218 /** 提供设置监听器的set方法 */219 public void setondragListener(ondragListener ondragListener) {220 this.mondragListener = ondragListener;221 }222 223 //============================状态的监听end================================224 225 @Override226 public void computeScroll() {227 super.computeScroll();228 // 若如果没有移动到正确的位置,需要刷新229 if (mViewDragHelper.continueSettling(true)) {230 ViewCompat.postInvalidateOnAnimation(this);231 }232 }233 234 /** 限定主界面的滑动范围 */235 protected int reviseLeft(int left) {236 if (left < 0) {237 left = 0;238 } else if (left > mRange) {239 left = mRange;240 }241 return left;242 }243 244 /** 控件尺寸发生改变时,回调该方法 */245 @Override246 protected void onSizeChanged(int w int h int oldw int oldh) {247 super.onSizeChanged(w h oldw oldh);248 // 获取DrawLayout的宽高249 mWidth = getMeasuredWidth();250 mHeight = getMeasuredHeight();251 // 拖拽的比例252 mRange = (int) (mWidth * 0.6f);253 }254 255 /** 打开侧拉菜单 */256 protected void open() {257 mViewDragHelper.smoothSlideViewTo(mMainView mRange 0);258 // 刷新界面259 ViewCompat.postInvalidateOnAnimation(this);260 }261 262 /** 关闭侧拉菜单 */263 protected void close() {264 mViewDragHelper.smoothSlideViewTo(mMainView 0 0);265 // 刷新界面266 ViewCompat.postInvalidateOnAnimation(this);267 }268 269 /** 侧滑菜单是否打开 */270 public boolean isOpen() {271 return mCurrentStatus == DragStatus.OPEN;272 }273 274 }

      

       2.4 创建MyLinearLayout.java文件,处理侧拉与主菜单的冲突事件

     1 package com.xiaowu.draglayout.view; 2 3 import android.content.Context; 4 import android.util.AttributeSet; 5 import android.view.MotionEvent; 6 import android.widget.LinearLayout; 7 8 /** 9 * Created by ${VINCENT} on 2016/11/9.10 */11 12 public class MyLinearLayout extends LinearLayout {13 14 private DragLayout mDragLayout;15 16 public MyLinearLayout(Context context) {17 super(context);18 }19 20 public MyLinearLayout(Context context AttributeSet attrs) {21 super(context attrs);22 }23 24 /** 根据它的打开状态决定是否要拦截事件 */25 public void setDragLayout(DragLayout dragLayout) {26 this.mDragLayout = dragLayout;27 }28 29 /** 如果侧滑菜单打开了,禁止主菜单的列表滑动 */30 @Override31 public boolean onInterceptTouchEvent(MotionEvent ev) {32 if (mDragLayout.isOpen()) {33 return true;34 }35 return super.onInterceptTouchEvent(ev);36 }37 38 /** 如果侧滑菜单打开了,消费主菜单的触摸事件,禁止通过滑动主菜单使侧拉菜单的列表滑动 */39 @Override40 public boolean onTouchEvent(MotionEvent event) {41 if (mDragLayout.isOpen()) {42 return true;43 }44 return super.onTouchEvent(event);45 }46 }

       2.5 接下来是MainActivity的代码实现

     1 package com.xiaowu.draglayout; 2 3 import android.graphics.Color; 4 import android.support.v7.app.AppCompatActivity; 5 import android.os.Bundle; 6 import android.view.View; 7 import android.view.ViewGroup; 8 import android.view.Window; 9 import android.widget.ArrayAdapter;10 import android.widget.ImageView;11 import android.widget.ListView;12 import android.widget.TextView;13 import android.widget.Toast;14 15 import com.xiaowu.draglayout.view.DragLayout;16 import com.xiaowu.draglayout.view.MyLinearLayout;17 18 public class MainActivity extends AppCompatActivity {19 20 private ImageView mIvHeader;21 private MyLinearLayout mMyLinearLayout;22 private DragLayout mDragLayout;23 24 @Override25 protected void onCreate(Bundle savedInstanceState) {26 super.onCreate(savedInstanceState);27 requestWindowFeature(Window.FEATURE_NO_TITLE);28 29 setContentView(R.layout.activity_main);30 mIvHeader = (ImageView) findViewById(R.id.iv_header);31 32 initDragLayout();33 mMyLinearLayout = (MyLinearLayout) findViewById(R.id.my_ll);34 // 根据打开的状态决定是否拦截事件35 mMyLinearLayout.setDragLayout(mDragLayout);36 37 initListView();38 }39 40 private void initListView() {41 ListView lvMenu = (ListView) findViewById(R.id.lv_menu);42 ListView lvMain = (ListView) findViewById(R.id.lv_main);43 44 lvMenu.setAdapter(new ArrayAdapter<String>(this android.R.layout.simple_list_item_145 Constant.MENUS){46 @Override47 public View getView(int position View convertView ViewGroup parent) {48 TextView view = (TextView) super.getView(position convertView parent);49 view.setTextSize(dp2px(16));50 view.setTextColor(Color.WHITE);51 return view;52 }53 });54 55 lvMain.setAdapter(new ArrayAdapter<String>(this android.R.layout.simple_list_item_156 Constant.LIST_DATAS));57 58 }59 60 private void initDragLayout() {61 mDragLayout = (DragLayout) findViewById(R.id.drag_layout);62 mDragLayout.setondragListener(new DragLayout.ondragListener() {63 @Override64 public void onOpen() {65 showToast(&#39;打开&#39;);66 }67 68 @Override69 public void onClose() {70 showToast(&#39;关闭&#39;);71 }72 73 @Override74 public void ondragging(float percent) {75 mIvHeader.setAlpha(1 - percent );76 }77 });78 }79 80 /** toast使用单例模式,可以随状态刷新 */81 private Toast mToast;82 83 public void showToast(String msg) {84 if (mToast == null) {85 mToast = Toast.makeText(this msg Toast.LENGTH_LONG);86 }87 mToast.setText(msg);88 mToast.show();89 }90 91 public int dp2px(int dp) {92 float density = this.getResources().getDisplayMetrics().density;93 return (int) (dp * density + 0.5f);94 }95 96 }

       2.6 布局文件的最终完善

     1 <com.xiaowu.draglayout.view.DragLayout 2 xmlns:android=&#39;http://schemas.android.com/apk/res/android&#39; 3 android:id=&#39;@+id/drag_layout&#39; 4 android:layout_width=&#39;match_parent&#39; 5 android:layout_height=&#39;match_parent&#39; 6 android:background=&#39;@drawable/bg&#39;> 7 8 <!-- 侧拉菜单界面 --> 9 <LinearLayout10 android:layout_width=&#39;match_parent&#39;11 android:layout_height=&#39;match_parent&#39;12 android:orientation=&#39;vertical&#39;13 android:gravity=&#39;center_vertical&#39;14 android:padding=&#39;15dp&#39; >15 16 <ImageView17 android:layout_width=&#39;40dp&#39;18 android:layout_height=&#39;40dp&#39;19 android:background=&#39;@drawable/head&#39;/>20 21 <ListView22 android:id=&#39;@+id/lv_menu&#39;23 android:layout_width=&#39;match_parent&#39;24 android:layout_height=&#39;match_parent&#39;25 android:layout_marginTop=&#39;10dp&#39; />26 27 </LinearLayout>28 29 <!-- 主菜单界面 -->30 <com.xiaowu.draglayout.view.MyLinearLayout31 android:id=&#39;@+id/my_ll&#39;32 android:layout_width=&#39;match_parent&#39;33 android:layout_height=&#39;match_parent&#39;34 android:orientation=&#39;vertical&#39;35 android:gravity=&#39;center&#39;36 android:background=&#39;#fff&#39;>37 <RelativeLayout38 android:layout_width=&#39;match_parent&#39;39 android:layout_height=&#39;45dp&#39;40 android:background=&#39;#18B4ED&#39; >41 42 <ImageView43 android:id=&#39;@+id/iv_header&#39;44 android:layout_width=&#39;40dp&#39;45 android:layout_height=&#39;40dp&#39;46 android:layout_centerVertical=&#39;true&#39;47 android:layout_marginLeft=&#39;10dp&#39;48 android:background=&#39;@drawable/head&#39; />49 </RelativeLayout>50 51 <ListView52 android:id=&#39;@+id/lv_main&#39;53 android:layout_width=&#39;match_parent&#39;54 android:layout_height=&#39;match_parent&#39;55 android:layout_weight=&#39;1&#39; />56 57 </com.xiaowu.draglayout.view.MyLinearLayout>58 59 </com.xiaowu.draglayout.view.DragLayout>

       2.7 Constant静态类可以自己定义,不过我还是善良的贴了出来

    package com.xiaowu.draglayout;public class Constant { /** 菜单列表数据 */ public static final String[] MENUS = new String[] { &#39;纸杯蛋糕[Cupcake]&#39; &#39;甜甜圈[Donut]&#39; &#39;闪电泡芙[Eclair]&#39; &#39;冻酸奶[Froyo]&#39; &#39;姜饼[Gingerbread]&#39; &#39;蜂巢[Honeycomb]&#39; &#39;冰淇淋三明治[Ice Cream Sandwich]&#39; &#39;果冻豆[Jelly Bean]&#39; &#39;奇巧[KitKat]&#39; &#39;棒棒糖[Lollipop]&#39; &#39;姜饼[Gingerbread]&#39; &#39;蜂巢[Honeycomb]&#39; &#39;冰淇淋三明治[Ice Cream Sandwich]&#39; &#39;果冻豆[Jelly Bean]&#39; &#39;奇巧[KitKat]&#39; &#39;棒棒糖[Lollipop]&#39; &#39;棉花糖[Marshmallow]&#39; }; /** 列表数据1 */ public static final String[] LIST_DATAS = { &#39;API1--1.0 [没有开发代号]&#39; &#39;API2--1.1 Petit Four&#39; &#39;API3--1.5 Cupcake&#39; &#39;API4--1.6 Donut&#39; &#39;API5--2.0 Eclair&#39; &#39;API6--2.0.1 Eclair&#39; &#39;API7--2.1 Eclair&#39; &#39;API8--2.2 - 2.2.3 Froyo&#39; &#39;API9--2.3 - 2.3.2 Gingerbread&#39; &#39;API10--2.3.3-2.3.7 Gingerbread&#39; &#39;API11--3.0 Honeycomb&#39; &#39;API12--3.1 Honeycomb&#39; &#39;API13--3.2 Honeycomb&#39; &#39;API14--4.0 - 4.0.2 Ice Cream Sandwich&#39; &#39;API15--4.0.3 - 4.0.4 Ice Cream Sandwich&#39; &#39;API16--4.1 Jelly Bean&#39; &#39;API17--4.2 Jelly Bean&#39; &#39;API18--4.3 Jelly Bean&#39; &#39;API19--4.4 KitKat&#39; &#39;API20--4.4W&#39; &#39;API21--5.0 Lollipop&#39; &#39;API22--5.1 Lollipop&#39; &#39;API23--6.0 Marshmallow&#39; };}

    三、一些可以借鉴的东西

      *比如使用Toast的时候可以采用单例模式,使得Toast可以随时改变,而不会产生停顿延迟的问题(顶部效果图)

     1 /** toast使用单例模式,可以随状态刷新 */ 2 private Toast mToast; 3 4 public void showToast(String msg) { 5 if (mToast == null) { 6 mToast = Toast.makeText(this msg Toast.LENGTH_LONG); 7 } 8 mToast.setText(msg); 9 mToast.show();10 }

    四、提供给博友我的源代码

        **下载链接:http://pan.baidu.com/s/1o8k4cZo  密码:m0fl


本站所有站内信息仅供娱乐参考,不作任何商业用途,不以营利为目的,专注分享快乐,欢迎收藏本站!
所有信息均来自:百度一下 (威尼斯人官网)