位置:首页 » 技术 » SwipeBackLayout源码解析

SwipeBackLayout源码解析

日期:2016-05-08 阅读:0num
Advertisement

Github: SwipeBackLayout 分析版本: e4ddae6

SwipeBackLayout 是一个仿 IOS 通过手势退出界面的开源库。

SwipeBackLayout

SwipeBackLayout源码解析

SwipeBackLayout 可以通过在左、右和下边缘来拖动整个 Activity 达到退出 Activity 的效果。

使用

添加到 Gradle :

compile 'me.imid.swipebacklayout.lib:library:1.0.0'

继承 SwipeBackActivity

public class DemoActivity extends SwipeBackActivity {}
  • onCreatesetContentView() 照常使用
  • 可以通过 getSwipeBackLayout() 定制 SwipeBackLayout

styles.xml 中的主题中添加:

<item name="android:windowIsTranslucent">true</item>

注意

需要在项目中添加最新的 supportV4 包

demo

public class DemoActivity extends SwipeBackActivity implements View.OnClickListener {    private int[] mBgColors;

private static int mBgIndex = 0;

private String mKeyTrackingMode;

private RadioGroup mTrackingModeGroup;

private SwipeBackLayout mSwipeBackLayout;

@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_demo);        changeActionBarColor();        findViews();        mKeyTrackingMode = getString(R.string.key_tracking_mode);        mSwipeBackLayout = getSwipeBackLayout();

mTrackingModeGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {            @Override            public void onCheckedChanged(RadioGroup group, int checkedId) {                int edgeFlag;                switch (checkedId) {                    case R.id.mode_left:                        edgeFlag = SwipeBackLayout.EDGE_LEFT;                        break;                    case R.id.mode_right:                        edgeFlag = SwipeBackLayout.EDGE_RIGHT;                        break;                    case R.id.mode_bottom:                        edgeFlag = SwipeBackLayout.EDGE_BOTTOM;                        break;                    default:                        edgeFlag = SwipeBackLayout.EDGE_ALL;                }                mSwipeBackLayout.setEdgeTrackingEnabled(edgeFlag);                saveTrackingMode(edgeFlag);            }        });    }...

源码

SwipeBackActivity

public class SwipeBackActivity extends AppCompatActivity implements SwipeBackActivityBase {    private SwipeBackActivityHelper mHelper;

@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mHelper = new SwipeBackActivityHelper(this);        mHelper.onActivityCreate();    }

@Override    protected void onPostCreate(Bundle savedInstanceState) {        super.onPostCreate(savedInstanceState);        mHelper.onPostCreate();    }

@Override    public View findViewById(int id) {        View v = super.findViewById(id);        if (v == null && mHelper != null)            return mHelper.findViewById(id);        return v;    }

