Android scroller控件,Android Scroller完全解析

2023-11-03

在Android中,任何一个控件都是可以滚动的,因为在View类当中有scrollTo()和scrollBy()这两个方法,如下图所示:

b4a8131d3e458ee8214c2f252a8c1192.png

这两个方法的主要作用是将View/ViewGroup移至指定的坐标中,并且将偏移量保存起来。另外:

mScrollX代表X轴方向的偏移坐标

mScrollY代表Y轴方向的偏移坐标

这两个方法都是用于对View进行滚动的,那么它们之间有什么区别呢?简单点讲,scrollBy()方法是让View相对于当前的位置滚动某段距离,而scrollTo()方法则是让View相对于初始的位置滚动某段距离。

关于偏移量的设置我们可以参看下源码:

public class View {

....

protected int mScrollX; //该视图内容相当于视图起始坐标的偏移量,X轴方向

protected int mScrollY; //该视图内容相当于视图起始坐标的偏移量,Y轴方向

//返回值

public final int getScrollX() {

return mScrollX;

}

public final int getScrollY() {

return mScrollY;

}

public void scrollTo(int x, int y) {

//偏移位置发生了改变

if (mScrollX != x || mScrollY != y) {

int oldX = mScrollX;

int oldY = mScrollY;

mScrollX = x; //赋新值,保存当前便宜量

mScrollY = y;

//回调onScrollChanged方法

onScrollChanged(mScrollX, mScrollY, oldX, oldY);

if (!awakenScrollBars()) {

invalidate(); //一般都引起重绘

}

}

}

// 看出区别了吧 。 mScrollX 与 mScrollY 代表我们当前偏移的位置 , 在当前位置继续偏移(x ,y)个单位

public void scrollBy(int x, int y) {

scrollTo(mScrollX + x, mScrollY + y);

}

//...

}

于是,在任何时刻我们都可以获取该View/ViewGroup的偏移位置了,即调用getScrollX()方法和getScrollY()方法。

下面我们写个例子看下它们的区别吧:

xmlns:tools="http://schemas.android.com/tools"

android:id="@+id/layout"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/scroll_to_btn"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="scrollTo"/>

android:id="@+id/scroll_by_btn"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginTop="10dp"

android:text="scrollBy"/>

外层使用了一个LinearLayout,在里面包含了两个按钮,一个用于触发scrollTo逻辑,一个用于触发scrollBy逻辑。

public class MainActivity extends AppCompatActivity {

private LinearLayout layout;

private Button scrollToBtn;

private Button scrollByBtn;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

layout = (LinearLayout) findViewById(R.id.layout);

scrollToBtn = (Button) findViewById(R.id.scroll_to_btn);

scrollByBtn = (Button) findViewById(R.id.scroll_by_btn);

scrollToBtn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

layout.scrollTo(getResources().getDimensionPixelOffset(R.dimen.horizontal_scroll),

getResources().getDimensionPixelOffset(R.dimen.horizontal_scroll));

}

});

scrollByBtn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

layout.scrollBy(getResources().getDimensionPixelOffset(R.dimen.horizontal_scroll),

getResources().getDimensionPixelOffset(R.dimen.horizontal_scroll));

}

});

}

}

-20dp

-30dp

当点击了scrollTo按钮时,我们调用了LinearLayout的scrollTo()方法,当点击了scrollBy按钮时,调用了LinearLayout的scrollBy()方法。那有的朋友可能会问了,为什么都是调用的LinearLayout中的scroll方法?这里一定要注意,不管是scrollTo()还是scrollBy()方法,滚动的都是该View内部的内容,而LinearLayout中的内容就是我们的两个Button,如果你直接调用button的scroll方法的话,那结果一定不是你想看到的。

另外还有一点需要注意,就是两个scroll方法中传入的参数,第一个参数x表示相对于当前位置横向移动的距离,正值向左移动,负值向右移动。第二个参数y表示相对于当前位置纵向移动的距离,正值向上移动,负值向下移动。

