1. 首先看一下最终的效果图

2. 需求拆解

第一眼看见上面的效果,是不是有些朋友觉得这个效果很酷,有的高手会觉得这个效果很简单。笔者昨天刚拿到需求的时候,最开始也是觉得这个很简单,可是越分析越发现好像实现出来并不是那么容易。单个的效果可能很简单,但是这么多的效果叠在一起,可能就比较复杂了。我简单得将这个效果任务拆解一下:

  1. 一屏要展示3个View,支持左右滑动
  2. 屏幕中间两侧的View移向中间时,会有一个放大的效果;中间的View移向两侧时,会有一个缩小的效果
  3. 点击屏幕两侧的View,会自动滚动到屏幕的中间,同理效果要满足第2点
  4. 当滑动到最左侧或者最右侧时,需要立马衔接最末尾的View和最开头的View,也就是无限滚动
  5. 考虑性能问题,必须有一个缓存机制。否则控件的count大于30的时候,会有非常明显的卡顿

起初我想过用HorizontalScrollView来做,但是在性能问题和无限滚动的实现上有一点困难;然后我想过用RecyclerView来做横向的列表,但是在无限滚动和居中放大的实现上有一点困难;然后我考虑直接重写View来做,但是这样的风险和成本实在太高,我不能不负责任地把这种代码投放到实际项目中。最后想来想去,我觉得用ViewPager来实现是最稳妥的,虽然里面仍然有不少坑,不过都被我解决了。

3. 实现一个普通的ViewPager

这里实现一个最最最普通的ViewPager,一页一个Item,支持横向滑动:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><android.support.v4.view.ViewPager
        android:id="@+id/viewPager"android:layout_width="match_parent"android:layout_height="wrap_content"/>
</LinearLayout>

MainActivity.java

public class MainActivity extends Activity {private ViewPager viewPager;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);viewPager = (ViewPager) findViewById(R.id.viewPager);OnePageThreeItemAdapter adapter = new OnePageThreeItemAdapter(this);viewPager.setAdapter(adapter);}}

OnePageThreeItemAdapter.java