@Override    public SwipeBackLayout getSwipeBackLayout() {//SwipeBackActivityBase接口中的方法        return mHelper.getSwipeBackLayout();    }

@Override    public void setSwipeBackEnable(boolean enable) {//SwipeBackActivityBase接口中的方法        getSwipeBackLayout().setEnableGesture(enable);    }

@Override    public void scrollToFinishActivity() {//SwipeBackActivityBase接口中的方法        Utils.convertActivityToTranslucent(this);        getSwipeBackLayout().scrollToFinishActivity();    }}

SwipeBackActivity 中实现了 SwipeBackActivityBase 接口,在 Activity 的生命周期函数 onCreate() 中创建了 SwipeBackActivityHelper 对象, 该类的作用是设置 Activity 的透明和在 DecorView 中替换 SwipeBackLayout 。 onPostCreate() 是在 Activity 完全运行起来之后才会被调用。其中 findViewById() 方法进行了判断,首先在 Activity 的 contentView 中获取,获取不到再到 SwipeBackLayout 中获取。

SwipeBackActivityHelper

SwipeBackActivityonCreate() 中的调用方法:

public class SwipeBackActivityHelper {

private SwipeBackLayout mSwipeBackLayout;

public SwipeBackActivityHelper(Activity activity) {        mActivity = activity;    }

@SuppressWarnings("deprecation")    public void onActivityCreate() {        //设置Window的background为透明        mActivity.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));        //设置decorView没有background        mActivity.getWindow().getDecorView().setBackgroundDrawable(null);        //inflate一个SwipeBackLayout出来        mSwipeBackLayout = (SwipeBackLayout) LayoutInflater.from(mActivity).inflate(                me.imid.swipebacklayout.lib.R.layout.swipeback_layout, null);        //设置手势滑动监听器        mSwipeBackLayout.addSwipeListener(new SwipeBackLayout.SwipeListener() {            @Override            public void onScrollStateChange(int state, float scrollPercent) {            }

@Override            public void onEdgeTouch(int edgeFlag) {                //当有边界触摸的时候设置成透明的                Utils.convertActivityToTranslucent(mActivity);            }

@Override            public void onScrollOverThreshold() {

}        });    }}

onActivityCreate 中主要就是将 window 、 decorView 的背景设置为透明的。

SwipeBackActivityonPostCreate() 中的调用方法:

public class SwipeBackActivityHelper {    public void onPostCreate() {        mSwipeBackLayout.attachToActivity(mActivity);    }}

attachToActivity 中的操作就是将 decorView 中的 childView 换成 SwipeBackLayout ,然后将 childView 添加到 SwipeBackLayout 中。

其他的方法:

public class SwipeBackActivityHelper {    public View findViewById(int id) {        if (mSwipeBackLayout != null) {            return mSwipeBackLayout.findViewById(id);        }        return null;    }

public SwipeBackLayout getSwipeBackLayout() {        return mSwipeBackLayout;    }}

SwipeBackLayout

SwipeBackLayout 是一个 View ,可以从构造函数开始看:

public class SwipeBackLayout extends FrameLayout {

/**     * Minimum velocity that will be detected as a fling     */    private static final int MIN_FLING_VELOCITY = 400; // dips per second

private static final int[] EDGE_FLAGS = {            EDGE_LEFT, EDGE_RIGHT, EDGE_BOTTOM, EDGE_ALL    };    private int mEdgeFlag;

private ViewDragHelper mDragHelper;

public SwipeBackLayout(Context context) {        this(context, null);    }

public SwipeBackLayout(Context context, AttributeSet attrs) {        this(context, attrs, R.attr.SwipeBackLayoutStyle);    }

public SwipeBackLayout(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs);        mDragHelper = ViewDragHelper.create(this, new ViewDragCallback());

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeBackLayout, defStyle,                R.style.SwipeBackLayout);

//与边缘可拖动的距离        int edgeSize = a.getDimensionPixelSize(R.styleable.SwipeBackLayout_edge_size, -1);        if (edgeSize > 0) {            //设置给ViewDragHelper            setEdgeSize(edgeSize);        }        //边缘模式,分为EDGE_LEFT, EDGE_RIGHT, EDGE_BOTTOM, EDGE_ALL        int mode = EDGE_FLAGS[a.getInt(R.styleable.SwipeBackLayout_edge_flag, 0)];        //设置给ViewDragHelper        setEdgeTrackingEnabled(mode);        //边缘滑动的时候的阴影        int shadowLeft = a.getResourceId(R.styleable.SwipeBackLayout_shadow_left,                R.drawable.shadow_left);        int shadowRight = a.getResourceId(R.styleable.SwipeBackLayout_shadow_right,                R.drawable.shadow_right);        int shadowBottom = a.getResourceId(R.styleable.SwipeBackLayout_shadow_bottom,                R.drawable.shadow_bottom);        setShadow(shadowLeft, EDGE_LEFT);        setShadow(shadowRight, EDGE_RIGHT);        setShadow(shadowBottom, EDGE_BOTTOM);        a.recycle();        //得到密度        final float density = getResources().getDisplayMetrics().density;        //手势滑动最小速度        final float minVel = MIN_FLING_VELOCITY * density;        //设置给ViewDragHelper        mDragHelper.setMinVelocity(minVel);        mDragHelper.setMaxVelocity(minVel * 2f);    }

/**     * Set the size of an edge. This is the range in pixels along the edges of     * this view that will actively detect edge touches or drags if edge     * tracking is enabled.     *     * @param size The size of an edge in pixels     */    public void setEdgeSize(int size) {        mDragHelper.setEdgeSize(size);    }

/**     * Enable edge tracking for the selected edges of the parent view. The     * callback's     * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#onEdgeTouched(int, int)}     * and     * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#onEdgeDragStarted(int, int)}     * methods will only be invoked for edges for which edge tracking has been     * enabled.     *     * @param edgeFlags Combination of edge flags describing the edges to watch     * @see #EDGE_LEFT     * @see #EDGE_RIGHT     * @see #EDGE_BOTTOM     */    public void setEdgeTrackingEnabled(int edgeFlags) {        mEdgeFlag = edgeFlags;        mDragHelper.setEdgeTrackingEnabled(mEdgeFlag);    }

public void setShadow(int resId, int edgeFlag) {        setShadow(getResources().getDrawable(resId), edgeFlag);    }

/**     * Set a drawable used for edge shadow.     *     * @param shadow    Drawable to use     * @param edgeFlag Combination of edge flags describing the edge to set     * @see #EDGE_LEFT     * @see #EDGE_RIGHT     * @see #EDGE_BOTTOM     */    public void setShadow(Drawable shadow, int edgeFlag) {        if ((edgeFlag & EDGE_LEFT) != 0) {            mShadowLeft = shadow;        } else if ((edgeFlag & EDGE_RIGHT) != 0) {            mShadowRight = shadow;        } else if ((edgeFlag & EDGE_BOTTOM) != 0) {            mShadowBottom = shadow;        }        invalidate();    }

//处理ViewDragHelper    @Override    public boolean onInterceptTouchEvent(MotionEvent event) {        if (!mEnable) {            return false;        }        try {            return mDragHelper.shouldInterceptTouchEvent(event);        } catch (ArrayIndexOutOfBoundsException e) {            // FIXME: handle exception            // issues #9            return false;        }    }

//处理ViewDragHelper    @Override    public boolean onTouchEvent(MotionEvent event) {        if (!mEnable) {            return false;        }        mDragHelper.processTouchEvent(event);        return true;    }}

SwipeBackLayout 继承自 FrameLayout ,其中手势的操作是通过 ViewDragHelper 来实现的。在构造函数中一些必要的参数设置给 ViewDragHelper

public class SwipeBackLayout extends FrameLayout {   /**     * Edge flag indicating that the left edge should be affected.     */    public static final int EDGE_LEFT = ViewDragHelper.EDGE_LEFT;

/**     * Edge flag indicating that the right edge should be affected.     */    public static final int EDGE_RIGHT = ViewDragHelper.EDGE_RIGHT;

/**     * Edge flag indicating that the bottom edge should be affected.     */    public static final int EDGE_BOTTOM = ViewDragHelper.EDGE_BOTTOM;

/**     * Edge flag set indicating all edges should be affected.     */    public static final int EDGE_ALL = EDGE_LEFT | EDGE_RIGHT | EDGE_BOTTOM;    /**     * Default threshold of scroll     * 超过0.3f的屏幕比例的距离之后可以滑动出去了,临界值是0.3f     */    private static final float DEFAULT_SCROLL_THRESHOLD = 0.3f;

private static final int OVERSCROLL_DISTANCE = 10;

private float mScrimOpacity;

/**     * Edge being dragged     */    private int mTrackingEdge;

//滑动了距离和整个屏幕的的百分比     private float mScrollPercent;

private int mContentLeft;

private int mContentTop;

/**     * Threshold of scroll, we will close the activity, when scrollPercent over     * this value;     */    private float mScrollThreshold = DEFAULT_SCROLL_THRESHOLD;

private class ViewDragCallback extends ViewDragHelper.Callback {        private boolean mIsScrollOverValid;

//如果可拖动则返回true 否则为false        @Override        public boolean tryCaptureView(View view, int i) {//i是pointerId            //是否touch到了边缘            boolean ret = mDragHelper.isEdgeTouched(mEdgeFlag, i);             //哪个边缘被touch了            if (ret) {                if (mDragHelper.isEdgeTouched(EDGE_LEFT, i)) {                    mTrackingEdge = EDGE_LEFT;                } else if (mDragHelper.isEdgeTouched(EDGE_RIGHT, i)) {                    mTrackingEdge = EDGE_RIGHT;                } else if (mDragHelper.isEdgeTouched(EDGE_BOTTOM, i)) {                    mTrackingEdge = EDGE_BOTTOM;                }                  //回调出去                if (mListeners != null && !mListeners.isEmpty()) {                    for (SwipeListener listener : mListeners) {                        listener.onEdgeTouch(mTrackingEdge);                    }                }                mIsScrollOverValid = true;            }            boolean directionCheck = false;           //是否达到了滑动的门槛            if (mEdgeFlag == EDGE_LEFT || mEdgeFlag == EDGE_RIGHT) {                directionCheck = !mDragHelper.checkTouchSlop(ViewDragHelper.DIRECTION_VERTICAL, i);            } else if (mEdgeFlag == EDGE_BOTTOM) {                directionCheck = !mDragHelper                        .checkTouchSlop(ViewDragHelper.DIRECTION_HORIZONTAL, i);            } else if (mEdgeFlag == EDGE_ALL) {                directionCheck = true;            }            return ret & directionCheck;        }

//返回指定View在横向上能滑动的最大距离        @Override        public int getViewHorizontalDragRange(View child) {            return mEdgeFlag & (EDGE_LEFT | EDGE_RIGHT);        }

//返回指定View在纵向上能滑动的最大距离        @Override        public int getViewVerticalDragRange(View child) {            return mEdgeFlag & EDGE_BOTTOM;        }

//当子视图位置变化时,会回调这个函数        @Override        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {            super.onViewPositionChanged(changedView, left, top, dx, dy);            //计算当前滑动比例            if ((mTrackingEdge & EDGE_LEFT) != 0) {                mScrollPercent = Math.abs((float) left                        / (mContentView.getWidth() + mShadowLeft.getIntrinsicWidth()));            } else if ((mTrackingEdge & EDGE_RIGHT) != 0) {                mScrollPercent = Math.abs((float) left                        / (mContentView.getWidth() + mShadowRight.getIntrinsicWidth()));            } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) {                mScrollPercent = Math.abs((float) top                        / (mContentView.getHeight() + mShadowBottom.getIntrinsicHeight()));            }            mContentLeft = left;            mContentTop = top;            invalidate();             //当滑动比例小于可滑动出去的时候,且mIsScrollOverValid已经为false的时候            if (mScrollPercent < mScrollThreshold && !mIsScrollOverValid) {                mIsScrollOverValid = true;            }            if (mListeners != null && !mListeners.isEmpty()                    && mDragHelper.getViewDragState() == STATE_DRAGGING                    && mScrollPercent >= mScrollThreshold && mIsScrollOverValid) {                mIsScrollOverValid = false;                 //回调出去,已经达到可以滑出结束Activity的标准了                for (SwipeListener listener : mListeners) {                    listener.onScrollOverThreshold();                }            }           //当比例大于等于1的时候,就可以关闭掉Activity了            if (mScrollPercent >= 1) {                if (!mActivity.isFinishing()) {                    mActivity.finish();                    mActivity.overridePendingTransition(0, 0);                }            }        }

//当手指从子视图松开时,会调用这个函数,同时返回在x轴和y轴上当前的速度        @Override        public void onViewReleased(View releasedChild, float xvel, float yvel) {            final int childWidth = releasedChild.getWidth();            final int childHeight = releasedChild.getHeight();

int left = 0, top = 0;            if ((mTrackingEdge & EDGE_LEFT) != 0) {//左边边缘                 //速度满足>=0且已经滑过了临界点0.3f,滑到最右边,不然滑到0的位置                left = xvel > 0 || xvel == 0 && mScrollPercent > mScrollThreshold ? childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE : 0;            } else if ((mTrackingEdge & EDGE_RIGHT) != 0) {//右边边缘                //速度满足>=0且已经滑过了临界点0.3f,滑到最左边,不然滑到0的位置                left = xvel < 0 || xvel == 0 && mScrollPercent > mScrollThreshold ? -(childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE) : 0;            } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) {//上边边缘                //速度满足>=0且已经滑过了临界点0.3f,滑到最下边,不然滑到0的位置                top = yvel < 0 || yvel == 0 && mScrollPercent > mScrollThreshold ? -(childHeight + mShadowBottom.getIntrinsicHeight() + OVERSCROLL_DISTANCE) : 0;            }          //移动View            mDragHelper.settleCapturedViewAt(left, top);            //刷新View            invalidate();        }

//返回一个值,告诉Helper,这个view能滑动的最大(或者负向最大)的横向坐标        @Override        public int clampViewPositionHorizontal(View child, int left, int dx) {            int ret = 0;            if ((mTrackingEdge & EDGE_LEFT) != 0) {                ret = Math.min(child.getWidth(), Math.max(left, 0));            } else if ((mTrackingEdge & EDGE_RIGHT) != 0) {                ret = Math.min(0, Math.max(left, -child.getWidth()));            }            return ret;        }

//返回一个值,告诉Helper,这个view能滑动的最大(或者负向最大)的纵向坐标        @Override        public int clampViewPositionVertical(View child, int top, int dy) {            int ret = 0;            if ((mTrackingEdge & EDGE_BOTTOM) != 0) {                ret = Math.min(0, Math.max(top, -child.getHeight()));            }            return ret;        }

//当边缘开始拖动的时候,会调用这个回调        @Override        public void onViewDragStateChanged(int state) {            super.onViewDragStateChanged(state);            if (mListeners != null && !mListeners.isEmpty()) {                for (SwipeListener listener : mListeners) {                    listener.onScrollStateChange(state, mScrollPercent);                }            }        }    }

@Override    public void computeScroll() {       //调用mDragHelper.settleCapturedViewAt(left, top)之后会进到这里        mScrimOpacity = 1 - mScrollPercent;        if (mDragHelper.continueSettling(true)) {            ViewCompat.postInvalidateOnAnimation(this);        }    }

@Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        mInLayout = true;        if (mContentView != null) {            mContentView.layout(mContentLeft, mContentTop,                    mContentLeft + mContentView.getMeasuredWidth(),                    mContentTop + mContentView.getMeasuredHeight());        }        mInLayout = false;    }

@Override    public void requestLayout() {        if (!mInLayout) {            super.requestLayout();        }    }}

ViewDragHelper.Callback 的手势判断中,处理的主要逻辑主要在 tryCaptureViewonViewPositionChangedonViewReleased 三个方法中,分别是在准备滑动、滑动时、和放手的时候的逻辑。

tryCaptureView 中主要进行了边缘的判断,以及是否满足滑动条件;在 onViewPositionChanged 中计算了当前滑动距离与整个 ContentView 的距离的比例,是否超越临界值等;在 onViewReleased 中处理了手抬起之后的操作,比如将 View 滑归位或者滑出去等。

现在基本上了解了滑动的机制了,那么回过头来看看 attachToActivity

public class SwipeBackLayout extends FrameLayout {

private View mContentView;

public void attachToActivity(Activity activity) {        mActivity = activity;        TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{                android.R.attr.windowBackground        });        int background = a.getResourceId(0, 0);        a.recycle();

ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();       // 拿到decorView的第一个子view        ViewGroup decorChild = (ViewGroup) decor.getChildAt(0);        decorChild.setBackgroundResource(background);       //把这个decorChild从decorView删除掉        decor.removeView(decorChild);       //将decorView添加到SwipeBackLayout中        addView(decorChild);         //将decorChild赋值给成员变量mContentView        setContentView(decorChild);        // 在DecorView下增加SwipeBackLayout        decor.addView(this);    }

/**     * Set up contentView which will be moved by user gesture     *     * @param view     */    private void setContentView(View view) {        mContentView = view;    }}

通过 attachToActivity 将 decorView 中的 contentView 换成了 SwipeBackLayout ,而 contentView 则被添加到了 SwipeBackLayout 中。与正常的相比,之间多了一个 SwipeBackLayout

在滑动的时候哪些阴影是怎么出现的呢:

public class SwipeBackLayout extends FrameLayout {

private static final int DEFAULT_SCRIM_COLOR = 0x99000000;

private float mScrimOpacity;

private int mScrimColor = DEFAULT_SCRIM_COLOR;

private float mScrollPercent;

private Drawable mShadowLeft;    private Drawable mShadowRight;    private Drawable mShadowBottom;

private Rect mTmpRect = new Rect();

@Override    public void computeScroll() {        mScrimOpacity = 1 - mScrollPercent;        if (mDragHelper.continueSettling(true)) {            ViewCompat.postInvalidateOnAnimation(this);        }    }

@Override    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {        final boolean drawContent = child == mContentView;

boolean ret = super.drawChild(canvas, child, drawingTime);        if (mScrimOpacity > 0 && drawContent                && mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE) {            drawShadow(canvas, child);            drawScrim(canvas, child);        }        return ret;    }

private void drawScrim(Canvas canvas, View child) {       //得到alpha值        final int baseAlpha = (mScrimColor & 0xff000000) >>> 24;         //得到新的alpha值        final int alpha = (int) (baseAlpha * mScrimOpacity);        //得到新的color        final int color = alpha << 24 | (mScrimColor & 0xffffff);      //绘制        if ((mTrackingEdge & EDGE_LEFT) != 0) {            canvas.clipRect(0, 0, child.getLeft(), getHeight());        } else if ((mTrackingEdge & EDGE_RIGHT) != 0) {            canvas.clipRect(child.getRight(), 0, getRight(), getHeight());        } else if ((mTrackingEdge & EDGE_BOTTOM) != 0) {            canvas.clipRect(child.getLeft(), child.getBottom(), getRight(), getHeight());        }        canvas.drawColor(color);    }

private void drawShadow(Canvas canvas, View child) {        final Rect childRect = mTmpRect;          //得到当前View的位置        child.getHitRect(childRect);

if ((mEdgeFlag & EDGE_LEFT) != 0) {            //给drawable设置位置            mShadowLeft.setBounds(childRect.left - mShadowLeft.getIntrinsicWidth(), childRect.top, childRect.left, childRect.bottom);            //设置透明度            mShadowLeft.setAlpha((int) (mScrimOpacity * FULL_ALPHA));            //画到canvas上            mShadowLeft.draw(canvas);        }     //给drawable设置位置、设置透明度、画到canvas上        if ((mEdgeFlag & EDGE_RIGHT) != 0) {            mShadowRight.setBounds(childRect.right, childRect.top, childRect.right + mShadowRight.getIntrinsicWidth(), childRect.bottom);            mShadowRight.setAlpha((int) (mScrimOpacity * FULL_ALPHA));            mShadowRight.draw(canvas);        }       //给drawable设置位置、设置透明度、画到canvas上        if ((mEdgeFlag & EDGE_BOTTOM) != 0) {            mShadowBottom.setBounds(childRect.left, childRect.bottom, childRect.right, childRect.bottom + mShadowBottom.getIntrinsicHeight());            mShadowBottom.setAlpha((int) (mScrimOpacity * FULL_ALPHA));            mShadowBottom.draw(canvas);        }    }}

就这样,阴影就绘制出来了。

再看看 scrollToFinishActivity

public class SwipeBackLayout extends FrameLayout {    /**     * Scroll out contentView and finish the activity     */    public void scrollToFinishActivity() {       //得到contentView的宽高        final int childWidth = mContentView.getWidth();        final int childHeight = mContentView.getHeight();        //要移动到的位置        int left = 0, top = 0;        if ((mEdgeFlag & EDGE_LEFT) != 0) {            left = childWidth + mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE;            mTrackingEdge = EDGE_LEFT;        } else if ((mEdgeFlag & EDGE_RIGHT) != 0) {            left = -childWidth - mShadowRight.getIntrinsicWidth() - OVERSCROLL_DISTANCE;            mTrackingEdge = EDGE_RIGHT;        } else if ((mEdgeFlag & EDGE_BOTTOM) != 0) {            top = -childHeight - mShadowBottom.getIntrinsicHeight() - OVERSCROLL_DISTANCE;            mTrackingEdge = EDGE_BOTTOM;        }

mDragHelper.smoothSlideViewTo(mContentView, left, top);        invalidate();    }

@Override    public void computeScroll() {          //调用mDragHelper.smoothSlideViewTo(mContentView, left, top);之后进到这里        mScrimOpacity = 1 - mScrollPercent;        if (mDragHelper.continueSettling(true)) {            ViewCompat.postInvalidateOnAnimation(this);        }    }}

Utils

public class Utils {    private Utils() {    }

/**     * Convert a translucent themed Activity     * {@link android.R.attr#windowIsTranslucent} back from opaque to     * translucent following a call to     * {@link #convertActivityFromTranslucent(android.app.Activity)} .     * <p>     * Calling this allows the Activity behind this one to be seen again. Once     * all such Activities have been redrawn     * <p>     * This call has no effect on non-translucent activities or on activities     * with the {@link android.R.attr#windowIsFloating} attribute.     */    public static void convertActivityToTranslucent(Activity activity) {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {            convertActivityToTranslucentAfterL(activity);        } else {            convertActivityToTranslucentBeforeL(activity);        }    }

/**     * Calling the convertToTranslucent method on platforms before Android 5.0     */    public static void convertActivityToTranslucentBeforeL(Activity activity) {        try {            Class<?>[] classes = Activity.class.getDeclaredClasses();            Class<?> translucentConversionListenerClazz = null;            for (Class clazz : classes) {                if (clazz.getSimpleName().contains("TranslucentConversionListener")) {                    translucentConversionListenerClazz = clazz;                }            }            Method method = Activity.class.getDeclaredMethod("convertToTranslucent",                    translucentConversionListenerClazz);            method.setAccessible(true);            method.invoke(activity, new Object[] {                null            });        } catch (Throwable t) {        }    }

/**     * Calling the convertToTranslucent method on platforms after Android 5.0     */    private static void convertActivityToTranslucentAfterL(Activity activity) {        try {            Method getActivityOptions = Activity.class.getDeclaredMethod("getActivityOptions");            getActivityOptions.setAccessible(true);            Object options = getActivityOptions.invoke(activity);

Class<?>[] classes = Activity.class.getDeclaredClasses();            Class<?> translucentConversionListenerClazz = null;            for (Class clazz : classes) {                if (clazz.getSimpleName().contains("TranslucentConversionListener")) {                    translucentConversionListenerClazz = clazz;                }            }            Method convertToTranslucent = Activity.class.getDeclaredMethod("convertToTranslucent",                    translucentConversionListenerClazz, ActivityOptions.class);            convertToTranslucent.setAccessible(true);            convertToTranslucent.invoke(activity, null, options);        } catch (Throwable t) {        }    }}

通过反射改变 Activity 的属性值。

相关文章
  • SwipeBackLayout源码解析 SwipeBackLayout源码解析

    Github: SwipeBackLayout 分析版本: e4ddae6 SwipeBackLayout 是一个仿 IOS 通过手势退出界面的开源库. SwipeBackLayout SwipeBackLayout 可以通过在左.右和下边缘来拖动整个 Activity 达到退出 Activity 的效果. 使用 添加到 Gradle : compile 'me.imid.swipebacklayout.lib:library:1.0.0' 继承 SwipeBackActivity : publ

  • DynamicLoadApk 源码解析

    本文为 Android 开源项目源码解析 中 DynamicLoadApk 部分 项目地址: DynamicLoadApk ,分析的版本: 144571b ,Demo 地址: DynamicLoadApk Demo 分析者: FFish ,分析状态:完成,校对者: Trinea ,校对状态:初审完成 1. 功能介绍 1.1 简介 DynamicLoadApk 是一个开源的 Android 插件化框架. 插件化的优点包括:(1) 模块解耦,(2) 动态升级,(3) 高效并行开发(编译速度更快) (

  • Android 开源项目源码解析(第二期)

    android-Ultra-Pull-To-Refresh 源码解析 一个强大的 Andriod 下拉刷新框架.主要特点:(1) 继承于 ViewGroup,Content 不仅可以是常用的 ListView.GridView,还可以是其他任何 View.(2) 简洁完善的 Header 抽象,方便进行拓展,构建符合需求的头部.UltraPTR 总体设计比较简单清晰,首先抽象出了两个接口,功能接口和 UI 接口.PtrHandler 代表下拉刷新的功能接口,包含刷新功能回调方法以及判断是否可以下

  • Android开发周报:Google I/O 2015回顾、Context源码解析

    新闻 <Android:Google I/O 背后的抱负> :让人满怀憧憬的Google I/O 2015已经缤纷落幕了.Android M.可穿戴设备.物联网.虚拟现实.以及Google Now.照片.地图产品的各种升级让人目不暇接.但是,回想起来,似乎又没有太多新鲜的东西.大会的跟踪报道和综述已经是不少了,基本上都大同小异:流水账式地挑一些吸引眼球的产品,枚举一下新功能.那么,你看懂了这一次的Google I/O吗? <Android M 正式登场,偏重打磨和品质> :果然如传

  • Andriod 开源项目源码解析第一期发布 Andriod 开源项目源码解析第一期发布

    忙了这么久了,终于在昨天对外发布了 Andriod 开源项目源码解析第一期 ,算是个结束点,更是个起点 感谢参与开源项目第一期所有的成功,包括一部分退出的.具体一期成员信息见 aopa-phase1-member ,大家可以多关注他们,他们都很棒! 开源项目源码解析网站: http://www.codekk.com GitHub 项目地址: android-open-project-analysis 微博:code-kk 微信公众号: codekk 微博和微信会分享此次分析每一篇文章,并专注于开

  • 4store源码解析系列(6)–查询处理细节 4store源码解析系列(6)–查询处理细节

    在上一篇中,我们对4store查询的基本流程进行了梳理.在这一篇中,我们将对查询在客户端和服务端的一些处理细节展开讨论. 客户端的triple查询处理 关于triple查询的最底层实现,我们在 上一篇 源码解析中已经具体介绍了,我们这里要讨论的是在底层查询接口之上的一些优化和处理. 缓存处理 一般来说,在单条query查询语句中虽然可能涉及多条triple查询,但这些triple查询请求很少会重复.但是,我们在使用4store进行多次query查询时,则很有可能会出现重复的triple查询了.我

  • 4store源码解析系列(7)–查询优化 4store源码解析系列(7)–查询优化

    在之前的两篇4store源码解析系列文章中,我们已经对单条triple的查询流程和细节处理进行了说明.在这一篇中,我们将对如何处理有嵌套关系的多条triple查询问题展开讨论,并进一步分析4store对这个问题的处理优化. graph pattern解析和优化 如 查询流程概述 中所述,4store并不直接参与sparql语句的解析,而只对Rasqal库解析了后的graph结构进行分析. 要了解4store如何二次解析Rasqal对sparql查询语句的解析结果,我们首先需要了解Rasqa解析结

  • FutureTask 源码解析 FutureTask 源码解析

    版权声明:本文为本作者原创文章,转载请注明出处.http://www.liuinsect.com/2014/02/17/FutureTask 源码解析/ 站在使用者的角度,future是一个经常在多线程环境下使用的Runnable,使用它的好处有两个: 1. 线程执行结果带有返回值 2. 提供了一个线程超时的功能,超过超时时间抛出异常后返回. 那,怎么实现future这种超时控制呢?来看看代码: FutureTask的实现只是依赖了一个内部类Sync实现的,Sync是AQS (AbstractQ

  • OKHttp源码解析-ConnectionPool对Connection重用机制&amp;Http/Https/SPDY协议选择

    距离上一次的OKHttp源码解析过去快3月了.最近一直在忙工作上的事情,另外也再尝试一门新的语言Go.所以一直没花很多心思在Android这边.最近看到一些网友建议把okhttp的连接池对Connection的重用维护机制以及HTTP和SPDY协议如何得到区分这两个核心内容做深入的分析. 因此,这几天就打算好好说一说这块儿的实现方式.SPDY既是http1.x的增强版也是http2.x的过渡版本,虽然现在很多都直接切入到http2.0,不过SPDY的应用仍然值得关注. ConnectionPoo

  • Android 开源项目源码解析第一期正式发布 Android 开源项目源码解析第一期正式发布

    由 Trinea 发起的开源项目源码分析第一期正式结束 在线网站:www.codekk.com 分析文档 作者 Volley 源码解析 grumoon Universal Image Loader 源码分析 huxian99 Dagger 源码解析 扔物线 EventBus 源码解析 Trinea xUtils 源码解析 Caij ViewPagerindicator 源码解析 lightSky HoloGraphLibrary 源码解析 aaronplay CircularFloatingAc

  • JS教程:lightbox源码解析

    网页制作poluoluo文章简介:lightbox源码解析. lightbox源码解析 function getPageScroll(){ var yScroll; if (self.pageYOffset) { yScroll = self.pageYOffset; //NS } else if (document.documentElement && document.documentElement.scrollTop){ // Explorer 6 Strict yScroll =

  • ViewPager动态转换效果之SCViewPager源码解析 ViewPager动态转换效果之SCViewPager源码解析

    ViewPager动态变换效果之SCViewPager源码解析 序言: 我们知道在ViewPager中给我们提供了PageTransformer接口用于ViewPager切换的动画效果实现,一般我们需要实现这个接口里的transformPage方法实现切换的动画效果,这样我们就实现了ViewPager中Item之间切换的效果,例如下面的效果(摘自鸿洋博客图片): 我们发现,这里实现的是Item之间切换的效果,但是每个Item页里面的View并没有动画效果,我们也知道在做App的Guide引导页的

  • IPerf——网络测试工具引见与源码解析(4) IPerf——网络测试工具引见与源码解析(4)

    IPerf--网络测试工具介绍与源码解析(4) 上篇随笔讲到了TCP模式下的客户端,接下来会讲一下TCP模式普通场景下的服务端,说普通场景则是暂时不考虑双向测试的可能,毕竟了解一项东西还是先从简单的情况下入手会快些. 对于服务端,并不是我们认为的直接创建服务端线程,而是先创建一个监听者线程,在本地绑定套接字后进行蹲点监听. 在Listener类中,Run成员函数执行一个do-while循环接收等待来自对端的连接,循环中调用Accept函数,该函数会阻塞,直至接收到对端的连接并通过thread_S

  • IPerf——网络测试工具引见与源码解析(1) IPerf——网络测试工具引见与源码解析(1)

    IPerf--网络测试工具介绍与源码解析(1) IPerf是一个开源的测试网络宽带并能统计并报告延迟抖动.数据包丢失率信息的控制台命令程序,通过参数选项可以方便地看出,通过设置不同的选项值对网络带宽的影响,对于学习网络编程还是有一定的借鉴意义,至少可以玩上一段时间. IPerf开始出现的时候是在03年,版本是1.7.0,在网上找到的仅有的系列源码解析篇 http://blog.chinaunix.net/uid/11568125/cid-131106-abstract-1.html 就是基于1.

  • jQuery源码解析(五)—— Animation动画

    jQuery源码解析(5)-- Animation动画 闲话 jQuery的动画机制有800行, 虽然不如样式的1300行,难度上却是不减.由于事前不了解animate接口的细节使用规则,看代码期间吃了很多苦头,尤其是深恶痛绝的defaultPrefilter函数,靠着猜想和数次的逐行攻略,终于全部拿下.本文将一点一点拆解出jq的动画机制及具体实现,解析各种做法的目的.解耦的方式.必要的结构.增强的辅助功能. 需要提前掌握queue队列的知识,css样式机制.data缓存.deferred对象至

  • jQuery源码解析(三)—— ready加载、queue队列

    jQuery源码解析(3)-- ready加载.queue队列 ready.queue放在一块写,没有特殊的意思,只是相对来说它俩可能源码是最简单的了.ready是在dom加载完成后,以最快速度触发,很实用.queue是队列,比如动画的顺序触发就是通过默认队列'fx'处理的. (本文采用 1.12.0 版本进行讲解,用 #number 来标注行号) ready 很多时候,我们需要尽快的加载一个函数,如果里面含有操作dom的逻辑,那么最好在dom刚刚加载完成时调用.window的load事件会在页

  • jQuery源码解析(二)—— Callback、Deferred异步编程

    jQuery源码解析(2)-- Callback.Deferred异步编程 闲话 这篇文章,一个月前就该出炉了.跳票的原因,是因为好奇标准的promise/A+规范,于是学习了es6的promise,由于兴趣,又完整的学习了<ECMAScript 6入门>. 本文目的在于解析jQuery对的promise实现(即Deferred,是一种非标准的promise实现),顺便剖析.挖掘观察者模式的能力.建议读完后参考下面这篇博文的异步编程部分,了解Promise.Generator.Async. E

  • jQuery 源码解析1:jQuery 类库整体架构设计解析 jQuery 源码解析1:jQuery 类库整体架构设计解析

    jQuery 源码解析一:jQuery 类库整体架构设计解析 如果是做 web 的话,相信都要对 Dom 进行增删查改,那大家都或多或少接触到过 jQuery 类库,其最大特色就是强大的选择器,让开发者脱离原生 JS 一大堆 getElementById.getElementsByName...官方提供超长方法 api . jQuery 整体源码,本人也还在阅读中,暂时记录一下.(为什么要看源码,原因很简单---- 一 好好了解一下 jQuery 原理 二 为了装逼显摆). 一 使用 jQuer

  • 源码解析经验

    源码解析心得 1. 解析的目的 主要搞清楚该模块如何完成功能,包括初始化和入口,总体功能框架,主要流程(函数调用),主要数据结构等. 2. 如何开展解析工作(五角星代表难度指数) ① 解析准备:从各种能帮的上的材料入手,了解模块的体制和框架,明确解析点.★★ ② 类解析:从主要类入手,解析类的结构和功能,包括类的注释,类的声明,类的成员,构造方法等.★★★ ③ 功能解析:从具体功能入手,解析该功能的函数调用,包括各种分支进入的条件(什么情况下),调用的函数,携带的参数等.★★★★ ④ 函数解析:

  • java 源码解析-ArrayList java 源码解析-ArrayList

    java 源码解析--ArrayList ArrayList 1.看源码前应该知道的知识点 /** * */ package com.study.collection; import java.util.ArrayList; import java.util.Arrays; /** * * @className :ArrayListTestLearn * @package : com.study.collection * @Description :解读ArrayList前必须知道的几点知识点

最新文章
  • 黑五美亚 PS4 + GTA V + The Last of Us 399 刀怎样

    http://www.amazon.com/gp/product/B00O9JLAX4/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1 --cut-- txx在2014-11-28 12:24:2回答到: 从惠惠助手上看,这货一直这个价.... zypatroon在2014-11-28 12:30:5回答到: 一般.last of us 现在兑换码普遍20刀出头.newegg eBay 今天给了 330 刀的裸机价格.看朋友玩 GTA V 觉得不错.如果热

  • 最近自己学习英文,发现学习单词的话,英英翻译效果好一些,想自己做一个应用,用的韦氏词典的 api,解析应该如何做啊

    这是接口返回的数据, http://www.dictionaryapi.com/api/v1/references/learners/xml/thank?key=0bc7da64-2140-4447-8a5a-be792d931104 感觉挺乱的,他们有一个文档,看起来更乱,想问问应该如何来解析和展示好一些 http://www.dictionaryapi.com/content/products/documentation/collegiate-tag-description.txt 谢谢各位

  • 入手 JetDrive™ Lite 330 MacBook Pro 专用扩展卡 入手 JetDrive™ Lite 330 MacBook Pro 专用扩展卡

    之前一直关注这个卡,曾经想海淘但一直闲麻烦没动手. 目前京东搞活动,原价688,立减50,还能叠加减100的券,最终折后价 538 对比美亚的 79.99刀,基本上不相上下了,想买的赶快动手吧 折腾详情: http://app711.com/mac/53.html --cut-- jameszeng在2014-11-05 09:06:4回答到: 用这个再读相机存储卡就很不方便了 holong2000在2014-11-05 09:11:0回答到: 这个卡最大的问题是会突出来一大截,移动的时候相当不

  • 描写马蹄莲的句子

    1. 马蹄莲美得像出污泥而不染的荷花,纯得如在邈远的蓝天中浮荡的白云,净得似光彩熠熠的湖水,嫩得同刚刚探出脑袋的小草--哦,多么圣洁,多么高雅! 2. 几天后,花瓣缓缓舒展开来,露出了一条长长的黄澄澄的花蕊,茎也越来越挺拔,有力地撑托着上面大大的花朵. 3. 今天,花瓣慢慢地向外伸展开来,渐渐地,整朵花都开了.你看桃红的花瓣儿,呈马蹄状,中间一个比花瓣儿稍红一些的花蕊. 4. 如今,这两株马蹄莲都是绿油油的,那清翠欲滴的样子让我看到了它的未来,它是那么的高,那么的直,那么的挺. 5. 马蹄莲的茎

  • IE6 hack for js 集锦

    1. ie6下iframe不显示的问题 在ie6下有时会碰到iframe不显示的问题,刷新后才能显示,此问题不是页面加载慢导致的,出现该问题的原因有多种可能: src属性放在iframe中最前面(未曾遇见) 高度设置为100%,ie6下iframe不能取到该高度实际值,解决方案:设置为固定高度值 使用a标签触发onclick事件动态创建iframe时也会出现该现象,解决方案:click函数中增加return false; 如: 2. ie6下css sprite多次加载问题 在ie6下css s

  • 民间艺术“秘密花园手册”——爱普生V19扫描仪让民间艺术更易传承 民间艺术“秘密花园手册”——爱普生V19扫描仪让民间艺术更易传承

    上周六,跟许久不见的朋友聚了个会,全程都在听旅行达人小张讲她的旅途故事.后来她在她的背包里拿出了一个精美的册子,聚焦了所有人的目光,于是小张又开始一边翻册子一边徐徐道来有关它的故事. 小张说这一张张精美的图画,都是一位阿婆经过多年手艺的练习用纸剪出来的花样.为了更好的还原原版留存下来,阿婆的儿子特别精挑细选买了一台爱普生V19扫描仪送给阿婆.通过爱普生扫描仪独有的专业色彩管理模式,剪纸的色彩和质感得到高辨识度的还原.这样不仅使阿婆的精湛手艺得到了完好的电子留存,还可以高清输出,印刷成册更好的推广

  • 魔兽世界蓝贴:弹性团人数及输出或将调账 魔兽世界蓝贴:弹性团人数及输出或将调账

    今天,暴雪社区经理Bashiok开了一个蓝贴,征求玩家对于弹性团"人数断点"的反馈: 目前有一些关于弹性团里导致首领机制变化的人数断点,有的说法很实在.比如诺鲁什战斗中的光球数量. 希望大家能具体指出在哪些战斗里会出现类似的人数断点,其对应机制又是什么.大家都希望能让亲朋好友进组打本,而不会受到一些战斗机制的限制. 贴里提到的类似问题还不少,Bashiok只用了诺鲁什为例.达到断点时,团队需要更多DPS或者更多治疗量才能击杀BOSS.傲之煞的囚笼也是类似的机制--在超过一定人数后,一共

  • 宇宙尘埃中发现水  或预示宇宙中的生命普遍存在 宇宙尘埃中发现水 或预示宇宙中的生命普遍存在

    据国外媒体报道,研究人员声称,漂浮在我们太阳系中的尘埃颗粒已经被发现含有微型的水囊,当尘埃颗粒被来自太阳的带电粒子击中时就会形成水囊,这一现象是在实验室中创造的,但是之前并未在真正的宇宙尘埃中中得到确认. 这项研究的研究者之一,夏威夷大学的Hope Ishii解释道:"这种含义有可能是巨大的,这是一个特别令人兴奋的可能,太阳系星球表面的这些尘埃就像降雨一样不断落下,其中包含着生命起源所需要的水和有机化合物.Ishii说道,这个产生水的反应需要尘埃和太阳能,它很可能发生在任何拥有恒星甚至是超新星的

  • JAVA文件操作 随机流的一些理解

    昨天debug到半夜都是因为文件操作问题--心累了,关于文件操作,什么语言里都不是很会,JAVA里确实提供了很多种类,但这么多种反倒让人觉得更加烦恼了. 在随机流中的使用和其他语言比较类似(C/PHP)所以最终还是使用了他,功能也比较强大. 代码如下 复制代码 RandomAccessFile objectOut = new RandomAccessFile(accountNumber+"_info.txt", "rw"); 提供一堆read和write,默认当然是

  • fckeditor文件上传路径修改方法

    VS打开FCK项目,打开文件"FCKeditor.Net_2.6.3FileBrowserFileWorkerBase.cs" 找到这行 C# code // Map the virtual path to the local server path. string sServerDir = this.ServerMapFolder( resourceType, currentFolder, isQuickUpload ); //如果不关心按类型存放文件的话,FCK把类型分为image

热门推荐