PullScrollView详解(二)——Animation、Layout与下拉回弹

2024-02-28 08:38

本文主要是介绍PullScrollView详解(二)——Animation、Layout与下拉回弹,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这篇先给大家梳理下有关TranslateAnimation和Layout的知识点,然后我们初步做一个下拉回弹的效果出来。

一、TranslateAnimation和Layout
我们这里只讲这里所能用到的最重要的知识点,有关各个TranslateAnimation详细的使用方法,可以参考我以前写的博客系列:<Animation动画详解(三)—— 代码生成alpha、scale、translate、rotate、set及插值器动画>

1、TranslateAnimation
public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
其中
fromXDelta:是指开始运动的起点的X坐标
toXDelta:指结束运动的终点的X坐标
fromYDelta:指开始运动的起点的Y坐标
toYDelta:指结束运动的终点的Y坐标

使用方法:
TranslateAnimation animation = new TranslateAnimation(0, 0, 0, -100);
animation.setDuration(200);
view.startAnimation(animation);
这段代码的意思是,将view从(0,0)位置移动的(0,-100)的位置,也就是向上移动100像素。
那这里有个疑问:移动所按的坐标系是哪一个?是view父控件的还是自己的坐标系?
当然是按照自己的坐标系,从最后一句代码也可以看出来:
view.startAnimation(animation);
这个animation是运用在view中的,当然是以view的左上角为坐标系的原点来计算运动坐标的。理解不了也没关系,下面我们会用个例子来详细讲一讲。
2、Layout
先看看layout的声明:
public void layout(int left, int top, int right, int bottom)
对应的使用方法:
view.layout(view.getLeft(),view.getTop()+100,view.getRight(),view.getBottom()+100);
ayout函数有四个参数,分别是上、下、左、右,四个点的坐标。很明显,这是用来定位view所有位置的。 
即上面的函数,即是将上和底分别向下移动了100像素。左、右的坐标不变。 
这里要注意的是,它移动的坐标是按谁的来呢?我们这里传进去的值的时候,是通过类似view.getLeft()来获取左顶点X坐标的。这个坐标就相对父控件的坐标系的。所以layout的坐标是以父控件的坐标系来计算的。 
其实这也好理解,因为layout可以随意的根据用户传进去的坐标来改变大小和位置。如果使用自己的坐标系来改变大小和位置的话,会很难计算;使用别人做为参照物来改变自己就相对容易的多。所以谷歌那帮老头用的是别人的坐标系来改变自己的位置和大小的。所以,一般情况下,改变自己位置和大小的函数,所用到的坐标系,一般都是父控件的坐标系;

3、示例
下面我们就来写个例子来试验一下TranslateAnimation的动画构造Animation时使用的坐标是通过自己的坐标系计算的。 
下面是效果图:

首先,点击animation_up按钮,底部的TextView的移动动画为:

TranslateAnimation(0, 0, 0, -100);
即从(0,0)点移动到(0,-100)点;从图中也确实可以看到TextView从原始位置向上移动了;从图中也可以看到开始运动的(0,0)点,就是控件所有的原始位置。因为如果不在它原始位置的话,TextView会先移动到这个位置开始动画。
然后,我们点击layout_down按钮,将TextView控件通过layout函数将其下移100像素:
mRootView.layout(mRootView.getLeft(),mRootView.getTop()+100,mRootView.getRight(),mRootView.getBottom()+100);
这时候再点击animation_up按钮,同样是让它从(0,0)点运动到(0,-100)点;但它已经不是从上次的原始位置来开始运动了。而是从当前的控件位置向上移动;从这个示例可以看出:layout()函数,不仅能通过指定上、下、左、右四个点坐标来指定控件的大小和位置。同时,该控件坐标系的(0,0)原点,也随着左上角位置的变动而变动。即控件的左上角始终是该控件坐标系的(0,0)原点; 
好了,下面我们就看看上面例子的代码实现吧: 
(1)activity_main.xml 
从效果图也可以看出,布局很简单,使用的垂直布局,依次三个控件而已:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
 
    <Button
        android:id="@+id/layout_down"
        android:text="layout_down"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
 
    <Button
        android:id="@+id/animation_up"
        android:text="animation_up"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
 
    <TextView
        android:id="@+id/tv"
        android:layout_width="200dp"
        android:layout_height="400dp"
        android:layout_gravity="center_horizontal"
        android:background="#ff00ff"/>
