图:四川九寨沟

本文来自徐俊的独家投稿,徐俊的blog地址为:博客地址:http://blog.csdn.net/gdutxiaoxu/,点击【阅读原文】,可查看本文链接。

使用CoordinatorLayout打造各种炫酷的效果

自定义Behavior —— 仿知乎,FloatActionButton隐藏与展示

NestedScrolling 机制深入解析

一步步带你读懂 CoordinatorLayout 源码

自定义 Behavior -仿新浪微博发现页的实现

效果图

我们先来看一下新浪微博发现页的效果:

接下来我们在来看一下我们仿照新浪微博实现的效果

实现思路分析

我们这里先定义两种状态,open 和 close 状态。

  • open 状态指 Tab+ViewPager 还没有滑动到顶部的时候,header 还 没有被完全移除屏幕的时候

  • close 状态指 Tab+ViewPager 滑动到顶部的时候,Header 被移除屏幕的时候

从效果图,我们可以看到 在 open 状态下,我们向上滑动 ViewPager 里面的 RecyclerView 的 时候,RecyclerView 并不会向上移动(RecyclerView 的滑动事件交给 外部的容器处理,被被全部消费掉了),而是整个布局(指 Header + Tab +ViewPager)会向上偏移 。当 Tab 滑动到顶部的时候,我们向上滑动 ViewPager 里面的 RecyclerView 的时候,RecyclerView 可以正常向上滑动,即此时外部容器没有拦截滑动事件

同时我们可以看到在 open 状态的时候,我们是不支持下拉刷新的,这个比较容易实现,监听页面的状态,如果是 open 状态,我们设置 SwipeRefreshLayout setEnabled 为 false,这样不会 拦截事件,在页面 close 的时候,设置 SwipeRefreshLayout setEnabled 为 TRUE,这样就可以支持下拉刷新了。

基于上面的分析,我们这里可以把整个效果划分为两个部分,第一部分为 Header,第二部分为 Tab+ViewPager。下文统一把第一部分称为 Header,第二部分称为 Content 。

需要实现的效果为:在页面状态为 open 的时候,向上滑动 Header 的时候,整体向上偏移,ViewPager 里面的 RecyclerView 向上滑动的时候,消费其滑动事件,并整体向上移动。在页面状态为 close 的时候,不消耗 RecyclerView 的 滑动事件。

在上一篇博客 一步步带你读懂 CoordinatorLayout 源码 中,我们有提到在 CoordinatorLayout中,我们可以通过 给子 View 自定义 Behavior 来处理事件。它是一个容器,实现了 NestedScrollingParent 接口。它并不会直接处理事件,而是会尽可能地交给子 View 的 Behavior 进行处理。因此,为了减少依赖,我们把这两部分的关系定义为 Content 依赖于 Header。Header 移动的时候,Content 跟着 移动。所以,我们在处理滑动事件的时候,只需要处理好 Header 部分的 Behavior 就oK了,Content 部分的 Behavior 不需要处理滑动事件,只需依赖于 Header ,跟着做相应的移动即可。


Header 部分的实现

Header 部分实现的两个关键点在于

  1. 在页面状态为 open 的时候,ViewPager 里面的 RecyclerView 向上滑动的时候,消费其滑动事件,并整体向上移动。在页面状态为 close 的时候,不消耗 RecyclerView 的 滑动事件

  2. 在页面状态为 open 的时候,向上滑动 Header 的时候,整体向上偏移。

第一个关键点的实现

这里区分页面状态是 open 还是 close 状态是通过 Header 是否移除屏幕来区分的,即 child.getTranslationY() == getHeaderOffsetRange() 。

private boolean isClosed(View child) {boolean isClosed = child.getTranslationY() == getHeaderOffsetRange();return isClosed;
}

