最近因为项目需要,写了一个跑马灯效果的控件,过程中也学到一些东西,在这里和大家分享一下。

首先让我们来看一下效果:

其中的图标,文字,甚至每行的整个布局都是可以自定义的,我们可以使用整个控件很方便做出自己想要的效果。

闲话少说,我们先来看在代码里面怎么使用这个控件,首先是xml文件里面,直接引用即可。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="fill_parent"android:layout_height="fill_parent"><com.example.kaiyicky.myapplication.VerticalScrollViewandroid:id="@+id/mscroll"android:scrollbars="none"android:layout_width="match_parent"android:background="#ffffff"android:layout_marginTop="200dp"android:layout_height="60dp"></com.example.kaiyicky.myapplication.VerticalScrollView>
</LinearLayout>

可以看到我们的控件名字叫VerticalScrollView,注意我们一般要给这个控件一个固定的高度和宽度。

然后在Activity里面这样设置:

public class MainActivity extends FragmentActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//获取对象VerticalScrollView mscroll= (VerticalScrollView) findViewById(R.id.mscroll);mscroll.setDirection(-1);//设置方向//自定义行布局和数据mscroll.setTabAdapter(new TabAdapter(R.layout.vertical_scroll_item,new String[]{"哈哈", "呵呵", "哦哦"}){@Overridepublic void getView(View v,String text,String icon) {TextView t = (TextView) v.findViewById(R.id.item_text);t.setText(text);}});//开始动画mscroll.startAutoScroll();}
}

在获得控件对象以后,我们可以设置跑马灯的方向,-1代表从上到下,1代表从下到上

然后为mscroll设置adapter,这个adapter也是我们自定义的哦,不要以为是Android提供的,这里我借鉴了listview中setAdapter的思想,实现数据和布局的分离

这样我们就可以为自己的行设置想要的布局,同时可以获取布局中的控件设置需要的值,例如为textView设置文字或者icon

最后只要主动调用startAutoScroll()就可以是整个控件跑起来了

使用方式就是这么简单,接下来大家跟着我,看看整个控件是怎么写的。

我先说一下我的思路,我是通过继承ScrollView来写这个控件的,为什么选择它,因为ScrollView有一个smoothSrollTo()方法,可以使内部的布局滑动到一个特定的位置,从而实现图片里面的滑动效果。有了这个想法以后,我们要知道ScrollView里面一般只能有一个子控件,显然我们需要一个LinearLayout,然后根据数据的条数,主动渲染行布局,将生成的View逐个添加入LinearLayout就可以了。

可是还有一个问题,怎么实现无限的自动滚动呢?一开始我思路是创建一个子线程,里面有一个死循环,每隔一定时间,就调用smoothSrollTo()去移动LinearLayout的位置,这样就实现了无限循环。(注意你不能再UI线程里面写死循环啊,所以只能另起一个子线程了)。

后来觉得这样写会造成内存泄露,因为这个线程没有合适的终止条件,也就说我们跳转到另外一个Activity以后,这个线程也不会结束,这样的话内存就无法释放。

解决办法是,通过handler来实现死循环,我们知道调用handler的sendEmptyMessageDelayed()方法,可以延长一段时间以后发送一个信息。

然后在handleMessage里面会接收到这个信息,如果我们在handleMessage的最后,又再一次调用sendEmptyMessageDelayed()方法,这样就可以不断循环了。

OK,说道这里,大家可能觉得有点抽象,等下我们看代码的时候回明白。

上面解决了滑动问题和无限循环的问题,可是还有一个问题,就是我们怎么在循环过程中,例如我们循环展示的条目是{1,2,3,4,5}

怎么才能展示5之后,又回到头去展示1呢?或者说展示1以后,怎么回到尾去展示5呢?(这两个问题只是方向不同而已)

这里有一个简单的算法,我是这样做的。针对第一种情况,如图(图片是水平的,实际方向是竖直的,但原理一样):

我们看到,对于1-5,我创建了一个{1,2,3,4,5,1}的数组,也就是在末尾增加了一个和头部一样的数。

假设滚动方向是从1到5,到5以后,再往下滚就是1了,再滚动1以后,我马上调用ScrollTo()方法,使整个控件回到头部

由于这个方法使瞬时的(等于cpu渲染速度),肉眼没有办法分辨出这次移动

这样我们就又回到头部了,又可以再次向下滚,重复上述过程。