运行一下程序:

724e4db463e9dafad65c6e554c778cf3.png

当我们点击scrollTo按钮时,两个按钮会一起向右下方滚动,之后再点击scrollTo按钮就没有任何作用了,界面不会再继续滚动,只有点击scrollBy按钮界面才会继续滚动,并且不停点击scrollBy按钮界面会一起滚动下去。

Scroller类

从上面例子运行结果可以看出,利用scrollTo()/scrollBy()方法把一个View偏移至指定坐标(x,y)处,整个过程是直接跳跃的,没有对这个偏移过程有任何控制,对用户而言不太友好。于是,基于这种偏移控制,Scroller类被设计出来了,该类的主要作用是为偏移过程制定一定的控制流程,从而使偏移更流畅,更完美。

我们分析下源码里去看看Scroller类的相关方法,其源代码(部分)如下: 路径位于 \frameworks\base\core\Java\android\widget\Scroller.java

public class Scroller {

private int mStartX; //起始坐标点 , X轴方向

private int mStartY; //起始坐标点 , Y轴方向

private int mCurrX; //当前坐标点 X轴, 即调用startScroll函数后,经过一定时间所达到的值

private int mCurrY; //当前坐标点 Y轴, 即调用startScroll函数后,经过一定时间所达到的值

private float mDeltaX; //应该继续滑动的距离, X轴方向

private float mDeltaY; //应该继续滑动的距离, Y轴方向

private boolean mFinished; //是否已经完成本次滑动操作, 如果完成则为 true

//构造函数

public Scroller(Context context) {

this(context, null);

}

public final boolean isFinished() {

return mFinished;

}

//强制结束本次滑屏操作

public final void forceFinished(boolean finished) {

mFinished = finished;

}

public final int getCurrX() {

return mCurrX;

}

/* Call this when you want to know the new location. If it returns true,

* the animation is not yet finished. loc will be altered to provide the

* new location. */

//根据当前已经消逝的时间计算当前的坐标点,保存在mCurrX和mCurrY值中

public boolean computeScrollOffset() {

if (mFinished) { //已经完成了本次动画控制,直接返回为false

return false;

}

int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);

if (timePassed < mDuration) {

switch (mMode) {

case SCROLL_MODE:

float x = (float)timePassed * mDurationReciprocal;

...

mCurrX = mStartX + Math.round(x * mDeltaX);

mCurrY = mStartY + Math.round(x * mDeltaY);

break;

...

}

else {

mCurrX = mFinalX;

mCurrY = mFinalY;

mFinished = true;

}

return true;

}

//开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,即到达坐标为(startX+dx , startY+dy)出

public void startScroll(int startX, int startY, int dx, int dy, int duration) {

mFinished = false;

mDuration = duration;

mStartTime = AnimationUtils.currentAnimationTimeMillis();

mStartX = startX; mStartY = startY;

mFinalX = startX + dx; mFinalY = startY + dy;

mDeltaX = dx; mDeltaY = dy;

...

}

}

其中比较重要的两个方法为:

public boolean computeScrollOffset()函数功能说明:根据当前已经消逝的时间计算当前的坐标点,保存在mCurrX和mCurrY值中。

public void startScroll(int startX, int startY, int dx, int dy, int duration)函数功能说明:开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,到达坐标为(startX+dx , startY+dy)处。

computeScroll()方法介绍:为了易于控制滑屏控制,Android框架提供了 computeScroll()方法去控制这个流程。在绘制View时,会在draw()过程调用该方法。因此, 再配合使用Scroller实例,我们就可以获得当前应该的偏移坐标,手动使View/ViewGroup偏移至该处。

computeScroll()方法原型如下,该方法位于ViewGroup.java类中

/**

* Called by a parent to request that a child update its values for mScrollX and mScrollY if necessary. This will typically be done if the child is animating a scroll using a {@link android.widget.Scroller Scroller}

* object.

* 由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制 */

