本文转载于:http://www.cnblogs.com/avenwu/archive/2014/04/16/3669367.html

侧拉菜单作为常见的导航交互控件,最开始在没有没有android官方控件时,很多时候都是使用开源的SlidingMenu,一直没机会分析侧拉菜单的实现机理,本文将分析android.support.v4.widget.DrawerLayout的使用及实现。

官方介绍

DrawerLayout acts as a top-level container for window content that allows for interactive "drawer" views to be pulled out from the edge of the window.

Drawer positioning and layout is controlled using the android:layout_gravity attribute
on child views corresponding to which side of the view you want the drawer to emerge from: left or right. (Or start/end on platform versions that support layout direction.)

To use a DrawerLayout, position your primary content view as the first child with a width and height of match_parent.
Add drawers as child views after the main content view and set the layout_gravity appropriately.
Drawers commonly use match_parent for
height with a fixed width.

DrawerLayout.DrawerListener can
be used to monitor the state and motion of drawer views. Avoid performing expensive operations such as layout during animation as it can cause stuttering; try to perform expensive operations during the STATE_IDLE state. DrawerLayout.SimpleDrawerListener offers
default/no-op implementations of each callback method.

As per the Android Design guide, any drawers positioned to the left/start should always contain content for navigating around the application, whereas any drawers positioned to the right/end
should always contain actions to take on the current content. This preserves the same navigation left, actions right structure present in the Action Bar and elsewhere

DrawerLayout直译的事抽屉布局的意思,作为视窗内的顶层容器,它允许用户通过抽屉式的推拉操作,从而把视图视窗外边缘拉到屏幕内,如右图:

抽屉菜单的摆放和布局通过android:layout_gravity属性来控制,可选值为left、right或start、end。通过xml来布局的话,需要把DrawerLayout作为父容器,组界面布局作为其第一个子节点,抽屉布局则紧随其后作为第二个子节点,这样就做就已经把内容展示区和抽屉菜单区独立开来,只需要分别非两个区域设置内容即可。android提供了一些实用的监听器,重载相关的回调方法可以在菜单的交互过程中书写逻辑业务。下面是一个demo布局:

<android.support.v4.widget.DrawerLayout

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:id="@+id/drawer_layout"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context="com.aven.myapplication2.app.MainActivity">

<FrameLayout

android:id="@+id/container"

android:layout_width="match_parent"

android:layout_height="match_parent"/>

<fragmentandroid:id="@+id/navigation_drawer"

android:layout_width="@dimen/navigation_drawer_width"

android:layout_height="match_parent"

android:layout_gravity="start"

android:name="com.aven.myapplication2.app.NavigationDrawerFragment"

tools:layout="@layout/fragment_navigation_drawer"/>

</android.support.v4.widget.DrawerLayout>

 
 
所以DrawerLayout的使用非常简单,和很多容器类布局一样,它本身也继承自ViewGroup,只是在内部实现中会默认将第一个子节点作为内容区,第二个作为抽屉菜单,所以写布局的事后必须牢记,好在现在的IDE已经非常智能,通过引导来创建Drawerlayout时,会自动生成Activity和xml layout布局,比如使用AndroidStudio就非常方便。
 

源码分析

DrawerLayout实例化相关辅助类

既然DrawerLayout使用是作为顶层布局layout,那先看看他的构造函数:

public DrawerLayout(Context context, AttributeSet
attrs, int defStyle) {

super(context, attrs, defStyle);

//根据屏幕分辨率密度计算最小的边距

final float density = getResources().getDisplayMetrics().density;

mMinDrawerMargin = (int) (MIN_DRAWER_MARGIN * density + 0.5f);

final float minVel = MIN_FLING_VELOCITY * density;

//实例化视图滑动的回调接口,包括左右两边

mLeftCallback = new ViewDragCallback(Gravity.LEFT);

mRightCallback = new ViewDragCallback(Gravity.RIGHT);

//创建滑动手势的的辅助类,负责具体的滑动监听实现

mLeftDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mLeftCallback);

mLeftDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);

mLeftDragger.setMinVelocity(minVel);

mLeftCallback.setDragger(mLeftDragger);

mRightDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mRightCallback);

mRightDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_RIGHT);

mRightDragger.setMinVelocity(minVel);

mRightCallback.setDragger(mRightDragger);

// So that we can catch the back button

setFocusableInTouchMode(true);

ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegate());

ViewGroupCompat.setMotionEventSplittingEnabled(this,false);

}

