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

转载请标明出处:

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

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

(一).前言:

这两天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. 重写事件拦截处理方法onInterceptTouch和onTouchEvent
  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

同时欢迎大家star和fork整个开源快速开发框架项目~

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

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

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

关注我的微博,可以获得更多精彩内容

时间: 12-08

【FastDev4Android框架开发】打造QQ6.X最新版本侧滑界面效果(三十八)的相关文章

【FastDev4Android框架开发】CardView完全解析与RecyclerView结合使用(三十二)

转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50000733 本文出自:[江清清的博客] (一).前言: 作为Android L开始,Google更新的除了RecyclerView之外的另一控件就是CardView,其中Google官方应用Google Now就采用了CardView控件,下面我们详细了解一下CardView和使用方法. (二).基本介绍: CardView继承自FrameLayout,可以让我们

【FastDev4Android框架开发】RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout(三十一)

转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/49992269 本文出自:[江清清的博客] (一).前言: [好消息]个人网站已经上线运行,后面博客以及技术干货等精彩文章会同步更新,请大家关注收藏:http://www.lcode.org 话说RecyclerView已经面市很久,也在很多应用中得到广泛的使用,在整个开发者圈子里面也拥有很不错的口碑,那说明RecyclerView拥有比ListView,GridVi

QT开发(三十八)——Model/View框架编程

QT开发(三十八)--Model/View框架编程 一.自定义模型 1.自定义只读模型 QAbstractItemModel为自定义模型提供了一个足够灵活的接口,能够支持数据源的层次结构,能够对数据进行增删改操作,还能够支持拖放.QT提供了 QAbstarctListModel和QAbstractTableModel两个类来简化非层次数据模型的开发,适合于结合列表和表格使用. 自定义模型需要考虑模型管理的的数据结构适合的视图的显示方式.如果模型的数据仅仅用于列表或表格的显示,那么可以使用QAbs

【FastDev4Android框架开发】神器ViewGragHelper完全解析之详解实现QQ5.X侧滑酷炫效果(三十四)

转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50043159 本文出自:[江清清的博客] (一).前言: 这几天正在更新录制实战项目,整体框架是采用仿照QQ5.X侧滑效果的.那么我们一般的做法就是自定义ViewGroup或者采用开源项目MenuDrawer或者Google提供的控件DrawerLayout等方式来实现.这些的控件的很多效果基本上都是采用实现onInterceptTouchEvent和onTouch

【FastDev4Android框架开发】RecyclerView完全解析之结合AA(Android Annotations)注入框架实例(三十)

(一).前言: 话说RecyclerView已经面市很久,也在很多应用中得到广泛的使用,在整个开发者圈子里面也拥有很不错的口碑,那说明RecyclerView拥有比ListView,GridView之类控件有很多的优点,例如:数据绑定,Item View创建,View的回收以及重用等机制.本系列文章会包括到以下三个部分: RecyclerView控件的基本使用,包括基础,进阶,高级部分,动画之类(点击进入) RecyclerView控件的实战实例(点击进入) RecyclerView控件集合AA

【FastDev4Android框架开发】实例解析之SwipeRefreshLayout+RecyclerView+CardView(三十五)

转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/50087873 本文出自:[江清清的博客] (一).前言: 作为Android L开始,Google更新了新控件RecyclerView和CardView,这两个控件在之前的文章中已经做了详细介绍和使用,同时在前面还对下拉刷新组件SwipeRefreshLayout进行相关讲解.本来该专题不在更新了,正好昨天有一个群友问到了怎么样结合SwipeRefreshLayou

应用程序框架实战三十八:项目示例VS解决方案的创建(一)

进行项目开发的第一步,是创建出适合自己团队习惯的VS解决方案,虽然我已经提供了项目示例,但毕竟是我创建的,你直接使用可能并不合适,另外你如果尝试模仿重新创建该示例,中间可能碰到各种障碍,特别是项目间的依赖关系. 本文的目的是帮助.Net架构初学者能顺利搭建起适合自己的VS解决方案,我会在本文演示曾经用过的几种不同风格的目录结构,你可以根据自己的习惯选择一种并自行修改. 本系列假定你已经熟悉如何创建.NET类库等基础知识,并具有.Net开发经验,我不会详细到每一个细节.如果你是.Net初学者,尚未

Dynamic CRM 2013学习笔记(三十八)流程1 - 操作(action)开发与配置详解

CRM 2013 里流程有4个类别:操作(action).业务流程(business process flow).对话(dialog)和工作流(workflow).它们都是从 setting –> Process 进入,然后点击New按钮来创建: 这篇主要介绍操作:什么是操作.什么时候使用操作.如何创建以及如何调用 一.什么是操作 操作是CRM 2013 新增加的一个功能,用来扩展系统的标准功能.业务人员可以用它来实现业务逻辑,然后开发人员可以在系统事件里(比如update,create)来使用

ioS开发知识(三十八)