在NestedScrolling 机制深入解析博客中,我们对 NestedScrolling 机制做了如下的总结。

  • 在 Action_Down 的时候,Scrolling child 会调用 startNestedScroll 方法,通过 childHelper 回调 Scrolling Parent 的 startNestedScroll 方法。

  • 在 Action_move 的时候,Scrolling Child 要开始滑动的时候,会调用dispatchNestedPreScroll 方法,通过 ChildHelper 询问 Scrolling Parent 是否要先于 Child 进行 滑动,若需要的话,会调用 Parent 的 onNestedPreScroll 方法,协同 Child 一起进行滑动

  • 当 ScrollingChild 滑动完成的时候,会调用 dispatchNestedScroll 方法,通过 ChildHelper 询问 Scrolling Parent 是否需要进行滑动,需要的话,会 调用 Parent 的 onNestedScroll 方法

  • 在 Actiondown,Actionmove 的时候,会调用 Scrolling Child 的stopNestedScroll ,通过 ChildHelper 询问 Scrolling parent 的 stopNestedScroll 方法。

  • 如果需要处理 Fling 动作,我们可以通过 VelocityTrackerCompat 获得相应的速度,并在 Action_up 的时候,调用 dispatchNestedPreFling 方法,通过 ChildHelper 询问 Parent 是否需要先于 child 进行 Fling 动作 在 Child 处理完 Fling 动作时候,如果 Scrolling Parent 还需要处理 Fling 动作,我们可以调用 dispatchNestedFling 方法,通过 ChildHelper ,调用 Parent 的 onNestedFling 方法

而 RecyclerView 也是 Scrolling Child (实现了 NestedScrollingChild 接口),RecyclerView 在开始滑动的 时候会先调用 CoordinatorLayout 的 startNestedScroll 方法,而 CoordinatorLayout 会 调用子 View 的 Behavior 的 startNestedScroll 方法。并且只有 boolean startNestedScroll 返回 TRUE 的 时候,才会调用接下里 Behavior 中的 onNestedPreScroll 和 onNestedScroll 方法。

所以,我们在 WeiboHeaderPagerBehavior 的 onStartNestedScroll 方法可以这样写,可以确保 只拦截垂直方向上的滚动事件,且当前状态是打开的并且还可以继续向上收缩的时候还会拦截

@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, ViewdirectTargetChild, View target, int nestedScrollAxes) {if (BuildConfig.DEBUG) {Log.d(TAG, "onStartNestedScroll: nestedScrollAxes=" + nestedScrollAxes);}boolean canScroll = canScroll(child, 0);//拦截垂直方向上的滚动事件且当前状态是打开的并且还可以继续向上收缩return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 && canScroll &&!isClosed(child);}

拦截事件之后,我们需要在 RecyclerView 滑动之前消耗事件,并且移动 Header,让其向上偏移。

@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target,int dx, int dy, int[] consumed) {super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);//dy>0 scroll up;dy<0,scroll downLog.i(TAG, "onNestedPreScroll: dy=" + dy);float halfOfDis = dy;//    不能滑动了,直接给 Header 设置 终值,防止出错if (!canScroll(child, halfOfDis)) {child.setTranslationY(halfOfDis > 0 ? getHeaderOffsetRange() : 0);} else {child.setTranslationY(child.getTranslationY() - halfOfDis);}//consumed all scroll behavior after we started Nested Scrollingconsumed[1] = dy;
}

当然,我们也需要处理 Fling 事件,在页面没有完全关闭的 时候,消费所有 fling 事件。

@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target,float velocityX, float velocityY) {// consumed the flinging behavior until Closedreturn !isClosed(child);
}

至于滑动到顶部的动画,我是通过 mOverScroller + FlingRunnable 来实现的 。完整代码如下。

