本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

毫无疑问,RecyclerView 是现在 Android 世界中最重要的系统组件之一,它的出现就是为了高效代替 ListView 和 GridView。当时它的出现解决了我一个大的需求,这个需求就是在电视盒子界面上横向加载应用列表,由于 ListView 没有横向加载的功能,而网络上开源的那些 HorizontalListView 又不满足需求,所以我们只能自定义 ViewGroup 来实现需求,但是回收机制不是很完善,所以性能并不好,所以当 RecyclerView 横空出世时,我第一时间拥抱了它,并推荐 Android 开发小组成员们去了解它。

但后来,我发现 RecyclerView 除了比 ListView 好用外,某些地方它却更复杂了,它将更多的权力交给了开发者自己,比如布局,比如 ITEM 的分割线,比如点击监听等等。但总归它是好东西,所以我们得多花些时间来学习,平常开发我们一般按照 RecyclerView 的基本用法便可以实现绝大多数需求,但是某些场景下却远远不够,比如我们不想局限于 LinearLayoutManager 想自己定义 LayoutManager,我们需要定义时光轴的效果,我们想实现美妙的添加删除动画等等,这些情况下解决问题的话需要我们对 RecyclerView 本身有足够的了解。

今天,这篇文章不讲 RecyclerView 基本的知识和用法,讲它一个有趣的知识点 ItemDecoration。

ItemDecoration

Decoration 的英文意思是装饰物的意思,引申到这里来,肯定也是与 RecyclerView 的界面装饰有关。我们常见的就是分割线了。
我们在使用 ListView 的时候只要在 xml 文件中,使用 android:divider 就可以,但是很遗憾 RecyclerView 却没有相应的控制。

我们新建一个工程,然后在一个页面里面添加一个 RecyclerView。创建相关的 Adapter,加载布局文件,这里布局文件很简单,就是一个 TextView,再之后在 Activity 初始化它。

public class DividerActivity extends AppCompatActivity {RecyclerView mRecyclerView;List<String> data;TestAdapter mAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_divider);mRecyclerView = (RecyclerView) findViewById(R.id.divider_recyclerview);initDatas();mAdapter = new TestAdapter(data);mRecyclerView.setAdapter(mAdapter);LinearLayoutManager layoutmanager = new LinearLayoutManager(this);layoutmanager.setOrientation(LinearLayoutManager.VERTICAL);mRecyclerView.setLayoutManager(layoutmanager);}private void initDatas() {data = new ArrayList<>();for (int i = 0; i < 56;i++) {data.add(i+" test ");}}
}

可以看到所有的选项都混在一起,为了美观应该需要 1 px 的分割线,之前我一般在 Item 的布局文件中设置它的 topMargin 或者是 bottomMargin,所以我们可以在相关的 Adapter 中这样修改。

public TestHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_item,parent,false);RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();layoutParams.topMargin = 1;view.setLayoutParams(layoutParams);TestHolder holder = new TestHolder(view);return holder;
}

效果如下:

现在我们同样可以通过给 RecyclerView 添加 ItemDecoration 来实现它。

首先,我们需要自定义一个 ItemDecoration,按照目前的需求,我们只需要实现它的一个方法就可以了。

public class TestDividerItemDecoration extends RecyclerView.ItemDecoration {@Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {super.getItemOffsets(outRect, view, parent, state);//        //如果不是第一个,则设置top的值。if (parent.getChildAdapterPosition(view) != 0){//这里直接硬编码为1pxoutRect.top = 1;}}
}

然后在 Activity 中添加它到 RecyclerView 就可以了。

mRecyclerView = (RecyclerView) findViewById(R.id.divider_recyclerview);
initDatas();
mAdapter = new TestAdapter(data);
mRecyclerView.setAdapter(mAdapter);
LinearLayoutManager layoutmanager = new LinearLayoutManager(this);
layoutmanager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(layoutmanager);
mRecyclerView.addItemDecoration(new TestDividerItemDecoration());

效果如图:

getItemOffsets()

我们可以看到自定义的 TestDividerItemDeoration 只实现了一个方法 getItemOffsets()。方法里面有四个参数。