从构造函数中,我们发现有两个关键的类ViewDragCallback, ViewDragHelper,命名上来看前者和滑动的回调相关,后者和view的滑动操作实现有关,所以先看ViewDragHelper。

ViewDragHelper负责实现drag操作

从它的类注释信息中可以看到,这个helper是个辅助类,里面封装了一些便于用户拖动ViewGroup内子view的操作及状态记录方法。

/**

* ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number

* of useful operations and state tracking for allowing a user to drag and reposition

* views within their parent ViewGroup.

*/

 
现在来看看这个helper到底是怎么封装的滑动操作,从上面的实例化我们知道这个helper通过工厂方法来构造实例,工厂方法有两个如下:

/**

* Factory method to create a new ViewDragHelper.

*

* @param forParent Parent view to monitor

* @param cb Callback to provide information and receive events

* @return a new ViewDragHelper instance

*/

public static ViewDragHelper create(ViewGroup
forParent, Callback cb) {

return new ViewDragHelper(forParent.getContext(), forParent, cb);

}

/**

* Factory method to create a new ViewDragHelper.

*

* @param forParent Parent view to monitor

* @param sensitivity Multiplier for how sensitive the helper should be about detecting

*                    the start of a drag. Larger values are more sensitive. 1.0f is normal.

* @param cb Callback to provide information and receive events

* @return a new ViewDragHelper instance

*/

public static ViewDragHelper create(ViewGroup
forParent, float sensitivity, Callback cb) {

final ViewDragHelper helper = create(forParent, cb);

helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));

return helper;

}

这第二个工厂方法create就是刚才看到的上层调用来创建helper实例的,我们传入了一个viewgroup,也就是说helper将持有我们的DrawerLayout实例引用,第二是一个浮点数,和drag操作的敏感性相关,数值越大表示drag操作更易被监听,最后是一个Callback,即ViewDragCallback实例,它本身继承自ViewDragHelper.Callback,现在来看helper的构造方法:

/**

* Apps should use ViewDragHelper.create() to get a new instance.

* This will allow VDH to use internal compatibility implementations for different

* platform versions.

*

* @param context Context to initialize config-dependent params from

* @param forParent Parent view to monitor

*/