可能还有朋友没有想明白,假设我们不这样做,也就是数组只有{1,2,3,4,5},当我们滚动5的时候,如果马上让5瞬间到1,这样就丢失了5到1这个滚动过程了。

OK,整个控件的思路和重点就介绍到这里,如果没有想清楚没有关系,我们上代码!

首先是属性和构造函数

/*** Created by kaiyi.cky on 2015/8/5.* 跑马灯控件*/
public class VerticalScrollView extends ScrollView{private LinearLayout mLinearLayout;/*** 当前显示的序号*/private int curIndex = 1;/*** 滚动方向* 1为向下,-1为向上*/private int direction = 1;/*** 滚动间隔时间*/private int spentTime = 2000;private final static int SCROLL_WHAT = 99;/*** 自定义Scroller来控制smoothScroll的动画时间*/private CustomDurationScroller scroller = null;/*** 滚动动画时间比率,该比率乘以250ms是动画时间*/private double mScrollFactor = 1d;MyHandler myHandler;/*** 用于数据与布局*/TabAdapter mTabAdapter;public VerticalScrollView(Context context) {super(context);init(context);}public VerticalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}public VerticalScrollView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public void init(Context context){mLinearLayout = new LinearLayout(context);mLinearLayout.setOrientation(LinearLayout.VERTICAL);addView(mLinearLayout, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));myHandler = new MyHandler();setViewPagerScroller();}
}

在构造函数里面,我为ScrollView添加了一个LinearLyout
然后又一个MyHandler,先看看整个对象是用来做什么的

 /*** 用于不断向主线程发送信息,使行状态改变* 在handleMessage()里面调用sendMessageDelayed()可以实现自动无限滚动*/private class MyHandler extends Handler {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what){case SCROLL_WHAT:SetItemImeediate();setCurrentItem(curIndex+direction);myHandler.removeMessages(SCROLL_WHAT);myHandler.sendEmptyMessageDelayed(SCROLL_WHAT, spentTime);break;}}}

可以看到,这个handler接受到一个消息以后,做了一系列操作,其中我们先关注这两句

myHandler.removeMessages(SCROLL_WHAT);
myHandler.sendEmptyMessageDelayed(SCROLL_WHAT, spentTime);

我清楚之前接收的信息,然后又向handler自己发送了一条延时信息

可想而知,在spendTime(我们自己定义的延时,也就是每次滚动的间隔时间)时间以后,myHanlder又会接收到这条信息

在这条信息的最后,又会给自己发一条一样的信息

这里我们就实现了自动无限循环滚动。

可是第一条信息是什么时候发生的呢?聪明的你应该知道,是在

/*** 开启动画,必须主动调用*/public void startAutoScroll(){myHandler.sendEmptyMessage(SCROLL_WHAT);}

这个方法里面!也就是说是我们主动调用的。

接着我们看真实的移动方法,注意到MyHanlder里面首先调用了SetItemImeediate()