  • Rect outRect
  • View view
  • RecyclerView parent
  • RecyclerView.State state

这四个参数分别干什么的呢?我们不妨在 AndroidStudio 中按 Ctrl 键点击方法名,就可以到了它被调用的位置。

Rect getItemDecorInsetsForChild(View child) {final LayoutParams lp = (LayoutParams) child.getLayoutParams();if (!lp.mInsetsDirty) {return lp.mDecorInsets;}if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {// changed/invalid items should not be updated until they are rebound.return lp.mDecorInsets;}final Rect insets = lp.mDecorInsets;insets.set(0, 0, 0, 0);final int decorCount = mItemDecorations.size();for (int i = 0; i < decorCount; i++) {mTempRect.set(0, 0, 0, 0);//在这里被调用mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);insets.left += mTempRect.left;insets.top += mTempRect.top;insets.right += mTempRect.right;insets.bottom += mTempRect.bottom;}lp.mInsetsDirty = false;return insets;
}

我们注意这一行代码 java mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState); 很容易得知,outRect 是一个全为 0 的 Rect。view 指 RecyclerView 中的 Item。parent 就是 RecyclerView 本身,state 就是一个状态。

我们可以看下面的这张图。

绿色区域代表 RecyclerView 中的一个 ItemView,而外面橙色区域也就是相应的 outRect,也就是 ItemView 与其它组件的偏移区域,等同于 margin 属性,通过复写 getItemOffsets() 方法,然后指定 outRect 中的 top、left、right、bottom 就可以控制各个方向的间隔了。注意的是这些属性都是偏移量,是指偏移 ItemView 各个方向的数值。在上面的例子中我设置了 outRect.top = 1; 所以每个 ItemView 之间有 1 px 的空隙,而这 1 px 空隙透露了下面背景色,所以看起来就像是分隔线,这实现了简单的分隔线效果,但这种方法分隔线的效果只能取决于背景色,如果我要定制分割线的颜色呢?这个时候就要讲到一个新的方法名 onDraw()。

onDraw()

在 Android 中的每一个 View 中 onDraw() 是很重要的一个方法,用来绘制组件的UI效果,所以在 ItemDecocration 中它自然也是用来绘制外观的。我们来看它的方法声明。
java public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state);

可以看到它传递了一个 Canvas 参数对象,所以它拥有了绘制的能力。但是怎么绘制呢?

其实它是配合了前面的 getItemOffsets 方法一起使用的,getItemOffsets 撑开了 ItemView 的上下左右间隔区域,而 onDraw 方法通过计算每个 ItemView 的坐标位置与它的 outRect 值来确定它要绘制内容的区间。

假设,我们要设计一个高度为 2 px 的红色分割线,那么我们就需要在每个 ItemView top位置上方画一个 2 px 高度的矩形,然后填充颜色为红色。
需要注意的一点是 getItemOffsets 是针对每一个 ItemView,而 onDraw 方法却是针对 RecyclerView 本身,所以在 onDraw 方法中需要遍历屏幕上可见的 ItemView,分别获取它们的位置信息,然后分别的绘制对应的分割线。

我们看下面的这张示意图

为了便于观察我将第一条分割线的颜色透明化了,我们可以看到每条分割线绘制的区域其实就是 outRect.top 至 ItemView.top 之间的区域,所以我们就需要在当初 getOffsets 方法进行位置偏移时就记录下每个 itemView 向上的间隔距离,之后的逻辑就是遍历屏幕上的 View,然后描绘分割线。