public class WeiboHeaderPagerBehavior extends ViewOffsetBehavior {private static final String TAG = "UcNewsHeaderPager";public static final int STATE_OPENED = 0;public static final int STATE_CLOSED = 1;public static final int DURATION_SHORT = 300;public static final int DURATION_LONG = 600;private int mCurState = STATE_OPENED;private OnPagerStateListener mPagerStateListener;private OverScroller mOverScroller;private WeakReference<CoordinatorLayout> mParent;private WeakReference<View> mChild;public void setPagerStateListener(OnPagerStateListener pagerStateListener) {mPagerStateListener = pagerStateListener;}public WeiboHeaderPagerBehavior() {init();}public WeiboHeaderPagerBehavior(Context context, AttributeSet attrs) {super(context, attrs);init();}private void init() {mOverScroller = new OverScroller(BaseAPP.getAppContext());}@Overrideprotected void layoutChild(CoordinatorLayout parent, View child, int layoutDirection) {super.layoutChild(parent, child, layoutDirection);mParent = new WeakReference<CoordinatorLayout>(parent);mChild = new WeakReference<View>(child);}@Overridepublic boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, ViewdirectTargetChild, View target, int nestedScrollAxes) {if (BuildConfig.DEBUG) {Log.d(TAG, "onStartNestedScroll: nestedScrollAxes=" + nestedScrollAxes);}boolean canScroll = canScroll(child, 0);//拦截垂直方向上的滚动事件且当前状态是打开的并且还可以继续向上收缩return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0 && canScroll &&!isClosed(child);}@Overridepublic boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target,float velocityX, float velocityY) {// consumed the flinging behavior until Closedboolean coumsed = !isClosed(child);Log.i(TAG, "onNestedPreFling: coumsed=" +coumsed);return coumsed;}@Overridepublic boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target,float velocityX, float velocityY, boolean consumed) {Log.i(TAG, "onNestedFling: velocityY=" +velocityY);return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY,consumed);}private boolean isClosed(View child) {boolean isClosed = child.getTranslationY() == getHeaderOffsetRange();return isClosed;}public boolean isClosed() {return mCurState == STATE_CLOSED;}private void changeState(int newState) {if (mCurState != newState) {mCurState = newState;if (mCurState == STATE_OPENED) {if (mPagerStateListener != null) {mPagerStateListener.onPagerOpened();}} else {if (mPagerStateListener != null) {mPagerStateListener.onPagerClosed();}}}}// 表示 Header TransLationY 的值是否达到我们指定的阀值, headerOffsetRange,到达了,返回 false,// 否则,返回 true。注意 TransLationY 是负数。private boolean canScroll(View child, float pendingDy) {int pendingTranslationY = (int) (child.getTranslationY() - pendingDy);int headerOffsetRange = getHeaderOffsetRange();if (pendingTranslationY >= headerOffsetRange && pendingTranslationY <= 0) {return true;}return false;}@Overridepublic boolean onInterceptTouchEvent(CoordinatorLayout parent, final View child, MotionEventev) {boolean closed = isClosed();Log.i(TAG, "onInterceptTouchEvent: closed=" + closed);if (ev.getAction() == MotionEvent.ACTION_UP && !closed) {handleActionUp(parent,child);}return super.onInterceptTouchEvent(parent, child, ev);}@Overridepublic void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target,int dx, int dy, int[] consumed) {super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);//dy>0 scroll up;dy<0,scroll downLog.i(TAG, "onNestedPreScroll: dy=" + dy);float halfOfDis = dy;//    不能滑动了,直接给 Header 设置 终值,防止出错if (!canScroll(child, halfOfDis)) {child.setTranslationY(halfOfDis > 0 ? getHeaderOffsetRange() : 0);} else {child.setTranslationY(child.getTranslationY() - halfOfDis);}//consumed all scroll behavior after we started Nested Scrollingconsumed[1] = dy;}//    需要注意的是  Header 我们是通过 setTranslationY 来移出屏幕的,所以这个值是负数private int getHeaderOffsetRange() {return BaseAPP.getInstance().getResources().getDimensionPixelOffset(R.dimen.weibo_header_offset);}private void handleActionUp(CoordinatorLayout parent, final View child) {if (BuildConfig.DEBUG) {Log.d(TAG, "handleActionUp: ");}if (mFlingRunnable != null) {child.removeCallbacks(mFlingRunnable);mFlingRunnable = null;}mFlingRunnable = new FlingRunnable(parent, child);if (child.getTranslationY() < getHeaderOffsetRange() / 6.0f) {mFlingRunnable.scrollToClosed(DURATION_SHORT);} else {mFlingRunnable.scrollToOpen(DURATION_SHORT);}}private void onFlingFinished(CoordinatorLayout coordinatorLayout, View layout) {changeState(isClosed(layout) ? STATE_CLOSED : STATE_OPENED);}public void openPager() {openPager(DURATION_LONG);}/*** @param duration open animation duration*/public void openPager(int duration) {View child = mChild.get();CoordinatorLayout parent = mParent.get();if (isClosed() && child != null) {if (mFlingRunnable != null) {child.removeCallbacks(mFlingRunnable);mFlingRunnable = null;}mFlingRunnable = new FlingRunnable(parent, child);mFlingRunnable.scrollToOpen(duration);}}public void closePager() {closePager(DURATION_LONG);}/*** @param duration close animation duration*/public void closePager(int duration) {View child = mChild.get();CoordinatorLayout parent = mParent.get();if (!isClosed()) {if (mFlingRunnable != null) {child.removeCallbacks(mFlingRunnable);mFlingRunnable = null;}mFlingRunnable = new FlingRunnable(parent, child);mFlingRunnable.scrollToClosed(duration);}}private FlingRunnable mFlingRunnable;/*** For animation , Why not use {@link android.view.ViewPropertyAnimator } to play animation* is of the* other {@link CoordinatorLayout.Behavior} that depend on this could not receiving the* correct result of* {@link View#getTranslationY()} after animation finished for whatever reason that i don't know*/private class FlingRunnable implements Runnable {private final CoordinatorLayout mParent;private final View mLayout;FlingRunnable(CoordinatorLayout parent, View layout) {mParent = parent;mLayout = layout;}public void scrollToClosed(int duration) {float curTranslationY = ViewCompat.getTranslationY(mLayout);float dy = getHeaderOffsetRange() - curTranslationY;if (BuildConfig.DEBUG) {Log.d(TAG, "scrollToClosed:offest:" + getHeaderOffsetRange());Log.d(TAG, "scrollToClosed: cur0:" + curTranslationY + ",end0:" + dy);Log.d(TAG, "scrollToClosed: cur:" + Math.round(curTranslationY) + ",end:" + Math.round(dy));Log.d(TAG, "scrollToClosed: cur1:" + (int) (curTranslationY) + ",end:" + (int) dy);}mOverScroller.startScroll(0, Math.round(curTranslationY - 0.1f), 0, Math.round(dy +0.1f), duration);start();}public void scrollToOpen(int duration) {float curTranslationY = ViewCompat.getTranslationY(mLayout);mOverScroller.startScroll(0, (int) curTranslationY, 0, (int) -curTranslationY,duration);start();}private void start() {if (mOverScroller.computeScrollOffset()) {mFlingRunnable = new FlingRunnable(mParent, mLayout);ViewCompat.postOnAnimation(mLayout, mFlingRunnable);} else {onFlingFinished(mParent, mLayout);}}@Overridepublic void run() {if (mLayout != null && mOverScroller != null) {if (mOverScroller.computeScrollOffset()) {if (BuildConfig.DEBUG) {Log.d(TAG, "run: " + mOverScroller.getCurrY());}ViewCompat.setTranslationY(mLayout, mOverScroller.getCurrY());ViewCompat.postOnAnimation(mLayout, this);} else {onFlingFinished(mParent, mLayout);}}}}/*** callback for HeaderPager 's state*/public interface OnPagerStateListener {/*** do callback when pager closed*/void onPagerClosed();/*** do callback when pager opened*/void onPagerOpened();}}

第二个关键点的实现

在页面状态为 open 的时候,向上滑动 Header 的时候,整体向上偏移。

在第一个关键点的实现上,我们是通过自定义 Behavior 来处理 ViewPager 里面 RecyclerView 的移动的,那我们要怎样监听整个 Header 的滑动了。

那就是重写 LinearLayout,将滑动事件交给 ScrollingParent(这里是CoordinatorLayout) 去处理,CoordinatorLayout 再交给子 View 的 behavior 去处理。

Content 部分的实现

Content 部分的实现也主要有两个关键点

