• 【FastDev4Android框架开发】打造QQ6.X最新版本号側滑界面效果(三十八)


    转载请标明出处:

    http://blog.csdn.net/developer_jiangqq/article/details/50253925

    本文出自:【江清清的博客】


    ().前言:   

             【好消息】个人站点已经上线执行,后面博客以及技术干货等精彩文章会同步更新。请大家关注收藏:http://www.lcode.org

            这两天QQ进行了重大更新(6.X)尤其在UI风格上面由之前的蓝色换成了白色居多了,側滑效果也发生了一些变化,那我们今天来模仿实现一个QQ6.X版本号的側滑界面效果。

    今天我们还是採用神器ViewDragHelper来实现。之前我们曾经基于ViewDragHelper的使用和打造QQ5.X效果了。基本用法能够点击以下的连接:

            假设对于ViewDragHelper不是特别了解的朋友能够查看上面的文章学习一下。

            本次实例详细代码已经上传到以下的项目中。欢迎各位去star和fork一下。

             https://github.com/jiangqqlmj/DragHelper4QQ

             FastDev4Android框架项目地址:https://github.com/jiangqqlmj/FastDev4Android

    ().ViewGragHelper的基本使用   

           前面我们学习ViewGragHelper的基本用法。同一时候也知道了里边的若干个方法的用途。以下我们还是把主要的使用步骤温习一下。要使用ViewGragHelper实现子View拖拽移动的过程例如以下:

    1. 创建ViewGragHelper实例(传入Callback)
    2. 重写事件拦截处理方法onInterceptTouchonTouchEvent
    3. 实现Callback,实现当中的相关方法tryCaptureView以及水平或者垂直方向移动的距离方法

          更加详细分析大家能够看前一篇博客,或者我们今天这边会通过详细实例解说一下。

    ().QQ5.X側滑效果实现分析:  

       在正式版本号QQ中的側滑效果例如以下:


                观察上面我们能够理解为两个View,一个是底部的相当于左側功能View,另外一个是上层主功能内容View,我们在上面进行拖拽上层View或者左右滑动的时候。上层和下层的View对应进行滑动以及View大小变化,同一时候增加相关的动画。当然我们点击上层的View能够进行打开或者关闭側滑菜单。

    ().側滑效果自己定义组件实现

             1.首先我们这边集成自FrameLayout创建一个自己定义View  DragLayout

    内部的定义的一些变量例如以下(主要包含一些配置类。手势,ViewDragHelper实例。屏幕宽高,拖拽的子视图View等)

    //是否带有阴影效果
       private boolean isShowShadow = true;
        //手势处理类
        private GestureDetectorCompat gestureDetector;
        //视图拖拽移动帮助类
        private ViewDragHelper dragHelper;
        //滑动监听器
        private DragListener dragListener;
        //水平拖拽的距离
        private int range;
        //宽度
        private int width;
        //高度
        private int height;
       //main视图距离在ViewGroup距离左边的距离
        private int mainLeft;
        private Context context;
        private ImageView iv_shadow;
        //左側布局
        private RelativeLayout vg_left;
       //右側(主界面布局)
        private CustomRelativeLayout vg_main;

       然后在内部还定义了一个回调接口主要处理拖拽过程中的一些页面打开。关闭以及滑动中的事件回调:

     /**
         * 滑动相关回调接口
         */
        public interface DragListener {
            //界面打开
            public void onOpen();
            //界面关闭
            public void onClose();
            //界面滑动过程中
            public void onDrag(float percent);
        }

     2.開始创建ViewDragHelper实例。依旧在自己定义View DragLayout初始化的时候创建。使用ViewGragHelper的静态方法:

     public DragLayout(Context context,AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            gestureDetector = new GestureDetectorCompat(context, new YScrollDetector());
            dragHelper =ViewDragHelper.create(this, dragHelperCallback);
        }

         当中create()方法创建的时候传入了一个dragHelperCallBack回调类,将会在第四点中讲到。

     3.接着须要重写ViewGroup中事件方法,拦截触摸事件给ViewGragHelper内部进行处理,这样达到拖拽移动子View视图的目的;

    /**
         * 拦截触摸事件
         * @param ev
         * @return
         */
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return dragHelper.shouldInterceptTouchEvent(ev) &&gestureDetector.onTouchEvent(ev);
        }
        /**
        * 将拦截的到事件给ViewDragHelper进行处理
         * @param e
         * @return
         */
        @Override
        public boolean onTouchEvent(MotionEvent e){
            try {
                dragHelper.processTouchEvent(e);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return false;
        }

       这边我们在onInterceptTouchEvent拦截让事件从父控件往子View中转移。然后在onTouchEvent方法中拦截让ViewDragHelper进行消费处理。

        4.開始自己定义创建ViewDragHelper.Callback的实例dragHelperCallback分别实现一个抽象方法tryCaptureView以及重写以下若干个方法来实现側滑功能。以下一个个来看一下。

       /**
            * 拦截全部的子View
             * @param child Child the user is attempting to capture
             * @param pointerId ID of the pointer attempting the capture
             * @return
             */
            @Override
            public boolean tryCaptureView(Viewchild, int pointerId) {
                return true;
            }

          该进行拦截ViewGroup(本例中为:DragLayout)中全部的子View,直接返回true,表示全部的子View都能够进行拖拽移动。

     /**
             * 水平方向移动
             * @param child Child view beingdragged
             * @param left Attempted motion alongthe X axis
             * @param dx Proposed change inposition for left
             * @return
             */
            @Override
            public int clampViewPositionHorizontal(View child, int left, int dx) {
                if (mainLeft + dx < 0) {
                    return 0;
                } else if (mainLeft + dx >range) {
                    return range;
                } else {
                    return left;
                }
            }

          实现该方法表示水平方向滑动,同一时候方法中会进行推断边界值,比如当上面的main view已经向左移动边界之外了。直接返回0,表示向左最左边仅仅能x=0。然后向右移动会推断向右最变得距离range。至于range的初始化后边会讲到。

    除了这两种情况之外,就是直接返回left就可以。

          /**
             * 设置水平方向滑动的最远距离
             *@param child Child view to check  屏幕宽度
             * @return
             */
            @Override
            public int getViewHorizontalDragRange(View child) {
                return width;
            }

         该方法有必要实现,由于该方法在Callback内部默认返回0,也就是说,假设的view的click事件为true,那么会出现整个子View没法拖拽移动的情况了。那么这边直接返回left view宽度了,表示水平方向滑动的最远距离了。

    /**
            * 当拖拽的子View。手势释放的时候回调的方法, 然后依据左滑或者右滑的距离进行推断打开或者关闭
             * @param releasedChild
             * @param xvel
             * @param yvel
             */
            @Override
            public void onViewReleased(View releasedChild, float xvel, float yvel) {
                super.onViewReleased(releasedChild,xvel, yvel);
                if (xvel > 0) {
                    open();
                } else if (xvel < 0) {
                    close();
                } else if (releasedChild == vg_main&& mainLeft > range * 0.3) {
                    open();
                } else if (releasedChild == vg_left&& mainLeft > range * 0.7) {
                    open();
                } else {
                    close();
                }
            }

         该方法在拖拽子View移动手指释放的时候被调用,这是会推断移动向左。向右的意图,进行打开或者关闭man view(上层视图)。以下是实现的最后一个方法:onViewPositionChanged

    /**
            * 子View被拖拽 移动的时候回调的方法
             * @param changedView View whoseposition changed
             * @param left New X coordinate of theleft edge of the view
             * @param top New Y coordinate of thetop edge of the view
             * @param dx Change in X position fromthe last call
             * @param dy Change in Y position fromthe last call
             */
            @Override
            public void onViewPositionChanged(View changedView, int left, int top,
                    int dx, int dy) {
                if (changedView == vg_main) {
                    mainLeft = left;
                } else {
                    mainLeft = mainLeft + left;
                }
                if (mainLeft < 0) {
                    mainLeft = 0;
                } else if (mainLeft > range) {
                    mainLeft = range;
                }
     
                if (isShowShadow) {
                    iv_shadow.layout(mainLeft, 0,mainLeft + width, height);
                }
                if (changedView == vg_left) {
                    vg_left.layout(0, 0, width,height);
                    vg_main.layout(mainLeft, 0,mainLeft + width, height);
                }
                dispatchDragEvent(mainLeft);
            }
        };

          该方法是在我们进行拖拽移动子View的过程中进行回调,依据移动坐标位置。然后进行又一次定义left view和main view。同一时候调用dispathDragEvent()方法进行拖拽事件相关处理分发同一时候依据状态来回调接口:

     /**
         * 进行处理拖拽事件
         * @param mainLeft
         */
        private void dispatchDragEvent(int mainLeft) {
            if (dragListener == null) {
                return;
            }
            float percent = mainLeft / (float)range;
           //依据滑动的距离的比例
            animateView(percent);
            //进行回调滑动的百分比
            dragListener.onDrag(percent);
            Status lastStatus = status;
            if (lastStatus != getStatus()&& status == Status.Close) {
                dragListener.onClose();
            } else if (lastStatus != getStatus()&& status == Status.Open) {
                dragListener.onOpen();
            }
        }

      该方法中有一行代码float percent=mainLeft/(float)range;算到一个百分比后面会用到

      5.至于子View布局的获取初始化以及宽高和水平滑动距离的大小设置方法:

    /**
         * 布局载入完毕回调
         * 做一些初始化的操作
         */
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            if (isShowShadow) {
                iv_shadow = new ImageView(context);
               iv_shadow.setImageResource(R.mipmap.shadow);
                LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                addView(iv_shadow, 1, lp);
            }
            //左側界面
            vg_left = (RelativeLayout)getChildAt(0);
           //右側(主)界面
            vg_main = (CustomRelativeLayout)getChildAt(isShowShadow ? 2 : 1);
            vg_main.setDragLayout(this);
            vg_left.setClickable(true);
            vg_main.setClickable(true);
        }
      以及控件大小发生变化回调的方法:
      @Override
        protected void onSizeChanged(int w, int h,int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            width = vg_left.getMeasuredWidth();
            height = vg_left.getMeasuredHeight();
           //能够水平拖拽滑动的距离 一共为屏幕宽度的80%
            range = (int) (width *0.8f);
        }

       在该方法中我们能够实时获取宽和高以及拖拽水平距离。

       6.上面的全部核心代码都为使用ViewDragHelper实现子控件View拖拽移动的方法,可是依据我们这边側滑效果还须要实现动画以及滑动过程中View的缩放效果。所以我们这边引入了一个动画开源库:   


         然后依据前面算出来的百分比来缩放View视图:

    /**
        * 依据滑动的距离的比例,进行平移动画
         * @param percent
         */
        private void animateView(float percent) {
            float f1 = 1 - percent * 0.5f;
     
            ViewHelper.setTranslationX(vg_left,-vg_left.getWidth() / 2.5f + vg_left.getWidth() / 2.5f * percent);
            if (isShowShadow) {
               //阴影效果视图大小进行缩放
                ViewHelper.setScaleX(iv_shadow, f1* 1.2f * (1 - percent * 0.10f));
                ViewHelper.setScaleY(iv_shadow, f1* 1.85f * (1 - percent * 0.10f));
            }
        }

        7.当然除了上面这些还缺少一个效果就是,当我们滑动过程中假如我们手指释放,依照常理来讲view就不会在进行移动了,那么这边我们须要一个加速度当我们释放之后。还能保持一定的速度。该怎么样实现呢?答案就是实现computeScroll()方法。

        

    /**
        * 有加速度,当我们停止滑动的时候,该不会马上停止动画效果
         */
        @Override
        public void computeScroll() {
            if (dragHelper.continueSettling(true)){
               ViewCompat.postInvalidateOnAnimation(this);
            }
        }

        OK上面关于DragLayout的核心代码就差点儿相同这么多了,以下是使用DragLayout类来实现側滑效果啦。

    ().側滑效果组件使用     

           1.首先使用的布局文件例如以下:

    <com.chinaztt.widget.DragLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/dl"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:background="@android:color/transparent"
        >
       <!--下层 左边的布局-->
        <includelayout="@layout/left_view_layout"/>
       <!--上层 右边的主布局-->
       <com.chinaztt.widget.CustomRelativeLayout
           android:layout_width="match_parent"
           android:layout_height="match_parent"
            android:background="#FFFFFF"
            >
            <LinearLayout
               android:layout_width="fill_parent"
               android:layout_height="fill_parent"
               android:orientation="vertical"
                >
            <RelativeLayout
               android:id="@+id/rl_title"
               android:layout_width="match_parent"
               android:layout_height="49dp"
                android:gravity="bottom"
               android:background="@android:color/holo_orange_light"
                 >
                 <includelayout="@layout/common_top_bar_layout"/>
            </RelativeLayout>
           <!--中间内容后面放入Fragment-->
            <FrameLayout
               android:layout_width="fill_parent"
               android:layout_height="fill_parent"
                >
               <fragment
                  android:id="@+id/main_info_fragment"
                  class="com.chinaztt.fragment.OneFragment"
                  android:layout_width="fill_parent"
                  android:layout_height="fill_parent"/>
            </FrameLayout>
            </LinearLayout>
       </com.chinaztt.widget.CustomRelativeLayout>
    </com.chinaztt.widget.DragLayout>

            该布局文件里父层View就是DragLayout,然后内部有两个RelativeLayout布局,分别充当下一层布局和上一层主布局。

         2.以下我们来看一下下层菜单布局,这边我专门写了一个left_view_layout.xml文件,当中主要分为三块,第一块顶部为头像个人基本信息布局,中间为功能入口列表,底部是设置等功能,详细布局代码例如以下:

    <RelativeLayout
       xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:app="http://schemas.android.com/apk/res-auto"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
        android:paddingTop="70dp"
       android:background="@drawable/sidebar_bg"
        >
        <LinearLayout
            android:id="@+id/ll1"
            android:paddingLeft="30dp"
           android:layout_width="fill_parent"
           android:layout_height="wrap_content"
           android:orientation="vertical">
        <!--头像,昵称信息-->
        <LinearLayout
           android:layout_width="match_parent"
            android:layout_height="70dp"
           android:orientation="horizontal"
           android:gravity="center_vertical"
            >
           <com.chinaztt.widget.RoundAngleImageView
               android:id="@+id/iv_bottom"
               android:layout_width="50dp"
               android:layout_height="50dp"
                android:scaleType="fitXY"
               android:src="@drawable/icon_logo"
                app:roundWidth="25dp"
               app:roundHeight="25dp"/>
            <LinearLayout
               android:layout_width="fill_parent"
               android:layout_height="wrap_content"
               android:gravity="center_vertical"
               android:layout_gravity="center_vertical"
               android:orientation="vertical">
                <RelativeLayout
                   android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
                   >
                    <TextView
                       android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:layout_centerVertical="true"
                       android:layout_marginLeft="15dp"
                        android:text="名字:jiangqqlmj"
                       android:textColor="@android:color/black"
                       android:textSize="15sp" />
                    <ImageButton
                       android:layout_alignParentRight="true"
                       android:layout_centerVertical="true"
                       android:layout_marginRight="100dp"
                       android:layout_width="22dp"
                       android:layout_height="22dp"
                       android:background="@drawable/qrcode_selector"/>
                </RelativeLayout>
     
                <TextView
                   android:layout_width="wrap_content"
                   android:layout_height="wrap_content"
                   android:layout_gravity="center_vertical"
                   android:layout_marginLeft="15dp"
                   android:text="QQ:781931404"
                   android:textColor="@android:color/black"
                   android:textSize="13sp" />
            </LinearLayout>
        </LinearLayout>
            <LinearLayout
               android:layout_width="fill_parent"
               android:layout_height="wrap_content"
               android:gravity="center_vertical"
               android:orientation="horizontal">
                <ImageView
                   android:layout_width="17dp"
                   android:layout_height="17dp"
                   android:scaleType="fitXY"
                   android:src="@drawable/sidebar_signature_nor"/>
                <TextView
                   android:layout_marginLeft="5dp"
                   android:textSize="13sp"
                   android:textColor="#676767"
                   android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
                    android:text="用心做产品!"/>
            </LinearLayout>
        </LinearLayout>
     
       <!--底部功能条-->
        <includelayout="@layout/left_view_bottom_layout"
            android:id="@+id/bottom_view"
            />
       <!--中间列表-->
        <ListView
            android:id="@+id/lv"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:layout_above="@id/bottom_view"
           android:layout_below="@id/ll1"
           android:layout_marginBottom="30dp"
           android:layout_marginTop="70dp"
           android:cacheColorHint="#00000000"
           android:listSelector="@drawable/lv_click_selector"
            android:divider="@null"
            android:scrollbars="none"
            android:textColor="#ffffff"/>
    </RelativeLayout>

            该布局还是比較简单的,对于上层主内容布局这边就放一个顶部导航栏和中的Fragment内容信息。留着后期大家功能扩展就可以。

    3.Activity使用例如以下:

    public class MainActivity extends BaseActivity {
    	private DragLayout dl;
    	private ListView lv;
    	private ImageView iv_icon, iv_bottom;
    	private QuickAdapter<ItemBean> quickAdapter;
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		setStatusBar();
    		initDragLayout();
    		initView();
    
    	}
    	private void initDragLayout() {
    		dl = (DragLayout) findViewById(R.id.dl);
    		dl.setDragListener(new DragLayout.DragListener() {
    			//界面打开的时候
    			@Override
    			public void onOpen() {
    			}
    			//界面关闭的时候
    			@Override
    			public void onClose() {
    			}
    
    			//界面滑动的时候
    			@Override
    			public void onDrag(float percent) {
    				ViewHelper.setAlpha(iv_icon, 1 - percent);
    			}
    		});
    	}
    
    	private void initView() {
    		iv_icon = (ImageView) findViewById(R.id.iv_icon);
    		iv_bottom = (ImageView) findViewById(R.id.iv_bottom);
    
    		lv = (ListView) findViewById(R.id.lv);
    		lv.setAdapter(quickAdapter=new QuickAdapter<ItemBean>(this,R.layout.item_left_layout, ItemDataUtils.getItemBeans()) {
    			@Override
    			protected void convert(BaseAdapterHelper helper, ItemBean item) {
    				helper.setImageResource(R.id.item_img,item.getImg())
    						.setText(R.id.item_tv,item.getTitle());
    			}
    		});
    		lv.setOnItemClickListener(new OnItemClickListener() {
    			@Override
    			public void onItemClick(AdapterView<?> arg0, View arg1,
    									int position, long arg3) {
    				Toast.makeText(MainActivity.this,"Click Item "+position,Toast.LENGTH_SHORT).show();
    			}
    		});
    		iv_icon.setOnClickListener(new OnClickListener() {
    			@Override
    			public void onClick(View arg0) {
    				dl.open();
    			}
    		});
    	}
    
    }

     初始化控件,设置滑动监听器,以及左側菜单功能列表设置就可以了,只是上面大家应该看了QuickAdapter的使用,该为BaseAdapterHelper框架使用,我们须要在项目build.gradle中作例如以下配置:


    详细关于BaseAdapter的使用解说博客地址例如以下:

    4.正式执行效果例如以下:


    5.由于这边底层须要ViewDragHelper类,所以大家在使用的时候须要导入V4包的,只是我这边直接把ViewGragHelper类的源码拷贝到项目中了。


    ().DragLayout源码带凝视   

          上面主要分析DragLayout的详细实现。只是我这边也贴一下DragLayout带有凝视的全部源码让大家能够更好的了解DragLayout的详细实现代码:

    /**
     *使用ViewRragHelper实现側滑效果功能
     */
    publicclass DragLayout extends FrameLayout {
        private boolean isShowShadow = true;
        //手势处理类
        private GestureDetectorCompat gestureDetector;
        //视图拖拽移动帮助类
        private ViewDragHelper dragHelper;
        //滑动监听器
        private DragListener dragListener;
        //水平拖拽的距离
        private int range;
        //宽度
        private int width;
        //高度
        private int height;
       //main视图距离在ViewGroup距离左边的距离
        private int mainLeft;
        private Context context;
        private ImageView iv_shadow;
        //左側布局
        private RelativeLayout vg_left;
       //右側(主界面布局)
        private CustomRelativeLayout vg_main;
       //页面状态 默觉得关闭
        private Status status = Status.Close;
     
        public DragLayout(Context context) {
            this(context, null);
        }
     
        public DragLayout(Context context,AttributeSet attrs) {
            this(context, attrs, 0);
            this.context = context;
        }
     
        public DragLayout(Context context,AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            gestureDetector = new GestureDetectorCompat(context, new YScrollDetector());
            dragHelper =ViewDragHelper.create(this, dragHelperCallback);
        }
     
        class YScrollDetector extends SimpleOnGestureListener {
            @Override
            public boolean onScroll(MotionEvent e1,MotionEvent e2, float dx, float dy) {
                return Math.abs(dy) <=Math.abs(dx);
            }
        }
        /**
        * 实现子View的拖拽滑动。实现Callback当中相关的方法
         */
        private ViewDragHelper.Callback dragHelperCallback = new ViewDragHelper.Callback() {
            /**
             * 水平方向移动
             * @param child Child view beingdragged
             * @param left Attempted motion alongthe X axis
             * @param dx Proposed change inposition for left
             * @return
             */
            @Override
            public int clampViewPositionHorizontal(View child, int left, int dx) {
                if (mainLeft + dx < 0) {
                    return 0;
                } else if (mainLeft + dx >range) {
                    return range;
                } else {
                    return left;
                }
            }
     
            /**
            * 拦截全部的子View
             * @param child Child the user isattempting to capture
             * @param pointerId ID of the pointerattempting the capture
             * @return
             */
            @Override
            public boolean tryCaptureView(View child, int pointerId) {
                return true;
            }
            /**
             * 设置水平方向滑动的最远距离
             *@param child Child view to check  屏幕宽度
             * @return
             */
            @Override
            public int getViewHorizontalDragRange(View child) {
                return width;
            }
     
            /**
            * 当拖拽的子View,手势释放的时候回调的方法, 然后依据左滑或者右滑的距离进行推断打开或者关闭
             * @param releasedChild
             * @param xvel
             * @param yvel
             */
            @Override
            public void onViewReleased(View releasedChild, float xvel, float yvel) {
                super.onViewReleased(releasedChild,xvel, yvel);
                if (xvel > 0) {
                    open();
                } else if (xvel < 0) {
                    close();
                } else if (releasedChild == vg_main&& mainLeft > range * 0.3) {
                    open();
                } else if (releasedChild == vg_left&& mainLeft > range * 0.7) {
                    open();
                } else {
                    close();
                }
            }
     
            /**
            * 子View被拖拽 移动的时候回调的方法
             * @param changedView View whoseposition changed
             * @param left New X coordinate of theleft edge of the view
             * @param top New Y coordinate of thetop edge of the view
             * @param dx Change in X position fromthe last call
             * @param dy Change in Y position fromthe last call
             */
            @Override
            public void onViewPositionChanged(View changedView, int left, int top,
                    int dx, int dy) {
                if (changedView == vg_main) {
                    mainLeft = left;
                } else {
                    mainLeft = mainLeft + left;
                }
                if (mainLeft < 0) {
                    mainLeft = 0;
                } else if (mainLeft > range) {
                    mainLeft = range;
                }
     
                if (isShowShadow) {
                    iv_shadow.layout(mainLeft, 0,mainLeft + width, height);
                }
                if (changedView == vg_left) {
                    vg_left.layout(0, 0, width,height);
                    vg_main.layout(mainLeft, 0,mainLeft + width, height);
                }
     
                dispatchDragEvent(mainLeft);
            }
        };
     
        /**
         * 滑动相关回调接口
         */
        public interface DragListener {
            //界面打开
            public void onOpen();
            //界面关闭
            public void onClose();
            //界面滑动过程中
            public void onDrag(float percent);
        }
        public void setDragListener(DragListener dragListener) {
            this.dragListener = dragListener;
        }
     
        /**
         * 布局载入完毕回调
         * 做一些初始化的操作
         */
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            if (isShowShadow) {
                iv_shadow = new ImageView(context);
               iv_shadow.setImageResource(R.mipmap.shadow);
                LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                addView(iv_shadow, 1, lp);
            }
            //左側界面
            vg_left = (RelativeLayout)getChildAt(0);
           //右側(主)界面
            vg_main = (CustomRelativeLayout)getChildAt(isShowShadow ? 2 : 1);
            vg_main.setDragLayout(this);
            vg_left.setClickable(true);
            vg_main.setClickable(true);
        }
     
        public ViewGroup getVg_main() {
            return vg_main;
        }
     
        public ViewGroup getVg_left() {
            return vg_left;
        }
     
        @Override
        protected void onSizeChanged(int w, int h,int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            width = vg_left.getMeasuredWidth();
            height = vg_left.getMeasuredHeight();
           //能够水平拖拽滑动的距离 一共为屏幕宽度的80%
            range = (int) (width * 0.8f);
        }
     
        /**
        * 调用进行left和main 视图进行位置布局
         * @param changed
         * @param left
         * @param top
         * @param right
         * @param bottom
         */
        @Override
        protected void onLayout(boolean changed,int left, int top, int right, int bottom) {
            vg_left.layout(0, 0, width, height);
            vg_main.layout(mainLeft, 0, mainLeft +width, height);
        }
     
        /**
         * 拦截触摸事件
         * @param ev
         * @return
         */
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            returndragHelper.shouldInterceptTouchEvent(ev) &&gestureDetector.onTouchEvent(ev);
        }
     
        /**
        * 将拦截的到事件给ViewDragHelper进行处理
         * @param e
         * @return
         */
        @Override
        public boolean onTouchEvent(MotionEvent e){
            try {
                dragHelper.processTouchEvent(e);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return false;
        }
     
        /**
         * 进行处理拖拽事件
         * @param mainLeft
         */
        private void dispatchDragEvent(intmainLeft) {
            if (dragListener == null) {
                return;
            }
            float percent = mainLeft / (float)range;
            //滑动动画效果
            animateView(percent);
            //进行回调滑动的百分比
            dragListener.onDrag(percent);
            Status lastStatus = status;
            if (lastStatus != getStatus()&& status == Status.Close) {
                dragListener.onClose();
            } else if (lastStatus != getStatus()&& status == Status.Open) {
                dragListener.onOpen();
            }
        }
     
        /**
        * 依据滑动的距离的比例,进行平移动画
         * @param percent
         */
        private void animateView(float percent) {
            float f1 = 1 - percent * 0.5f;
     
            ViewHelper.setTranslationX(vg_left,-vg_left.getWidth() / 2.5f + vg_left.getWidth() / 2.5f * percent);
            if (isShowShadow) {
               //阴影效果视图大小进行缩放
                ViewHelper.setScaleX(iv_shadow, f1* 1.2f * (1 - percent * 0.10f));
                ViewHelper.setScaleY(iv_shadow, f1* 1.85f * (1 - percent * 0.10f));
            }
        }
        /**
        * 有加速度,当我们停止滑动的时候,该不会马上停止动画效果
         */
        @Override
        public void computeScroll() {
            if (dragHelper.continueSettling(true)){
               ViewCompat.postInvalidateOnAnimation(this);
            }
        }
     
        /**
        * 页面状态(滑动,打开,关闭)
         */
        public enum Status {
            Drag, Open, Close
        }
     
        /**
         * 页面状态设置
         * @return
         */
        public Status getStatus() {
            if (mainLeft == 0) {
                status = Status.Close;
            } else if (mainLeft == range) {
                status = Status.Open;
            } else {
                status = Status.Drag;
            }
            return status;
        }
     
        public void open() {
            open(true);
        }
     
        public void open(boolean animate) {
            if (animate) {
               //继续滑动
                if(dragHelper.smoothSlideViewTo(vg_main, range, 0)) {
                   ViewCompat.postInvalidateOnAnimation(this);
                }
            } else {
                vg_main.layout(range, 0, range * 2,height);
                dispatchDragEvent(range);
            }
        }
     
        public void close() {
            close(true);
        }
     
        public void close(boolean animate) {
            if (animate) {
               //继续滑动
                if(dragHelper.smoothSlideViewTo(vg_main, 0, 0)) {
                   ViewCompat.postInvalidateOnAnimation(this);
                }
            } else {
                vg_main.layout(0, 0, width,height);
                dispatchDragEvent(0);
            }
        }
    }

    ().最后总结

               今天我们实现打造QQ最新版本号QQ6.X效果。同一时候里边用到了ViewDragHelper,BaseAdapterHelper的运用。详细该知识点的用法,我已经在我的博客中更新解说的文章,欢迎大家查看。

               本次详细实例凝视过的全部代码已经上传到Github项目中了。同一时候欢迎大家去Github站点进行clone或者下载浏览:

    https://github.com/jiangqqlmj/DragHelper4QQ

    同一时候欢迎大家starfork整个开源高速开发框架项目~

    本例所用其它知识点博文地址例如以下:

     

    特别致谢DragLayout组件开发人员:https://github.com/BlueMor/DragLayout


    尊重原创,转载请注明:From Sky丶清(http://blog.csdn.net/developer_jiangqq) 侵权必究!

    关注我的订阅号,每天分享移动开发技术(Android/IOS),项目管理以及博客文章!


    关注我的微博,能够获得很多其它精彩内容


  • 相关阅读:
    laydate 时间框弹窗点击弹出后立马消失的问题解决
    万字长文:ELK(V7)部署与架构分析
    人声消除的原理算法源码及方案实现
    主动降噪技术(ANC)的前生今世–行业分析
    主动降噪技术(ANC)的前生今世--原理仿真
    主动降噪技术(ANC)的前生今世--概念历史
    音频人生
    解决 Flask-sqlalchemy 中文乱码
    使用 key 登录时分开记录操作历史记录
    OpenSSH 使用技巧
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/6920621.html
Copyright © 2020-2023  润新知