public class ColorDividerItemDecoration extends RecyclerView.ItemDecoration {private float mDividerHeight;private Paint mPaint;public ColorDividerItemDecoration() {mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setColor(Color.RED);}@Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {super.getItemOffsets(outRect, view, parent, state);//        //第一个ItemView不需要在上面绘制分割线if (parent.getChildAdapterPosition(view) != 0){//这里直接硬编码为1pxoutRect.top = 1;mDividerHeight = 1;}}@Overridepublic void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {super.onDraw(c, parent, state);int childCount = parent.getChildCount();for ( int i = 0; i < childCount; i++ ) {View view = parent.getChildAt(i);int index = parent.getChildAdapterPosition(view);//第一个ItemView不需要绘制if ( index == 0 ) {continue;}float dividerTop = view.getTop() - mDividerHeight;float dividerLeft = parent.getPaddingLeft();float dividerBottom = view.getTop();float dividerRight = parent.getWidth() - parent.getPaddingRight();c.drawRect(dividerLeft,dividerTop,dividerRight,dividerBottom,mPaint);}}
}

然后我们在 Activity 将 ColorDividerItemDecoration 添加到相应的 RecyclerView 中就可以了。

mRecyclerView.addItemDecoration(new ColorDividerItemDecoration());

效果如下图:

至此,红色的分割线就搞定了。

但一定要注意的是,onDraw 方法可不只能绘制简单的线条,它可是拥有 Canvas 的,所以画圆、画矩形、画弧形、绘制图片都不在话下。为了提高本篇代码的技术含量,下面我们通过 ItemDecoration 来实现一个时光轴的效果。

通过 ItemDecoration 实现时光轴的效果

编码的开始先做设计,或者说先思考。思考我们要做什么,或者说要怎么做。

我们可以看到左边白色的图案就大概是我们时光轴要绘制的图形。我们通过 getItemOffsets 方法来对 ItemView 进行 left 和 top 的间距设置。然后确定好轴线的起始坐标,中间轴结点的图形或者是图案。我们可以通过 ItemView 将相应的时光轴片断分解,如下图。

主要是一些参数的确定,例如 DividerHeight,注意这个 DividerHeight 不是指 ItemView 向上的间隔值,而是相应的 ItemDecoration 的高度。中心坐标 (centerX,centerY),还有上下两段轴线的起始坐标。有了这些参数后,我们就能轻松地编码了。

public class TimelineItemDecoration extends RecyclerView.ItemDecoration {private Paint mPaint;//ItemView左边的间距private float mOffsetLeft;//ItemView右边的间距private float mOffsetTop;//时间轴结点的半径private float mNodeRadius;public TimelineItemDecoration(Context context) {mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setColor(Color.RED);mOffsetLeft = context.getResources().getDimension(R.dimen.timeline_item_offset_left);mNodeRadius = context.getResources().getDimension(R.dimen.timeline_item_node_radius);}@Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {super.getItemOffsets(outRect, view, parent, state);//        //第一个ItemView不需要在上面绘制分割线if (parent.getChildAdapterPosition(view) != 0){//这里直接硬编码为1pxoutRect.top = 1;mOffsetTop = 1;}outRect.left = (int) mOffsetLeft;}@Overridepublic void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {super.onDraw(c, parent, state);int childCount = parent.getChildCount();for ( int i = 0; i < childCount; i++ ) {View view = parent.getChildAt(i);int index = parent.getChildAdapterPosition(view);float dividerTop = view.getTop() - mOffsetTop;//第一个ItemView 没有向上方向的间隔if ( index == 0 ) {dividerTop = view.getTop();}float dividerLeft = parent.getPaddingLeft();float dividerBottom = view.getBottom();float dividerRight = parent.getWidth() - parent.getPaddingRight();float centerX = dividerLeft + mOffsetLeft / 2;float centerY = dividerTop + (dividerBottom - dividerTop) / 2;float upLineTopX = centerX;float upLineTopY = dividerTop;float upLineBottomX = centerX;float upLineBottomY = centerY - mNodeRadius;//绘制上半部轴线c.drawLine(upLineTopX,upLineTopY,upLineBottomX,upLineBottomY,mPaint);//绘制时间轴结点c.drawCircle(centerX,centerY,mNodeRadius,mPaint);float downLineTopX = centerX;float downLineTopY = centerY + mNodeRadius;float downLineBottomX = centerX;float downLineBottomY = dividerBottom;//绘制上半部轴线c.drawLine(downLineTopX,downLineTopY,downLineBottomX,downLineBottomY,mPaint);}}
}

然后效果如下图:


感觉不怎么美观,我们尝试将结点的实心圆改成空心圆。

//绘制时间轴结点
mPaint.setStyle(Paint.Style.STROKE);
c.drawCircle(centerX,centerY,mNodeRadius,mPaint);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);

效果如下:

感觉美观了许多。

同时,我们可以将轴结点用图标代替圆或者圆圈。

上面的图标感觉更好看了。