</LinearLayout>
2、MainActivity.java
先看看整体代码:
public class MainActivity extends Activity implements View.OnClickListener{
 
    private TextView mRootView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        mRootView = (TextView)findViewById(R.id.tv);
 
        Button btnMoveDwn = (Button)findViewById(R.id.layout_down);
        Button btnAnimationUp = (Button)findViewById(R.id.animation_up);
 
        btnMoveDwn.setOnClickListener(this);
        btnAnimationUp.setOnClickListener(this);
    }
 
    /**
     * 两个点:
     * 首先mRootView.startAnimation()中Animation的移动坐标是以mRootView的布局为原点的,如果使用mRootView.layout()将其改变,那么原点位置也将会改变.
     * Animation不会改变布局的坐标;layout则不一样,在改变位置的同时,layout的左上角始终是这个布局的(0,0)原点
     *
     * @param v
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.layout_down:{
                mRootView.layout(mRootView.getLeft(),mRootView.getTop()+100,mRootView.getRight(),mRootView.getBottom()+100);
            }
            break;
            case R.id.animation_up:{
                TranslateAnimation animation = new TranslateAnimation(0, 0, 0, -100);
                animation.setDuration(200);
                mRootView.startAnimation(animation);
            }
            break;
        }
    }
}
核心的代码我们都讲过了,这里也没什么好讲的了; 
通过这个例子,我们要知道:Animation使用的坐标系是以自己控件的左上角为原点的自己的坐标系;而layout函数则会改变控件大小和位置,在改变位置的同时,也改变了控件的坐标系,即layout的左上角始终是这个布局的(0,0)原点; 
源码在文章底部给出
二、下拉回弹
上面把用到的两个函数给大家讲了讲,下面我们就看看怎么用这两个函数来做一个简单的下拉回弹的效果吧。先看看效果图: 


从图中可以看到,当我们下拉猫咪图片之后,图片自动反弹回去。 
这里涉及两个问题:

怎么让布局跟随手指下移而下移
怎么让布局反弹回去
首先,第一个问题:怎么让布局跟随手指下移而下移? 
上面我们讲了,通过layout()函数可以改变布局的位置,而且,通过onTouchEvent()可以捕捉到手指的移动方向和距离。所以在onTouchEvent()中根据手指的移动距离,通过layout()函数将布局跟随移动即可。 
然后,第二问题:怎么让布局反弹回去? 
很明显,我们可以使用Animation动画,将布局从当前位置回到原来的位置即可。但这里需要注意的一点是,我们上面讲了,通过layout()函数在移动布局的同时,把布局的控件坐标系也改变了,即布局当前位置的左上角始终为该控件坐标系的原点位置。而Animation的坐标却又是根据该布局的左上角位置(原点)来计算的。 
所以,在构造Aimation动画时,传入的运动参数要特别注意。这点, 我们会结合代码具体讲。
1、XML布局(activity_main.xml)
从上面的效果图中也可以看到,首先,我们的布局是可以滚动的,所以肯定会有ScrollView控件,然后在ScrollView中,各个控件是垂直布局的。 
所以,activity_main.xml的布局代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
 
    <com.harvic.com.touchscrollview.CustomScrollView
        android:layout_width="fill_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical">
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:scaleType="fitXY"
                android:src="@drawable/pic1"/>
            <TextView
                android:text="extra item 1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="16dp"/>
            <TextView
                android:text="extra item 2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="16dp"/>
            <TextView
                android:text="extra item 3"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="16dp"/>
            <TextView
                android:text="extra item 4"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="16dp"/>
 
        </LinearLayout>
 
    </com.harvic.com.touchscrollview.CustomScrollView>
 
</LinearLayout>
其中CustomScrollView是下面我们将要自定义的控件,派生自ScrollView;上面的布局代码很容易看懂,就不再细讲。
2、布局跟随手指移动
我们首先要完成让布局跟随手指下拉而移动的操作。所以我们要在onTouchEvent()中捕捉手指的移动距离,通过layout()函数,让布局跟着移动 
(1)布局初始化
private View mRootView;
@Override
protected void onFinishInflate() {
    // TODO Auto-generated method stub
    mRootView = getChildAt(0);
    super.onFinishInflate();
}
由于ScrollView只能有一个子布局,所以,我们通过getChildAt(0)直接获取ScrollView下的子布局即可; 
onFinishInflate()函数,明显是在布局解析结束后会调用的函数。 
(2)onTouchEvent拦截 
让布局跟随手指移动的代码如下:
private int mpreY=0;
public boolean onTouchEvent(MotionEvent event) {
    float curY = event.getY();    
    switch (event.getAction()) {
    case MotionEvent.ACTION_MOVE:{
        int delta = (int)((curY - mpreY)*0.25);
        if (delta>0) {
            mRootView.layout(mRootView.getLeft(), mRootView.getTop()+delta, mRootView.getRight(), mRootView.getBottom()+delta);
        }
    }
    break;
    }
    mpreY = (int) curY;
    return super.onTouchEvent(event);
}
首先,通过mpreY来保存每次手指移动的最新位置。
然后,通过 event.getY() - mpreY就可以得到当前手指的移动距离
而我们的代码中却是这么写的:
float curY = event.getY();    
int delta = (int)((curY - mpreY)*0.25);
为什么这里要乘以0.25?
这是因为,如果我们不想让布局的移动距离跟手指的移动距离等同。让布局的移动距离比手指移动距离小,会给用户一种有阻力的感觉。这样,用户体验比较好。比较符合常规:当下拉一个东西的时候,总是感觉是有阻力的。这个定义阻力大小的数值就叫阻尼系数。这个值大家可以随便定义,越小,代表阻力越大。即布局实际移动距离就越小。
最后,是根据delta移动布局:
mRootView.layout(mRootView.getLeft(), mRootView.getTop()+delta, mRootView.getRight(), mRootView.getBottom()+delta);
由于,我们不改变大小,也不改变左右的位置,只让它向下移动,所以只需要在top和bottom上直接加上要移动的距离即可。
到目前为止,完整的CustomScrollView.java的代码为:
public class CustomScrollView extends ScrollView {
    // 根布局视图
    private View mRootView;
    private int mpreY = 0;
 
    public CustomScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }
 
    @Override
    protected void onFinishInflate() {
        // TODO Auto-generated method stub
        mRootView = getChildAt(0);
        super.onFinishInflate();
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float curY = event.getY();
        switch (event.getAction()) {
        case MotionEvent.ACTION_MOVE: {
            int delta = (int) ((curY - mpreY) * 0.25);
            if (delta > 0) {
                mRootView.layout(mRootView.getLeft(), mRootView.getTop()
                        + delta, mRootView.getRight(), mRootView.getBottom()
                        + delta);
            }
        }
            break;
        }
        mpreY = (int) curY;
        return super.onTouchEvent(event);
    }
 
}
3、回弹
回弹可能是理解起来难度相对比较大的一个地方,因为这里牵涉到动画的坐标问题。我们先看代码是怎么写的吧。 
(1)、保存初始化位置 
首先,我们在上面移动布局以前,把布局原来的位置保存起来,这样,我们回弹的时候就知道回到哪了。这个操作,我们在MotionEvent.ACTION_DOWN里完成:
//原始根视图对应的位置
private Rect mNormalRect;
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:{
            if (mRootView != null) {
                mNormalRect = new Rect(mRootView.getLeft(), mRootView.getTop(), mRootView.getRight(), mRootView.getBottom());
            }
        }
        break;
        …………
}
由于我们布局的初始化位置是不变的,所以,我们完全可以在其它地方获取原始布局的上、下、左、右位置,但由于布局的位置只有在真正被渲染出来以后才能得到,所以在 onFinishInflate()中是得不到的,因为这时候布局还没有被渲染出来。而当运行到onTouchEvent()时,布局肯定已经被渲染出来了。所以我放在了用户点击时获取初始化位置。
(2)、回弹
    public boolean onTouchEvent(MotionEvent event) {
        float curY = event.getY();
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:{
            if (mRootView != null) {
                mNormalRect = new Rect(mRootView.getLeft(), mRootView.getTop(), mRootView.getRight(), mRootView.getBottom());
            }
        }
        break;
        …………
        case MotionEvent.ACTION_UP:{
            int curTop = mRootView.getTop();
            mRootView.layout( mNormalRect.left, mNormalRect.top, mNormalRect.right, mNormalRect.bottom);
            TranslateAnimation animation = new TranslateAnimation(0, 0, curTop - mNormalRect.top, 0);
            animation.setDuration(200);
            mRootView.startAnimation(animation);
        }
        break;
        }
        mpreY = (int) curY;
        return super.onTouchEvent(event);
    }
非常注意在MotionEvent.ACTION_UP时的代码处理步骤: 
1、使用layout将布局还原到原始位置 
2、调用TranslateAnimation将布局从跟随手指移动到的位置运动到初始化位置。 
代码运行的流程图如下: 

首先,我们假定mRoot布局的初始化位置为A,即如图(2)中的位置所示;跟随手指移动到的位置为B,如图(1)中的位置所示; 
整个代码的流程是这样的: 
图示(1)显示的是,在MotionEvent.ACTION_MOVE中,让布局跟随手指移动后的状态。 
图示(2),(3)显示的是反弹时的代码操作,即在MotionEvent.ACTION_UP中的代码。 
这里非常要注意的一点是,我们反弹时,并不是只利用TranslateAnimation来将mRoot布局动画返回回去。 
在操作动画之前,我们利用layout()函数,将布局首先还原到了初始化位置。 

为什么要这样做呢? 
我们在开头讲过,Animation动画的坐标是以它自己布局的左上角为顶点来计算的。如果我们只是利用动画将mRoot布局返回回去,那它本身的坐标系仍然没有还原。这跟初始化状态是不一样的。由于跟初始状态不一样,那以下次再下拉的时候,根据初始状态来做的各种操作就肯定会出问题。所以,无论怎样,我们都需要利用layout()将布局还原。 

第二个问题,你先还原布局,然后再利用TranslateAnimation让布局返回到跟随手指移动到的位置(B位置),这样做,不会闪屏吗? 
由于layout()函数移动布局是瞬间完成的,并不会像Animation一样,它没有过程,直接到达目的地。而TranslateAnimation移动到动画开始移动的点的过程中(到B点),也是瞬间完成的;TranslateAnimation的动画,只存在于用户定义的动画开始位置到动画结束位置的过程中。其它地方都是瞬间完成的。所以由于从B到A是瞬间完成的(图示1到图示2的过程),从B到A也是瞬间完成的(从图示2到图示3的过程),所以从图1到图3,用户看起来就是没有改变的。因为人眼要存在图象,必须图像的存在时间长于1/25秒,这才能被人眼采集进去,形成图像。而现在手机从图示(1)到图示(3)的处理时间,远低于我们人眼所能看到的时间,所以用户是根本看不到的。 


三、网上解决方案及存在问题
大家可能看过网上的其它文章,在回弹的时候,我跟他们的方式不一样,大家可能会有疑问:网上很多人的回弹代码是下面这么写的?你为什么不这么写呢?
1、概述
网上的代码:(千篇一律)
TranslateAnimation ta = new TranslateAnimation(0, 0, curTop,normal.top);  
ta.setDuration(200);  
inner.startAnimation(ta);  
// 设置回到正常的布局位置  
inner.layout(normal.left, normal.top, normal.right, normal.bottom);
这段代码是不正确的,首先给大家说他的一个BUG,如果
TranslateAnimation ta = new TranslateAnimation(0, 0, curTop,normal.top);
中的normal.top不等于0,也就是说,你可以给mRootView添加上一个参数andoird:marginTop=”100dp”,你会明显的发现动画会出现问题。 
添加位置如下: 
即直接在MyScrollView下,我们要移动的布局,即mRoot所对应的XML控件,这里是LinearLayout 

我们来对比原添加前后的效果图: 
添加前:

添加后 

明显看到,在添加以后的示图中,当手指松开以后,mRoot视图会先往下运动一下,然后再向上运动。我们后面会讲它的这个BUG要怎么解,但我们先来看一个更重要的问题。

2、问题一:为什么先动画后还原依然没问题?
再来看他回弹的代码:
TranslateAnimation ta = new TranslateAnimation(0, 0, curTop,normal.top);  
ta.setDuration(200);  
inner.startAnimation(ta);  
// 设置回到正常的布局位置  
inner.layout(normal.left, normal.top, normal.right, normal.bottom);  
首先,我们要明白的一个问题是,在MotionEvent.ACTION_MOVE中,我们已经利用layout()函数,将布局随手指移动到了指定位置(B位置)
我们上次说过,Animation动画的坐标是以自己左上角来算的,那么,也就是说当前的Animation坐标是以mRoot在B位置的坐标来算的。看下图

图(1)显示的是控件初始化位置。所示的O点是mRoot的原点的坐标。对应的值是(normal.left,normal.top,normal.right,normal.bottom)
当mRoot布局跟着手指下移,到图(2)的位置,由于我们移动布局使用的是layout()函数,所以在图(2)中,mRoot的左上角原点O,所对应的位置就是(mRoot.getLeft(),mRoot.getTop(),mRoot.getRight(),mRoot.getBottom),
如果在抬起手指时,先运行:

TranslateAnimation ta = new TranslateAnimation(0, 0, mRoot.getTop(),normal.top);  
上面我们讲过Animation的动画坐标是以控件本身左上角来计算的,所以它移动的位置应该如图(3)所示,而我们为什么没有看出下先向下移动然后再向上移动呢?
这是为什么呢,我考虑了很久,猜想原因应该是这样的:再来看看他反弹的代码:
TranslateAnimation ta = new TranslateAnimation(0, 0, mRoot.getTop(),normal.top);  
ta.setDuration(200);  
mRoot.startAnimation(ta);  
// 设置回到正常的布局位置  
mRoot.layout(normal.left, normal.top, normal.right, normal.bottom); 
注意,他在动画以后,直接利用mRoot.layout()将动画还原。我觉得,由于mRoot.layout()的操作是一瞬间的事情,而动画的渲染速度要比mRoot.layout()慢,所以当开始动画的时候,mRoot就已经回到了初始化的布局状态。此时的原点,依然是初始化状态的原点。所以看起来没有什么问题,但这完全是投机的算法。不是正规流程。
3、问题二:为什么给mRoot添加上anroid:margin_top:"100dp"以后,松手时会先向下运动一下?
这个问题依然很难讲,我都有点想放弃讲解了,还是说说吧,大家能看得懂就最好了,看不懂就算了……还是不要为难自己了……
最关键的问题还是在松手时,Animation动画的坐标是以本身的左上角为原点来计算的。

TranslateAnimation ta = new TranslateAnimation(0, 0, mRoot.getTop(),normal.top);  
ta.setDuration(200);  
mRoot.startAnimation(ta);  
// 设置回到正常的布局位置  
mRoot.layout(normal.left, normal.top, normal.right, normal.bottom); 
还是来看图吧:

图示1:显示的是mRoot的初始化位置,控件上面的空白,就是margin_top的高度
图示2:显示的是跟随手指移动的最终位置。此时利用mRoot.getTop()是在父控件上面的高度,即除了手指的移动距离还多了个margin_top的高度。
图示3:这个就显示了,当我们真正使用TranslateAnimation来做动画的时候,它的初始化移动位置,是要比图示2的位置要靠下,因为我们传给TranslateAnimation的TOP值是mRoot.getTop(),而TranslateAnimation的计算的原点不是在父控件的原点,而是控件初始化位置的原点,即多了个margin_top的高度:

TranslateAnimation ta = new TranslateAnimation(0, 0, mRoot.getTop(),normal.top);  
如果要修改的话也比较容易,想办法把目的位置改成手指的移动距离就好了;
同样,TranslateAnimation不应该移动到(0,normal.top),因为要移动到初始位置,也就是当前布局左上角所在的(0,0)点,所以修复后的代码应该是:
TranslateAnimation ta = new TranslateAnimation(0, 0, mRoot.getTop()- normal.top, 0);


好了,到这里,这篇文章就结束了,大家对于第三部分,讲解网上代码所存在问题的部分,可以忽略,有兴趣的朋友可以仔细看看,感觉讲解起来难度很大啊,也不知道大家能不能看得懂,不行就只有自己根据理解画画出看。
 

这篇关于PullScrollView详解(二)——Animation、Layout与下拉回弹的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/754970

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

嵌入式Openharmony系统构建与启动详解

大家好,今天主要给大家分享一下,如何构建Openharmony子系统以及系统的启动过程分解。 第一:OpenHarmony系统构建      首先熟悉一下,构建系统是一种自动化处理工具的集合,通过将源代码文件进行一系列处理,最终生成和用户可以使用的目标文件。这里的目标文件包括静态链接库文件、动态链接库文件、可执行文件、脚本文件、配置文件等。      我们在编写hellowor

LabVIEW FIFO详解

在LabVIEW的FPGA开发中,FIFO(先入先出队列)是常用的数据传输机制。通过配置FIFO的属性,工程师可以在FPGA和主机之间,或不同FPGA VIs之间进行高效的数据传输。根据具体需求,FIFO有多种类型与实现方式,包括目标范围内FIFO(Target-Scoped)、DMA FIFO以及点对点流(Peer-to-Peer)。 FIFO类型 **目标范围FIFO(Target-Sc

019、JOptionPane类的常用静态方法详解

目录 JOptionPane类的常用静态方法详解 1. showInputDialog()方法 1.1基本用法 1.2带有默认值的输入框 1.3带有选项的输入对话框 1.4自定义图标的输入对话框 2. showConfirmDialog()方法 2.1基本用法 2.2自定义按钮和图标 2.3带有自定义组件的确认对话框 3. showMessageDialog()方法 3.1

脏页的标记方式详解

脏页的标记方式 一、引言 在数据库系统中,脏页是指那些被修改过但还未写入磁盘的数据页。为了有效地管理这些脏页并确保数据的一致性,数据库需要对脏页进行标记。了解脏页的标记方式对于理解数据库的内部工作机制和优化性能至关重要。 二、脏页产生的过程 当数据库中的数据被修改时,这些修改首先会在内存中的缓冲池(Buffer Pool)中进行。例如,执行一条 UPDATE 语句修改了某一行数据,对应的缓

OmniGlue论文详解(特征匹配)

OmniGlue论文详解(特征匹配) 摘要1. 引言2. 相关工作2.1. 广义局部特征匹配2.2. 稀疏可学习匹配2.3. 半稠密可学习匹配2.4. 与其他图像表示匹配 3. OmniGlue3.1. 模型概述3.2. OmniGlue 细节3.2.1. 特征提取3.2.2. 利用DINOv2构建图形。3.2.3. 信息传播与新的指导3.2.4. 匹配层和损失函数3.2.5. 与Super

web群集--nginx配置文件location匹配符的优先级顺序详解及验证

文章目录 前言优先级顺序优先级顺序(详解)1. 精确匹配(Exact Match)2. 正则表达式匹配(Regex Match)3. 前缀匹配(Prefix Match) 匹配规则的综合应用验证优先级 前言 location的作用 在 NGINX 中,location 指令用于定义如何处理特定的请求 URI。由于网站往往需要不同的处理方式来适应各种请求,NGINX 提供了多种匹