说到RecyclerView,相信大家都不陌生,它是我们经典级ListView的升级版,升级后的RecyclerView展现了极大的灵活性。同时内部直接封装了ViewHolder,不用我们自己定义ViewHolder就能实现item的回收和复用功能。当然它肯定不止这些好处,比如我们可以自定义分割线,可以更加方便的实现列表的布局方式等等。虽说我们自己在第一次使用时,会比使用listView和gridView稍微的复杂一些,需要自定义的也多了一点,但是它却更好的体现了灵活性,可以随自己的喜好来随便的定义,当然最主要的是能更好的复用,只需一次的定义,却可随处的复用。

下面,我们来好好的学习下它的使用。

首先,我们要是用RecyclerView必须引入support-V7包,拿android studio来举例:

先打开File->选择Project Structure,之后在左边Modules选择你的项目,然后在点击右边的Dependencies,然后点击绿色的+号选择添加Library,然后找到recyclerview-v7双击加入到依赖库中。然后可以在build.gradle中查看:

dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:recyclerview-v7:23.3.0'
}

可以找dependencies 中找到对recyclerview的支持,则说明添加成功,如果还没有的,可以clean下自己的工程。

接下来,我们就进入Recyclerview的学习。RecyclerView的学习目标就是以下四个方法,把以下四个方法完全的掌握了,也就真正的掌握了RecyclerView。

RecyclerView.setAdapter:用来设置adapter,显示数据

RecyclerView.setLayoutManager :用来设置显示布局的,目前系统给出三种布局,分别是垂直,水平和瀑布流式布局

RecyclerView.setItemAnimator :用来设置显示动画的

RecyclerView.addItemDecoration :用来设置列表分割线的

接下来我们就学习怎么使用以上四个方法来真正掌握Recyclerview的使用。要使用Recyclerview,我们必须先定义一个类(CustomRecyclerAdapter)并继承Recyclerview.Adapter,且实现它里面的方法,代码如下:

public class CustomRecyclerAdapter extends RecyclerView.Adapter<CustomRecyclerAdapter.ViewHolderHelper>{

    @Override
public ViewHolderHelper onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(ViewHolderHelper holder, int position) { }
@Override
public int getItemCount() {
return 0;
} public class ViewHolderHelper extends RecyclerView.ViewHolder{ public ViewHolderHelper(View itemView) {
super(itemView);
}
}
}

在我们还没正式开始使用之前,先大体上了解下上面三个方法是做什么的:

A. onCreateViewHolder()方法:该方法就是将布局文件转化为View并传递给RecyclerView封装好的ViewHolder。

B. onBindViewHolder()方法:该方法将会在固定的位置上把ViewHolder里的itemView数据映射在item中。

C. getItemCount()方法:该方法和listView中的getCount()一样,都是返回Item的个数。

了解了这三个方法,我们来先实现最简单的应用,把我们的数据显示在app中。

首先,我们创建一个布局文件recycler_view.xml,如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView> </LinearLayout>

创建RecycerActivity用来加载布局文件:

public class RecycerActivity extends Activity {
private RecyclerView mRecyclerView;
private List<String> mData;
private CustomRecyclerAdapter mCustomRecyclerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recycer_view);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
initData();
//线性布局管理器
recyclerViewLayoutManager = new LinearLayoutManager(this);
//设置布局管理器
mRecyclerView.setLayoutManager(recyclerViewLayoutManager);
//设置显示动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//设置adapter
mCustomRecyclerAdapter = new CustomRecyclerAdapter(mData);
mRecyclerView.setAdapter(mCustomRecyclerAdapter); }
private void initData() {
mData = new ArrayList<String>();
for(int i = 0; i < 10; i++){
mData.add("第"+i+"item");
}
}
}

经过修改的CustomRecyclerAdapter 如下,

public class CustomRecyclerAdapter extends RecyclerView.Adapter<CustomRecyclerAdapter.ViewHolderHelper>{