public void computeScroll() { //空方法 ,自定义ViewGroup必须实现方法体

}

为了实现偏移控制,一般自定义View/ViewGroup都需要重载该方法 。其调用过程位于View绘制流程draw()过程中,如下:

@Override

protected void dispatchDraw(Canvas canvas){

...

for (int i = 0; i < count; i++) {

final View child = children[getChildDrawingOrder(count, i)];

if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {

more |= drawChild(canvas, child, drawingTime);

}

}

}

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {

...

child.computeScroll();

...

}

实例演示

ViewPager相信每个人都再熟悉不过了,因此它实在是太常用了,我们可以借助ViewPager来轻松完成页面之间的滑动切换效果,但是如果问到它是如何实现的话,我感觉大部分人还是比较陌生的。其实说到ViewPager最基本的实现原理主要就是两部分内容,一个是事件分发,一个是Scroller。对于事件分发,不了解的同学可以参考我这篇博客Android事件的分发、拦截和执行。

接下来我将结合事件分发和Scroller来实现一个简易版的ViewPager。首先自定义一个ViewGroup,不了解的可以参考Android自定义ViewGroup(一)之CustomGridLayout这篇文章。平滑偏移的主要做法如下:

第一、调用Scroller实例去产生一个偏移控制(对应于startScroll()方法)

第二、手动调用invalid()方法去重新绘制,剩下的就是在computeScroll()里根据当前已经逝去的时间,获取当前应该偏移的坐标(由Scroller实例对应的computeScrollOffset()计算而得)

第三、当前应该偏移的坐标,调用scrollBy()方法去缓慢移动至该坐标处。

新建一个ScrollerLayout并让它继承自ViewGroup来作为我们的简易ViewPager布局,代码如下所示:

public class ScrollerLayout extends ViewGroup {

private Scroller mScroller; //用于完成滚动操作的实例

private VelocityTracker mVelocityTracker = null ; //处理触摸的速率

public static int SNAP_VELOCITY = 600 ; //最小的滑动速率

private int mTouchSlop = 0 ; //最小滑动距离,超过了,才认为开始滑动

private float mLastionMotionX = 0 ; //上次触发ACTION_MOVE事件时的屏幕坐标

private int curScreen = 0 ; //当前屏幕

private int leftBorder; //界面可滚动的左边界

private int rightBorder; //界面可滚动的右边界

//两种状态: 是否处于滑屏状态

private static final int TOUCH_STATE_REST = 0; //什么都没做的状态

private static final int TOUCH_STATE_SCROLLING = 1; //开始滑屏的状态

private int mTouchState = TOUCH_STATE_REST; //默认是什么都没做的状态

public ScrollerLayout(Context context, AttributeSet attrs) {

super(context, attrs);

// 创建Scroller的实例

mScroller = new Scroller(context);

//初始化一个最小滑动距离

mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int childCount = getChildCount();

for (int i = 0; i < childCount; i++) {

View childView = getChildAt(i);

// 为ScrollerLayout中的每一个子控件测量大小

measureChild(childView, widthMeasureSpec, heightMeasureSpec);

}

}

@Override

protected void onLayout(boolean changed, int l, int t, int r, int b) {

if (changed) {

int childCount = getChildCount();

for (int i = 0; i < childCount; i++) {

View childView = getChildAt(i);

// 为ScrollerLayout中的每一个子控件在水平方向上进行布局

childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight());

}

}

// 初始化左右边界值

leftBorder = getChildAt(0).getLeft();

rightBorder = getChildAt(getChildCount() - 1).getRight();

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

final int action = ev.getAction();

//表示已经开始滑动了,不需要走该Action_MOVE方法了(第一次时可能调用)。

//该方法主要用于用户快速松开手指,又快速按下的行为。此时认为是处于滑屏状态的。

if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {

return true;

}

final float x = ev.getX();

switch (action) {

case MotionEvent.ACTION_MOVE:

final int xDiff = (int) Math.abs(mLastionMotionX - x);

//超过了最小滑动距离,就可以认为开始滑动了

if (xDiff > mTouchSlop) {

mTouchState = TOUCH_STATE_SCROLLING;

}

break;

case MotionEvent.ACTION_DOWN:

mLastionMotionX = x;

mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;

break;

case MotionEvent.ACTION_CANCEL:

case MotionEvent.ACTION_UP:

mTouchState = TOUCH_STATE_REST;

break;

}

return mTouchState != TOUCH_STATE_REST;

}