需要注意的是 onDraw 方法,ItemDecoration 是在 ItemView 的下方绘制的,也就是 ItemView 可能会覆盖 ItemDecoration 的内容。我们可以验证一下,在时间轴与 ItemView 的边界画一个完整的圆,观察它的效果。

     mPaint.setStyle(Paint.Style.STROKE);c.drawCircle(view.getLeft(),centerY,mNodeRadius,mPaint);mPaint.setStyle(Paint.Style.FILL_AND_STROKE);


可以看到,在重合的地方,圆圈确实被 ItemView 内容覆盖了。

大家可能会想到,ItemDecoration 内容能不能覆盖在 ItemView 内容之上呢?

答案是肯定的,但不是在 onDraw() 方法实现,而是另外一个方法 onDrawOver()。

onDrawOver 和角标。

现实中的APP或者网站经常有一些排行榜比如下面:


或者这样。

这些角标都是绘制在 ItemView 之上的,现在有了 ItemDecoration 我们也可以轻松而优雅地实现它。

比如我们要实现一个图书销量排行榜。我们有大概的草图。

然后我们就可以编码了。
布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent" android:layout_height="wrap_content"android:background="@android:color/white"><TextView
        android:id="@+id/tv_rank_oder"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_alignParentLeft="true"android:layout_marginLeft="24dp"android:gravity="center"android:textColor="#6c6c6c"/><ImageView
        android:id="@+id/iv_cover"android:layout_width="80dp"android:layout_height="80dp"android:layout_centerVertical="true"android:layout_marginLeft="60dp"/><TextView
        android:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_toRightOf="@id/iv_cover"android:layout_marginLeft="12dp"android:layout_marginTop="12dp"android:textColor="@android:color/black"/><TextView
        android:id="@+id/tv_price"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_toRightOf="@id/iv_cover"android:layout_below="@id/tv_title"android:layout_marginLeft="12dp"android:layout_marginTop="12dp"android:textColor="@android:color/holo_red_dark"/>
</RelativeLayout>

相应的 Adapter 代码:

public class BookRankAdapter extends RecyclerView.Adapter<BookRankAdapter.TestHolder> {List<String> data;int[] mIconResouces;public BookRankAdapter(List<String> data,int[] ids) {this.data = data;this.mIconResouces = ids;}public void setData(List<String> data,int[] ids) {this.data = data;mIconResouces = ids;notifyDataSetChanged();}@Overridepublic TestHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_ranklist_item,parent,false);TestHolder holder = new TestHolder(view);return holder;}@Overridepublic void onBindViewHolder(TestHolder holder, int position) {if (data != null && data.size() > 0 ) {String text = data.get(position);String[] infos = text.split("-");holder.tvOrder.setText(position+"");holder.tvTitle.setText(infos[0]);holder.tvPrice.setText(infos[1]);holder.ivCover.setImageResource(mIconResouces[position]);}}@Overridepublic int getItemCount() {return data == null ? 0 : data.size();}static class TestHolder extends  RecyclerView.ViewHolder{public TextView tvOrder;public TextView tvTitle;public TextView tvPrice;public ImageView ivCover;public TestHolder(View itemView) {super(itemView);tvOrder = (TextView) itemView.findViewById(R.id.tv_rank_oder);tvTitle = (TextView) itemView.findViewById(R.id.tv_title);tvPrice = (TextView) itemView.findViewById(R.id.tv_price);ivCover = (ImageView) itemView.findViewById(R.id.iv_cover);}}
}

自定义 FlagItemDecoration