    private List<String> mData;
public CustomRecyclerAdapter(List<String> data) {
mData = data;
} @Override
public ViewHolderHelper onCreateViewHolder(ViewGroup parent, int viewType) {
//onCreateViewHolder方法就是将布局文件转化为View并传递给RecyclerView封装好的ViewHolder
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false);
return new ViewHolderHelper(view);
} @Override
public void onBindViewHolder(ViewHolderHelper holder, int position) {
holder.textView.setText(mData.get(position));
} @Override
public int getItemCount() {
return mData.size();
}
}

item_view.xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_item"
android:layout_toRightOf="@+id/iv_item"
android:layout_width="match_parent"
android:layout_height="30dp"
android:text="I am item view"/> </RelativeLayout>

好,基础工作已做足,我们先来看看效果吧

已经显示出来了,不过,大家看着是不是很别扭呢,连个分割线也没有,还不如listView呢,别急,我们在上面也提到过,RecyclerView给了我们最大的发挥自由度,它本身并没有给定列表的分割线,这是需要我们自己定义的。由此我们来定义自己的分割线。自定义分割线是需要我们继承RecyclerView.ItemDecoration类,并实现它的onDraw()方法。请看代码:

public class DividerItemDecoration extends RecyclerView.ItemDecoration{

    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private int mOrientation;
private Context mContext;
private TextPaint mTextPaint;
private float listDividerSize = 2;
private int listDividerColor;
public DividerItemDecoration(Context context,int orientation){
mContext = context;
mTextPaint = new TextPaint();
mTextPaint.setColor(Color.RED);
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent) {
super.onDraw(c, parent);
if(mOrientation == HORIZONTAL_LIST){
drawHorizontal(c, parent);
}else{
drawVertical(c, parent);
}
}
/**
* 绘制垂直分割线
* @param c
* @param parent
*/
private void drawVertical(Canvas c, RecyclerView parent) {
//分割线的左边界 = 子View的左padding值
int rectLeft = parent.getPaddingLeft();
//分割线的右边界 = 子View的宽度 - 子View的右padding值
int rectRight = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for(int i = 0; i < childCount; i ++){
View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
// 分割线的top = 子View的底部 + 子View的margin值
int rectTop = child.getBottom() + layoutParams.bottomMargin;
// 分割线的bottom = 分割线的top + 分割线的高度
float rectBottom = rectTop + listDividerSize;
c.drawRect(rectLeft,rectTop,rectRight,rectBottom,mTextPaint);
}
} /**
* 绘制水平分割线
* @param c
* @param parent
*/
private void drawHorizontal(Canvas c, RecyclerView parent) {
//分割线的上边界 = 子View的上padding值
int rectTop = parent.getPaddingTop();
//分割线的下边界 = 子View的高度 - 子View的底部padding值
int rectBottom = parent.getHeight() - parent.getPaddingBottom();
int childCount = parent.getChildCount();
for(int i = 0; i < childCount; i ++){
View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
//分割线的Left = 子View的右边界 + 子View的左margin值
int rectLeft = child.getRight() + layoutParams.rightMargin;
//分割线的right = 分割线的Left + 分割线的宽度
float rectRight = rectLeft + listDividerSize;
c.drawRect(rectLeft,rectTop,rectRight,rectBottom,mTextPaint);
}
} @Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if(mOrientation == VERTICAL_LIST){
outRect.set(0,0,0,(int)listDividerSize);
} else{
outRect.set(0,0,(int)listDividerSize,0);
}
}
}

代码很好理解,这里考虑了两个情况,分别是垂直和水平的布局,然后再ondraw()里面计算出四角边值,最后直接绘制一个矩形即可。

在RecycerActivity 中的onCreate中添加上一句

mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));

现在再来看看效果。

现在已显示出来了,分割线也出来了,在这里只列出了垂直方向的布局,就不再列出其他样式的布局代码了,伙伴们可自行写写看。

或许有经验的小伙伴们已经知道我们的RecyclerView自己是没有实现点击事件的,这里需要我们来根据业务的需求自己来实现。这里我们利用事件回调机制来完成事件的触发。

首先我们需要在CustomRecyclerAdapter中定义一个接口,并在其中定义两个可用的事件方法,如下:

public interface OnItemClickListener{
void onItemClickListener();
void onLongItemClickListener();
}

这里提供了用于点击和长按的事件方法,接下来我们需要对外暴露该接口用于被调用

 public void setOnClickItemListener(OnItemClickListener onItemClickListener){
mOnItemClickListener = onItemClickListener;
}

然后我们可以在ViewHolderHelper 做如下的修改:

public class ViewHolderHelper extends RecyclerView.ViewHolder{

        private TextView textView;
public ViewHolderHelper(View itemView) {
super(itemView);
textView = (TextView)itemView.findViewById(R.id.tv_item);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClickListener();
}
});
textView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mOnItemClickListener.onLongItemClickListener();
return false;
}
});
}
}

首先得到我们itemView中的textView对应的Id,然后为textView添加事件,但是只是添加却并不实现,它的用意是谁调用谁实现。

最后在我们的RecycerActivity中添加如下代码,请看:

mCustomRecyclerAdapter.setOnClickItemListener(new CustomRecyclerAdapter.OnItemClickListener() {
@Override
public void onItemClickListener() {
Toast.makeText(getContext(),"单击",Toast.LENGTH_SHORT).show();
} @Override
public void onLongItemClickListener() {
Toast.makeText(getContext(),"长单击",Toast.LENGTH_SHORT).show();
}
});

下面在来看看是否可以实现事件的触发了呢?

ok,学到这里大家至少对RecylerView有了一个初步的认识,但是我们一想,这样写的话肯定达不到我们标题所说的无限复用,甚至连复用也遥不可及,是的,这样写是不可能完成复用的,接下来我们一步一步的慢慢调整,让它可以支持一次编写N次复用,达到极大多数的重复使用,即使不符合需求,我们也只需要修改丁点即可满足需求,这是我们的目标,接下来一步一步的实现。

首先,我们先整理下,看看可以调整哪些目标能逐步的实现重复使用的目标:

1. 数据类型:我们在使用List集合时,是无法固定类型的,有可能是String,int等等类型,所以我们不应该固定为哪一种类型。

2. 在onCreateViewHolder方法中,它需要映射一个布局文件并转化为View或是一个自定义View传递给RecyclerView封装好的ViewHolder,为了可以达到复用,所以我们就不可以在此直接映射布局文件。

3. 在onBindViewHolder方法中也不应该直接为itemView设置属性,如上面的:holder.textView.setText(mData.get(position));

4. 我们不应该在ViewHolder的构造方法中直接获取我们的itemView,并给它添加触发事件

以上几个是我们能很直观的得到的能重构的问题所在,至于其他的不容易想到的我们再重构的时候慢慢讲解。现在我们逐一的解决上面的问题,使我们能更达到重复使用的目的。

1,针对数据类型的不一致,我们可以根据具体的使用场景利用泛型进行传递到Adapter中,比如:我们再定义CustomRecyclerAdapter时使用泛型,让调用者传递过来它所拥有的类型,这样我们就可以不用考虑类型的不一致了。请看下面片段代码

public class CustomRecyclerAdapter<T> extends RecyclerView.Adapter<CustomViewHolderHelper>{
private Context mContext;
private List<T> mData;
private CustomOnItemClickListener mOnItemClickListener;
public CustomRecyclerAdapter(Context context, List<T> data) {
mContext = context;
mData = data;
}
}
...

RecycerActivity中在调用时可以这样使用:

mCustomRecyclerAdapter = new CustomRecyclerAdapter(this,mData);

这样就可以把类型给确定下来了,同时也解决了问题1的复用。

2 , 在onCreateViewHolder方法中,和问题一的解决方案是一样的,我们把需要的itemView给传递过去而不是固定写死在方法中