  • 整体置于 Header 之下

  • Content 跟着 Header 移动。即 Header 位置发生变化的时候,Content 也需要随着调整位置。

第一个关键点的实现

整体置于 Header 之下。这个我们可以参考 APPBarLayout 的 behavior,它是这样处理的。

这个基类的代码还是很好理解的,因为之前就说过了,正常来说被依赖的 View 会优先于依赖它的 View 处理,所以需要依赖的 View 可以在 measure/layout 的时候,找到依赖的 View 并获取到它的测量/布局的信息,这里的处理就是依靠着这种关系来实现的.

我们的实现类,需要重写的除了抽象方法 findFirstDependency 外,还需要重写 getScrollRange,我们把 Header 的 Id idweiboheader 定义在 ids.xml 资源文件内,方便依赖的判断.

至于缩放的高度,根据 结果图 得知是 0,得出如下代码

private int getFinalHeight() {Resources resources = BaseAPP.getInstance().getResources();return 0;
}@Overrideprotected int getScrollRange(View v) {if (isDependOn(v)) {return Math.max(0, v.getMeasuredHeight() - getFinalHeight());} else {return super.getScrollRange(v);}}

第二个关键点的实现:

Content 跟着 Header 移动。即 Header 位置发生变化的时候,Content 也需要随着调整位置。

主要的逻辑就是 在 layoutDependsOn 方法里面,判断 dependcy 是不是 HeaderView ,是的话,返回TRUE,这样在 Header 位置发生变化的时候,会回调 onDependentViewChanged 方法,在该方法里面,做相应的偏移。TranslationY 是根据比例算出来的 translationY = (int) (-dependencyTranslationY / (getHeaderOffsetRange() * 1.0f) * getScrollRange(dependency));

完整代码如下:

public class WeiboContentBehavior extends HeaderScrollingViewBehavior {private static final String TAG = "WeiboContentBehavior";public WeiboContentBehavior() {}public WeiboContentBehavior(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {return isDependOn(dependency);}@Overridepublic boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {if (BuildConfig.DEBUG) {Log.d(TAG, "onDependentViewChanged");}offsetChildAsNeeded(parent, child, dependency);return false;}private void offsetChildAsNeeded(CoordinatorLayout parent, View child, View dependency) {float dependencyTranslationY = dependency.getTranslationY();int translationY = (int) (-dependencyTranslationY / (getHeaderOffsetRange() * 1.0f) * getScrollRange(dependency));Log.i(TAG, "offsetChildAsNeeded: translationY=" + translationY);child.setTranslationY(translationY);}@Overrideprotected View findFirstDependency(List<View> views) {for (int i = 0, z = views.size(); i < z; i++) {View view = views.get(i);if (isDependOn(view)) return view;}return null;}@Overrideprotected int getScrollRange(View v) {if (isDependOn(v)) {return Math.max(0, v.getMeasuredHeight() - getFinalHeight());} else {return super.getScrollRange(v);}}private int getHeaderOffsetRange() {return BaseAPP.getInstance().getResources().getDimensionPixelOffset(R.dimen.weibo_header_offset);}private int getFinalHeight() {Resources resources = BaseAPP.getInstance().getResources();return 0;}private boolean isDependOn(View dependency) {return dependency != null && dependency.getId() == R.id.id_weibo_header;}
}

题外话

