当前位置: 首页 > news >正文

Android 实现菜单拖拽排序

效果图

简介

本文主角是ItemTouchHelper。

它是RecyclerView对于item交互处理的一个「辅助类」,主要用于拖拽以及滑动处理。

以接口实现的方式,达到配置简单、逻辑解耦、职责分明的效果,并且支持所有的布局方式。

功能拆解

功能实现

4.1、实现接口

自定义一个类,实现ItemTouchHelper.Callback接口,然后在实现方法中根据需求简单配置即可。

class DragCallBack(adapter: DragAdapter, data: MutableList<String>) : ItemTouchHelper.Callback() {
}

ItemTouchHelper.Callback必须实现的3个方法:

  • getMovementFlags

  • onMove

  • onSwiped

其他方法还有onSelectedChanged、clearView等。

4.1.1、getMovementFlags

用于创建交互方式,交互方式分为两种:

1. 拖拽,网格布局支持上下左右,列表只支持上下(LEFT、UP、RIGHT、DOWN)。

2. 滑动,只支持前后(START、END)。

最后,通过makeMovementFlags把结果返回回去,makeMovementFlags接收两个参数,dragFlags和swipeFlags,即上面拖拽和滑动组合的标志位。

override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {var dragFlags = 0var swipeFlags = 0when (recyclerView.layoutManager) {is GridLayoutManager -> {// 网格布局dragFlags = ItemTouchHelper.LEFT or ItemTouchHelper.UP or ItemTouchHelper.RIGHT or ItemTouchHelper.DOWNreturn makeMovementFlags(dragFlags, swipeFlags)}is LinearLayoutManager -> {// 线性布局dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWNswipeFlags = ItemTouchHelper.START or ItemTouchHelper.ENDreturn makeMovementFlags(dragFlags, swipeFlags)}else -> {// 其他情况可自行处理return 0}}
}

4.1.2、onMove

拖拽时回调,这里我们主要对起始位置和目标位置的item做一个数据交换,然后刷新视图显示。

override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {// 起始位置val fromPosition = viewHolder.adapterPosition// 结束位置val toPosition = target.adapterPosition// 固定位置if (fromPosition == mAdapter.fixedPosition || toPosition == mAdapter.fixedPosition) {return false}// 根据滑动方向 交换数据if (fromPosition < toPosition) {// 含头不含尾for (index in fromPosition until toPosition) {Collections.swap(mData, index, index + 1)}} else {// 含头不含尾for (index in fromPosition downTo toPosition + 1) {Collections.swap(mData, index, index - 1)}}// 刷新布局mAdapter.notifyItemMoved(fromPosition, toPosition)return true
}

4.1.3、onSwiped

滑动时回调,这个回调方法里主要是做数据和视图的更新操作。

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {if (direction == ItemTouchHelper.START) {Log.i(TAG, "START--->向左滑")} else {Log.i(TAG, "END--->向右滑")}val position = viewHolder.adapterPositionmData.removeAt(position)mAdapter.notifyItemRemoved(position)}

4.2、绑定RecyclerView

上面接口实现部分我们已经简单写好了,逻辑也挺简单,总共不超过100行代码。

接下来就是把这个辅助类绑定到RecyclerView。

RecyclerView显示的实现就是基础的样式,就不展开了,可以查看源码。

val dragCallBack = DragCallBack(mAdapter, list)
val itemTouchHelper = ItemTouchHelper(dragCallBack)
itemTouchHelper.attachToRecyclerView(mBinding.recycleView)

绑定只需要调用attachToRecyclerView就好了。

至此,简单的效果就已经实现了。下面开始优化和进阶的部分。

4.3、设置分割线

RecyclerView网格布局实现等分,我们一般先是自定义ItemDecoration,然后调用addItemDecoration来实现的。

但是我在实现效果的时候遇到一个问题,因为我加了布局切换的功能,在每次切换的时候,针对不同的布局分别设置layoutManager和ItemDecoration,这就导致随着切换次数的增加,item的间隔就越大。

addItemDecoration,顾名思义是添加,通过查看源码发现RecyclerView内部是有一个ArrayList来维护的,所以当我们重复调用addItemDecoration方法时,分割线是以递增的方式在增加的,并且在绘制的时候会从集合中遍历所有的分割线绘制。

部分源码:

@Override
public void draw(Canvas c) {super.draw(c);final int count = mItemDecorations.size();for (int i = 0; i < count; i++) {mItemDecorations.get(i).onDrawOver(c, this, mState);}//...
}

既然知道了问题所在,也大概想到了3种解决办法:

1. 调用addItemDecoration前,先调用removeItemDecoration方法remove掉之前所有的分割线。

2. 调用addItemDecoration(@NonNull ItemDecoration decor, int index),通过index来维护。

3. add时通过一个标示来判断,添加过就不添加了。

好像可行,实际上并不太行...因为始终都有两个分割线实例。

我们再来梳理一下:

  • 两种不同的布局

  • 都有分割线

  • 分割线只需设置一次

我想到另外一个办法,不对RecyclerView做处理了,既然两种布局都有分割线,是不是可以把分割线合二为一了,然后根据LayoutManager去绘制不同的分割线?

理论上是可行的,事实上也确实可以...

自定义分割线:

class GridSpaceItemDecoration(private val spanCount: Int, private val spacing: Int = 20, private var includeEdge: Boolean = false) :RecyclerView.ItemDecoration() {override fun getItemOffsets(outRect: Rect, view: View, recyclerView: RecyclerView, state: RecyclerView.State) {recyclerView.layoutManager?.let {when (recyclerView.layoutManager) {is GridLayoutManager -> {val position = recyclerView.getChildAdapterPosition(view) // 获取item在adapter中的位置val column = position % spanCount // item所在的列if (includeEdge) {outRect.left = spacing - column * spacing / spanCountoutRect.right = (column + 1) * spacing / spanCountif (position < spanCount) {outRect.top = spacing}outRect.bottom = spacing} else {outRect.left = column * spacing / spanCountoutRect.right = spacing - (column + 1) * spacing / spanCountif (position >= spanCount) {outRect.top = spanCount}outRect.bottom = spacing}}is LinearLayoutManager -> {outRect.top = spanCountoutRect.bottom = spacing}}}}}

4.4、选中放大/背景变色

为了提升用户体验,可以在拖拽的时候告诉用户当前拖拽的是哪个item,比如选中的item放大、背景高亮等。

  • 网格布局,选中变大。

  • 列表布局,背景变色。

这里用到ItemTouchHelper.Callback中的两个方法,onSelectedChanged和clearView,我们需要在选中时改变视图显示,结束时再恢复。

4.4.1、onSelectedChanged

拖拽或滑动 发生改变时回调,这时我们可以修改item的视图。

override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {viewHolder?.let {// 因为拿不到recyclerView,无法通过recyclerView.layoutManager来判断是什么布局,所以用item的宽度来判断// itemView.width > 500 用这个来判断是否是线性布局,实际取值自己看情况if (it.itemView.width > 500) {// 线性布局 设置背景颜色val drawable = it.itemView.background as GradientDrawabledrawable.color = ContextCompat.getColorStateList(it.itemView.context, R.color.greenDark)} else {// 网格布局 设置选中放大ViewCompat.animate(it.itemView).setDuration(200).scaleX(1.3F).scaleY(1.3F).start()}}}super.onSelectedChanged(viewHolder, actionState)
}

actionState:

  • ACTION_STATE_IDLE 空闲状态。

  • ACTION_STATE_SWIPE 滑动状态。

  • ACTION_STATE_DRAG 拖拽状态。

4.4.2、clearView

拖拽或滑动 结束时回调,这时我们要把改变后的item视图恢复到初始状态。

override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {// 恢复显示// 这里不能用if判断,因为GridLayoutManager是LinearLayoutManager的子类,改用when,类型推导有区别when (recyclerView.layoutManager) {is GridLayoutManager -> {// 网格布局 设置选中大小ViewCompat.animate(viewHolder.itemView).setDuration(200).scaleX(1F).scaleY(1F).start()}is LinearLayoutManager -> {// 线性布局 设置背景颜色val drawable = viewHolder.itemView.background as GradientDrawabledrawable.color = ContextCompat.getColorStateList(viewHolder.itemView.context, R.color.greenPrimary)}}super.clearView(recyclerView, viewHolder)
}

4.5、固定位置

在实际需求中,交互可能要求我们第一个菜单不可以变更顺序,只能固定,比如效果中的第一个菜单「推荐」固定在首位这种情况。

4.5.1、修改adapter

定义一个固定值,并设置不同的背景色和其他菜单区分开。

class DragAdapter(private val mContext: Context, private val mList: List<String>) : RecyclerView.Adapter<DragAdapter.ViewHolder>() {val fixedPosition = 0 // 固定菜单override fun onBindViewHolder(holder: ViewHolder, position: Int) {holder.mItemTextView.text = mList[position]// 第一个固定菜单val drawable = holder.mItemTextView.background as GradientDrawableif (holder.adapterPosition == 0) {drawable.color = ContextCompat.getColorStateList(mContext, R.color.greenAccent)}else{drawable.color = ContextCompat.getColorStateList(mContext, R.color.greenPrimary)}}//...
}

4.5.1、修改onMove回调

在onMove方法中判断,只要是固定位置就直接返回false。

class DragCallBack(adapter: DragAdapter, data: MutableList<String>) : ItemTouchHelper.Callback() {/*** 拖动时回调*/override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {// 起始位置val fromPosition = viewHolder.adapterPosition// 结束位置val toPosition = target.adapterPosition// 固定位置if (fromPosition == mAdapter.fixedPosition || toPosition == mAdapter.fixedPosition) {return false}// ...return true}
}

虽然第一个菜单无法交换位置了,但是它还是可以拖拽的。

效果实现了吗,好像也实现了,可是又好像哪里不对,就好像填写完表单点击提交时你告诉我格式不正确一样,你不能一开始就告诉我吗?

为了进一步提升用户体验,可以让固定位置不可以拖拽吗?

可以,ItemTouchHelper.Callback中有两个方法:

1. isLongPressDragEnabled 是否可以长按拖拽。

2. isItemViewSwipeEnabled 是否可以滑动。

这俩方法默认都是true,所以即使不能交换位置,但默认也是支持操作的。

4.5.3、重写isLongPressDragEnabled

以拖拽举例,我们需要重写isLongPressDragEnabled方法把它禁掉,然后在非固定位置的时候去手动开启。

override fun isLongPressDragEnabled(): Boolean {//return super.isLongPressDragEnabled()return false
}

禁掉之后什么时候再触发呢?

因为我们现在的交互是长按进入编辑,那就需要在长按事件中再调用startDrag手动开启。

mAdapter.setOnItemClickListener(object : DragAdapter.OnItemClickListener {//...override fun onItemLongClick(holder: DragAdapter.ViewHolder) {if (holder.adapterPosition != mAdapter.fixedPosition) {itemTouchHelper.startDrag(holder)}}
})

ok,这样就完美实现了。

4.6、其他

4.6.1、position

因为有拖拽操作,下标其实是变化的,在做相应的操作时,要取实时位置。

holder.adapterPosition

4.6.2、重置

不管是拖拽还是滑动,其实本质都是对Adapter内已填充的数据进行操作,实时数据通过Adapter获取即可。

如果想要实现重置功能,直接拿最开始的原始数据重新塞给Adapter即可。

源码探索

看源码时,找对一个切入点,往往能达到事半功倍的效果。

这里就从绑定RecyclerView开始吧。

val dragCallBack = DragCallBack(mAdapter, list)
val itemTouchHelper = ItemTouchHelper(dragCallBack)
itemTouchHelper.attachToRecyclerView(mBinding.recycleView)

实例化ItemTouchHelper,然后调用其attachToRecyclerView方法绑定到RecyclerView。

5.1、attachToRecyclerView

public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {if (mRecyclerView == recyclerView) {return; // nothing to do}if (mRecyclerView != null) {destroyCallbacks();}mRecyclerView = recyclerView;if (recyclerView != null) {final Resources resources = recyclerView.getResources();mSwipeEscapeVelocity = resources.getDimension(R.dimen.item_touch_helper_swipe_escape_velocity);mMaxSwipeVelocity = resources.getDimension(R.dimen.item_touch_helper_swipe_escape_max_velocity);setupCallbacks();}
}

这段代码其实有点意思的,解读一下:

1. 第一个if判断,避免重复操作,直接return。

2. 第二个if判断,调用了destroyCallbacks,在destroyCallbacks里面做了一些移除和回收操作,说明只能绑定到一个RecyclerView;同时,注意这里判断的主体是mRecyclerView,不是我们传进来的recyclerView,而且我们传进来的recyclerView是支持Nullable的,所以我们可以传个空值走到destroyCallbacks里来做解绑操作。

3. 第三个if判断,当我们传的recyclerView不为空时,调用setupCallbacks。

5.2、setupCallbacks

private void setupCallbacks() {ViewConfiguration vc = ViewConfiguration.get(mRecyclerView.getContext());mSlop = vc.getScaledTouchSlop();mRecyclerView.addItemDecoration(this);mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);mRecyclerView.addOnChildAttachStateChangeListener(this);startGestureDetection();
}

这个方法里已经大概可以看出内部实现原理了。

两个关键点:

  • addOnItemTouchListener

  • startGestureDetection

通过触摸和手势识别来处理交互显示。

5.3、mOnItemTouchListener

private final OnItemTouchListener mOnItemTouchListener = new OnItemTouchListener() {@Overridepublic boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent event) {mGestureDetector.onTouchEvent(event);if (action == MotionEvent.ACTION_DOWN) {//...if (mSelected == null) {if (animation != null) {//...select(animation.mViewHolder, animation.mActionState);}}} else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {select(null, ACTION_STATE_IDLE);} else if (mActivePointerId != ACTIVE_POINTER_ID_NONE) {//...if (index >= 0) {checkSelectForSwipe(action, event, index);}}return mSelected != null;}@Overridepublic void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent event) {mGestureDetector.onTouchEvent(event);//...if (activePointerIndex >= 0) {checkSelectForSwipe(action, event, activePointerIndex);}switch (action) {case MotionEvent.ACTION_MOVE: {if (activePointerIndex >= 0) {moveIfNecessary(viewHolder);}break;}//...}}@Overridepublic void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {select(null, ACTION_STATE_IDLE);}
};

这段代码删减之后还是有点多,不过没关系,提炼一下,核心通过判断MotionEvent调用了几个方法:

  • select

  • checkSelectForSwipe

  • moveIfNecessary

5.3.1、select

void select(@Nullable ViewHolder selected, int actionState) {if (selected == mSelected && actionState == mActionState) {return;}//...if (mSelected != null) {if (prevSelected.itemView.getParent() != null) {final float targetTranslateX, targetTranslateY;switch (swipeDir) {case LEFT:case RIGHT:case START:case END:targetTranslateY = 0;targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth();break;//...}//...} else {removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);mCallback.clearView(mRecyclerView, prevSelected);}}//...mCallback.onSelectedChanged(mSelected, mActionState);mRecyclerView.invalidate();
}

这里面主要是在拖拽或滑动时对translateX/Y的计算和处理,然后通过mCallback.clearView和mCallback.onSelectedChanged回调给我们,最后调用invalidate()实时刷新。

5.3.2、checkSelectForSwipe

void checkSelectForSwipe(int action, MotionEvent motionEvent, int pointerIndex) {//...if (absDx < mSlop && absDy < mSlop) {return;}if (absDx > absDy) {if (dx < 0 && (swipeFlags & LEFT) == 0) {return;}if (dx > 0 && (swipeFlags & RIGHT) == 0) {return;}} else {if (dy < 0 && (swipeFlags & UP) == 0) {return;}if (dy > 0 && (swipeFlags & DOWN) == 0) {return;}}select(vh, ACTION_STATE_SWIPE);
}

这里是滑动处理的check,最后也是收敛到select()方法统一处理。

5.3.3、moveIfNecessary

void moveIfNecessary(ViewHolder viewHolder) {if (mRecyclerView.isLayoutRequested()) {return;}if (mActionState != ACTION_STATE_DRAG) {return;}//...if (mCallback.onMove(mRecyclerView, viewHolder, target)) {// keep target visiblemCallback.onMoved(mRecyclerView, viewHolder, fromPosition,target, toPosition, x, y);}
}

这里检查拖拽时是否需要交换item,通过mCallback.onMoved回调给我们。

5.4、startGestureDetection

private void startGestureDetection() {mItemTouchHelperGestureListener = new ItemTouchHelperGestureListener();mGestureDetector = new GestureDetectorCompat(mRecyclerView.getContext(),mItemTouchHelperGestureListener);
}

5.4.1、ItemTouchHelperGestureListener

private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {//...@Overridepublic void onLongPress(MotionEvent e) {//...View child = findChildView(e);if (child != null) {ViewHolder vh = mRecyclerView.getChildViewHolder(child);if (vh != null) {//...if (pointerId == mActivePointerId) {//...if (mCallback.isLongPressDragEnabled()) {select(vh, ACTION_STATE_DRAG);}}}}}
}

这里主要是对长按事件的处理,最后也是收敛到select()方法统一处理。

5.5、源码小结

1. 绑定RecyclerView。

2. 注册触摸手势监听。

3. 根据手势,先是内部处理各种校验、位置计算、动画处理、刷新等,然后回调给ItemTouchHelper.Callback。

事儿大概就是这么个事儿,主要工作都是源码帮我们做了,我们只需要在回调里根据结果处理业务逻辑即可。

源码地址

https://github.com/yechaoa/MaterialDesign

相关文章:

Android 实现菜单拖拽排序

效果图简介本文主角是ItemTouchHelper。它是RecyclerView对于item交互处理的一个「辅助类」&#xff0c;主要用于拖拽以及滑动处理。以接口实现的方式&#xff0c;达到配置简单、逻辑解耦、职责分明的效果&#xff0c;并且支持所有的布局方式。功能拆解功能实现4.1、实现接口自…...

通过window.open打开新的页面并修改样式添加内容

const img new Image(); img.src res; //res是图片的路径地址 const newWin window.open(, _blank); newWin.document.write(img.outerHTML); // newWin.document.body.style.background #000; newWin.document.body.style.textAlign center; newWin.document.body.oncl…...

Java中 Synchronized 的用法

《编程思想之多线程与多进程(1)——以操作系统的角度述说线程与进程》一文详细讲述了线程、进程的关系及在操作系统中的表现&#xff0c;这是多线程学习必须了解的基础。本文将接着讲一下Java线程同步中的一个重要的概念synchronized. synchronized是Java中的关键字&#xff0c…...

Rust语言的基本介绍

rust缘起和目标 rust的英文是锈菌&#xff0c;是一种真菌&#xff0c;这种真菌的生命力非常顽强&#xff0c;其 在生命周期内可以产生多达5种孢子类型&#xff0c;这5种生命形态还可以相互转 化。“Rust”也有“铁锈”的意思&#xff0c;暗合“裸金属”之意&#xff0c;代表了R…...

新冠小阳人症状记录

原想挺过春节后再养&#xff0c;发现事与愿违。生理期期间抵抗力下降&#xff0c;所以在生理期第二天就有些症状了。可能是生理期前一天出去采购食物染上&#xff0c;也可能是合租夫妻染上。anyway&#xff0c;记录下自己的症状与相应有效的偏方&#xff1a; 第一天&#xff1a…...

SQL零基础入门学习(十四)

上篇&#xff1a;SQL零基础入门学习&#xff08;十三&#xff09; SQL NULL 值 NULL 值代表遗漏的未知数据。 默认地&#xff0c;表的列可以存放 NULL 值。 如果表中的某个列是可选的&#xff0c;那么我们可以在不向该列添加值的情况下插入新记录或更新已有的记录。这意味着该…...

Excel工作表不能移动或复制?看看是不是这两个原因

Excel工作表不能移动或复制&#xff1f;今天来看看如何解决。 大家都知道&#xff0c;Excel表格分为工作簿和工作表&#xff0c;工作簿就是整个Excel文件&#xff1b;工作簿里面&#xff0c;也就是Excel表可以有多个工作表。 而各个工作表之间是可以相互移动或复制的&#xf…...

利用递归实现括号匹配

案例引入以下则是各个字符串经过括号处理之后的结果&#xff1a;12((21))(12-->12(21)1232((((2121)212(21)-->32(2121)212(21)ABDF((SA)SA)SA(SA)SA(((-->ABDF((SA)SA)SA(SA)SA算法思路&#xff1a;这个问题的解决方法就是将字符按顺序逐一加入到新的string容器store…...

14.线程数量怎么制定?

什么是CPU 密集型任务和耗时 IO 型任务 &#xff1f; CPU 密集型任务 CPU 密集型任务&#xff0c;比如加密、解密、压缩、计算等一系列需要大量耗费 CPU 资源的任务。 耗时 IO 型任务 数据库、文件的读写&#xff0c;网络通信等任务&#xff0c;这种任务的特点是并不会特别消耗…...

C++中STL标准模板库学习记录

文章目录&#xff1a;1.vector1.1 遍历方式1.2 构造函数1.3 容量大小问题1.4 插入和删除1.5 存取值1.6 交换两个vectot的元素1.7 预定义存储空间2.string3. deque4. stack4.1 常用函数5. queue5.1 特点5.2 方法6. list6.1 优点6.2 缺点6.3 构造函数6.4 交换6.5 大小6.6 插入和删…...

《数据库系统概论》学习笔记——第六章 关系数据理论

教材为数据库系统概论第五版&#xff08;王珊&#xff09; 这一章重点在于各种范式的概念和将低级范式转为高级范式。一定要看多值依赖和4NF&#xff08;因为这个概念很绕又烦&#xff0c;但是期中期末都考了&#xff09;。最后计算题就是一定要会&#xff1a;算闭包&#xff0…...

Odoo | Webserivce | 5分钟学会【JSONRPC】接口开发

文章目录Odoo - JsonRPC1. Odoo内方法结构&#xff08;接收端&#xff09;2. POST接口请求结构&#xff08;发送端&#xff09;3. 实例测试Odoo - JsonRPC 1. Odoo内方法结构&#xff08;接收端&#xff09; # -*- coding: utf-8 -*- import odoo import logging import trac…...

搜广推 NeuralCF - 改进协同过滤+矩阵分解的思想

😄 NeuralCF:2017新加坡国立大学提出。【后文简称NCF】 😄 PNN:2016年上海交通大学提出。 文章目录 NeuralCF动机原理general NCFNCF终极版(GMF+MLP的结合)缺点优点ReferenceNeuralCF 动机 前面学了MF,可知MF在用户-物品评分矩阵的基础上做矩阵分解(用户矩阵Q和物品…...

dbever连接kerberos认证的hive

文章目录一、本地安装kerberos客户端二、本地kerberos客户端登录三、dbever连接hive一、本地安装kerberos客户端 下载地址&#xff1a;https://web.mit.edu/kerberos/dist/index.html 安装&#xff1a;下一步或者自定义安装即可 安装后会自动生成配置文件&#xff1a;C:\Pro…...

pom依赖产生的各种问题

文章目录问题一(org.apache.ibatis.session.Configuration)解决方法问题二(ERROR StatusLogger No log4j2)解决方法问题三(com.google.common.util.concurrent)解决方法问题四(start bean documentationPluginsBootstrapper)解决方法问题五(Unable to infer base url. )解决办法…...

RPC编程:RPC框架设计目标

一&#xff1a;前导知识 Http是超文本传输协议&#xff0c;跨平台性非常好。Http可以传输文本&#xff0c;更多的时候传输的是文本&#xff0c;我们也是可以传输二进制的&#xff0c;我们基于Http进行下载的时候&#xff0c;就是走的Http协议。 Tcp协议&#xff0c;处理的时候…...

RBAC 权限模型介绍

RBAC 权限&#xff1a; 一、关系&#xff1a; 这基于角色的访问控制的结构就叫RBAC结构。 二、RBAC 重要对象&#xff1a; 用户&#xff08;Employee&#xff09;&#xff1a;角色施加的主体&#xff1b;用户通过拥有某个或多个角色以得到对应的权限。角色&#xff08;Role&…...

西电面向对象程序设计核心考点汇总(期末真题)

文章目录前言一、往年真题与答案1.1 改错题1.2 读程题1.3 面向对象程序设计二、易错知识点2.1 构造函数2.2 静态成员变量和静态成员函数2.3 权限2.4 继承2.5 多态总结前言 主要针对西安电子科技大学《面向对象程序设计》的核心考点进行汇总&#xff0c;包含总共8章的核心简答。…...

判断一个用字符串表达的数字是否可以被整除

一.问题引出 当一个数字很大的时候,我们常用字符串进行表达,(超过了int和long等数据类型可以存储的最大范围),但是这个时候我们该如何判断他是否可以被另一个数整除呢? 这个时候我们不妨这样来考虑问题,每次将前边求模之后的数保存下来,然后乘以10和这一位的数字进行相加的操…...

这是一款值得开发人员认真研究的软件,数据库优化,应用服务器安全优化...

1.查询数据库死锁相关信息2.查看数据库的链接情况3.当前实例上的所有用户4.创建数据库独立密码5.查看数据库使用的端口号6.当前数据库设置的最大连接数7.当前数据库最大的理论可连接数8.当前数据库实例的连接数9.当前数据库连接数10.当前数据库连接超时设置11.当前sqlserver 超…...

栈与队列小结

一、理论基础1.队列是先进先出&#xff0c;栈是先进后出2.栈和队列是STL&#xff08;C标准库&#xff09;里面的两个数据结构。栈提供push和pop等等接口&#xff0c;所有元素必须符合先进后出规则&#xff0c;所以栈不提供走访功能&#xff0c;也不提供迭代器。3.栈是以底层容器…...

SpringBoot整合(五)HikariCP、Druid数据库连接池—多数据源配置

在项目中&#xff0c;数据库连接池基本是必不可少的组件。在目前数据库连接池的选型中&#xff0c;主要是 Druid &#xff0c;为监控而生的数据库连接池。HikariCP &#xff0c;号称性能最好的数据库连接池。 在Spring Boot 2.X 版本&#xff0c;默认采用 HikariCP 连接池。而…...

ShardingSphere水平、垂直分库、分表和公共表

目录一、ShardingSphere简介二、ShardingSphere-分库分表1、垂直拆分&#xff08;1&#xff09;垂直分库&#xff08;2&#xff09;垂直分表2、水平拆分&#xff08;1&#xff09;水平分库&#xff08;2&#xff09;水平分表三、水平分库操作1、创建数据库和表2、配置分片的规则…...

《分布式技术原理与算法解析》学习笔记Day24

分布式缓存 在计算机领域&#xff0c;缓存是一个非常重要的、用来提升性能的技术。 什么是分布式缓存&#xff1f; 缓存技术是指用一个更快的存储设备存储一些经常用到的数据&#xff0c;供用户快速访问。 分布式缓存是指在分布式环境或者系统下&#xff0c;把一些热门数据…...

强化学习RL 02: Value-based Reinforcement Learning

DQN和TD更新算法。 目录 Review 1. Deep Q-Network(DQN) 1.1 Approximate the Q*(s,a) Function 1.2 Apply DQN to Play Game 1.3 Temporal Difference(TD) Learning 1.4 TD Learning for DQN 1.4.1 TD使用条件 condition 1.4.2 Train DQN using TD learning 1.5 summ…...

08_MySQL聚合函数

1. 聚合函数介绍什么是聚合函数聚合函数作用于一组数据&#xff0c;并对一组数据返回一个值。聚合函数类型AVG()SUM()MAX()MIN()COUNT()注意&#xff1a;聚合函数不能嵌套调用。比如不能出现类似“AVG(SUM(字段名称))”形式的调用。1.1 AVG和SUM函数可以对数值型数据使用AVG 和…...

「TCG 规范解读」词汇表

可信计算组织&#xff08;Ttrusted Computing Group,TCG&#xff09;是一个非盈利的工业标准组织&#xff0c;它的宗旨是加强在相异计算机平台上的计算环境的安全性。TCG于2003年春成立&#xff0c;并采纳了由可信计算平台联盟&#xff08;the Trusted Computing Platform Alli…...

第三阶段-03MyBatis 中使用XML映射文件详解

MyBatis 中使用XML映射文件 什么是XML映射 使用注解的映射SQL的问题&#xff1a; 长SQL需要折行&#xff0c;不方便维护动态SQL查询拼接复杂源代码中的SQL&#xff0c;不方便与DBA协作 MyBatis建议使用XML文件映射SQL才能最大化发挥MySQL的功能 统一管理SQL&#xff0c; 方…...

从0开始学python -41

Python3 命名空间和作用域 命名空间 先看看官方文档的一段话&#xff1a; A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries。 命名空间(Namespace)是从名称到对象的映射&#xff0c;大部分的命名空间都是…...

如何将Google浏览器安装到D盘(内含教学视频)

如何将Google浏览器安装到D盘&#xff08;内含教学视频&#xff09; 教学视频下载链接地址&#xff1a;https://download.csdn.net/download/weixin_46411355/87503968 目录如何将Google浏览器安装到D盘&#xff08;内含教学视频&#xff09;教学视频下载链接地址&#xff1a;…...

个人网站怎么做有创意/磁力岛引擎

VLOOKUP函数的作用使用VLOOKUP函数可以方便地进行数据查询&#xff0c;建立数据查询表。VLOOPUP函数实现的就是人们日常所使用查询数据的方式。比如&#xff0c;要找到某个学生的平均成绩 &#xff0c;一般会在姓名列中从上到下找到该学生&#xff0c;然后从左至右找到对应的平…...

网上做图赚钱的网站/品牌策划公司排行榜

SUID: 只对二进制程序有效执行者对于程序需要有x权限在程序运行过程中&#xff0c;执行者拥有程序拥有者的权限例如&#xff1a;普通用户执行passwd命令。首先查看passwd命令的绝对路径&#xff1a;查看passwd命令权限&#xff1a;passwd的拥有者是root&#xff0c;且拥有者权限…...

徐州做公司网站多少钱/百度官方优化指南

目录错误场景解决办法错误 ./components/HelloWorld.vue in ./node_modules/cache-loader/dist/cjs.js??ref–0-0!./node_modules/vue-loader-v16/dist??ref–0-1!./src/App.vue?vue&typescript&langjs 场景 因为安装element-plus后把app.vue里的代码替换了&#…...

wordpress怎么安装asp主题/上海培训机构排名榜

sView.getHolder().setFixedSize(fitYSize.x, fitYSize.y);...

关于织金县网站建设的论文/万州网站建设

日志信息 故障现象描述 与硬盘关系 scsi1: ERROR on channel 0, id 7, lun 0, CDB: Read (10) 00 73 fc 62 bf 00 00 80 00 Info fld0x73fc6326, Current sdi: sense key Medium Error Additional sense: Unrecovered read error SMART规范定义“Medium Error”错误是一种不…...

wordpress robots写法/网络舆情报告

本篇介绍一些关于C语言的时间计算的函数如时间戳、当前时间、当前本地时间、时区时间、时间差以及时间的字符串字符串格式化等。 /* * Author:W&#xff1b; * 时间函数 *///引入头文件&#xff1a;头文件包含了程序中必需的或有用的信息【单行注释】 #include <iostream&g…...