public boolean onTouchEvent(MotionEvent event){

super.onTouchEvent(event);

//获得VelocityTracker对象,并且添加滑动对象

if (mVelocityTracker == null) {

mVelocityTracker = VelocityTracker.obtain();

}

mVelocityTracker.addMovement(event);

//触摸点

float x = event.getX();

switch(event.getAction()){

case MotionEvent.ACTION_DOWN:

//如果屏幕的动画还没结束,你就按下了,我们就结束上一次动画,即开始这次新ACTION_DOWN的动画

if(mScroller != null){

if(!mScroller.isFinished()){

mScroller.abortAnimation();

}

}

mLastionMotionX = x ; //记住开始落下的屏幕点

break ;

case MotionEvent.ACTION_MOVE:

int detaX = (int)(mLastionMotionX - x ); //每次滑动屏幕,屏幕应该移动的距离

if (getScrollX() + detaX < leftBorder) { //防止用户拖出边界这里还专门做了边界保护,当拖出边界时就调用scrollTo()方法来回到边界位置

scrollTo(leftBorder, 0);

return true;

} else if (getScrollX() + getWidth() + detaX > rightBorder) {

scrollTo(rightBorder - getWidth(), 0);

return true;

}

scrollBy(detaX, 0);//开始缓慢滑屏咯。 detaX > 0 向右滑动 , detaX < 0 向左滑动

mLastionMotionX = x ;

break ;

case MotionEvent.ACTION_UP:

final VelocityTracker velocityTracker = mVelocityTracker ;

velocityTracker.computeCurrentVelocity(1000);

//计算速率

int velocityX = (int) velocityTracker.getXVelocity() ;

//滑动速率达到了一个标准(快速向右滑屏,返回上一个屏幕) 马上进行切屏处理

if (velocityX > SNAP_VELOCITY && curScreen > 0) {

// Fling enough to move left

snapToScreen(curScreen - 1);

}

//快速向左滑屏,返回下一个屏幕

else if(velocityX < -SNAP_VELOCITY && curScreen < (getChildCount()-1)){

snapToScreen(curScreen + 1);

}

//以上为快速移动的 ,强制切换屏幕

else{

//我们是缓慢移动的,因此先判断是保留在本屏幕还是到下一屏幕

snapToDestination();

}

//回收VelocityTracker对象

if (mVelocityTracker != null) {

mVelocityTracker.recycle();

mVelocityTracker = null;

}

//修正mTouchState值

mTouchState = TOUCH_STATE_REST ;

break;

case MotionEvent.ACTION_CANCEL:

mTouchState = TOUCH_STATE_REST ;

break;

}

return true ;

}

//我们是缓慢移动的,因此需要根据偏移值判断目标屏是哪个

private void snapToDestination(){

//判断是否超过下一屏的中间位置,如果达到就抵达下一屏,否则保持在原屏幕

//公式意思是:假设当前滑屏偏移值即 scrollCurX 加上每个屏幕一半的宽度,除以每个屏幕的宽度就是我们目标屏所在位置了。

int destScreen = (getScrollX() + getWidth() / 2 ) / getWidth() ;

snapToScreen(destScreen);

}

//真正的实现跳转屏幕的方法