  • NestedScrolling 机制,对比传统的事件分发机制真的很强大。这种仿新浪微博发现页效果, 如果用传统的事件分发机制来做,估计很难实现,处理起来会有一大堆坑。

  • 看完了这种仿新浪微博发现页的效果,你是不是学到了什么?如果让你 模仿 仿 QQ 浏览器首页效果,你能实现话。

最后,特别感谢写这篇博客 自定义Behavior的艺术探索-仿UC浏览器主页 的开发者,没有这篇博客作为参考,这种效果我很大几率是实现 不了的。大家觉得效果还不错的话,顺手到 github 上面给我 star,谢谢。github 地址:https://github.com/gdutxiaoxu/CoordinatorLayoutExample


参考文章:

自定义Behavior的艺术探索-仿UC浏览器主页

第一时间获得不止个人原创 android/音视频技术干货,问题深度总结,FrameWork源码解析,插件化研究,FFmpeg研究,直播技术,最新开源项目推荐,还有更多职场思考,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码

自定义 Behavior - 仿新浪微博发现页的实现相关推荐

  1. android 仿新浪微博发现效果

    新浪微博发现效果 最开始写这个效果借鉴过两个例子,这两个都有坑,我总结下避免踩坑. 1,自定义 Behavior -仿新浪微博发现页的实现,链接地址:http://blog.csdn.net/gdut ...

  2. 移动软件开发-仿微信发现页

    一.实验目标 1.了解安卓开发的一些基础知识:2.学习如何仿微信发现页创建布局,了解安卓开发的线性布局和一些对应的组件. 二.实验步骤 1.实验准备: 下载android studio,并配置好相应的 ...

  3. 样式集(七)仿微信发现页样式