public class OnePageThreeItemAdapter extends PagerAdapter {private Context context;private List<String> sourceData = new ArrayList<String>();public OnePageThreeItemAdapter(Context context) {this.context = context;initData();}/*** 初始化原始数据*/public void initData(){sourceData.clear();for(int i = 0 ; i < 5; i++){sourceData.add(i + "");}}@Overridepublic int getCount() {return sourceData.size();}@Overridepublic boolean isViewFromObject(View view, Object object) {return view == object;}@Overridepublic Object instantiateItem(ViewGroup container, final int position) {View view = View.inflate(context, R.layout.item, null);view.setTag(String.valueOf(position));TextView txt = (TextView) view.findViewById(R.id.txt);txt.setText(sourceData.get(position));container.addView(view);return view;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {container.removeView((View) object);}
}

item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="vertical"><ImageView
        android:id="@+id/img"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@mipmap/ic_launcher" /><TextView
        android:id="@+id/txt"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:gravity="center"android:textColor="@android:color/black"android:textSize="15sp" /></LinearLayout>

4. 实现一屏三个View

这个功能网上已经有很多了,相信大家都已经看见过了,所以我也就简单得说一下

A. 首先ViewPager和包含ViewPager的ViewGroup设置clipChildren属性

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:clipChildren="false"android:layerType="software"android:orientation="vertical"><android.support.v4.view.ViewPager
        android:id="@+id/viewPager"android:layout_width="match_parent"android:layout_height="wrap_content"android:clipChildren="false"/>
</LinearLayout>

clipChildren的意思是是否裁剪,当设置为false时,一屏有多个View的时候不裁剪多余的View;layerType的关闭硬件加速,具体原因我也不知道,网友是这样给的,我这样弄了能跑起来就没管了,若有知情者可以告知我哟。

B. 重写PagerAdapter的getPageWidth方法

@Override
public float getPageWidth(int position) {return 1.0f / ONE_PAGE_ITEM_COUNT; //ONE_PAGE_ITEM_COUNT = 3
}

这个方法返回的是每一个Item所占当前屏幕的比例,我们需要一屏展示三个View,所以这里返回1/3。

5. 实现居中时放大,左右滑动时缩小的动画效果

提起ViewPager的动画效果,大家第一反应肯定是PageTransformer,没错,本文的确是用PageTransformer来实现的,不过因为是一屏展示多个View,所以这里的实现上跟网上的大部分都有很大的差异。首先我们了解一下PageTransformer的transformPage方法的参数

A. 参数讲解

transformPage方法有两个参数,第一个View代表当前的子View,这个没什么好说的。主要是第二个参数position,是一个float类型的。这个position是什么类型呢,我们分两种情况演示一下:

如果一屏只有一个View,也就是最普通最简单的情况

一屏有三个View

可以看出,如果一屏只有一个View,那么当前View就是0;如果一屏有多个View,那么当前页的第一个View就是0。然后向左依次递减每一个View的宽度;向右依次递增每一个View的宽度。

B. 一屏三个View的实现

通过上面的图,我们可以知道,我们需要处理的区间有(-INFINITE, 0)、(0, 1/3)、(1/3, 2/3)、(2/3, +INFINITE)

public class OnePageThreeItemTransformer implements ViewPager.PageTransformer {@Overridepublic void transformPage(View page, float position) {//这里的position是指每一个Item相对于Page所占的比例,当前页的第一个Item是0,左边的依次减去Item的宽度,右边的依次加上Item的宽度float centerPosition = 1.0f / ONE_PAGE_ITEM_COUNT;if (position <= 0) {page.setScaleX(1);page.setScaleY(1);} else if (position <= centerPosition) {page.setScaleX(1 + (itemMaxScale - 1) * position / centerPosition);page.setScaleY(1 + (itemMaxScale - 1) * position / centerPosition);} else if (position <= centerPosition * 2) {page.setScaleX(1 + (itemMaxScale - 1) * (2 * centerPosition - position) / centerPosition);page.setScaleY(1 + (itemMaxScale - 1) * (2 * centerPosition - position) / centerPosition);} else {page.setScaleX(1);page.setScaleY(1);}}
}

其中itemMaxScale代表最大放大的比例,这里我暂定的2倍,值可以随便修改。

然后设置ViewPager的PageTransformer就可以了

viewPager.setPageTransformer(true, new OnePageThreeItemTransformer());

6. 点击屏幕中的View,实现居中效果

网上很多种实现,原理大约是通过TouchEvent获取内存中的View所在的index,然后再调用setCurrentItem方法平滑过去。在这里,我愤怒的告诉大家:这种做法是完全错误的。顺带黑一句,中国的博客写手很多都不严谨,代码都没有经过分析与验证,就直接发布出来或者转发。大家都知道ViewPager和ListView等都是有缓存原理的,内存中的View所在的index,跟setCurrentItem的参数position,是完全两码事的东西。

后来经过琢磨,我找到了一种实现思路:
在PagerAdapter的instantiateItem方法中,给每一个View设置点击监听器,instantiateItem的position是指每一个View的position,这个position跟数据源的position是保持一致的。在View的点击监听器中调用setCurrentItem方法,注意到setCurrentItem的position参数的意义:如果一屏只有一个View,那么position就是当前页的position;如果一屏有三个View,那么position是指当前页的第一个View的position。我举个例子:

如图所示,左边一页的position为3;中间一页的position为6;右边一页的position为9。

也就是说,如果我想要指定position居中,那么我只需要调用setCurrentItem(position - 1)就可以了,这里要考虑position为0的情况。

@Override
public Object instantiateItem(ViewGroup container, final int position) {View view = View.inflate(context, R.layout.item, null);view.setTag(String.valueOf(position));TextView txt = (TextView) view.findViewById(R.id.txt);txt.setText(sourceData.get(position));view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//这里的position是指每一页的第一个Item的positionviewPager.setCurrentItem(position == 0 ? 0 : position - 1, true);}});container.addView(view);return view;
}

这里设置Tag是为了下一节实现无限循环滚动做了必要准备。

7. 实现无限循环滚动

A. 修复添加数据源

网上已经有很多实现ViewPager无限滚动的原理了,但是因为我们这儿是一屏有三个View,因此这里的逻辑需要变动一点。

在数据源的前后各加3个数据源,前面加入原数据源的末三位;后面加入原数据源的前三位。如图:

3-7是指我们原来的5个数据源;0-2是添加的头数据源,具体的索引指向5-7;8-10是添加的尾数据源,具体的索引指向3-5。代码如下

/*** 初始化原始数据*/
public void initData(){sourceData.clear();for(int i = 0 ; i < 5; i++){sourceData.add(i + "");}
}/*** 为了能无限滚动,将末三项复制加入列表开头,将前三项复制加入到列表末尾*/
private void repairDataForLoop(){repairData.clear();List<String> beforeTemp = new ArrayList<String>();List<String> afterTemp = new ArrayList<String>();for(int i = 0; i < ONE_PAGE_ITEM_COUNT; i++){beforeTemp.add(0, sourceData.get(sourceData.size() - 1 - i));afterTemp.add(sourceData.get(i));}repairData.addAll(beforeTemp);repairData.addAll(sourceData);repairData.addAll(afterTemp);
}

我们将原生的数据源放入sourceData,便于数据检索。而将修复后的数据源放入repairData。

B. 无限滚动的要领

然后当界面滚动到position=0时,立马调用setCurrentItem滚动到position=5的位置;当界面滚动到position=8时,立马调用setCurrentItem滚动到position=3的位置。注意这里的position是指当前页的第一个View的position,上面已经讲过了。这里需要有几点需要注意一下:

  1. setCurrentItem的第二个参数要传false,这样才能无动画滚动,才能对用户无感。
  2. 在OnPageChangeListener中,需要在onPageScrollStateChanged中处理而不是在onPageSelected。因为onPageScrollStateChanged的SCROLL_STATE_IDLE状态比onPageSelected晚调用。否则会出现界面突然跳动没有平滑动画的问题。
  3. 调用setCurrentItem(position, false)时,设置的PageTransformer动画效果会失效。也就是说中间的View不会有放大的效果。所以必须手动设置放大的效果。这里就需要前面instantiateItem中设置Tag的帮助了。
/*** 真正处理无限滚动的逻辑,在滑动到边界时,立马跳转到对应的位置*/
private void handleLoop(){viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {// 是否应该强制跳转到首页boolean shouldToBefore = false;// 是否应该强制跳转到末页boolean shouldToAfter = false;@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}@Overridepublic void onPageSelected(int position) {//这里的position是指每一页的第一个Item的positionif (position == 0){shouldToAfter = true;}else if(position == getCount() - ONE_PAGE_ITEM_COUNT){shouldToBefore = true;}}@Overridepublic void onPageScrollStateChanged(int state) {//onPageScrollStateChanged的SCROLL_STATE_IDLE比onPageSelected晚调用,如果在onPageSelected中处理方法,则不会有滑动动画效果if(state == ViewPager.SCROLL_STATE_IDLE){if(shouldToAfter){//这里的position是指每一页的第一个Item的positionviewPager.setCurrentItem(getCount() - ONE_PAGE_ITEM_COUNT * 2, false);//居中的position比当前position多1handleScale(getCount() - ONE_PAGE_ITEM_COUNT * 2 + 1);shouldToAfter = false;}if(shouldToBefore){//这里的position是指每一页的第一个Item的positionviewPager.setCurrentItem(ONE_PAGE_ITEM_COUNT, false);//居中的position比当前position多1handleScale(ONE_PAGE_ITEM_COUNT + 1);shouldToBefore = false;}}}});
}/*** 调用了setCurrentItem(position, false)后,设置的PageTransformer不会生效,* 也就是说中间需要放大的项不会放大,所以手动将这一项放大*/
private void handleScale(int position){View view = findCenterView(position);if(view == null){return;}view.setScaleX(itemMaxScale);view.setScaleY(itemMaxScale);
}/*** 通过在instantiateItem方法给每个View设置的Tag来标记,找出内存中的View*/
private View findCenterView(int position){for(int i = 0; i < viewPager.getChildCount(); i++){View view = viewPager.getChildAt(i);if(String.valueOf(position).equals(view.getTag())){return view;}}return null;
}

8. 完整的代码:

item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="vertical"><ImageView
        android:id="@+id/img"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@mipmap/ic_launcher" /><TextView
        android:id="@+id/txt"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:gravity="center"android:textColor="@android:color/black"android:textSize="15sp" /></LinearLayout>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:clipChildren="false"android:layerType="software"android:orientation="vertical"><android.support.v4.view.ViewPager
        android:id="@+id/viewPager"android:layout_width="match_parent"android:layout_height="wrap_content"android:clipChildren="false"/>
</LinearLayout>

OnePageThreeItemAdapter.java

/*** Created by chenchen on 2017/8/15.** 代码中有几个position需要注意一些* 1. setCurrentItem的position参数,是指当前页的第一个Item的所在position* 2. OnPageChangeListener的position参数,同样也是指当前页的第一个Item的所在position* 3. instantiateItem的position参数,是指每一个Item的所在position* 4. PageTransformer的position参数,是指每一个Item相对于Page所占的比例,当前页的第一个Item是0,左边的依次减去Item的宽度,右边的依次加上Item的宽度*    比如一页只有一个Item。那么当前页的position为0。左边的position分别为-1。右边的position分别为1;*    比如一页有三个Item。那么当前页的Item的position分别为0、1/3、2/3。左边的position分别为-1、-2/3、-1/3。右边的position分别为1、4/3、5/3。*/
public class OnePageThreeItemAdapter extends PagerAdapter {//一屏最多3个item,不能随便改变这个值,改这个数量必须要改动很多逻辑private static final int ONE_PAGE_ITEM_COUNT = 3;private Context context;private ViewPager viewPager;// 居中的Item的最大放大值private float itemMaxScale;//在原始的数据前后各加了3项数据private List<String> repairData = new ArrayList<String>();//原始的数据,用于数据检索等private List<String> sourceData = new ArrayList<String>();public OnePageThreeItemAdapter(ViewPager viewPager, float itemMaxScale) {this.viewPager = viewPager;this.context = viewPager.getContext();this.itemMaxScale = itemMaxScale;initData();repairDataForLoop();handleLoop();}/*** 调用之前保证initData方法已被调用*/@Overridepublic void notifyDataSetChanged() {repairDataForLoop();super.notifyDataSetChanged();}/*** 初始化原始数据*/public void initData(){sourceData.clear();for(int i = 0 ; i < 5; i++){sourceData.add(i + "");}}/*** 为了能无限滚动,将末三项复制加入列表开头,将前三项复制加入到列表末尾*/private void repairDataForLoop(){repairData.clear();List<String> beforeTemp = new ArrayList<String>();List<String> afterTemp = new ArrayList<String>();for(int i = 0; i < ONE_PAGE_ITEM_COUNT; i++){beforeTemp.add(0, sourceData.get(sourceData.size() - 1 - i));afterTemp.add(sourceData.get(i));}repairData.addAll(beforeTemp);repairData.addAll(sourceData);repairData.addAll(afterTemp);}/*** 真正处理无限滚动的逻辑,在滑动到边界时,立马跳转到对应的位置*/private void handleLoop(){viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {// 是否应该强制跳转到首页boolean shouldToBefore = false;// 是否应该强制跳转到末页boolean shouldToAfter = false;@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}@Overridepublic void onPageSelected(int position) {//这里的position是指每一页的第一个Item的positionif (position == 0){shouldToAfter = true;}else if(position == getCount() - ONE_PAGE_ITEM_COUNT){shouldToBefore = true;}}@Overridepublic void onPageScrollStateChanged(int state) {//onPageScrollStateChanged的SCROLL_STATE_IDLE比onPageSelected晚调用,如果在onPageSelected中处理方法,则不会有滑动动画效果if(state == ViewPager.SCROLL_STATE_IDLE){if(shouldToAfter){//这里的position是指每一页的第一个Item的positionviewPager.setCurrentItem(getCount() - ONE_PAGE_ITEM_COUNT * 2, false);handleScale(getCount() - ONE_PAGE_ITEM_COUNT * 2 + 1);shouldToAfter = false;}if(shouldToBefore){//这里的position是指每一页的第一个Item的positionviewPager.setCurrentItem(ONE_PAGE_ITEM_COUNT, false);handleScale(ONE_PAGE_ITEM_COUNT + 1);shouldToBefore = false;}}}});}/*** 调用了setCurrentItem(position, false)后,设置的PageTransformer不会生效,* 也就是说中间需要放大的项不会放大,所以手动将这一项放大*/private void handleScale(int position){View view = findCenterView(position);if(view == null){return;}view.setScaleX(itemMaxScale);view.setScaleY(itemMaxScale);}/*** 通过在instantiateItem方法给每个View设置的Tag来标记,找出内存中的View*/private View findCenterView(int position){for(int i = 0; i < viewPager.getChildCount(); i++){View view = viewPager.getChildAt(i);if(String.valueOf(position).equals(view.getTag())){return view;}}return null;}@Overridepublic int getCount() {return repairData.size();}@Overridepublic boolean isViewFromObject(View view, Object object) {return view == object;}@Overridepublic Object instantiateItem(ViewGroup container, final int position) {View view = View.inflate(context, R.layout.item, null);view.setTag(String.valueOf(position));TextView txt = (TextView) view.findViewById(R.id.txt);txt.setText(repairData.get(position));view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//这里的position是指每一页的第一个Item的positionviewPager.setCurrentItem(position == 0 ? 0 : position - 1, true);Toast.makeText(context, "点击了" + repairData.get(position), Toast.LENGTH_SHORT).show();}});container.addView(view);return view;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {container.removeView((View) object);}@Overridepublic float getPageWidth(int position) {return 1.0f / ONE_PAGE_ITEM_COUNT;}public class OnePageThreeItemTransformer implements ViewPager.PageTransformer {@Overridepublic void transformPage(View page, float position) {//这里的position是指每一个Item相对于Page所占的比例,当前页的第一个Item是0,左边的依次减去Item的宽度,右边的依次加上Item的宽度float centerPosition = 1.0f / ONE_PAGE_ITEM_COUNT;if (position <= 0) {page.setScaleX(1);page.setScaleY(1);} else if (position <= centerPosition) {page.setScaleX(1 + (itemMaxScale - 1) * position / centerPosition);page.setScaleY(1 + (itemMaxScale - 1) * position / centerPosition);} else if (position <= centerPosition * 2) {page.setScaleX(1 + (itemMaxScale - 1) * (2 * centerPosition - position) / centerPosition);page.setScaleY(1 + (itemMaxScale - 1) * (2 * centerPosition - position) / centerPosition);} else {page.setScaleX(1);page.setScaleY(1);}}}/*** 配置ViewPager一些其他的属性*/public void configViewPager(){viewPager.setOffscreenPageLimit(ONE_PAGE_ITEM_COUNT + 2);viewPager.setPageTransformer(true, new OnePageThreeItemTransformer());viewPager.setCurrentItem(ONE_PAGE_ITEM_COUNT);}
}

MainActivity.java

public class MainActivity extends Activity {private ViewPager viewPager;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);viewPager = (ViewPager) findViewById(R.id.viewPager);OnePageThreeItemAdapter adapter = new OnePageThreeItemAdapter(viewPager, 2);viewPager.setAdapter(adapter);adapter.configViewPager();}}

Android中ViewPager支持一屏多个View、切换动画以及无限滚动相关推荐