private ViewDragHelper(Context context, ViewGroup
forParent, Callback cb) {

if (forParent == null) {

throw new IllegalArgumentException("Parent
view may not be null");

}

if (cb == null) {

throw new IllegalArgumentException("Callback
may not be null");

}

mParentView = forParent;

mCallback = cb;

final ViewConfiguration vc = ViewConfiguration.get(context);

finalfloat density = context.getResources().getDisplayMetrics().density;

mEdgeSize = (int) (EDGE_SIZE * density + 0.5f);

mTouchSlop = vc.getScaledTouchSlop();

mMaxVelocity = vc.getScaledMaximumFlingVelocity();

mMinVelocity = vc.getScaledMinimumFlingVelocity();

mScroller = ScrollerCompat.create(context, sInterpolator);

}

首先需要检测我们传入的DrawerLayout和回调Callback,不允许为空。接下来从ViewConfiguration中获取一些view的默认配置,

vc.getScaledTouchSlop是获取一个pix为单位的距离,代表view在滑动的值;

vc.getScaledMaximumFlingVelocity获取触发view
fling的最大每秒滚动的距离,也是pix为单位;

获取view fling的最小每秒滚动距离,同样pix为单位;

这里有scroll和fling,我的理解是scroll表示手没有离开屏幕产生的滑动效果,二fling则是用力一划,然后view自己开始滚动的效果。

最后实例化一个Scroller,这是专门用来处理滚动的一个类,这里用的是扩展包里的campact类做版本兼容。

到此DrawerLayout已经准备好所有资源,接下来就是手势分发时候的各种调用,这一部分留到下一篇文章在做分析

Source:

git clone https://github.com/avenwu/DrawerDemo.git

作者:小文字
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

继续分析DrawerLayout的手势分发部分

谈到手势分发,这本身就是个好话题,DrawerLayout作为继承自ViewGroup得布局他可以拦截手势也可以分发给子view,也就是在onInterceptTouchEvent中做的操作,但是他的下面还有一个onTouchEvent方法,先看哪个呢?追溯代码我们可以知道ViewGroup继承自View,而onTouchEvent是View的方法

我们还是先花点时间把两者的关系先确认再继续。

onInterceptTouchEvent和onTouchEvent---鸡和蛋?

定位到ViewGroup,可以发现onInterceptTouchEvent分定义如下,从它前面一段非常长的注释就可以看出其重要性和复杂,默认的返回是false

/**

* Implement this method to intercept all touch screen motion events.  This

* allows you to watch events as they are dispatched to your children, and

* take ownership of the current gesture at any point.

*

* <p>Using this function takes some care, as it has a fairly complicated

* interaction with {@link View#onTouchEvent(MotionEvent)

* View.onTouchEvent(MotionEvent)}, and using it requires implementing

* that method as well as this one in the correct way.  Events will be

* received in the following order:

*

* <ol>

* <li> You will receive the down event here.

* <li> The down event will be handled either by a child of this view

* group, or given to your own onTouchEvent() method to handle; this means

* you should implement onTouchEvent() to return true, so you will

* continue to see the rest of the gesture (instead of looking for

* a parent view to handle it).  Also, by returning true from

* onTouchEvent(), you will not receive any following

* events in onInterceptTouchEvent() and all touch processing must

* happen in onTouchEvent() like normal.

* <li> For as long as you return false from this function, each following

* event (up to and including the final up) will be delivered first here

* and then to the target's onTouchEvent().

* <li> If you return true from here, you will not receive any

* following events: the target view will receive the same event but

* with the action {@link MotionEvent#ACTION_CANCEL}, and all further

* events will be delivered to your onTouchEvent() method and no longer

* appear here.

* </ol>

*

* @param ev The motion event being dispatched down the hierarchy.

* @return Return true to steal motion events from the children and have

* them dispatched to this ViewGroup through onTouchEvent().

* The current target will receive an ACTION_CANCEL event, and no further

* messages will be delivered here.

*/

public boolean onInterceptTouchEvent(MotionEvent
ev) {

return false;

}

前两段告诉我们,复写onInterceptTouchEvent方法,可以实现监听所有的动作事件MotionEvent,在向子view传递事件前做我们需要的操作,当然这指的是和这个viewgroup相关的事件;同时我们需要慎重处理该函数,因为他和onTouchEvent关系非常紧密,下面是事件接收的顺序:

首先接收的的事按下事件,down事件,他可以被view处理也可以在自身的onTouchEvent里处理,所以实现onTouchEvent并且返回true,这样onTouchEvent继续才能收到down之后的其他事件,同时onInterceptTouchEvent不会在收到后续事件,因为已经转移到onTouchEvent处理了。

那么什么时候onInterceptTouchEvent会把后续事件转移到他的onTouchEvent呢?这取决于onInterceptTouchEvent的返回值,如果返回false,所有事件都会先分发到这里,然后再到目标view的onTouchEvent;相反如果返回true,那么onInterceptTouchEvent将不再收到后续事件,并且目标view会收到cancel事件,接着自身的onTouchEvent几首后续的事件。

这其实从名字来看是比较好理解的onInterceptTouchEvent表示在截取触摸事件的被调用的方法,既然是截取就可以直接吧事件截下来后不再往后传递,这是就是上面的第二种情况,返回true,即我们自己消耗了触摸事件,子view将没有机会得到唤醒。

大致意思就是如果希望自身消耗掉改事件就可以直接返回true,这一点和onTouchEvent的返回类似目的。

博客园有篇文章对这些事件分发做了很好的分析:http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html

详细的阐述了了dispatchTouchEvent,onInterceptTouchEvent以及onTouchEvent之间的关系

现在我们回过头来看DrawerLayout里的分发是如何写的:

重写了后面两个方法,先看onInterceptTouchEvent:

@Override

public boolean onInterceptTouchEvent(MotionEvent
ev) {

final int action = MotionEventCompat.getActionMasked(ev);

// "|" used deliberately here; both methods should be invoked.

final boolean interceptForDrag = mLeftDragger.shouldInterceptTouchEvent(ev) |

mRightDragger.shouldInterceptTouchEvent(ev);

boolean interceptForTap = false;

switch (action) {

case MotionEvent.ACTION_DOWN: {

final float x = ev.getX();

final float y = ev.getY();

mInitialMotionX = x;

mInitialMotionY = y;

if (mScrimOpacity > 0 &&

isContentView(mLeftDragger.findTopChildUnder((int) x, (int) y))) {

interceptForTap = true;

}

mDisallowInterceptRequested = false;

mChildrenCanceledTouch = false;

break;

}

case MotionEvent.ACTION_MOVE: {

// If we cross the touch slop, don't perform the delayed peek for an edge touch.

if (mLeftDragger.checkTouchSlop(ViewDragHelper.DIRECTION_ALL)) {

mLeftCallback.removeCallbacks();

mRightCallback.removeCallbacks();

}

break;

}

case MotionEvent.ACTION_CANCEL:

case MotionEvent.ACTION_UP: {

closeDrawers(true);

mDisallowInterceptRequested = false;

mChildrenCanceledTouch = false;

}

}

return interceptForDrag || interceptForTap || hasPeekingDrawer() || mChildrenCanceledTouch;

}

1.首先从touch event里面获取当前具体的action动作,MotionEventCompat.getActionMasked(ev),内部实际上做了一次按位于操作event.getAction()
& ACTION_MASK;

2.检查当前是否满足截取drag状态,用于决定onInterceptTouchEvent返回值,这里有个注解说是故意用了|或,而不是||或,两者区别在于||只要第一个条件满足就不在执行第二个检查,二|不同,无论如何都会将两个条件检查一遍;

3.接下来是几个case,根据当前的action做处理;

ACTION_DOWN,当按下时记录按下点的x,y坐标值,根据条件设置当前是否满足tap状态,具体条件有两个,一是mScrimOpacity,表示子view中在屏幕上占据的最大宽度(0-1),二时根据坐标点的位置取得改点对应的最上层view对象,如果是预定义的content
view即DrawerLayout里的主内容展示view,也就是同时满足view在屏幕上且点击的位置直接落在了content view上。

ACTION_MOVE,当手按下后开始在屏幕上移动时,如果垂直和水平上的位移差量达到了drag helper的阀值则一处左右两边的回调接口

ACTION_CANCLE和ACTION_UP,手势结束后,关闭菜单

最后结合几个状态来那个来决定onInterceptTouchEvent返回true还是false,

未完待续

在[UI]抽屉菜单DrawerLayout分析(一)和[UI]抽屉菜单DrawerLayout分析(二)中分别介绍了DrawerLayout得基本框架结构和ViewDragerHelper的作用以及手势分发,本文一起来分析其中的Scroller的使用情况。

在ViewDragerHelper中可以发现private ScrollerCompat mScroller;说明抽屉菜单的具体滑动也是依赖于Scroller的使用,检索一下mScroller的引用,定位到forceSettleCapturedViewAt,这个方法回调用Scroller的startScroll来计算位移,它本身适用于计算和保存位移在特定时间的变化情况,最终的在绘制view时我可以获取其保存的x,y坐标值。

/**

* Settle the captured view at the given (left, top) position.

*

* @param finalLeft Target left position for the captured view

* @param finalTop Target top position for the captured view

* @param xvel Horizontal velocity

* @param yvel Vertical velocity

* @return true if animation should continue through {@link #continueSettling(boolean)} calls

*/

private boolean forceSettleCapturedViewAt(int finalLeft, int finalTop, int xvel, int yvel) {

final int startLeft = mCapturedView.getLeft();

final int startTop = mCapturedView.getTop();

final int dx = finalLeft - startLeft;

final int dy = finalTop - startTop;

if (dx == 0 && dy == 0) {

// Nothing to do. Send callbacks, be done.

mScroller.abortAnimation();

setDragState(STATE_IDLE);

returnfalse;

}

final int duration = computeSettleDuration(mCapturedView, dx, dy, xvel, yvel);

mScroller.startScroll(startLeft, startTop, dx, dy, duration);

setDragState(STATE_SETTLING);

returntrue;

}

这里用的是v4扩展包里的ScrollerCompat用于低版本兼容,它继承自ScrollerCompatImpl,可以看到里面主要的方法声明:

interface ScrollerCompatImpl{

Object createScroller(Context context, Interpolator interpolator);

boolean isFinished(Object scroller);

int getCurrX(Object scroller);

int getCurrY(Object scroller);

float getCurrVelocity(Object scroller);

boolean computeScrollOffset(Object scroller);

void startScroll(Object scroller, int startX, int startY, int dx, int dy);

void startScroll(Object scroller, int startX, int startY, int dx, int dy, int duration);

void fling(Object scroller, int startX, int startY, int velX, int velY,

int minX, int maxX, int minY, int maxY);

void fling(Object scroller, int startX, int startY, int velX, int velY,

int minX, int maxX, int minY, int maxY, int overX, int overY);

void abortAnimation(Object scroller);

void notifyHorizontalEdgeReached(Object scroller, int startX, int finalX, int overX);

void notifyVerticalEdgeReached(Object scroller, int startY, int finalY, int overY);

boolean isOverScrolled(Object scroller);

int getFinalX(Object scroller);

int getFinalY(Object scroller);

}

从Scroller一直往上追溯,可以得到如图的调用流程。

当滑动屏幕时,DrawerLayout中的手势分发被触发,先执行onInterceptTouchEvent根据返回结果确定是否执行onTouchEvent,之后就是一些和ViewDragHelper之间的回调接口处理。

接下来追踪一下什么时候从Scroller中取出x,y来使用:

在View里面有一个实现为空的computeScroll,DrawerLayout对它进行重写,这个方法应该是在view自动重绘是会被调用,回到continueSettling:

/**

* Move the captured settling view by the appropriate amount for the current time.

* If <code>continueSettling</code> returns true, the caller should call it again

* on the next frame to continue.

*

* @param deferCallbacks true if state callbacks should be deferred via posted message.

*                       Set this to true if you are calling this method from

*                       {@link android.view.View#computeScroll()} or similar methods

*                       invoked as part of layout or drawing.

* @return true if settle is still in progress

*/

public boolean continueSettling(boolean deferCallbacks) {

if (mDragState == STATE_SETTLING) {

boolean keepGoing = mScroller.computeScrollOffset();

final int x = mScroller.getCurrX();

final int y = mScroller.getCurrY();

final int dx = x - mCapturedView.getLeft();

final int dy = y - mCapturedView.getTop();

if (dx != 0) {

mCapturedView.offsetLeftAndRight(dx);

}

if (dy != 0) {

mCapturedView.offsetTopAndBottom(dy);

}

if (dx != 0 || dy != 0) {

mCallback.onViewPositionChanged(mCapturedView, x, y, dx, dy);

}

if (keepGoing && x == mScroller.getFinalX() && y == mScroller.getFinalY()) {

// Close enough. The interpolator/scroller might think we're still moving

// but the user sure doesn't.

mScroller.abortAnimation();

keepGoing = mScroller.isFinished();

}

if (!keepGoing) {

if (deferCallbacks) {

mParentView.post(mSetIdleRunnable);

} else {

setDragState(STATE_IDLE);

}

}

}

return mDragState == STATE_SETTLING;

}

当状态处于STATE_SETTLING时开始获取Scroller中的x,y值,结合当前运动view的left,top位置,计算出偏移量,通过offsetLeftAndRight设置,里面是一些具体的位置改变,挺复杂的。

/**

* Offset this view's horizontal location by the specified amount of pixels.

*

* @param offset the number of pixels to offset the view by

*/

public void offsetLeftAndRight(int offset) {

if (offset != 0) {

updateMatrix();

final boolean matrixIsIdentity = mTransformationInfo == null

|| mTransformationInfo.mMatrixIsIdentity;

if (matrixIsIdentity) {

if (mDisplayList != null) {

invalidateViewProperty(false, false);

} else {

final ViewParent p = mParent;

if (p != null && mAttachInfo != null) {

final Rect r = mAttachInfo.mTmpInvalRect;

int minLeft;

int maxRight;

if (offset < 0) {

minLeft = mLeft + offset;

maxRight = mRight;

} else {

minLeft = mLeft;

maxRight = mRight + offset;

}

r.set(0, 0, maxRight - minLeft, mBottom - mTop);

p.invalidateChild(this, r);

}

}

} else {

invalidateViewProperty(false, false);

}

mLeft += offset;

mRight += offset;

if (mDisplayList != null) {

mDisplayList.offsetLeftAndRight(offset);

invalidateViewProperty(false, false);

} else {

if (!matrixIsIdentity) {

invalidateViewProperty(false, true);

}

invalidateParentIfNeeded();

}

}

}

小结

至此DrawerLayout的基本工作流程分析完毕,简单做一个总结,v4包提供了ViewDragHelper类,里面封装了对scroller合view的位移操作,和Callback接口,通过DrawerLayout内的onInterceptTouchEvent和onTouchEvent的重载,触发ViewDragHelper内的相关方法,同时在DrawerLayout内实现ViewDragHelp.Callback.

作者:小文字
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

[UI]抽屉菜单DrawerLayout分析(一)的更多相关文章

  1. [UI]抽屉菜单DrawerLayout分析(三)

    在[UI]抽屉菜单DrawerLayout分析(一)和[UI]抽屉菜单DrawerLayout分析(二)中分别介绍了DrawerLayout得基本框架结构和ViewDragerHelper的作用以及手 ...

  2. [UI]抽屉菜单DrawerLayout分析(二)

    继续分析DrawerLayout的手势分发部分 谈到手势分发,这本身就是个好话题,DrawerLayout作为继承自ViewGroup得布局他可以拦截手势也可以分发给子view,也就是在 onInte ...

  3. Android抽屉菜单DrawerLayout的实现案例

    (1)项目布局文件 activity_main.xml <android.support.v4.widget.DrawerLayout xmlns:android="http://sc ...

  4. android 5.X Toolbar+DrawerLayout实现抽屉菜单

    前言  android5.X新增的一个控件Toolbar,这个控件比ActionBar更加自由,可控,因为曾经的ActionBar的灵活性比較差,所以google逐渐使用Toolbar替代Action ...

  5. 可折叠的ToolBar+抽屉菜单NavigationView+浮动按钮FloatButton

    使用Material Design风格的ToolBar和抽屉导航 先看个简单的运行效果 主要记录下布局的写法 1 用到的Google Design依赖和V7包依赖 compile 'com.andro ...

  6. AndroidUI 侧滑菜单 DrawerLayout的使用

    直接上代码: activity_main.xml: <android.support.v4.widget.DrawerLayout xmlns:android="http://sche ...

  7. wemall doraemon中Android app商城系统解决左侧抽屉菜单和viewpager不能兼容问题

    完美解决左侧抽屉菜单和viewpager不能兼容左右滑动的问题,可进行参考. WeMall-Client/res/layout/wemall_main_ui.xml </RadioGroup&g ...

  8. 使用AsyncTask异步更新UI界面及原理分析

    概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类.AsyncTask的内部实现是一个线程池,所有提交的异步任务都会在这个线程池中的工作线 ...

  9. Android 高仿QQ5.2双向側滑菜单DrawerLayout实现源代码

    Android 高仿QQ5.2双向側滑菜单DrawerLayout实现源代码 左右側滑效果图 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a ...

随机推荐

  1. Theoretical comparison between the Gini Index and Information Gain criteria

    Knowledge Discovery in Databases (KDD) is an active and important research area with the promise for ...

  2. mount: /dev/sdb1 already mounted or /mnt/hdb busy 导致NameNode无法启动

    最近,公司由于断电导致的服务器关机,等到来电了,重启集群发现Namenode无法启动,查看原因是由于无法加在faimage文件,在查看一下Namenode的磁盘挂在情况(df -h命令查看),发现磁盘 ...

  3. knockout.js 简介

    Knockout是一个轻量级的UI类库,通过应用MVVM模式使JavaScript前端UI简单化. Knockout有如下4大重要概念: 声明式绑定 (Declarative Bindings):使用 ...

  4. hive学习

    大数据的仓库Hive学习  10期-崔晓光 2016-06-20  大数据   hadoop   10原文链接 我们接着之前学习的大数据来学习.之前说到了NoSql的HBase数据库以及Hadoop中 ...

  5. UIKit框架之UITouch

    1.继承链:NSObject 2.获取触发点的位置 (1)- (CGPoint)locationInView:(UIView *)view :返回指定视图的触发点的位置 (2)- (CGPoint)p ...

  6. 【转】javascript变量声明 及作用域

    javascript变量声明提升(hoisting) javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面. 先看 ...

  7. pip自动生成requirements.txt依赖关系清单

    Python项目中经常会带requirements.txt文件,里面是项目所依赖的包的列表,也就是依赖关系清单,这个清单也可以使用pip命令自动生成. pip命令: 1 pip freeze > ...

  8. SqlServer性能优化索引(五)

    导入表结构: select * into ProductCategory from AdventureWorksDW2014.dbo.DimProductCategory select * into ...

  9. sql文件批量导入mysql数据库

    有一百多个sql文件肿么破?一行一行地导入数据库肯定是极其愚蠢的做法,但是我差点就这么做了... 网上首先找到的方法是:写一个xxx.sql文件,里边每一行都是source *.sql ...,之后再 ...

  10. PHP生成二维码图片

    下载二维码文件:http://files.cnblogs.com/isboy/qr_code.zip 需要加入的HTML:<img id="js_pc_qr_code_img" ...