/*** 瞬间设置当前行*/private void SetItemImeediate(){if (direction==1&&curIndex == mTabAdapter.getSize()-1){//如果向下滚动并且滚动到最后一条,瞬间跳跃到第二条,实现循环效果View v = mLinearLayout.getChildAt(1);scrollTo((int) v.getX(), (int) v.getY());curIndex = 1;}else if (direction==-1&&curIndex == 0){//如果向上滚动并且滚动到第一条,瞬间跳跃到倒数第二条,实现循环效果View v = mLinearLayout.getChildAt(mTabAdapter.getSize()-2);scrollTo((int) v.getX(), (int) v.getY());curIndex = mTabAdapter.getSize()-2;}}

这个方法,就是思路里面提到的,瞬间跳动的方法,判断当前滚动到的条目是不是最后一条/第一条(根据滚动方向不同)

从而调用ScrollTo()方法,瞬间回到位置。

然后就是实现滚动的

/*** 滚动到index行* @param index*/public void setCurrentItem(int index){View v = mLinearLayout.getChildAt(index);smoothScrollTo((int) v.getX(), (int) v.getY());curIndex = index;}

可以看到,我们只需要或等当前行对象,获取它的坐标X,Y,然后调用smoothScrollTo就可以滚动到它所在的位置了

smoothScrollTo()很方便地为我们实现了滚动动画,但是它也有缺点,就是不能定制动画时间。如果我们希望滚动快一点,慢一点都没有办法调控。

因为这个方法是ScrollView内部的,默认动画时间是250ms,后面我会讲到一个巧妙的方法去解决这个问题。

OK,滚动的实现就是这么简单,开始数据在哪里呢?行布局在哪里添加的呢?

前面说过,我借鉴了Adapter的思想实现数据分离,这里我先来看一下自定义的Adapter类

/*** Created by kaiyi.cky on 2015/8/6.* 用于使用者自定义处理VerticalScrollView的数据与布局*/
public abstract class TabAdapter{final int mResId;private ArrayList<String> textArray = new ArrayList<String>();private ArrayList<String> iconArray = new ArrayList<String>();public TabAdapter(int resId, String[] texts){mResId = resId;setTexts(texts);}public TabAdapter(int resId,String[] texts, String[] icons){mResId = resId;setIconTexts(texts, icons);}/*** 返回index对应文字* @param index* @return*/public String getText(int index){return textArray.get(index);}/*** 返回index对应icon地址* @param index* @return*/public String getIcon(int index){return iconArray.isEmpty()?"":iconArray.get(index);}/*** 设置文字,不设置图标* @param texts* @throws Exception*/private void setTexts(String[] texts){setIconTexts(texts, null);}/*** 设置文字和图标* 要求texts必须不为空* @param texts* @param icons*/private void setIconTexts(String[] texts, String[] icons) {if(texts==null||texts.length<=0) return;if(icons!=null){if(icons.length<=0||texts.length!=icons.length) return;}textArray.clear();textArray.add(texts[texts.length - 1]);textArray.addAll(Arrays.asList(texts));textArray.add(texts[0]);iconArray.clear();if(icons!=null&&icons.length!=0) {iconArray.add(icons[icons.length - 1]);iconArray.addAll(Arrays.asList(icons));iconArray.add(icons[0]);}}/*** 返回数据条目总数* @return*/public int getSize(){return textArray.size();}/*** 必须继承该方法,在该方法内可以自定义布局文字,图标等* @param v* @param text* @param icon*/public abstract void getView(View v,String text,String icon);
}

这个类是一个抽象类,其中getView()是一个抽象方法,我们必须继承。

从构造函数我们可以看出,必须为其提供一个布局文件和一个数组作为文字数据

然后我对数据做了一些处理,其实就是添加了头尾,目的是实现循环

例如我们传入的数组是{1,2,3,4,5},在构造函数里面,我就要转换成{1,2,3,4,5,1}存储起来啊,这和前面说到的原理是一致的。

当然icon也就是图片地址,我们不一定要传,函数如下:

/*** 设置文字和图标* 要求texts必须不为空* @param texts* @param icons*/private void setIconTexts(String[] texts, String[] icons) {if(texts==null||texts.length<=0) return;if(icons!=null){if(icons.length<=0||texts.length!=icons.length) return;}textArray.clear();textArray.add(texts[texts.length - 1]);//将最后一个元素加到头textArray.addAll(Arrays.asList(texts));textArray.add(texts[0]);//将第一个元素加到尾iconArray.clear();if(icons!=null&&icons.length!=0) {iconArray.add(icons[icons.length - 1]);iconArray.addAll(Arrays.asList(icons));iconArray.add(icons[0]);}}

OK,我们在回过头来看一下怎么使用这个Adapter,在Activity里面是这样使用的

//自定义行布局和数据mscroll.setTabAdapter(new TabAdapter(R.layout.vertical_scroll_item,new String[]{"哈哈", "呵呵", "哦哦"}){@Overridepublic void getView(View v,String text,String icon) {TextView t = (TextView) v.findViewById(R.id.item_text);t.setText(text);}});

调用setAdapter()方法传入一个adapter就可以,记得要传行布局地址和数据数组哦!

然后我们在getView()方法里面,就可以获得由行布局渲染的View对象了,有了这个View对象,使用者就可以根据需要设置文字啊,或者别的信息

关键是setAdapter()方法做了什么,怎么getView()方法可以获得这个View对象呢?

看代码:

/*** 该函数必须在startAutoScroll()之前调用* @param mTabAdapter*/public void setTabAdapter(TabAdapter mTabAdapter) {this.mTabAdapter = mTabAdapter;notifyDataSetChanged();}

接着看:

/*** 逐个添加布局*/private void notifyDataSetChanged(){mLinearLayout.removeAllViews();for(int i=0;i<mTabAdapter.getSize();i++){if(mTabAdapter!=null){addTab(i);}}requestLayout();}

遍历数据,为每个数据创建布局,关键是addTab()方法

/*** 添加布局* @param index*/private void addTab(int index){View v = LayoutInflater.from(getContext()).inflate(mTabAdapter.mResId,mLinearLayout,false);mTabAdapter.getView(v,mTabAdapter.getText(index),mTabAdapter.getIcon(index));mLinearLayout.addView(v);}

OK,看了吗?我调用LayoutInflater.from()渲染出行布局,然后调用了mTabApdater.getView()方法,将View传了进入!

就是这样我们调用了getView(),所以getView()就会被执行,我们自定义的东西就会执行!

最后将view添加到LinearLayout就可以了。

到此为止,整个控件,从自动无限滚动,循环滚动,和根据数据添加布局,这几个方面都说完了。

最后,让我们一起来关注怎么控制动画速度的问题。

我们注意到,在VerticalScrollView构造函数里面,调用了setViewPagerScroller()方法,这个方法使做什么的呢?

看看:

/*** 使用反射设置Scroller*/private void setViewPagerScroller() {try {Field scrollerField = ScrollView.class.getDeclaredField("mScroller");scrollerField.setAccessible(true);scroller = new CustomDurationScroller(getContext());scroller.setScrollDurationFactor(mScrollFactor);scrollerField.set(this, scroller);} catch (Exception e) {e.printStackTrace();}}

通过反射设置Scroller,如果看过我以前的文章 http://blog.csdn.net/crazy__chen/article/details/45896961,一定指定Scroller是用来控制滑动的一个辅助类

而实际上ScrollView内部,也是通过这个类来提供滑动信息的,我们看一段ScrollView的源码:

public class ScrollView extends FrameLayout {static final int ANIMATED_SCROLL_GAP = 250;static final float MAX_SCROLL_FACTOR = 0.5f;private static final String TAG = "ScrollView";private long mLastScroll;private final Rect mTempRect = new Rect();private OverScroller mScroller;

我们看到在ScrollView内部,有一个私有的OverScroller类型的mScroller对象,就是这个对象来控制动画速度的。

问题是这个对象是私有的啊,我们没有办法获得,除了使用反射!

我们用反射获得这个属性以后,为这个属性重新设置一个值,这个值就是我们自己定义的CustomDurationScroller啦

我看看:

/*** 自定义Scroller来控制smoothScroll的动画时间*/private class CustomDurationScroller extends OverScroller{private double scrollFactor = 1;public CustomDurationScroller(Context context) {super(context);}/*** 设置动画时长比,默认动画时间是250ms*/public void setScrollDurationFactor(double scrollFactor) {this.scrollFactor = scrollFactor;}@Overridepublic void startScroll(int startX, int startY, int dx, int dy, int duration) {super.startScroll(startX, startY, dx, dy, (int)(duration * scrollFactor));}}

这个CustomDurationScroller同样继承了OverScroller,只是添加了一个scrollFactor属性,这样我们就可以在startScroll()方法里面更改动画时间了

由于它拥有OverScroller的所有功能,所以不会对原理的ScrollView造成影响。

通过反射,我们就巧妙地解决了这个问题。

最后VerticalScrollView只要提供一个set方法来让使用者可以设置scrollFactor就可以了

public void setScrollFactor(double scrollFactor){mScrollFactor = scrollFactor>0?scrollFactor:1;}

OK,到此为止,所有的问题都迎刃而解啦,大家可以随意使用这个控件,定制自己需要的效果哦!

源码下载地址:http://download.csdn.net/detail/kangaroo835127729/8980817
转载请注明出处!http://blog.csdn.net/crazy__chen/article/details/47375419

最后贴上源码:

/*** Created by kaiyi.cky on 2015/8/5.* 跑马灯控件*/
public class VerticalScrollView extends ScrollView{private LinearLayout mLinearLayout;/*** 当前显示的序号*/private int curIndex = 1;/*** 滚动方向* 1为向下,-1为向上*/private int direction = 1;/*** 滚动间隔时间*/private int spentTime = 2000;private final static int SCROLL_WHAT = 99;/*** 自定义Scroller来控制smoothScroll的动画时间*/private CustomDurationScroller scroller = null;/*** 滚动动画时间比率,该比率乘以250ms是动画时间*/private double mScrollFactor = 1d;MyHandler myHandler;/*** 用于数据与布局*/TabAdapter mTabAdapter;public VerticalScrollView(Context context) {super(context);init(context);}public VerticalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}public VerticalScrollView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public void init(Context context){mLinearLayout = new LinearLayout(context);mLinearLayout.setOrientation(LinearLayout.VERTICAL);addView(mLinearLayout, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));myHandler = new MyHandler();setViewPagerScroller();}/*** 使用反射设置Scroller*/private void setViewPagerScroller() {try {Field scrollerField = ScrollView.class.getDeclaredField("mScroller");scrollerField.setAccessible(true);scroller = new CustomDurationScroller(getContext());scroller.setScrollDurationFactor(mScrollFactor);scrollerField.set(this, scroller);} catch (Exception e) {e.printStackTrace();}}/*** 自定义Scroller来控制smoothScroll的动画时间*/private class CustomDurationScroller extends OverScroller{private double scrollFactor = 1;public CustomDurationScroller(Context context) {super(context);}/*** 设置动画时长比,默认动画时间是250ms*/public void setScrollDurationFactor(double scrollFactor) {this.scrollFactor = scrollFactor;}@Overridepublic void startScroll(int startX, int startY, int dx, int dy, int duration) {super.startScroll(startX, startY, dx, dy, (int)(duration * scrollFactor));}}/*** 用于不断向主线程发送信息,使行状态改变* 在handleMessage()里面调用sendMessageDelayed()可以实现自动无限滚动*/private class MyHandler extends Handler {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what){case SCROLL_WHAT:SetItemImeediate();setCurrentItem(curIndex+direction);myHandler.removeMessages(SCROLL_WHAT);myHandler.sendEmptyMessageDelayed(SCROLL_WHAT, spentTime);break;}}}/*** 瞬间设置当前行*/private void SetItemImeediate(){if (direction==1&&curIndex == mTabAdapter.getSize()-1){//如果向下滚动并且滚动到最后一条,瞬间跳跃到第二条,实现循环效果View v = mLinearLayout.getChildAt(1);scrollTo((int) v.getX(), (int) v.getY());curIndex = 1;}else if (direction==-1&&curIndex == 0){//如果向上滚动并且滚动到第一条,瞬间跳跃到倒数第二条,实现循环效果View v = mLinearLayout.getChildAt(mTabAdapter.getSize()-2);scrollTo((int) v.getX(), (int) v.getY());curIndex = mTabAdapter.getSize()-2;}}/*** 滚动到index行* @param index*/public void setCurrentItem(int index){View v = mLinearLayout.getChildAt(index);smoothScrollTo((int) v.getX(), (int) v.getY());curIndex = index;}/*** 逐个添加布局*/private void notifyDataSetChanged(){mLinearLayout.removeAllViews();for(int i=0;i<mTabAdapter.getSize();i++){if(mTabAdapter!=null){addTab(i);}}requestLayout();}/*** 开启动画,必须主动调用*/public void startAutoScroll(){myHandler.sendEmptyMessage(SCROLL_WHAT);}/*** 添加布局* @param index*/private void addTab(int index){View v = LayoutInflater.from(getContext()).inflate(mTabAdapter.mResId,mLinearLayout,false);mTabAdapter.getView(v,mTabAdapter.getText(index),mTabAdapter.getIcon(index));mLinearLayout.addView(v);}/*** 该函数必须在startAutoScroll()之前调用* @param mTabAdapter*/public void setTabAdapter(TabAdapter mTabAdapter) {this.mTabAdapter = mTabAdapter;notifyDataSetChanged();}@Overridepublic boolean onTouchEvent(MotionEvent ev) {return true;//返回true,防止手动滑动ScrollView}public int getSpentTime() {return spentTime;}public void setSpentTime(int spentTime) {this.spentTime = spentTime;}public int getDirection() {return direction;}public void setDirection(int direction) {this.direction = direction>0?1:-1;}public void setScrollFactor(double scrollFactor){mScrollFactor = scrollFactor>0?scrollFactor:1;}
}

教你自定义竖直跑马灯效果(广告专用)相关推荐

  1. html纵向的跑马灯效果,HTML+CSS入门 如何实现跑马灯/走马灯效果

    本篇教程介绍了HTML+CSS入门 如何实现跑马灯/走马灯效果,希望阅读本篇文章以后大家有所收获,帮助大家HTML+CSS入门. < 实现跑马灯的方法很多,其中最简单的是采用一句Html代码来实 ...

  2. 圣诞节祝福小Demo:JingleBells背景音乐+礼物闪烁下落+跑马灯效果

    源码已经上传至我的github上:https://github.com/junmei520/MyChristmas 同时我在微博上也以视频的形式展示了Demo的运行效果:http://weibo.co ...

  3. 自定义ListView实现任意View跑马灯效果

    自定义ListView实现任意View跑马灯效果 标签(空格分隔): 开源项目 看图 话不多说,先来看下大图效果吧,这里的GIF录制有点渣,不过真实的跑出来的效果还是挺不错的. 前言 最近项目中会加入 ...

  4. android 图片跑马灯动画,【Android自定义View】- 文本跑马灯效果

    简介 有些时候,文字过长,或者有多条需要展示的文本时,我们需要将文本进行左右滚动,多条文本时,还得上下滚动以实现展示不同的文本内容.这时候就需要我们自定义view来实现文本跑马灯效果了. 效果图 jj ...

  5. Android自定义不需要焦点的TextView以实现跑马灯效果

    使用原生的TextView实现"跑马灯"效果,需要特别注意其是否获取到了焦点(特别是程序当中有多个需要赋值的 Textview或进度条之类的需要改变其值的View);TextVie ...

  6. Android 自定义View实现文本水平方向的跑马灯效果

    自定义View实现文本水平方向的跑马灯效果,可以设置文本相关属性及滚动速度,以及滚动方式 /*** Created by wyl on 2018/10/10.*/ public class Marqu ...

  7. 自学小程序,我教你呀(三)实现大转盘 仿天猫抽奖 跑马灯效果

    往期精选: 自学小程序,我教你呀(一)简单倒计时 自学小程序,我教你呀(二)小程序实现电商秒杀倒计时效果 基本实现功能 1,小程序仿天猫超市抽奖大转盘 2,跑马灯效果 3,开始抽奖,抽奖完成后有弹窗 ...

  8. 微信小程序实现跑马灯效果(自定义组件详解)

    首先看一下效果吧 首先我们在项目根目录建一个公共文件夹,这里我命名为components 在components里面创建一个组件, dt-horse-race-lamp > index   最后 ...

  9. iOS LED跑马灯效果实现

    iOS中实现LED跑马灯效果 实现原理是使用scrollView, 将需要滚动的label添加两次到 scrollView的subView下面, 然后通过滚动scrollView来实现跑马灯效果. 具 ...

最新文章

  1. 百度大脑开放日第三期:四大全新平台、两大场景方案助力开发者逐梦 AI
  2. arm ida 伪代码 安卓 符号表_IDA调试界面介绍及快捷键
  3. matlab 不同尺度的矩阵存储
  4. OpenCV基本mat重建的实例(附完整代码)
  5. 关于ios手机上传图片旋转问题的解决
  6. Suricata的初始化脚本
  7. 如何禁止掉root登录,使用key密钥登录
  8. Net Core平台灵活简单的日志记录框架NLog+SqlServer初体验
  9. 计算分数的浮点数值(信息学奥赛一本通-T1010)
  10. python版本安装
  11. html样式超出出现滚动条,CSS 设置的高度超出屏幕高度为什么没出现滚动条?
  12. mysql导入超大sql文件方法
  13. 虚拟机是怎么实现的?(转)
  14. emqx_auth_mysql报错_EMQ插件组合实现物联网边缘平台的设备通信管理
  15. 常用的数据库软件各自有什么特点?
  16. Python实现连点器
  17. 单行文本溢出省略以及多行文本溢出省略css实现方式
  18. python求解立方根_求解立方根
  19. Java+MYSQL基于ssm的网上出差审批与费用报销管理系统
  20. 算法Day8|字符串专题二 剑指 Offer 58 - II. 左旋转字符串,28. 找出字符串中第一个匹配项的下标,459. 重复的子字符串

热门文章

  1. 快速复制浏览器控制台接口传参数据的小技巧
  2. C#学生管理系统课题设计
  3. java 用边读边下载文件,可用于大数据excel下载
  4. c语言 飞机,C语言写的飞机源码
  5. 不要忘记最后那个 default 分支
  6. 智慧治理 成都郫都区创新城市发展管理的有效抓手
  7. 【C++】二叉搜索树(BST)
  8. Intellij IDEA的Facets 意义
  9. html input dropdown,选择下拉插件-Dropdown.js
  10. vue3-antd-admin管理后台框架