private void snapToScreen(int whichScreen){

//简单的移到目标屏幕,可能是当前屏或者下一屏幕,直接跳转过去,不太友好,为了友好性,我们在增加一个动画效果

curScreen = whichScreen ;

//防止屏幕越界,即超过屏幕数

if(curScreen > getChildCount() - 1)

curScreen = getChildCount() - 1 ;

//为了达到下一屏幕或者当前屏幕,我们需要继续滑动的距离.根据dx值,可能向左滑动,也可能向右滑动

int dx = curScreen * getWidth() - getScrollX() ;

mScroller.startScroll(getScrollX(), 0, dx, 0, Math.abs(dx) * 2);

//由于触摸事件不会重新绘制View,所以此时需要手动刷新View 否则没效果

invalidate();

}

@Override

public void computeScroll() {

//重写computeScroll()方法,并在其内部完成平滑滚动的逻辑

if (mScroller.computeScrollOffset()) {

scrollTo(mScroller.getCurrX(), mScroller.getCurrY());

invalidate();

}

}

}

代码比较长,但思路比较清晰。

(1)首先在ScrollerLayout的构造函数里面我们创建Scroller的实例,由于Scroller的实例只需创建一次,因此我们把它放到构造函数里面执行。另外在构建函数中我们还初始化的TouchSlop的值,这个值在后面将用于判断当前用户的操作是否是拖动。

(2)接着重写onMeasure()方法和onLayout()方法,在onMeasure()方法中测量ScrollerLayout里的每一个子控件的大小,在onLayout()方法中为ScrollerLayout里的每一个子控件在水平方向上进行布局,布局类似于方向为horizontal的LinearLayout。

(3) 接着重写onInterceptTouchEvent()方法, 在这个方法中我们记录了用户手指按下时的X坐标位置,以及用户手指在屏幕上拖动时的X坐标位置,当两者之间的距离大于TouchSlop值时,就认为用户正在拖动布局,置状态为TOUCH_STATE_SCROLLING,当用户手指抬起,重置状态为TOUCH_STATE_REST。这里当状态值为TOUCH_STATE_SCROLLING时返回true,将事件在这里拦截掉,阻止事件传递到子控件当中。

(4)那么当我们把事件拦截掉之后,就会将事件交给ScrollerLayout的onTouchEvent()方法来处理。

如果当前事件是ACTION_MOVE,说明用户正在拖动布局,那么我们就应该对布局内容进行滚动从而影响拖动事件,实现的方式就是使用我们刚刚所学的scrollBy()方法,用户拖动了多少这里就scrollBy多少。另外为了防止用户拖出边界这里还专门做了边界保护,当拖出边界时就调用scrollTo()方法来回到边界位置。

如果当前事件是ACTION_UP时,说明用户手指抬起来了,但是目前很有可能用户只是将布局拖动到了中间,我们不可能让布局就这么停留在中间的位置,因此接下来就需要借助Scroller来完成后续的滚动操作。首先计算滚动速率,判断当前动作是scroll还是fling。如果是fling,再根据fling的方向跳转到上一页或者下一页,调用函数snapToScreen。如果是scroll,就调用函数snapToDestination,函数中首先根据当前的滚动位置来计算布局应该继续滚动到哪一页,滚动到哪一页同样调用snapToScreen。再来看看snapToScreen写法吧,其实是调用startScroll()方法来滚动数据,紧接着调用invalidate()方法来刷新界面。

(5)重写computeScroll()方法,并在其内部完成平滑滚动的逻辑 。在整个后续的平滑滚动过程中,computeScroll()方法是会一直被调用的,因此我们需要不断调用Scroller的computeScrollOffset()方法来进行判断滚动操作是否已经完成了,如果还没完成的话,那就继续调用scrollTo()方法,并把Scroller的curX和curY坐标传入,然后刷新界面从而完成平滑滚动的操作。

现在ScrollerLayout已经准备好了,接下来我们修改activity_main.xml布局中的内容,如下所示:

android:layout_width="match_parent"

android:layout_height="match_parent">