  1. Android 屏幕适配 - 支持刘海屏

    刘海屏是指某些设备显示屏上的一个区域延伸到显示面,这样既能为用户提供全面屏体验,又能为设备正面的重要传感器留出空间.Android 在搭载 Android 9(API 级别 28)及更高版本的设备上正 ...

  2. android 弹出fragment,Android中ViewPager获取当前显示的Fragment

    前言 在项目中,有时会用到在ViewPager中显示同样类型的Fragment,同时这样的Fragment的个数是动态的,但是PagerAdapter没有给我们提供getCurrentFragment ...

  3. 微信小程序手把手教你实现类似Android中ViewPager控件效果

    微信小程序手把手教你实现类似Android中ViewPager控件效果 前言 需求分析 头部TAB 滑动的内容部分 最终版本 尾巴 前言 在做Android开发的时候,ViewPager是开发者使用频 ...

  4. Android中的popupwindow从底部进入和退出的动画效果

    今天,简单讲讲android使用popupwindow时设置popupwindow进入和退出的动画效果. 其实这个很简单,之前设置popupwindow从底部进入和退出的效果时,当时记不清了,所以在网 ...

  5. Android 中Touch(触屏)事件传递机制

    版本:2.0 日期:2014.3.21 2014.3.29 版权:© 2014 kince 转载注明出处 一.基本概念 在实际开发中,经常会遇到与触屏事件有关的问题,最典型的一个就是滑动冲突.比如在使 ...