    效果图: <!--pages/find/find.wxml--><include src="/components/common/common" /> &l ...

  4. 仿微博发现页吸顶效果

    整个首页布局采用 CoordinatorLayout 布局,实现正常的吸顶效果.然后监听 AppBarLayout 的展开与折叠.我直接上代码吧. 首页外部布局: <androidx.coord ...

  5. 自定义 behavior - 完美仿 QQ 浏览器首页,美团商家详情页

    前言 记得两年前的时候,曾写过自定义 behavior 的文章 自定义 Behavior -仿新浪微博发现页的实现,到现在差不多有一万多的阅读量吧. image 今天,对该 behavior 进行升级 ...

  6. 自定义Behavior的艺术探索-仿UC浏览器主页

    出处:http://www.jianshu.com/p/f7989a2a3ec2 前言&效果预览 最近几个周末基本在研究CoordinatorLayout控件和自定义Behavior当中,这期 ...

  7. android 微博字体高亮,安卓开发札记——高仿新浪微博文字处理(实现关键字高亮,自定义表情替换并加入点击事件实现)...

    安卓开发笔记--高仿新浪微博文字处理(实现关键字高亮,自定义表情替换并加入点击事件实现) 先让大家看下效果图,这个是我自己在闲暇时间仿写的新浪微博客户端: 今天来讲讲如何实现上图的效果,这里需要用到S ...

  8. 仿新浪微博IOS客户端(v5.2.8)——自定义UITabBar替换系统默认的(添加“+”号按钮)

    转载请标明出处:http://blog.csdn.net/android_ls/article/details/45896395 声明:仿新浪微博项目,所用所有图片资源都来源于官方新浪微博IOS客户端 ...

  9. Flutter开发实战 高仿微信(二)发现页

    Flutter开发实战 高仿微信(二)发现页 Flutter开发实战 高仿微信(二)发现页 1.1 微信发现页面简述 1.2 APP框架优化 1.2.1 配置APP Logo和启动图片 1.2.2 配 ...

  10. HTML5+CSS大作业——仿新浪微博个人主(4页) html期末作业代码网页设计

    HTML5+CSS大作业--仿新浪微博个人主(4页) 常见网页设计作业题材有 个人. 美食. 公司. 学校. 旅游. 电商. 宠物. 电器. 茶叶. 家居. 酒店. 舞蹈. 动漫. 明星. 服装. 体 ...

最新文章

  1. 华为手机像素密度排行_2020拍照手机十大排行:华为128分破纪录,苹果无一上榜...
  2. 李宏毅线性代数笔记1:系统
  3. 13.跨进程读写内存
  4. qtextedit非编辑时去边框_Photoshop玩腻了!这10个图形编辑神器你知道吗
  5. 第三次学JAVA再学不好就吃翔(part89)--HashSet
  6. java public object_Java_Object
  7. 一个简单的dotnet tool
  8. 数据可视化【十一】树状图
  9. java语言 编译原理_【Java学习】深入分析Java的编译原理
  10. python中反斜杠_Python中的正斜杠/与反斜杠\
  11. ant编译web项目
  12. Cocos2dx 小技巧(十五)话说ScrollView的delegate实现过程
  13. Mysql得隔离级别
  14. github private链接访问_Hands-On Design Patterns With C++(十八)访问者模式与多分派(下)...
  15. Vulkan开发环境配置 Win10+VS2019
  16. verilog实现串行通讯协议(serial communications protocols)
  17. 第三十三章 SQL命令 DROP INDEX
  18. 手机离线地图——基于OruxMaps离线高清卫星地图制作发布
  19. 爱情 何为爱情(搜集)
  20. 使用Java生成图形验证码(后端)

热门文章

  1. 算法笔记_面试题_13.二叉树的最近公共祖先
  2. ubuntu 安装多个CUDA版本并可以随时切换
  3. sitck-breaking折棍法理解
  4. Tensorflow训练CIFAR10源代码
  5. csv文件的使用,csv空白行问题
  6. 使用authentication进行身份验证,与Forms表单登陆
  7. CMD命令行下编译.Net Visual Studio 项目
  8. day10.函数,函数的参数
  9. php strcmp bypass漏洞
  10. JAVA SE、JAVA EE、JAVA ME的联系与区别