public class FlagItemDecoration extends RecyclerView.ItemDecoration {private Paint mPaint;private Bitmap mIcon;private float mFlagLeft;public FlagItemDecoration(Context context) {mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setColor(Color.RED);mIcon = BitmapFactory.decodeResource(context.getResources(),R.drawable.hotsale);mFlagLeft = context.getResources().getDimension(R.dimen.flag_left);}@Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {//  super.getItemOffsets(outRect, view, parent, state);//第一个ItemView不需要在上面绘制分割线if (parent.getChildAdapterPosition(view) == 0){outRect.top = 0;} else {outRect.top = 2;}}@Overridepublic void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {super.onDrawOver(c, parent, state);int childCount = parent.getChildCount();for ( int i = 0; i < childCount; i++ ) {View view = parent.getChildAt(i);int index = parent.getChildAdapterPosition(view);float top = view.getTop();if ( index < 3 ) {c.drawBitmap(mIcon,mFlagLeft,top,mPaint);}}}
}

然后在 Activity 进行相应的数据处理,这里的数据都是为了测试用的,所以比较随意。

public class BookRankActivity extends AppCompatActivity {RecyclerView mRecyclerView;List<String> data;BookRankAdapter mAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_bookrank);mRecyclerView = (RecyclerView) findViewById(R.id.bookrank_recyclerview);initDatas();int resouces[] = new int[] {R.drawable.book_renmin,R.drawable.book_huochetou,R.drawable.book_jieyouzahuodian,R.drawable.book_tensoflow,R.drawable.book_wangyangming,R.drawable.book_renmin,R.drawable.book_huochetou,R.drawable.book_jieyouzahuodian,R.drawable.book_tensoflow,R.drawable.book_wangyangming,R.drawable.book_renmin,R.drawable.book_huochetou,R.drawable.book_jieyouzahuodian,R.drawable.book_tensoflow,R.drawable.book_wangyangming};mAdapter = new BookRankAdapter(data,resouces);mRecyclerView.setAdapter(mAdapter);LinearLayoutManager layoutmanager = new LinearLayoutManager(this);layoutmanager.setOrientation(LinearLayoutManager.VERTICAL);mRecyclerView.setLayoutManager(layoutmanager);mRecyclerView.addItemDecoration(new FlagItemDecoration(this));}private void initDatas() {data = new ArrayList<>();data.add("人民的名义- ¥ 33.5");data.add("火车头 - ¥ 27.5");data.add("解忧杂货店- ¥ 19.9");data.add("TensorFlow - ¥ 102.5");data.add("王阳明心学 - ¥ 60");data.add("人民的名义1- ¥ 33.5");data.add("火车头1 - ¥ 27.5");data.add("解忧杂货店1- ¥ 19.9");data.add("TensorFlow1 - ¥ 102.5");data.add("王阳明心学1 - ¥ 60");data.add("人民的名义2 - ¥ 33.5");data.add("火车头2 - ¥ 27.5");data.add("解忧杂货店2- ¥ 19.9");data.add("TensorFlow2 - ¥ 102.5");data.add("王阳明心学2 - ¥ 60");}
}

最终效果如下图:

有人在想,通过 ItemView 中的布局文件不就可以完成这样的操作吗?是的,确实是可以的,将 Flag 角标定义在每一个 ItemView 布局文件中,然后在 Adapter 的 onBindViewHolder 方法中根据 postion 的值来决定是否加载角标。

但是这里是为了说明 ItemDecoration 中的 onDrawOver 方法,为了说明它确实能让 ItemDecoration 图像绘制在 ItemView 内容之上。事实上,ItemDecoration 的妙处还有好多好多。

总结

自定义一个 ItemDecoration 通常要根据需要,复写它的 3 个方法。
* getItemOffsets 撑开 ItemView 上、下、左、右四个方向的空间
* onDraw 在 ItemView 内容之下绘制图形
* onDrawOver 在 ItemView 内容之上绘制图形。

提醒

由于文章篇幅,ItemDecoration 最让我兴奋的内容我需要另写一篇文章,那就是通过 ItemDecoration 自定义 RecyclerView 中的头部或者是粘性头部。相信大家对头部这个概念比较了解,现在通过 ItemDecoration 就可以优雅地实现它,记住优雅两个字,条条大路通罗马,但是有人就优雅、有人就显得手忙脚乱。所以,我将文章标题定了一个词,叫小甜点,吃了让人舒心,ItemDecoration 用了也让人舒心。

好了,文章就到这里结束。

完整源码地址

github地址

更有意思的内容请看这篇《RecyclerView探索之通过ItemDecoration实现StickyHeader效果》