  6. Android中如何APP视屏如何去除广告

    生死看淡,不服就干! http://www.wjdiankong.cn 目录视图 摘要视图 订阅 微信小程序实战项目--点餐系统 程序员11月书讯,评论得书啦 Get IT技能知识库,50个领域一键直 ...

  7. android n进入分屏代码分析_平板电脑全面进入多任务时代,Android N原生支持分屏显示...

    去年十月苹果WWDC上发布iOS9首次原生支持iPad平板电脑的分屏显示,希望通过大屏多任务iPad产品,向微软的Surface为代表的Windows10平板PC发起挑战,撬开商务和企业级市场.近日据 ...

  8. 随笔:说说第一次在android中嵌入非全屏显示的unity游戏时的坑之——界面切换时出现延迟/卡顿/花屏等现象解决方法

    One 最近对接了个unity3D做的小游戏到android项目中:游戏嵌入项目的界面且非全屏显示,项目几经波折,到我手上的时候,已经采用了在同一个activity中用两个View来分别显示andro ...

  9. android视频播放器ui,ArtVideoPlayer 一个灵活的Android视频播放器,支持全屏,小屏播放...

    ArtPlayer 简介 Kotlin实现的视频播放器,将MediaPlayer与VideoView解耦合,支持切换播放器内核(如ExoPlayer和ijkPlayer),支持自定义控制视图,提供Me ...

最新文章