public class CustomRecyclerAdapter<T> extends RecyclerView.Adapter<CustomViewHolderHelper>{
private Context mContext;
private List<T> mData;
protected int mLayoutResId;
private CustomOnItemClickListener mOnItemClickListener;
public CustomRecyclerAdapter(Context context, int layoutResId, List<T> data) {
mContext = context;
mLayoutResId = layoutResId;
mData = data;
} /**
* Called when RecyclerView needs a new ViewHolder of the given type to represent
* an item.
* 当 RecyclerView 依据给出的类型需要一个新的 ViewHolder 去展示一个 item 时,该方法将会被调用
*
* 这个给出的类型是在 getItemViewType返回的,默认返回 0 。
*
* @param parent
* @param viewType
* @return
*/
@Override
public CustomViewHolderHelper onCreateViewHolder(ViewGroup parent, int viewType) {
//onCreateViewHolder方法就是将布局文件转化为View并传递给RecyclerView封装好的ViewHolder
View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutResId, parent, false);
return new CustomViewHolderHelper(view);
}
}

RecycerActivity中在调用时可以这样使用:

mCustomRecyclerAdapter = new CustomRecyclerAdapter(this,R.layout.item_view,mData);

3 , 在onBindViewHolder方法中也不应该直接为itemView设置属性,我们可以在这里记录itemView的position,并给它设置监听事件,更重要的我们这这里可以创建一个抽象方法,让调用者自己去实现业务逻辑。请看代码:

public abstract class CustomRecyclerAdapter<T> extends RecyclerView.Adapter<CustomViewHolderHelper>
implements View.OnClickListener,View.OnLongClickListener{
private Context mContext;
private List<T> mData;
protected int mLayoutResId;
private CustomOnItemClickListener mOnItemClickListener;
public CustomRecyclerAdapter(Context context, int layoutResId, List<T> data) {
mContext = context;
mLayoutResId = layoutResId;
mData = data;
} /**
* Called when RecyclerView needs a new ViewHolder of the given type to represent
* an item.
* 当 RecyclerView 依据给出的类型需要一个新的 ViewHolder 去展示一个 item 时,该方法将会被调用
*
* 这个给出的类型是在 getItemViewType返回的,默认返回 0 。
*
* @param parent
* @param viewType
* @return
*/
@Override
public CustomViewHolderHelper onCreateViewHolder(ViewGroup parent, int viewType) {
//onCreateViewHolder方法就是将布局文件转化为View并传递给RecyclerView封装好的ViewHolder
View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutResId, parent, false);
return new CustomViewHolderHelper(view);
} /**
* Called by RecyclerView to display the data at the specified position. This method should
* update the contents of the ViewHolder#itemView to reflect the item at the given position.
*
* RecyclerView 将要在特殊的位置上显示数据时,该方法将被调用。该方法将会在固定的位置上
* 把ViewHolder里的itemView数据映射在item中。
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(CustomViewHolderHelper holder, int position) {
//把每一个itemView设置一个标签,方便以后根据标签获取到该itemView以便做其他事项,比如点击事件
holder.itemView.setTag(position);
holder.itemView.setOnClickListener(this);
holder.itemView.setOnLongClickListener(this);
T itemData = mData.get(position);
displayContents(holder,itemData);
} /**
*用来在holder中设置每个ItemView的显示数据
* 设定为抽象方法,是为:自己本身并不实现,谁使用谁设置
* @param holder
* @param itemData
*/
protected abstract void displayContents(CustomViewHolderHelper holder, T itemData);
}

上面注解也写的很清楚,相信大家一看就明白,至于为什么可以直接使用holder.itemView来获取每个itemView是因为在onCreateViewHolder()方法中,我们返回了一个新的对象引用,这个对象的构造方法中使用super(itemView);把我们的itemView传递到了ViewHolder中,请看它的源码构造方法:

 public ViewHolder(View itemView) {
if (itemView == null) {
throw new IllegalArgumentException("itemView may not be null");
}
this.itemView = itemView;
}

因此我们可以在onBindViewHolder()方法中,直接使用holder.itemView来获取itemView。那么接下来,在RecycerActivity中,我们这样使用:

 mCustomRecyclerAdapter = new CustomRecyclerAdapter<String>(this, R.layout.item_view, mData) {
@Override
protected void displayContents(CustomViewHolderHelper holder, String itemData) {
holder.setText(R.id.tv_item,itemData);
}
};

当然你必须也得在ViewHolder中定义相应的方法,如:

public class CustomViewHolderHelper extends RecyclerView.ViewHolder{

    private SparseArray<View> views;
public CustomViewHolderHelper(View itemView) {
super(itemView);
views = new SparseArray<View>();
}
private <T extends View> T converToViewFromId(int resId) {
View view = views.get(resId);
if(view == null){
view = itemView.findViewById(resId);
}
views.put(resId,view);
return (T)view;
} public CustomViewHolderHelper setText(int resId, String value){
TextView itemView = converToViewFromId(resId);
if (TextUtils.isEmpty(value)) {
itemView.setText("");
} else {
itemView.setText(value);
}
return this;
}
}

ok,这样我们连第四个问题也一并解决了,看下效果吧,完全的一样,这样我们就实现的重复使用,但是有人会有疑问,这里也就只能使用TextView啊,其实已经在ViewHolder中给出了答案,大家只需要在添加相对应的方法即可,比如

public CustomViewHolderHelper setImageResource(int viewId, int imageResId) {
ImageView view = converToViewFromId(viewId);
view.setImageResource(imageResId);
return this;
}
public CustomViewHolderHelper setOnClickListener(int viewId, View.OnClickListener listener) {
View view = converToViewFromId(viewId);
view.setOnClickListener(listener);
return this;
}

上面我又添加了两个方法,用于点击事件和加载图片的ImageView,下面我们再把itemView布局文件修改下:

item_view.xml布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"> <ImageView
android:id="@+id/iv_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_item"
android:layout_toRightOf="@+id/iv_item"
android:layout_width="match_parent"
android:layout_height="30dp"
android:text="I am item view"/>
<Button
android:id="@+id/btn_item"
android:layout_toRightOf="@+id/iv_item"
android:layout_below="@+id/tv_item"
android:layout_width="wrap_content"
android:text="点我"
android:layout_height="wrap_content" />
</RelativeLayout>

RecycerActivity中在调用时可以这样使用:

mCustomRecyclerAdapter = new CustomRecyclerAdapter<String>(this, R.layout.item_view, mData) {
@Override
protected void displayContents(CustomViewHolderHelper holder, String itemData) {
holder.setText(R.id.tv_item,itemData)
.setImageResource(R.id.iv_item,R.mipmap.ic_launcher)
.setOnClickListener(R.id.btn_item, new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(RecycerActivity.this,"您单击了按钮",Toast.LENGTH_SHORT).show();
}
});
}
};

ok,来看下效果吧

是不是可以了呢,这样我们就可以完全的一次定义N次复用了,每次使用只需要更换不同的布局文件即可而不需要再次编写代码,学会了吧。

其实我们这节课主要讲解的RecyclerView.setAdapter的内容,其他的三个我们并没有详细的介入,我们会再以后的博文中陆续的讲解。

好了,今天就讲到这里吧,祝大家学习愉快。

更多资讯请关注微信平台,有博客更新会及时通知。爱学习爱技术。