小甜点,RecyclerView 之 ItemDecoration 讲解及高级特性实践相关推荐

  1. 一文通透讲解Redis高级特性,多线程/持久化/淘汰机制等统统搞定

    Redis 是一个开源的,基于内存的可持久化的非关系型数据库存储系统.在实际项目中可以用 Redis 做缓存或消息服务器,Redis 也是目前互联网中使用比较广泛的非关系型数据库,下面就来深入分析Re ...

  2. Flink教程(19)- Flink高级特性(BroadcastState)

    文章目录 01 引言 02 BroadcastState介绍 03 BroadcastState API介绍 04 BroadcastState 案例 4.1 需求 4.2 编码步骤 4.3 编码实现 ...

  3. 【Nvidia】nvidia 高级特性MIG详细介绍(一)

    博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发. 在5G早期负责终端数据业务层.核心网相关的开发工作,目前牵头6G ...

  4. 年会活跃微信群小游戏有哪些?塔防小游戏经典玩法讲解

    马上接近年底了,很多企业都在准备办一个适合当下环境的年会活动,现在仍是疫情防御阶段,那该如何筹备一个线上活动呢?经调查发现,不少公司会选择TOM小游戏作为一个活动的项目,哪选择什么样的游戏适合呢?一起 ...

  5. 小波包能量 - python代码讲解

    小波包能量 - python代码讲解 **1.本文背景** **2.小波包分解介绍** **3.文件介绍** **4.读取数据** 4.1 定义一个数据读取函数 4.2画时域图 **5.进行小波包变换 ...

  6. 微信小程序黑马优购讲解

    微信小程序黑马优购讲解 1.准备工作 1.首先拿到项目后做好项目搭建 2.搭建基础页面 分类页面 列表页内容 详情页面 我的页面 意见反馈页面 1.准备工作 1.首先拿到项目后做好项目搭建 (1)搭建 ...

  7. Python高级特性(切片,迭代,列表生成式,生成器,迭代器)

    掌握了Python的数据类型.语句和函数,基本上就可以编写出很多有用的程序了. 比如构造一个1, 3, 5, 7, ..., 99的列表,可以通过循环实现: L = [] n = 1 while n ...

  8. Python学习笔记:高级特性

    前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...

  9. Java 高级特性 --- 反射

    From:Java 高级特性 --- 反射:https://www.jianshu.com/p/9be58ee20dee From:Java 基础之 --- 反射(非常重要):https://blog ...

最新文章

  1. Java笔试面试总结—try、catch、finally语句中有return 的各类情况
  2. poj2186(强连通分量)
  3. 信息太多,时间太少: 大脑如何区分重要和不重要的事?
  4. Linux部署Node.js应用
  5. ckeditor深入挖掘吃透
  6. YbtOJ-染色计划【树链剖分,线段树,tarjan】
  7. 高通camera调试
  8. Apache Cassandra 开源数据库软件修复高危RCE漏洞
  9. 红米k30pro工程测试代码_红米K30 PRO代号曝光,确定推出双版本,更强拍照对标荣耀30...
  10. 大学c语言第三章作业,c语言程序设计一章部分和第三章习题答案.doc
  11. 计算机上没有系统软件应用软件也一样能使用,2010判断题一般双击桌面上的程序图标可以打开该程序...
  12. python执行源程序的方式是_python源程序执行的方式
  13. PMP培训内容有哪些?
  14. 【STM32】简介及f4 GPIO八种模式及工作原理详解(含电路原理三极管等一步到位了解GPIO)
  15. (解决)Circular view path [index]: would dispatch back to the current handler URL [] again. Che
  16. 英雄联盟英雄技能总结
  17. (纪中)008. Oliver的成绩(score)【高精度】
  18. 流媒体服务器使用手册
  19. Laravel执行定时任务
  20. Python的并发并行[1] - 线程[3] - 多线程的同步控制

热门文章

  1. c51语言while,while 语句的三种控制/结束循环方式
  2. Kotlin的when、if 和循环语句
  3. vue格式化数字为财务数字金额
  4. netbios协议使用
  5. 允许网站使用相机和麦克风_实用小技巧(十九):获取相机和麦克风权限
  6. 网易数帆基础软件平台获2020南京软博会年度创新产品奖
  7. 通过阿里云短信服务群发短信
  8. generator object genexpr at 0x0000000002731828
  9. rust黑球_黑球——桌面重力,开局就是地狱模式的游戏你玩过没?
  10. 部署前端项目的几种方案并探讨优缺点