android:layout_width="match_parent"

android:layout_height="200dp"

android:background="@drawable/crazy_1" />

android:layout_width="match_parent"

android:layout_height="200dp"

android:background="@drawable/crazy_2" />

android:layout_width="match_parent"

android:layout_height="200dp"

android:background="@drawable/crazy_3" />

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android scroller控件,Android Scroller完全解析 的相关文章

  • laravel+element-ui+vue 搭建详细教程

    1 可用composer安装laravel框架项目 composer create project laravel laravel myblog 安装完成进入项目 cd myblog 2 安装前端资源和编译 npm install npm
  • 服务器监控功能(3种方案)

    服务器监控功能 3种方案 一 Actuator监控 1 添加依赖 2 application yaml配置 3 启动项目 访问 二 SpringBoot Admin 单体 1 Admin Server 端 2 Admin Client 端
  • android studio 安装说明

    转自 http www cnblogs com liuhongfeng archive 2015 12 30 5084896 html 1 下载android studio 含SDK版本 http www android studio or
  • node.js buffer的使用场景和方法及stream的应用

    buffer是什么 Node中 应用需要处理网络协议 操作数据库 处理图片 接收上传文件等 在网络流和文件的操作中 还要处理大量二进制数据 JavaScript自有的字符串远远不能满足这些需求 于是Buffer对象应运而生 Buffer作为
  • Java事务

    事务的ACID 1 原子性 原子性是说将事务看成一个整体 一个不可分割的单位 操作时要么同时成功 要么同时失败 2 一致性 一致性是指事务的前后数据的完整性保持一致 3 隔离性 隔离性是指多个用户并发访问数据库时 数据库给每一个用户开启一个
  • 什么是UTXO?

    经常看到区块链技术中提及UTXO 究竟什么是UTXO呢 个人技术公众号 解决方案工程师 欢迎同领域的朋友关注 相互交流 搜索了半天 讲的都不太好理解 尤其是不明白UTXO比现在的记账方式有哪些优势 集百家之长 成一家之言 我来通俗地讲一讲吧
  • 网络安全(黑客技术)0基础学习手册

    目录梗概 一 自学网络安全学习的误区和陷阱 二 学习网络安全的一些前期准备 三 网络安全学习路线 四 学习资料的推荐 想自学网络安全 黑客技术 首先你得了解什么是网络安全 什么是黑客 网络安全可以基于攻击和防御视角来分类 我们经常听到的 红
  • 用html+js实现一个简单的登录窗口

    文章目录 绘制UI界面 了解text文本框属性 UI界面效果 绘制登录后界面 实现登录验证脚本 运行效果 绘制UI界面 首先我们绘制出基本的不带功能的ui界面 这个界面需要创建一个表单 表单内包含一个username文本框 一个passwo
  • (六十六)支持向量机

    SVM的原理 SVM模型是将实例表示为空间中的点 这样映射就使得单独类别的实例被尽可能宽地明显间隔分开 然后将新的实例映射到同一空间 并基于它们落在间隔的哪一侧来预测所属类别 除了进行线性分类之外 SVM还可以使用所谓的核技巧有效地进行非线
  • java获取resource下的文件路径

    放了文件在 resources 目录下 只想拿到它的路径 详情如下 String path xxx class getClassLoader getResource targetFile txt getPath java获取文件目录 pom
  • 上网被阻断未经pppoe认证_Pppoe协议多运营商接入共用链路的方法

    Pppoe协议多运营商接入共用链路的方法 技术领域 0001 本发明属于计算机技术领域 具体涉及PPPOE协议多运营商接入共用链路方法 技术背景 0002 PPPOE 以太网上的点到点连接协议 认证目前大规模地应用于电信运营商的ADSL接入
  • 不拆无损,在北汽EU5,EU7,EX3,EX7安装app应用

    本文是破解过程 仅供学习参考 如果您只是安装软件 请直接看https blog csdn net robinhunan article details 105963936 这篇文章就可以了 app安装仅需要一个U盘 不需要输入任何命令 即可
  • RV1126 isp开发文档记录

    Rockchip IQ Tools Guide ISP2x v1 3 pdf 主要介绍 RKISP2 x Tuner 以下简称 Tuner 提供了一套便于用户调试 ISP 参数的工具 用户可以在 Tuner 中对所有 ISP 模块开展标定
  • Redis沙盒逃逸漏洞(CVE-2022-0543)复现以及流量特征分析

    Redis简介 Redis Labs Redis是美国Redis Labs公司的一套开源的使用ANSI C编写 支持网络 可基于内存亦可持久化的日志型 键值 Key Value 存储数据库 并提供多种语言的API 漏洞介绍 Redis 存在
  • JdbcTemplate防注入的几种方式

    使用数组 public void deleteItemByName String name Object obj new Object name String sql DELETE FROM t item WHERE name jdbcTe
  • php开启opcache

    1 简介 OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能 存储预编译字节码的好处就是 省去了每次加载和解析 PHP 脚本的开销 注意 如果需要将 Xdebug 扩展和 OPcache 一起使用 必须
  • C++ 11学习

    原始字符串字面量 就不需要转义引脚 原始字符串字面量以R 开头 以 结尾 string str R Hello World c 中 nullptr无法意识转换为整形 但是可以隐式匹配指针类型 在C 11校准下 相比NULL和0 使用null
  • Nginx 状态页详解

    Nginx 状态页详解 基于nginx 模块 ngx http stub status module 实现 在编译安装nginx的时候需要添加编译参数 with http stub status module 否则配置完成之后监测会是提示语
  • Linux命令大全,唯一以案例详解文,持续更新中

    系列文章目录 软件测试功能到自动化学习路线图 2022年最新版技术栈 软件测试01 从了解测试岗位职能和测试流程开始 附作业 软件测试02 6大实际案例手把手教你设计测试点 软件测试03 用例执行以及缺陷管理的学习 附禅道下载使用流程 软件