Android 5.X新特性之RecyclerView基本解析及无限复用的更多相关文章

  1. android 5.0新特性学习--RecyclerView

    在过去很多年,我们的PC或者手机设备都是采用拟物化的设计风格,IOS采用扁平化的特性,android在2014年IO大会上说采用Material Design的设计风格,显示效果不能过于生硬的转换,而 ...

  2. Android 5.X新特性之为RecyclerView添加下拉刷新和上拉加载及SwipeRefreshLayout实现原理

    RecyclerView已经写过两篇文章了,分别是Android 5.X新特性之RecyclerView基本解析及无限复用 和 Android 5.X新特性之为RecyclerView添加Header ...

  3. Android 5.X新特性之为RecyclerView添加HeaderView和FooterView

    上一节我们讲到了 Android 5.X新特性之RecyclerView基本解析及无限复用 相信大家也应该熟悉了RecyclerView的基本使用,这一节我们来学习下,为RecyclerView添加H ...

  4. 《Android群英传》读书笔记 (5) 第十一章 搭建云端服务器 + 第十二章 Android 5.X新特性详解 + 第十三章 Android实例提高

    第十一章 搭建云端服务器 该章主要介绍了移动后端服务的概念以及Bmob的使用,比较简单,所以略过不总结. 第十三章 Android实例提高 该章主要介绍了拼图游戏和2048的小项目实例,主要是代码,所 ...

  5. Android 6.0 新特性 整理 资料来自网络

    Android 6.0新特性 Runtime Permissions Doze and App Standby Apache HTTP Client Removal BoringSSL Access ...

  6. 腾讯云安全:开发者必看|Android 8.0 新特性及开发指南

    欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~ 背景介绍 谷歌2017 I/O开发者大会今年将于5月17-19日在美国加州举办.大会将跟往年一样发布最新的 A ...

  7. Android 8.0 新特性

    Android 8.0 (Android Oreo(奥利奥))新特性介绍 通知渠道 - Notification Channels 通知渠道是由应用自行定义的通知内容类别,借助渠道,开发者可以让用户对 ...

  8. 开发者必看|Android 8.0 新特性及开发指南

    背景介绍 谷歌2017 I/O开发者大会今年将于5月17-19日在美国加州举办.大会将跟往年一样发布最新的 Android 系统,今年为 Android 8.0.谷歌在今年3 月21日发布 Andro ...

  9. android 7.0 新特性 和对开发者的影响

    android 7.0新特性 - jiabailong的专栏 - 博客频道 - CSDN.NEThttp://blog.csdn.net/jiabailong/article/details/5241 ...

随机推荐

  1. JS中关于字符串的几个常用又容易忘记的方法

    1>.字符串连接:concat(): 左边字符串. concat(连接的字符串1,字符串2,....); 获取指定位置的字符:charAt(): 返回指定位置的字符:  字符串.charAt(i ...

  2. Mac-OSX的Python3.5虚拟环境下安装Opencv

    Mac-OSX的Python3.5虚拟环境下安装Opencv 1   关键词 关键词:Mac,OSX,Python3.5,Virtualenv,Opencv 2   概述 本文是一篇 环境搭建 的基础 ...

  3. ARM的栈指令

    ARM的指令系统中关于栈指令的内容比较容易引起迷惑,这是因为准确描述一个栈的特点需要两个参数: 栈地址的增长方向:ARM将向高地址增长的栈称为递增栈(Descendent Stack),将向低地址增长 ...

  4. 怎样编写高质量的java代码

    代码质量概述     怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍.也请有过代码质量相关经验的朋友 ...

  5. Webgl的2D开发方案(一)spritebatcher

    使用TypeScript 和 webgl 开发   第一步:实现了SpriteBatcher 例子如下 http://oak2x0a9v.bkt.clouddn.com/test/index.html ...

  6. ABP(现代ASP.NET样板开发框架)系列之10、ABP领域层——实体

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之10.ABP领域层——实体 ABP是“ASP.NET Boilerplate Project (ASP.NET样板 ...

  7. magic方法的magic

    事实上,在python中一个类被实例化的时候首先被调用的并不是__init__方法,而是__new__方法.只是new方法一般很少重写.new方法会有返回值传给init方法.因此,init方法不能够有 ...

  8. 【MSP是什么】MSP认证之项目集与项目群的关系和区别

    项目群和项目集都是一个意思,翻译时没有统一口径造成的.只要能与项目组合区别开就可以了. 项目集与项目群的区别,不在于那些项目自身,而在于管理者的思想,管理者对待项目的态度.项目集与项目群,首先都是多个 ...

  9. iOS-推送,证书申请,本地推送

    介绍一点点背景资料 众所周知,使用推送通知是一个很棒的.给应用添加实时消息通知的方式.这样做的结局是,开发者和用户之间,彼此永远保持着一种令人愉悦的亲密关系. 然而不幸的是,iOS的推送通知并非那么容 ...

  10. .net正则表达式大全(.net 的 System.Text.RegularExpressions.Regex.Match()方法使用)

    正则表达式的本质是使用一系列特殊字符模式,来表示某一类字符串.正则表达式无疑是处理文本最有力的工具,而.NET的System.dll类库提供的System.Text.RegularExpression ...