zoukankan      html  css  js  c++  java
  • 侧滑

    一、概述

    转自:http://blog.csdn.net/lmj623565791/article/details/47396187; 

    中间拖了蛮长时间了,在上一篇我们介绍了ViewDragHelper,详情:ViewDragHelper完全解析,当然了,上一篇都是小示例的形式去演示代码功能,并不能给人一种实用的感觉。那么,本篇博客就准备实用ViewDragHelper来实现一个DrawerLayout的效果,当然了,大家也可以选择直接去看Drawerlayout的源码。相信侧滑大家肯定不陌生,网络上流传无数个版本,其实利用ViewDragHelper就能方便的写出比较不错的侧滑代码~

    那么首先上个效果图: 

    ok,其实和DrawerLayout效果类似,当然不是完全的一致~~本篇的主要目的也是演示VDH的实战用法,大家在选择侧滑的时候还是建议使用DrawerLayout.

    二、实战

    (一)布局文件

    首先看一下布局文件:

    <com.zhy.learn.view.LeftDrawerLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"                                   android:layout_width="match_parent"
    android:id="@+id/id_drawerlayout"                                     android:layout_height="match_parent"
        >
    
        <!--content-->
        <RelativeLayout
            android:clickable="true"
            android:layout_width="match_parent"
            android:background="#44ff0000"
            android:layout_height="match_parent">
    
            <TextView
                android:id="@+id/id_content_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="hello world"
                android:textSize="40sp"
                android:layout_centerInParent="true"/>
        </RelativeLayout>
    
        <!--menu-->
        <FrameLayout
            android:id="@+id/id_container_menu"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            >
        </FrameLayout>
    
    
    </com.zhy.learn.view.LeftDrawerLayout>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    ok,可以看到我们的LeftDrawerLayout里面一个是content,一个是menu~为了方便,我们就默认child0为content,child1为menu。

    (二)LeftDrawerLayout

    package com.zhy.learn.view;
    
    import android.content.Context;
    import android.support.v4.widget.ViewDragHelper;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    
    import com.zhy.util.common.L;
    
    /**
     * Created by zhy on 15/5/29.
     */
    public class LeftDrawerLayout extends ViewGroup
    {
        private static final int MIN_DRAWER_MARGIN = 64; // dp
        /**
         * Minimum velocity that will be detected as a fling
         */
        private static final int MIN_FLING_VELOCITY = 400; // dips per second
    
        /**
         * drawer离父容器右边的最小外边距
         */
        private int mMinDrawerMargin;
    
        private View mLeftMenuView;
        private View mContentView;
    
        private ViewDragHelper mHelper;
        /**
         * drawer显示出来的占自身的百分比
         */
        private float mLeftMenuOnScrren;
    
    
        public LeftDrawerLayout(Context context, AttributeSet attrs)
        {
            super(context, attrs);
            //setup drawer's minMargin
            final float density = getResources().getDisplayMetrics().density;
            final float minVel = MIN_FLING_VELOCITY * density;
            mMinDrawerMargin = (int) (MIN_DRAWER_MARGIN * density + 0.5f);
    
            mHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback()
            {
                @Override
                public int clampViewPositionHorizontal(View child, int left, int dx)
                {
                    int newLeft = Math.max(-child.getWidth(), Math.min(left, 0));
                    return newLeft;
                }
    
                @Override
                public boolean tryCaptureView(View child, int pointerId)
                {
                    L.e("tryCaptureView");
                    return child == mLeftMenuView;
                }
    
                @Override
                public void onEdgeDragStarted(int edgeFlags, int pointerId)
                {
                    L.e("onEdgeDragStarted");
                    mHelper.captureChildView(mLeftMenuView, pointerId);
                }
    
                @Override
                public void onViewReleased(View releasedChild, float xvel, float yvel)
                {
                    L.e("onViewReleased");
                    final int childWidth = releasedChild.getWidth();
                    float offset = (childWidth + releasedChild.getLeft()) * 1.0f / childWidth;
                    mHelper.settleCapturedViewAt(xvel > 0 || xvel == 0 && offset > 0.5f ? 0 : -childWidth, releasedChild.getTop());
                    invalidate();
                }
    
                @Override
                public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy)
                {
                    final int childWidth = changedView.getWidth();
                    float offset = (float) (childWidth + left) / childWidth;
                    mLeftMenuOnScrren = offset;
                    //offset can callback here 
                    changedView.setVisibility(offset == 0 ? View.INVISIBLE : View.VISIBLE);
                    invalidate();
                }
    
                @Override
                public int getViewHorizontalDragRange(View child)
                {
                    return mLeftMenuView == child ? child.getWidth() : 0;
                }
            });
            //设置edge_left track
            mHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
            //设置minVelocity
            mHelper.setMinVelocity(minVel);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
        {
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    
            setMeasuredDimension(widthSize, heightSize);
    
            View leftMenuView = getChildAt(1);
            MarginLayoutParams lp = (MarginLayoutParams)
                    leftMenuView.getLayoutParams();
    
            final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec,
                    mMinDrawerMargin + lp.leftMargin + lp.rightMargin,
                    lp.width);
            final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec,
                    lp.topMargin + lp.bottomMargin,
                    lp.height);
            leftMenuView.measure(drawerWidthSpec, drawerHeightSpec);
    
    
            View contentView = getChildAt(0);
            lp = (MarginLayoutParams) contentView.getLayoutParams();
            final int contentWidthSpec = MeasureSpec.makeMeasureSpec(
                    widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
            final int contentHeightSpec = MeasureSpec.makeMeasureSpec(
                    heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
            contentView.measure(contentWidthSpec, contentHeightSpec);
    
            mLeftMenuView = leftMenuView;
            mContentView = contentView;
    
    
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b)
        {
            View menuView = mLeftMenuView;
            View contentView = mContentView;
    
            MarginLayoutParams lp = (MarginLayoutParams) contentView.getLayoutParams();
            contentView.layout(lp.leftMargin, lp.topMargin,
                    lp.leftMargin + contentView.getMeasuredWidth(),
                    lp.topMargin + contentView.getMeasuredHeight());
    
            lp = (MarginLayoutParams) menuView.getLayoutParams();
    
            final int menuWidth = menuView.getMeasuredWidth();
            int childLeft = -menuWidth + (int) (menuWidth * mLeftMenuOnScrren);
            menuView.layout(childLeft, lp.topMargin, childLeft + menuWidth,
                    lp.topMargin + menuView.getMeasuredHeight());
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev)
        {
            boolean shouldInterceptTouchEvent = mHelper.shouldInterceptTouchEvent(ev);
            return shouldInterceptTouchEvent;
        }
    
    
        @Override
        public boolean onTouchEvent(MotionEvent event)
        {
            mHelper.processTouchEvent(event);
            return true;
        }
    
    
        @Override
        public void computeScroll()
        {
            if (mHelper.continueSettling(true))
            {
                invalidate();
            }
        }
    
        public void closeDrawer()
        {
            View menuView = mLeftMenuView;
            mLeftMenuOnScrren = 0.f;
            mHelper.smoothSlideViewTo(menuView, -menuView.getWidth(), menuView.getTop());
        }
    
        public void openDrawer()
        {
            View menuView = mLeftMenuView;
            mLeftMenuOnScrren = 1.0f;
            mHelper.smoothSlideViewTo(menuView, 0, menuView.getTop());
        }
    
        @Override
        protected LayoutParams generateDefaultLayoutParams()
        {
            return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        }
    
        public LayoutParams generateLayoutParams(AttributeSet attrs)
        {
            return new MarginLayoutParams(getContext(), attrs);
        }
    
        protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p)
        {
            return new MarginLayoutParams(p);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212

    哈,很少的代码就完成了我们的侧滑的编写,目测如果使用横向的LinearLayout代码更短。构造方法中,主要就是初始化我们的mDragHelper了,接下来onMeasure和onLayout都比较简单,onLayout也只是去将我们的menu设置到屏幕的左侧以至于不可见。onInterceptTouchEventonTouchEvent中都只需要简单的调用mDragHelper的方法。那么核心代码都在我们的ViewDragHelper.Callback的实例中了。注意一点:我们LeftDrawerLayout默认的LayoutParams为MarginLayoutParams,注意复写相关方法。

    接下来就把注意力放到我们的Callback中,我尽可能的按照调用的逻辑来解释各个方法:

    • onEdgeDragStarted 如果你仔细的看过上篇,那么一定知道这个方法的回调位置;因为我们的View不可见,所以我们没有办法直接通过触摸到它来把menu设置为captureView,所以我们只能设置边界检测,当MOVE时回调onEdgeDragStarted时,我们直接通过captureChildView手动捕获。

    • tryCaptureView 那么既然我们手动捕获了,为什么还需要这个方法呢?主要是因为当Drawer拉出的时候,我们通过拖拽Drawer也能进行移动菜单。

    • clampViewPositionHorizontal 主要是移动的时候去控制控制范围,可以看到我们的int newLeft = Math.max(-child.getWidth(), Math.min(left, 0));一定>=-child.getWidth() && <=0 。

    • getViewHorizontalDragRange 为什么要复写,我们上篇已经描述过,返回captureView的移动范围。

    • onViewReleased 则是释放的时候触发的,我们计算当前显示的百分比,以及加速度来决定是否显示drawer,从代码可以看出,当xvel > 0 || xvel == 0 && offset > 0.5f显示我们的菜单,其他情况隐藏。这里注意一点xvel的值只有大于我们设置的minVelocity才会出现大于0,如果小于我们设置的值则一直是0。

    • onViewPositionChanged 整个pos变化的过程中,我们计算offset保存,这里可以使用接口将offset回调出去,方便做动画。

    ok,我们重写的所有方法描述完成了~那么博文主要内容也就结束了~哈~是不是 so easy ~!

    呃,忘了MainActivity和Fragment的代码了~~按顺序贴

    (三)LeftDrawerLayoutActivity

    package com.imooc.testandroid;
    
    import android.os.Bundle;
    import android.support.v4.app.FragmentManager;
    import android.support.v7.app.ActionBarActivity;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.widget.TextView;
    
    import com.zhy.learn.view.LeftDrawerLayout;
    
    
    public class LeftDrawerLayoutActivity extends ActionBarActivity
    {
    
        private LeftMenuFragment mMenuFragment;
        private LeftDrawerLayout mLeftDrawerLayout ;
        private TextView mContentTv ;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_left_drawer_layout);
    
            mLeftDrawerLayout = (LeftDrawerLayout) findViewById(R.id.id_drawerlayout);
            mContentTv = (TextView) findViewById(R.id.id_content_tv);
    
            FragmentManager fm = getSupportFragmentManager();
            mMenuFragment = (LeftMenuFragment) fm.findFragmentById(R.id.id_container_menu);
            if (mMenuFragment == null)
            {
                fm.beginTransaction().add(R.id.id_container_menu, mMenuFragment = new LeftMenuFragment()).commit();
            }
    
            mMenuFragment.setOnMenuItemSelectedListener(new LeftMenuFragment.OnMenuItemSelectedListener()
            {
                @Override
                public void menuItemSelected(String title)
                {
                    mLeftDrawerLayout.closeDrawer();
                    mContentTv.setText(title);
                }
            });
    
        }

    可以看到drawer使用了一个Fragment ~~

    (四) LeftMenuFragment

    
    
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.v4.app.ListFragment;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ListView;
    
    /**
     * Created by zhy on 15/4/26.
     */
    public class LeftMenuFragment extends ListFragment {
    
        private static final int SIZE_MENU_ITEM = 3;
    
        private MenuItem[] mItems = new MenuItem[SIZE_MENU_ITEM];
    
        private LeftMenuAdapter mAdapter;
    
    
        private LayoutInflater mInflater;
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            mInflater = LayoutInflater.from(getActivity());
    
            MenuItem menuItem = null;
            for (int i = 0; i < SIZE_MENU_ITEM; i++) {
                menuItem = new MenuItem(getResources().getStringArray(R.array.array_left_menu)[i], false, R.drawable.music_36px, R.drawable.music_36px_light);
                mItems[i] = menuItem;
            }
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            return super.onCreateView(inflater, container, savedInstanceState);
        }
    
        @Override
        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            view.setBackgroundColor(0xffffffff);
            setListAdapter(mAdapter = new LeftMenuAdapter(getActivity(), mItems));
    
        }
    
        @Override
        public void onListItemClick(ListView l, View v, int position, long id) {
            super.onListItemClick(l, v, position, id);
    
            if (mMenuItemSelectedListener != null) {
                mMenuItemSelectedListener.menuItemSelected(((MenuItem) getListAdapter().getItem(position)).text);
            }
            mAdapter.setSelected(position);
        }
    
    
        //选择回调的接口
        public interface OnMenuItemSelectedListener {
            void menuItemSelected(String title);
        }
        private OnMenuItemSelectedListener mMenuItemSelectedListener;
    
        public void setOnMenuItemSelectedListener(OnMenuItemSelectedListener menuItemSelectedListener) {
            this.mMenuItemSelectedListener = menuItemSelectedListener;
        }
    }
    
    
    
    public class MenuItem
    {
    
        public MenuItem(String text, boolean isSelected, int icon, int iconSelected) {
            this.text = text;
            this.isSelected = isSelected;
            this.icon = icon;
            this.iconSelected = iconSelected;
        }
    
        boolean isSelected;
        String text;
        int icon;
        int iconSelected;
    }
    
    
    
    /**
     * Created by zhy on 15/4/26.
     */
    public class LeftMenuAdapter extends ArrayAdapter<MenuItem> {
    
    
        private LayoutInflater mInflater;
    
        private int mSelected;
    
    
        public LeftMenuAdapter(Context context, MenuItem[] objects) {
            super(context, -1, objects);
    
            mInflater = LayoutInflater.from(context);
    
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
    
    
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.item_left_menu, parent, false);
            }
    
            ImageView iv = (ImageView) convertView.findViewById(R.id.id_item_icon);
            TextView title = (TextView) convertView.findViewById(R.id.id_item_title);
            title.setText(getItem(position).text);
            iv.setImageResource(getItem(position).icon);
            convertView.setBackgroundColor(Color.TRANSPARENT);
    
            if (position == mSelected) {
                iv.setImageResource(getItem(position).iconSelected);
                convertView.setBackgroundColor(getContext().getResources().getColor(R.color.state_menu_item_selected));
            }
    
            return convertView;
        }
    
        public void setSelected(int position) {
            this.mSelected = position;
            notifyDataSetChanged();
        }
    
    
    }
    
    

    贴了一大串,主要就是Fragment的代码,内部有个ListView,所以还有个Adapter,这个Fragment在之前的博文中出现过,搬过来而已。item的布局文件就是一个TextView和ImageView~~不贴了~~大家自己下载源码~

    源码点击下载(导入方式注意看下ReadMe)

  • 相关阅读:
    进程-线程-消息队列
    用Ogre实现《天龙八部》场景中水面(TerrainLiquid)详解
    TCP协议三次握手过程分析【图解,简单清晰】
    excel批量删除sql语句
    批量删除指定盘指定格式文件
    Linux命令速查手册(第2版)学习
    List、Map、Set 三个接口,存取元素时,各有什么特点
    HashMap 什么时候进行扩容呢
    两个对象值相同 (x.equals(y) == true),但却可有不同的 hash code,这句话对不对?
    ArrayList,Vector, LinkedList 的存储性能和特性
  • 原文地址:https://www.cnblogs.com/zhang--xiang/p/4731856.html
Copyright © 2011-2022 走看看