随机推荐

  • 信贷风险指标你都懂吗?

    转自 简书 信贷指标 风控基本概念 贷款不良率 不良贷款率 逾期率Vintage统计法 Now和Ever DPD1 DPD30 DPD60 DPD90 引言 17年3月份的时候 有一篇文章曾经引爆金融界 称某国内知名互联网金融逾期率高达30
  • linux下解压zip文件命令

    Linux下的压缩解压缩命令详解及实例 实例 压缩服务器上当前目录的内容为xxx zip文件 zip r xxx zip 解压zip文件到当前目录 unzip filename zip 另 有些服务器没有安装zip包执行不了zip命令 但基
  • Apache Tomcat AJP 文件包含漏洞复现(CVE-2020-10487)

    影响范围 tomcat 7 lt 7 0 100 tomcat 8 lt 7 5 51 tomcat 9 lt 9 0 31 利用条件 1 漏洞版本范围内 2 默认开启ajp服务 漏洞复现 nmap扫描 默认端口8009 经测试存在更改默认
  • AI绘画调用OpenAI-api接口【人工智能里的未来之城】:4 座未来派塔楼,天桥上覆盖着茂密的树叶,数字艺术

    OpenAI绘画数字艺术是一种利用人工智能算法生成数字艺术的技术 该技术使用了一种称为GAN Generative Adversarial Networks 生成对抗网络 的深度学习模型 这种模型由两个神经网络组成 生成器和判别器 生成器的
  • 07-2_Qt 5.9 C++开发指南_二进制文件读写(stm和dat格式)

    文章目录 1 实例功能概述 2 Qt预定义编码文件的读写 2 1 保存为stm文件 2 2 stm文件格式 2 3 读取stm文件 3 标准编码文件的读写 3 1 保存为dat文件 3 2 dat文件格式 3 3 读取dat文件 4 框架及
  • 【Maven】Maven下载,配置以及基本概念

    文章目录 1 Maven简介 2 Maven下载 3 Maven环境配置 4 Maven的基本概念 4 1 仓库 4 2 坐标 4 3 仓库配置 修改IDEA默认Maven库 1 Maven简介 Maven是一个Java项目管理工具和构建工
  • 本博客全文目录索引

    本专栏博文索引 目录 涵盖 C C STL Data Structure Algorithm TCP IP Linux Interface Driver Kernel Netfilter 和 Projects C C 详解C指针 C 对象模
  • Java BufferedInputStream原理及设计模式分析

    文章目录 背景 源码分析 FileInputStream BufferedInputStream 装饰器模式 总结 背景 BufferedInputStream和其他InputStream常常放在一起使用 BufferedInputStre
  • 梯度有关问题

    1 偏导数 方向导数 导数是函数随自变量的变化率 对于 一元函数 只有一个自变量x 那么函数y f x 的导数是 在某一点处沿x轴正方向的变化率 多元函数 多个自变量 是多维向量 那么函数随自变量的变化怎么刻画呢 一个方法 就是衡量函数在给
  • 【终极区分】iterable和iterator

    1 凡是可作用于for循环的对象都是Iterable类型 2 凡是可作用于next 函数的对象都是Iterator类型 它们表示一个惰性计算的序列 3 集合数据类型如list dict str等是Iterable但不是Iterator 不过
  • C#中的拆箱与装箱

    1 什么是拆箱和装箱 在C 中 值类型是直接将数据存储在栈空间中 而引用类型是将数据存储在堆空间中 同时在栈空间中存储一个对该数据的引用 那么如果将一个值类型转换为一个它实现的某个接口或object会发生什么 结果必然是对一个存储位置的引用
  • Nodejs源码解析之module

    http blog csdn net leoleocs article details 50245677 module管理是Nodejs中比较有特色的部分 官方有详细的文档https nodejs org api modules html
  • el-tabs通过动态组件来更新单个tab页

    使用element ui的el tabs时 加载页面时会将所有单个tab页渲染出来 如果两个有联系的单个tab页 其中一个添加了数据 另一个tab页只有刷新才会显示出新的数据 使用动态组件来更新单个tab页 在data中定义tab列表 da
  • UnboundLocalError: cannot access local variable XXX where it is not associated with a value解决办法

    代码如图 a 1 def test a 1 test 此时运行代码会产生以下报错 UnboundLocalError cannot access local variable a where it is not associated wit
  • 查找两个字符串的相同代码块--Java

    前提 两个字符串中只有一个最大相同子串 public String getMaxSameString String str1 String str2 if str1 null str2 null String maxStr str1 len
  • 视图的定义与操作

    数据库系统 实验报告 实验名称 视图的定义与操作 实验地点 实验楼423 实验日期 一 实验目的及要求 1 掌握创建视图的SQL语句的用法 2 掌握修改视图的方法 3 熟悉视图更新与基本表更新的区别与联系 4 认识视图的作用 二 实验环境
  • vue获取+设置光标位置 光标定位 选择输入框文本

    版本 vue2 vant2 在vue是用ref r e f s 获取 d o m 的 在
  • STM32移植LVGL踩坑集锦

    这篇文章我主要讲解一下我在移植LVGL时所遇到的一些坑以及解决方法 LVGL的移植过程可以参考我前面的文章 http t csdn cn QSVOO 第一个 问题 在lvgl8 1以前的版本可能会出现MY DISP HOR RES 和 MY
  • QtDesigner中如何手动添加工具栏toolBar

    如下图 只需要把Action拖到工具栏中即可
  • Android scroller控件,Android Scroller完全解析

    在Android中 任何一个控件都是可以滚动的 因为在View类当中有scrollTo 和scrollBy 这两个方法 如下图所示 这两个方法的主要作用是将View ViewGroup移至指定的坐标中 并且将偏移量保存起来 另外 mScro