  1. 树莓派开发7-Pi摄像头+mjpg-streamer
  2. BZOJ5102:[POI2018]Prawnicy(贪心,堆)
  3. 北邮OJ 102. 最远距离 北邮2012网研院复试上机题
  4. 侯捷面向对象编程C++
  5. zabbix邮件报警配合logging模块排错的python脚本
  6. 【C语言】无参函数调用实例
  7. 苹果a10处理器_【突然】苹果宣布2019款iPad降价 发布不到半年最高降500
  8. python抽取指定url页面的title_Python新手写爬虫全过程记录分析
  9. 《矩阵分析》Ⅳ——三对角矩阵的追赶法matlab实现
  10. 项目总结,要做哪些内容
  11. Atitit webservice之道 艾提拉著 目录 1. 基本说明Web Service 1 2. 基本概念与内部构成 2 2.1. Web services要使用两种技术: XML SOAP
  12. Java程序员必备的一些流程图/架构图(拿走不谢)
  13. 迅为IMX6ULL开发板Linux RS232/485驱动实验(上)
  14. android图片的透明度变化,Android如何实现改变图片的透明度
  15. 基础矩阵F和本质矩阵E
  16. LTE相关协议2——下行峰值速率计算
  17. 软件测试用mac还是windows,为什么越来越多的Mac用户开始爱上Windows?
  18. Transformer代码详解与项目实战之Masking
  19. 【企业安全运营】企业级私有云公有云 防护安全建设
  20. 密码锁 Locker

热门文章

  1. 为什么说HashMap是线程不安全的?
  2. muduo Logging详解
  3. shell 编程 错误 bash: [: missing `]'
  4. Linux驱动——sdio type card(八)
  5. Solder Mask和Paste Mask
  6. 基于裸机工程移植内核
  7. 论什么叫牛逼的程序猿
  8. 云计算的可信新边界:边缘计算与协同未来
  9. 你能通过图灵测试吗?
  10. centos7php自启动,CentOS7.X中设置nginx和php-fpm的开机自启动