RecyclerVIew->加速再减速的RecyclerVIew平滑对齐工具类SnapHelper
XML
文件
ItemView
的XML
文件R.layout.shape_item_view
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="100dp"android:layout_height="100dp"android:background="@drawable/shape_item_view"><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textSize="16sp" />
</FrameLayout>
- 滑动到对齐
ItemView
的XML
文件R.drawable.shape_item_view_selected
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"><solid android:color="#FFFFFF" /><corners android:radius="8dp" /><stroke android:color="#FFFF00" android:width="5dp" />
</shape>
- 未滑动到对齐
ItemView
的XML
文件R.drawable.shape_item_view
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"><solid android:color="#FFFFFF" /><corners android:radius="8dp" /><stroke android:color="#000000" android:width="5dp" />
</shape>
Activity
的XML文件R.layout.activity_main
<?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:gravity="center"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="100dp"android:layout_marginLeft="@dimen/edit_crop_frame_padding"android:layout_marginRight="@dimen/edit_crop_frame_padding"android:orientation="horizontal" />
</LinearLayout>
RecyclerView
代码
Adapter
代码
class MyAdapter(private val numbers: List<Int>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {// 记录选中位置private var selectedPosition = RecyclerView.NO_POSITIONoverride fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.item_view, parent, false)return MyViewHolder(view)}override fun onBindViewHolder(holder: MyViewHolder, position: Int) {holder.textView.text = numbers[position].toString()if (selectedPosition == position) {holder.itemView.setBackgroundResource(R.drawable.shape_item_view_selected)} else {holder.itemView.setBackgroundResource(R.drawable.shape_item_view)}}override fun getItemCount() = numbers.size// 给外部工具类SnapHelper实现类使用,滚动到对齐位置,修改ItemView的轮廓fun setSelectedPosition(position: Int) {val oldPosition = selectedPositionselectedPosition = positionnotifyItemChanged(oldPosition)notifyItemChanged(selectedPosition)}class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {val textView: TextView = itemView.findViewById(R.id.textView)}
}
ItemDecoration
代码
class SpaceItemDecoration(private val spaceSize: Int, private val itemSize : Int) : RecyclerView.ItemDecoration() {private val paint = Paint().apply {color = Color.REDstyle = Paint.Style.FILL}var spacePadding = 0override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {super.getItemOffsets(outRect, view, parent, state)// 正常Item的DecorationoutRect.left = spaceSizeoutRect.right = spaceSize// 第一个和最后一个Item的DecorationspacePadding = (parent.measuredWidth / 2 - itemSize / 2)val size = parent.adapter?.itemCount ?: 0val position = parent.getChildAdapterPosition(view)if (position == 0) {outRect.left = spacePadding} else if (position == size - 1) {outRect.right = spacePadding}}override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {super.onDraw(c, parent, state)val childCount = parent.childCountfor (i in 0 until childCount) {val child = parent.getChildAt(i)val position = parent.getChildAdapterPosition(child)val params = child.layoutParams as RecyclerView.LayoutParamsvar left : Intvar right : Intvar top : Intvar bottom : Intif (position == 0) {left = child.left - params.leftMargin - spacePaddingright = child.right + params.rightMargin + spaceSizetop = child.top - params.topMarginbottom = child.bottom + params.bottomMargin} else if (position == parent.adapter?.itemCount!! - 1) {left = child.left - params.leftMargin - spaceSizeright = child.right + params.rightMargin + spacePaddingtop = child.top - params.topMarginbottom = child.bottom + params.bottomMargin} else {// 绘制其他 Item 的装饰left = child.left - params.leftMargin - spaceSizeright = child.right + params.rightMargin + spaceSizetop = child.top - params.topMarginbottom = child.bottom + params.bottomMargin}c.drawRect(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat(), paint)}}
}
RecyclerView
对齐工具类SnapHelper
实现类代码
- ①
attachToRecyclerView()
方法:将RecyclerView
对齐操作交给SnapHelper
实现类
override fun attachToRecyclerView(recyclerView: RecyclerView?) {Log.i(TAG, "attachToRecyclerView")mRecyclerView = recyclerViewsuper.attachToRecyclerView(recyclerView)
}
- ②
createScroller()
方法:创建惯性滑动的Scroller
onTargetFound()
回调方法:找到对齐位置之后回调,计算目标位置到对齐位置需要滚动的距离和时间calculateSpeedPerPixel()
回调方法:滚动一英寸所需时间除以屏幕密度,得到滚动一像素所需的时间
override fun createScroller(layoutManager: RecyclerView.LayoutManager?): RecyclerView.SmoothScroller? {if (layoutManager !is RecyclerView.SmoothScroller.ScrollVectorProvider) {return null}Log.i(TAG, "createScroller")return object : LinearSmoothScroller(mRecyclerView?.context) {override fun onTargetFound(targetView: View, state: RecyclerView.State, action: Action) {if (mRecyclerView == null) returnLog.i(TAG, "onTargetFound")// 计算当前位置到目标位置的距离val snapDistances : IntArray? = calculateDistanceToFinalSnap(mRecyclerView?.layoutManager!!, targetView)val dx = snapDistances?.get(0) ?: 0val dy = snapDistances?.get(1) ?: 0// 这里增加滑动时间,可以使得滑动速度变慢val time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy))) * 10if (time > 0) {// 这里传入的是LinearOutSlowInInterpolator(), 也就是先加速再减速的插值器// 相比LinearSnapHelper中的DecelerateInterpolator, 这个插值器更符合自然滑动的效果action.update(dx, dy, time, mInterpolator)}}override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics?): Float {Log.i(TAG, "calculateSpeedPerPixel")// 计算滑动一个像素的时间return MILLISECONDS_PER_INCH / displayMetrics?.densityDpi!!}}
}
- ③
findTargetSnapPosition()
方法:找到需要对齐的ItemView
的位置RecyclerView.SmoothScroller.ScrollVectorProvider.computeScrollVectorForPosition()
:计算从0
的位置滚动到ItemCount-1
的位置需要滚动的方向,vectorForEnd.x
表示水平方向(>0
向右,<0
向左),vectorForEnd.y
表示竖直方向(>0
向下,<0
向上),正负值由结束位置和开始位置的差值得出
override fun findTargetSnapPosition(layoutManager: RecyclerView.LayoutManager?, velocityX: Int, velocityY: Int): Int {// 判断layoutManager是否实现了RecyclerView.SmoothScroller.ScrollVectorProvider这个接口if (layoutManager !is RecyclerView.SmoothScroller.ScrollVectorProvider) return RecyclerView.NO_POSITION// 判断ItemView个数是否小于等于0val itemCount = layoutManager.itemCountif (itemCount == 0) return RecyclerView.NO_POSITION// 找到需要对齐的ItemViewval currentView = findSnapView(layoutManager) ?: return RecyclerView.NO_POSITION// 获取需要对齐ItemView的位置val currentPosition = layoutManager.getPosition(currentView)if (currentPosition == RecyclerView.NO_POSITION) return RecyclerView.NO_POSITION// 判断layoutManager的布局方向val vectorProvider = layoutManager as RecyclerView.SmoothScroller.ScrollVectorProviderval vectorForEnd = vectorProvider.computeScrollVectorForPosition(itemCount - 1) ?: return RecyclerView.NO_POSITIONLog.i(TAG, "findTargetSnapPosition")// 计算水平, 垂直方向最多能惯性滑动的ItemView个数,在当前ItemView的位置上,进行加减Position操作var maxHorizontalItemViewCount: Intvar maxVerticalItemViewCount: Intif (layoutManager.canScrollHorizontally()) {maxHorizontalItemViewCount = estimateNextPositionDiffForFling(layoutManager, getHorizontalHelper(layoutManager), velocityX, 0)var sign = Math.signum(velocityX.toFloat())if (sign == 0f) sign = 1f// 限制最多能惯性滑动的ItemView个数,限制滑动的 ItemView 的个数在 1 到 2 之间maxHorizontalItemViewCount = (sign * Math.min(Math.max(Math.abs(maxHorizontalItemViewCount), 0), 2)).toInt()if (vectorForEnd.x < 0) {maxHorizontalItemViewCount = - maxHorizontalItemViewCount}}else{maxHorizontalItemViewCount = 0}if (layoutManager.canScrollVertically()) {maxVerticalItemViewCount = estimateNextPositionDiffForFling(layoutManager, getVerticalHelper(layoutManager), 0, velocityY)var sign = Math.signum(velocityY.toFloat())if (sign == 0f) sign = 1f// 限制最多能惯性滑动的ItemView个数,限制滑动的 ItemView 的个数在 1 到 2 之间maxVerticalItemViewCount = (sign * Math.min(Math.max(Math.abs(maxVerticalItemViewCount), 0), 2)).toInt()if (vectorForEnd.y < 0) {maxVerticalItemViewCount = - maxVerticalItemViewCount}}else{maxVerticalItemViewCount = 0}// 根据滑动的方向,计算出最终的 ItemView 个数val finalItemCount = if(layoutManager.canScrollHorizontally()){maxHorizontalItemViewCount}else{maxVerticalItemViewCount}if (finalItemCount == 0) return RecyclerView.NO_POSITION// 确定最终的对齐位置,并做边界处理var targetPosition = currentPosition + finalItemCountif (targetPosition < 0) targetPosition = 0if (targetPosition >= layoutManager.itemCount) targetPosition = layoutManager.itemCount - 1return targetPosition
}
- ④
findSnapView()
方法:调用findCenterView()
找到最接近中心点的ItemView
findCenterView()
方法:拿到每个ItemView
的left
加上自身宽度的一半和RecyclerView
的中心点进行比较,找到最接近中心点的ItemView
override fun findSnapView(layoutManager: RecyclerView.LayoutManager?): View? {Log.i(TAG, "findSnapView")if (layoutManager!!.canScrollVertically()) {return findCenterView(layoutManager, getVerticalHelper(layoutManager))} else if (layoutManager.canScrollHorizontally()) {return findCenterView(layoutManager, getHorizontalHelper(layoutManager))}return null
}
private fun findCenterView(layoutManager: RecyclerView.LayoutManager, helper: OrientationHelper): View? {Log.i(TAG, "findCenterView")val childCount = layoutManager.childCountif (childCount == 0) return null// 最接近RecyclerView中心的ItemViewvar closestItemView: View? = null// RecyclerView的中心点val center = helper.startAfterPadding + helper.totalSpace / 2var absClosest = Int.MAX_VALUEfor (i in 0 until childCount) {val child = layoutManager.getChildAt(i)// ItemView的中心点, 这里用Left是因为有ItemDecoration的存在,val childCenter = child?.left!! + helper.getDecoratedMeasurement(child) / 2val childDistance = Math.abs(childCenter - center)// 找到最靠近RecyclerView中心的ItemViewif (childDistance < absClosest) {absClosest = childDistanceclosestItemView = child}}return closestItemView
}
- ⑤
estimateNextPositionDiffForFling()
方法:计算当前位置到目标对齐位置还差了几个ItemView
的个数calculateScrollDistance()
:计算RecyclerView
的滚动距离computeDistancePerChild()
:计算每个ItemView
的滚动距离
private fun estimateNextPositionDiffForFling(layoutManager: RecyclerView.LayoutManager, helper: OrientationHelper, velocityX: Int, velocityY: Int): Int {Log.i(TAG, "estimateNextPositionDiffForFling")val distances = calculateScrollDistance(velocityX, velocityY)val distancePerChild = computeDistancePerChild(layoutManager, helper)if (distancePerChild <= 0) return 0val distance = if (Math.abs(distances[0]) > Math.abs(distances[1])) distances[0] else distances[1]return Math.round(distance / distancePerChild)
}
override fun calculateScrollDistance(velocityX: Int, velocityY: Int): IntArray {Log.i(TAG, "calculateScrollDistance")return super.calculateScrollDistance(velocityX, velocityY)
}
private fun computeDistancePerChild(layoutManager: RecyclerView.LayoutManager, helper: OrientationHelper): Float {Log.i(TAG, "computeDistancePerChild")var minPositionView : View ?= nullvar maxPositionView : View ?= nullvar minPosition = Integer.MAX_VALUEvar maxPosition = Integer.MIN_VALUEval itemViewCount = layoutManager.childCountif (itemViewCount == 0) return INVALID_DISTANCE// 遍历所有ItemView, 找到最小位置和最大位置的ItemView,记录Positionfor (i in 0 until itemViewCount) {val child = layoutManager.getChildAt(i) ?: continueval position = layoutManager.getPosition(child)if (position == RecyclerView.NO_POSITION) continueif (position < minPosition) {minPosition = positionminPositionView = child}if (position > maxPosition) {maxPosition = positionmaxPositionView = child}}if (minPositionView == null || maxPositionView == null) return INVALID_DISTANCE// 计算最小位置和最大位置的ItemView离RecyclerView左边的距离val start = Math.min(helper.getDecoratedStart(minPositionView), helper.getDecoratedStart(maxPositionView))// 计算最小位置和最大位置的ItemView离RecyclerViewj右边的距离val end = Math.max(helper.getDecoratedEnd(minPositionView), helper.getDecoratedEnd(maxPositionView))// 计算最小位置和最大位置的ItemView的宽度val distance = end - startif (distance <= 0) return INVALID_DISTANCEreturn 1f * distance / (maxPosition - minPosition + 1)
}
- ⑥
onTargetFound()
方法:找对对齐ItemView
位置后回调calculateDistanceToFinalSnap()
:计算最终需要滚动到对齐ItemView
位置的距离calculateTimeForDeceleration()
:计算最终需要滚动到对齐ItemView
位置所花时间
override fun onTargetFound(targetView: View, state: RecyclerView.State, action: Action) {if (mRecyclerView == null) returnLog.i(TAG, "onTargetFound")// 计算当前位置到目标位置的距离val snapDistances : IntArray? = calculateDistanceToFinalSnap(mRecyclerView?.layoutManager!!, targetView)val dx = snapDistances?.get(0) ?: 0val dy = snapDistances?.get(1) ?: 0// 这里增加滑动时间,可以使得滑动速度变慢val time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy))) * 10if (time > 0) {// 这里传入的是LinearOutSlowInInterpolator(), 也就是先加速再减速的插值器// 相比LinearSnapHelper中的DecelerateInterpolator, 这个插值器更符合自然滑动的效果action.update(dx, dy, time, mInterpolator)}
}
override fun calculateDistanceToFinalSnap(layoutManager: RecyclerView.LayoutManager, targetView: View): IntArray? {Log.i(TAG, "calculateDistanceToFinalSnap")// 计算当前位置到目标位置的距离val out = IntArray(2)if (layoutManager.canScrollHorizontally()) {out[0] = distanceToCenter(targetView, getHorizontalHelper(layoutManager)!!)} else {out[0] = 0}if (layoutManager.canScrollVertically()) {out[1] = distanceToCenter(targetView, getVerticalHelper(layoutManager)!!)} else {out[1] = 0}return out
}
- ⑦
distanceToCenter()
方法:计算目标对齐ItemView
距离RecyclerView
中心点的距离
private fun distanceToCenter(targetView: View, helper: OrientationHelper): Int {Log.i(TAG, "distanceToCenter")// 计算目标ItemView的中心点(ItemView包含ItemDecoration的部分 + ItemView的宽度/高度的一半)的val childCenter = helper.getDecoratedStart(targetView) + helper.getDecoratedMeasurement(targetView) / 2// 计算RecyclerView的中心点(RecyclerView减去Padding的部分 + RecyclerView的宽度/高度的一半)val containerCenter = helper.startAfterPadding + helper.totalSpace / 2return childCenter - containerCenter
}
- 完整加减速的对齐工具类SnapHelper的代码
open class MySmoothSnapHelper : SnapHelper() {private val INVALID_DISTANCE = 1f // 无法计算有效对齐距离,返回这个值private val MILLISECONDS_PER_INCH = 25f // 滑动速度,每英寸25毫秒// 通过LayoutManager创建方向工具类,其中包含了RecyclerView的布局参数,包括padding,margin等private var mVerticalHelper : OrientationHelper ?= nullprivate var mHorizontalHelper : OrientationHelper ?= nullprivate var mRecyclerView : RecyclerView ?= null// 加速->减速插值器private val mInterpolator = LinearOutSlowInInterpolator()// 将RecyclerView交给SnapHelper, 计算惯性滑动后需要对齐的位置override fun attachToRecyclerView(recyclerView: RecyclerView?) {Log.i(TAG, "attachToRecyclerView")mRecyclerView = recyclerViewsuper.attachToRecyclerView(recyclerView)}// 创建惯性滑动的Scrolleroverride fun createScroller(layoutManager: RecyclerView.LayoutManager?): RecyclerView.SmoothScroller? {Log.i(TAG, "createScroller")if (layoutManager !is RecyclerView.SmoothScroller.ScrollVectorProvider) {return null}return object : LinearSmoothScroller(mRecyclerView?.context) {override fun onTargetFound(targetView: View, state: RecyclerView.State, action: Action) {if (mRecyclerView == null) returnLog.i(TAG, "onTargetFound")// 计算当前位置到目标位置的距离val snapDistances : IntArray? = calculateDistanceToFinalSnap(mRecyclerView?.layoutManager!!, targetView)val dx = snapDistances?.get(0) ?: 0val dy = snapDistances?.get(1) ?: 0// 这里增加滑动时间,可以使得滑动速度变慢val time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy))) * 10if (time > 0) {// 这里传入的是LinearOutSlowInInterpolator(), 也就是先加速再减速的插值器// 相比LinearSnapHelper中的DecelerateInterpolator, 这个插值器更符合自然滑动的效果action.update(dx, dy, time, mInterpolator)}}override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics?): Float {Log.i(TAG, "calculateSpeedPerPixel")// 计算滑动一个像素的时间return MILLISECONDS_PER_INCH / displayMetrics?.densityDpi!!}}}override fun calculateDistanceToFinalSnap(layoutManager: RecyclerView.LayoutManager, targetView: View): IntArray? {Log.i(TAG, "calculateDistanceToFinalSnap")// 计算当前位置到目标位置的距离val out = IntArray(2)if (layoutManager.canScrollHorizontally()) {out[0] = distanceToCenter(targetView, getHorizontalHelper(layoutManager)!!)} else {out[0] = 0}if (layoutManager.canScrollVertically()) {out[1] = distanceToCenter(targetView, getVerticalHelper(layoutManager)!!)} else {out[1] = 0}return out}private fun distanceToCenter(targetView: View, helper: OrientationHelper): Int {Log.i(TAG, "distanceToCenter")// 计算目标ItemView的中心点(ItemView包含ItemDecoration的部分 + ItemView的宽度/高度的一半)的val childCenter = helper.getDecoratedStart(targetView) + helper.getDecoratedMeasurement(targetView) / 2// 计算RecyclerView的中心点(RecyclerView减去Padding的部分 + RecyclerView的宽度/高度的一半)val containerCenter = helper.startAfterPadding + helper.totalSpace / 2return childCenter - containerCenter}override fun findSnapView(layoutManager: RecyclerView.LayoutManager?): View? {Log.i(TAG, "findSnapView")if (layoutManager!!.canScrollVertically()) {return findCenterView(layoutManager, getVerticalHelper(layoutManager))} else if (layoutManager.canScrollHorizontally()) {return findCenterView(layoutManager, getHorizontalHelper(layoutManager))}return null}private fun findCenterView(layoutManager: RecyclerView.LayoutManager, helper: OrientationHelper): View? {Log.i(TAG, "findCenterView")val childCount = layoutManager.childCountif (childCount == 0) return null// 最接近RecyclerView中心的ItemViewvar closestItemView: View? = null// RecyclerView的中心点val center = helper.startAfterPadding + helper.totalSpace / 2var absClosest = Int.MAX_VALUEfor (i in 0 until childCount) {val child = layoutManager.getChildAt(i)// ItemView的中心点, 这里用Left是因为有ItemDecoration的存在,val childCenter = child?.left!! + helper.getDecoratedMeasurement(child) / 2val childDistance = Math.abs(childCenter - center)// 找到最靠近RecyclerView中心的ItemViewif (childDistance < absClosest) {absClosest = childDistanceclosestItemView = child}}return closestItemView}override fun findTargetSnapPosition(layoutManager: RecyclerView.LayoutManager?, velocityX: Int, velocityY: Int): Int {Log.i(TAG, "findTargetSnapPosition")// 判断layoutManager是否实现了RecyclerView.SmoothScroller.ScrollVectorProvider这个接口if (layoutManager !is RecyclerView.SmoothScroller.ScrollVectorProvider) return RecyclerView.NO_POSITION// 判断ItemView个数是否小于等于0val itemCount = layoutManager.itemCountif (itemCount == 0) return RecyclerView.NO_POSITION// 找到需要对齐的ItemViewval currentView = findSnapView(layoutManager) ?: return RecyclerView.NO_POSITION// 获取需要对齐ItemView的位置val currentPosition = layoutManager.getPosition(currentView)if (currentPosition == RecyclerView.NO_POSITION) return RecyclerView.NO_POSITION// 判断layoutManager的布局方向val vectorProvider = layoutManager as RecyclerView.SmoothScroller.ScrollVectorProviderval vectorForEnd = vectorProvider.computeScrollVectorForPosition(itemCount - 1) ?: return RecyclerView.NO_POSITION// 计算水平, 垂直方向最多能惯性滑动的ItemView个数,在当前ItemView的位置上,进行加减Position操作var maxHorizontalItemViewCount: Intvar maxVerticalItemViewCount: Intif (layoutManager.canScrollHorizontally()) {maxHorizontalItemViewCount = estimateNextPositionDiffForFling(layoutManager, getHorizontalHelper(layoutManager), velocityX, 0)var sign = Math.signum(velocityX.toFloat())if (sign == 0f) sign = 1f// 限制最多能惯性滑动的ItemView个数,限制滑动的 ItemView 的个数在 1 到 2 之间maxHorizontalItemViewCount = (sign * Math.min(Math.max(Math.abs(maxHorizontalItemViewCount), 0), 2)).toInt()if (vectorForEnd.x < 0) {maxHorizontalItemViewCount = - maxHorizontalItemViewCount}}else{maxHorizontalItemViewCount = 0}if (layoutManager.canScrollVertically()) {maxVerticalItemViewCount = estimateNextPositionDiffForFling(layoutManager, getVerticalHelper(layoutManager), 0, velocityY)var sign = Math.signum(velocityY.toFloat())if (sign == 0f) sign = 1f// 限制最多能惯性滑动的ItemView个数,限制滑动的 ItemView 的个数在 1 到 2 之间maxVerticalItemViewCount = (sign * Math.min(Math.max(Math.abs(maxVerticalItemViewCount), 0), 2)).toInt()if (vectorForEnd.y < 0) {maxVerticalItemViewCount = - maxVerticalItemViewCount}}else{maxVerticalItemViewCount = 0}// 根据滑动的方向,计算出最终的 ItemView 个数val finalItemCount = if(layoutManager.canScrollHorizontally()){maxHorizontalItemViewCount}else{maxVerticalItemViewCount}if (finalItemCount == 0) return RecyclerView.NO_POSITION// 确定最终的对齐位置,并做边界处理var targetPosition = currentPosition + finalItemCountif (targetPosition < 0) targetPosition = 0if (targetPosition >= layoutManager.itemCount) targetPosition = layoutManager.itemCount - 1return targetPosition}override fun calculateScrollDistance(velocityX: Int, velocityY: Int): IntArray {Log.i(TAG, "calculateScrollDistance")return super.calculateScrollDistance(velocityX, velocityY)}private fun estimateNextPositionDiffForFling(layoutManager: RecyclerView.LayoutManager, helper: OrientationHelper, velocityX: Int, velocityY: Int): Int {Log.i(TAG, "estimateNextPositionDiffForFling")val distances = calculateScrollDistance(velocityX, velocityY)val distancePerChild = computeDistancePerChild(layoutManager, helper)if (distancePerChild <= 0) return 0val distance = if (Math.abs(distances[0]) > Math.abs(distances[1])) distances[0] else distances[1]return Math.round(distance / distancePerChild)}private fun computeDistancePerChild(layoutManager: RecyclerView.LayoutManager, helper: OrientationHelper): Float {Log.i(TAG, "computeDistancePerChild")var minPositionView : View ?= nullvar maxPositionView : View ?= nullvar minPosition = Integer.MAX_VALUEvar maxPosition = Integer.MIN_VALUEval itemViewCount = layoutManager.childCountif (itemViewCount == 0) return INVALID_DISTANCE// 遍历所有ItemView, 找到最小位置和最大位置的ItemView,记录Positionfor (i in 0 until itemViewCount) {val child = layoutManager.getChildAt(i) ?: continueval position = layoutManager.getPosition(child)if (position == RecyclerView.NO_POSITION) continueif (position < minPosition) {minPosition = positionminPositionView = child}if (position > maxPosition) {maxPosition = positionmaxPositionView = child}}if (minPositionView == null || maxPositionView == null) return INVALID_DISTANCE// 计算最小位置和最大位置的ItemView离RecyclerView左边的距离val start = Math.min(helper.getDecoratedStart(minPositionView), helper.getDecoratedStart(maxPositionView))// 计算最小位置和最大位置的ItemView离RecyclerViewj右边的距离val end = Math.max(helper.getDecoratedEnd(minPositionView), helper.getDecoratedEnd(maxPositionView))// 计算最小位置和最大位置的ItemView的宽度val distance = end - startif (distance <= 0) return INVALID_DISTANCEreturn 1f * distance / (maxPosition - minPosition + 1)}private fun getVerticalHelper(layoutManager: RecyclerView.LayoutManager): OrientationHelper {Log.i(TAG, "getVerticalHelper")if (mVerticalHelper == null || mVerticalHelper?.layoutManager != layoutManager) {mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager)}return mVerticalHelper!!}private fun getHorizontalHelper(layoutManager: RecyclerView.LayoutManager): OrientationHelper {Log.i(TAG, "getHorizontalHelper")if (mHorizontalHelper == null || mHorizontalHelper!!.layoutManager != layoutManager) {mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager)}return mHorizontalHelper!!}
}
Activity
代码
- 第一次
findSnapView
:正常滑动停止后触发,需要找到对齐的View - 第二次
findSnapView
:惯性滑动停止后触发,需要找到对齐的View
const val TAG = "Yang"
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val numberList = List(10){it}val layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)val mRv = findViewById<RecyclerView>(R.id.recyclerView)val mAdapter = MyAdapter(numberList)// 添加 ItemDecorationmRv.addItemDecoration(SpaceItemDecoration(dpToPx(this, 25f), dpToPx(this, 100f)))// 添加 LinearSnapHelperval linearSnapHelper = object : MySmoothSnapHelper() {override fun findSnapView(layoutManager: RecyclerView.LayoutManager?): View? {val snapView = super.findSnapView(layoutManager)val snapPosition = snapView?.let {mRv.getChildAdapterPosition(it) }snapPosition?.let {if (snapPosition != RecyclerView.NO_POSITION) {mAdapter.setSelectedPosition(snapPosition)}}return snapView}}linearSnapHelper.attachToRecyclerView(mRv)mRv?.layoutManager = layoutManagermRv?.adapter = mAdapter}fun dpToPx(context: Context, dp: Float): Int {val metrics = context.resources.displayMetricsreturn TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics).toInt()}
}// log
2024-06-21 01:18:42.794 17860-17860 Yang I attachToRecyclerView
2024-06-21 01:18:45.412 17860-17860 Yang I createScroller
2024-06-21 01:18:45.413 17860-17860 Yang I findTargetSnapPosition
2024-06-21 01:18:45.413 17860-17860 Yang I findSnapView
2024-06-21 01:18:45.413 17860-17860 Yang I getHorizontalHelper
2024-06-21 01:18:45.413 17860-17860 Yang I findCenterView
2024-06-21 01:18:45.413 17860-17860 Yang I getHorizontalHelper
2024-06-21 01:18:45.413 17860-17860 Yang I estimateNextPositionDiffForFling
2024-06-21 01:18:45.413 17860-17860 Yang I calculateScrollDistance
2024-06-21 01:18:45.413 17860-17860 Yang I computeDistancePerChild
2024-06-21 01:18:45.430 17860-17860 Yang I onTargetFound
2024-06-21 01:18:45.430 17860-17860 Yang I calculateDistanceToFinalSnap
2024-06-21 01:18:45.430 17860-17860 Yang I getHorizontalHelper
2024-06-21 01:18:45.430 17860-17860 Yang I distanceToCenter
2024-06-21 01:18:45.430 17860-17860 Yang I calculateSpeedPerPixel
2024-06-21 01:18:46.400 17860-17860 Yang I findSnapView
2024-06-21 01:18:46.400 17860-17860 Yang I getHorizontalHelper
2024-06-21 01:18:46.400 17860-17860 Yang I findCenterView
2024-06-21 01:18:46.400 17860-17860 Yang I calculateDistanceToFinalSnap
2024-06-21 01:18:46.400 17860-17860 Yang I getHorizontalHelper
2024-06-21 01:18:46.400 17860-17860 Yang I distanceToCenter
效果图
相关文章:
![](https://img-blog.csdnimg.cn/direct/2d720a11e147402b9f07c702e34d374b.gif)
RecyclerVIew->加速再减速的RecyclerVIew平滑对齐工具类SnapHelper
XML文件 ItemView的XML文件R.layout.shape_item_view <?xml version"1.0" encoding"utf-8"?> <FrameLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"100dp"android:layout_heig…...
![](https://img-blog.csdnimg.cn/direct/ae56c0d8596245d7bd8da7ed9fdf3c82.png)
突破SaaS产品运营困境:多渠道运营如何集中管理?
随着数字化时代的到来,SaaS(软件即服务)产品已成为企业日常运营不可或缺的工具。然而,在竞争激烈的市场环境下,SaaS产品运营越来越重视多渠道、多平台布局,以更广泛地触及潜在用户,然而…...
![](https://i-blog.csdnimg.cn/direct/01e20842236444409e0c711b28a0c671.jpeg)
智能语音热水器:置入NRK3301离线语音识别ic 迈向智能家居新时代
一、热水器语音识别芯片开发背景 在科技的今天,人们对于生活品质的追求已不仅仅满足于基本的物质需求,更渴望通过智能技术让生活变得更加便捷、舒适。热水器作为家庭生活中不可或缺的一部分,其智能化转型势在必行。 在传统热水器使用中&#…...
![](https://img-blog.csdnimg.cn/direct/bf2883daad7c4f90b27eb9a355db8c73.png)
Redis集群部署合集
目录 一. 原理简述 二. 集群配置 2.1 环境准备 2.2 编译安装一个redis 2.3 创建集群 2.4 写入数据测试 实验一: 实验二: 实验三: 实验四: 添加节点 自动分配槽位 提升节点为master: 实验…...
![](https://www.ngui.cc/images/no-images.jpg)
【HDFS】关于Hadoop的IPC.Client类的一些整理
org.apache.hadoop.ipc.Client 类是IPC服务的一个客户端。 IPC请求把一个Writable对象当做参数,返回一个Writable对象当做结果value。 一个IPC服务运行在某个端口上,并且由参数class和value class定义。 Router里的IPC.Client对象就两个 有这样一个类:ClientCache 看名字就…...
![](https://img-blog.csdnimg.cn/img_convert/acb9ebb45f0c4e5c136be197cf33e810.png)
Swoole v6 能否让 PHP 再次伟大?
现状 传统的 PHP-FPM 也是多进程模型的的运行方式,但每个进程只能处理完当前请求,才能接收下一个请求。而且对于 PHP 脚本来说,只是接收请求和响应请求,并不参与网络通信。对数据库资源的操作,也是一次请求一次有效&am…...
![](https://www.ngui.cc/images/no-images.jpg)
C++ STL Iterator Adapter
1. std::back_insert_iterator 使用 // back_insert_iterator example #include <iostream> // std::cout #include <iterator> // std::back_insert_iterator #include <vector> // std::vector #include <algorithm> // std::copy…...
![](https://www.ngui.cc/images/no-images.jpg)
android-aidl5
aidl类是实现Manager和Service通信的桥梁。 例如在修改Android Wifi功能的时候看到WifiManager管理WifiService; AIDL是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口。 比如onclick(),用oneway修…...
![](https://img-blog.csdnimg.cn/direct/8a03d717d89c4233b8a21c7a1bc68c9b.png)
day01-项目介绍及初始化-登录页
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 day01-项目介绍及初始化-登录页一、人力资源项目介绍1.1项目架构和解决方案主要模块解决的问题 二、拉取项目基础代码1.引入库2.升级core-js版本到3.25.5按照完整依…...
![](https://img-blog.csdnimg.cn/direct/770b47d9fd3a453f943a1027e45a430f.png)
华为开发者大会:全场景智能操作系统HarmonyOS NEXT
文章目录 一、全场景智能操作系统 - HarmonyOS NEXT1.1 系统特性1.2 关于架构、体验和生态 二、应用案例2.1 蚂蚁mpaas平台的性能表现 三、新版本应用框架发布3.1 新语言发布3.2 新数据库发布3.3 新版本编译器的发布 四、CodeArts和DataArts4.1 CodeArts4.2 DataArts 五、总结 …...
![](https://www.ngui.cc/images/no-images.jpg)
深度学习二分类评估详细解析与代码实战
深度学习二分类的实战代码:使用 Trainer API 微调模型. https://huggingface.co/learn/nlp-course/zh-CN/chapter3/3 如果你刚接触 自然语言处理,huggingface 是你绕不过去的坎。但是目前它已经被墙了,相信读者的实力,自行解决吧。…...
![](https://www.ngui.cc/images/no-images.jpg)
c++笔记容器详细介绍
C标准库提供了多种容器来存储和管理数据。这些容器属于<vector>, <list>, <deque>, <map>, <set>, <unordered_map>, <unordered_set>等头文件中。这些容器各有优缺点,适用于不同的场景。下面详细介绍几种主要的容器及其…...
![](https://img-blog.csdnimg.cn/direct/b837dd761dac4273baecc91fb23b77bb.png)
CS144 Lab3 TCPSender复盘
一.基础概念 1.TCPSender在TCPSocket中的地位与作用 Lab0中实现了基于内存模拟的流控制-字节流(ByteStream),底层使用std::deque实现,根据最大容量Capacity进行容量控制。个人理解它相当于应用层的输入输出缓存区,用户…...
![](https://img-blog.csdnimg.cn/direct/15fd9ef462d345469cf139fb57a33454.png#pic_center)
建筑可视化中使用云渲染的几大理由
在建筑行业中,可视化技术已成为不可或缺的一部分。无论是设计方案的展示、施工进度的模拟,还是最终效果的呈现,建筑可视化都发挥着至关重要的作用。 建筑可视化是指通过计算机技术和图形学算法,将建筑设计、规划和施工过程中的数据…...
![](https://img-blog.csdnimg.cn/direct/2efc9ba8753e44e19ae516ee2ebb3018.png)
Python数据可视化-地图可视化
1.首先绘制实现数据可视化的思维导图 具体要实现什么功能-怎么处理,先把思路写好 数据来源: 爬取的数据 运行结果: 部分代码: 完整代码请在下方↓↓↓👇获取 转载请注明出处!...
![](https://img-blog.csdnimg.cn/direct/ebf52667e85c4d37bf5054879b8ec06c.png)
leetcode 动态规划(基础版)单词拆分
题目: 题解: 一种可行的dp做法是基于完全背包问题,将s看成是一个背包,wordDict看作是物品,然后往s中放入物品判断最终是否可以变为给定的s即可。这道题和上一题都用到了在dp如何枚举连续子串和状态表示:枚…...
![](https://img-blog.csdnimg.cn/direct/dcfb20f770d94902ad112037555fc0f3.png)
Ubuntu/Linux调试安装南京来可CAN卡
准备好USB rules文件和can driver文件备用! 必做:放置USB rules文件到对应位置处理权限问题 而后:安装内核driver并编译。需求众多依赖编译环境,视情况安装填补。如GCC,G,make等等 进入对应64bit文件夹中,添加权限,执…...
![](https://www.ngui.cc/images/no-images.jpg)
vue2+TS获取到数据后自动叫号写法
1.父组件写法 初始化: //引入子组件 <odialog ref"odialogRef" onSure"onSurea"></odialog> //子传父private onSurea() {// 初始化信息/重新叫号来的数据this.initTabelData()setTimeout(() > {// 播放声音的数据this.search…...
![](https://www.ngui.cc/images/no-images.jpg)
28、架构-边界:微服务的粒度
微服务的粒度 在设计微服务架构时,确定微服务的粒度是一个关键问题。粒度过大或过小都会带来不同的问题,因此需要找到合理的粒度来划分微服务。下面详细探讨微服务粒度的合理范围及其影响因素。 1. 微服务粒度的上下界 微服务的粒度不应该只有唯一正确…...
![](https://img-blog.csdnimg.cn/direct/33d461e4ca89466b91d9d2da4480c9a5.png)
开源API网关-ApacheShenYu首次按照启动遇到的问题
一.背景 公司有API网关产品需求,希望有图形化的后台管理功能。看到了ApacheShenYu,作为Apache的顶级项目,直接认可了。首先,感谢各位大神的付出,初步看这个项目是国内大厂中的大神创立的,在此表示膜拜&…...
![](https://img-blog.csdnimg.cn/direct/2f7fa506dcbb440bae2b95c6c4fb0f15.png)
uniapp获取证书秘钥、Android App备案获取公钥、签名MD5值
一、 uniapp获取证书秘钥 打开uniapp开发者中心下载证书打开cmd输入以下这段代码,下载提供查看到的密钥证书密码就可以了!下载证书在 java 环境下运行才可以 // your_alias 换成 证书详情中的别名,your_keystore.keystore 改成自己的证书文件…...
![](https://www.ngui.cc/images/no-images.jpg)
QT 如何储存多种数据类型(QVariant )
QVariant 是 Qt 框架中用于存储各种数据类型的类。它提供了一个强大的类型系统,允许你在运行时存储和检索多种类型的数据,而不需要在编译时确定类型。QVariant 的主要优点在于它的灵活性和通用性,这使得它在 Qt 的很多组件和机制中都被广泛使…...
![](https://www.ngui.cc/images/no-images.jpg)
持续总结中!2024年面试必问的操作系统面试题(九)
上一篇地址:持续总结中!2024年面试必问的操作系统面试题(八)-CSDN博客 十七、解释什么是操作系统的安全性和它的重要性。 操作系统的安全性(Operating System Security)是指操作系统采取的一系列措施来保…...
![](https://www.ngui.cc/images/no-images.jpg)
操作系统入门 -- 文件管理
操作系统入门 – 文件管理 1.文件管理概述 1.1 文件系统基本功能 目前,计算机内存的容量依然有限,并且其特性决定了数据无法长时间保存,因此把执行的数据以文件形式保存在外存中,等到需要使用时再调入内存。所以,操…...
![](https://www.ngui.cc/images/no-images.jpg)
由浅入深,走进深度学习(2)
今天分享的学习内容主要就是神经网络里面的知识啦,用到的框架就是torch 在这里我也是对自己做一个学习记录,如果不符合大家的口味,大家划走就可以啦 可能没有什么文字或者原理上的讲解,基本上都是代码,但是我还是想说…...
![](https://img-blog.csdnimg.cn/direct/310a788eefd74616aa098f1882f86799.png)
【Python Tips】创建自己的函数包并安装进Anaconda,像引入标准包一样直接import导入
目录 一、引言 二、方法步骤 步骤一:创建包目录结构 步骤二:配置__init__.py文件 步骤三:文件夹外配置setup.py文件 步骤四:终端Pip安装 三、结尾 一、引言 在编写项目代码的时候,有些自定义功能的函数是可以复用的。…...
![](https://img-blog.csdnimg.cn/direct/1d8e038ffefd47028a1edc36afd83bb2.png)
【Python机器学习实战】 | 基于支持向量机(Support Vector Machine, SVM)进行分类和回归任务分析
🎩 欢迎来到技术探索的奇幻世界👨💻 📜 个人主页:一伦明悦-CSDN博客 ✍🏻 作者简介: C软件开发、Python机器学习爱好者 🗣️ 互动与支持:💬评论 &…...
![](https://img-blog.csdnimg.cn/direct/7377356665a94e9e990dfbcb537c521f.png)
备份和还原
stai和dnta snat:源地址转换 内网---外网 内网ip转换成可以访问外网的ip 内网的多个主机可以使用一个有效的公网ip地址访问外部网络 DNAT:目的地址转发 外部用户,可以通过一个公网地址访问服务内部的私网服务。 私网的ip和公网ip做一个…...
![](https://www.ngui.cc/images/no-images.jpg)
Java数组的初始化方法
Java数组的初始化方法 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!在Java编程中,数组是一种非常基础也非常重要的数据结构,它能够存储…...
![](https://www.ngui.cc/images/no-images.jpg)
通过分离有色和无色pdf页面减少打印费
前言 该工具是我认识的一位中科大的大佬在本科毕业的时候做的一个小工具,去打印店打印全彩的毕业论文的话会比较贵,他想到有没有一种方案可以实现有彩色页面的pdf和没有彩色页面的pdf分开打印,前者打印彩色,后者打印黑白…...
![](https://img-blog.csdnimg.cn/direct/df791a3dc1cb4ddabf474bb731dca4c0.png)
c语言--指针
前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文整理c语言中指针的相关知识点。 指针概念 指针存储的就是数据的地址。 直观理解: 李华家是北洋路130号1单元101 用变量处理数据: 我们去李华家拿数据。 用指针处理数据: 我们去北洋路130号1单元101拿数据…...
![](https://img-blog.csdnimg.cn/f9f686b7a5ec435ab24bf9b05ec6429c.jpg)
python-九九乘法表(对齐式1)
[题目描述] 输出九九乘法表,输出格式见样例。输入格式: 无输出格式: 输出乘法表,对齐方式见样例输出。样例输入 无样例输出 来源/分类(难度系数:一星) 完整代码展示: #对齐式1 a[] …...
![](https://www.ngui.cc/images/no-images.jpg)
thinkphp单独为某个接口设置缓存
参考 官方文档 $this->request->cache(__URL__,600);只需要在接口方法的开头添加这个代码即可...
![](https://img-blog.csdnimg.cn/direct/ba352745970a4484a1dbbe22990fe252.png)
OpenCV视觉--视频人脸微笑检测(超详细,附带检测资源)
目录 概述 具体实现 1.加载分类器 2.打开摄像头并识别人脸 3.处理人脸并检测是否微笑 效果 总结 概述 OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习库,广泛应用于图像处理和视频分析等领…...
![](https://img-blog.csdnimg.cn/direct/212f0040ebfe4daf812f83eaea683b69.png)
docker 搭建 AI大数据模型 --- 使用GPU
docker 搭建 AI大数据模型 — 使用GPU方式 搭建本地大模型,最简单的方法!效果直逼GPT 服务器GPU系统HP580 G8P40Rocky9.2 安装程序AnythingLLM前端界面Open WebUIChatOllamaollama 一、AnythingLLM 介绍 AnythingLLM 是 Mintplex Labs Inc. 开发的一…...
![](https://img-blog.csdnimg.cn/direct/3fbe39f0203646289992826c02cddd99.png)
面向对象, 常用类, 集合, 异常, JDBC, mysql数据库 复习
1.面向对象 (1)面向过程和面向对象 ● 面向过程的程序设计思想 (procedure -Oriented Programming),简称POP ● 关注的焦点是过程:过程就是操作数据的步骤。如果某个过程的实现代码重复出 现,那么就可…...
![](https://www.ngui.cc/images/no-images.jpg)
js取数组最大值之Math.max、Math.max.apply
js取数组最大值之Math.max、Math.max.apply Math.maxMath.max.applyapply()第一个参数为什么可以是null 最小值同理 Math.max Math.max(n1,n2,n3,…,nX) 支持传递多个参数,带有较大的值的那个数 Math.max(2,5,3,6,2,4,2,15,9,6,0,1)Math.max.apply apply() 语法&a…...
![](https://www.ngui.cc/images/no-images.jpg)
各种中间件的安装
文章目录 20232306mysql的wondows安装 2023 2306 mysql的wondows安装 常用mysql教程 springboot整合druid连接池SpringBoot配置Druid连接池 mysql的wondows安装 MySQL学习笔记 01、MySQL安装 这个是安装的具体思路 win10 安装 mysql 5.7 msi版的教程图文详解 这个是安装的…...
![](https://www.ngui.cc/images/no-images.jpg)
【Mysql】多表查询、隐式内链接、显式内连接、左外连接、右外连接
多表查询 什么是多表查询 •DQL: 查询多张表,获取到需要的数据 •比如 我们要查询家电分类下 都有哪些商品,那么我们就需要查询分类与商品这两张表 数据准备 创建db3_2 数据库 -- 创建 db3_2 数据库,指定编码 CREATE DATABASE db3_2 CHARACTER SET utf8;创建分类表与商品表 …...
![](https://img-blog.csdnimg.cn/direct/eea23983ebd44fd893f0519a6ed2afc4.png)
Linux驱动开发(三)--新字符设备驱动开发 LED驱动开发升级
1、新字符设备驱动原理 使用 register_chrdev 函数注册字符设备的时候只需要给定一个主设备号即可,但是这样会 带来两个问题 需要我们事先确定好哪些主设备号没有使用 会将一个主设备号下的所有次设备号都使用掉,比如现在设置 LED 这个主设备号为200&…...
![](https://img-blog.csdnimg.cn/direct/8ab7eac28ef44413be08920856a0dd1a.png)
MCU的最佳存储方案CS创世 SD NAND
大家都知道MCU是一种"麻雀"虽小,却"五脏俱全"的主控。它的应用领域非常广泛,小到手机手表,大到航空航天的设备上都会用到MCU.市面上目前几个主流厂商有意法半导体(其中最经典的一款就是STM32系列)…...
![](https://img-blog.csdnimg.cn/img_convert/55d46eba4dfb765c1d096fd83eed929e.jpeg)
40岁学习java是否需要报班学习?
在开始前刚好我有一些资料,是我根据网友给的问题精心整理了一份「java的资料从专业入门到高级教程」, 点个关注在评论区回复“666”之后私信回复“666”,全部无偿共享给大家!!!应该不需要。各种公开免费的…...
![](https://img-blog.csdnimg.cn/direct/17449499e847411487d6b919b721b881.png)
Vitis Accelerated Libraries 学习笔记--OpenCV 运行测试
目录 1. 简介 2. 实例测试 2.1 实例介绍 2.2 创建工程 2.2.1 创建工程 2.2.2 获取路径 2.2.3 设置路径 2.2.4 打开工程 2.2.5 添加文件 2.2.6 启动 GUI 2.2.7 配置 csim 参数 3 常见错误 3.1 核心共享库报错 4. 总结 1. 简介 在《Vitis Accelerated Libraries …...
![](https://img-blog.csdnimg.cn/direct/f8ff287aea394bcdb063008d7281f1f5.png)
加固三防平板如何提高轨道交通系统的运营效率?
在当今快节奏的社会中,轨道交通系统作为城市交通的重要组成部分,其运营效率的提升对于缓解交通拥堵、满足人们的出行需求以及促进城市的发展具有至关重要的意义。而加固三防平板作为一种先进的技术设备,正逐渐在轨道交通领域发挥着关键作用&a…...
![](https://www.ngui.cc/images/no-images.jpg)
Django 靓号管理系统:实现登录功能
本文将详细介绍如何在 Django 靓号管理系统中实现登录功能,包括用户认证、验证码生成、以及中间件的使用。我们将逐步展示所有相关代码,并附带详细注释。 1. 项目结构 首先,让我们看一下项目的基本结构: number ├── manage.py ├── monaco.ttf ├── number │ …...
![](https://img-blog.csdnimg.cn/direct/372a37fee8914d29b8ecc710b30c37e8.png)
【Solr 学习笔记】Solr 源码启动教程
Solr 源码启动教程 本教程记录了如何通过 IDEA 启动并调试 Solr 源码,从 Solr9 开始 Solr 项目已由 ant 方式改成了 gradle 构建方式,本教程将以 Solr 9 为例进行演示,IDE 选择使用 IntelliJ IDEA。 Solr github 地址:https://gi…...
![](https://www.ngui.cc/images/no-images.jpg)
Java中的事件驱动编程模型
Java中的事件驱动编程模型 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我将为大家介绍Java中的事件驱动编程模型。事件驱动编程模型是一种以事件为核心驱…...
![](https://img-blog.csdnimg.cn/img_convert/67c857e3801a011a6ba13adce8a6eb30.png)
Python 语法基础一
1.变量 python 中变量很简单,不需要指定数据类型,直接使用等号定义就好。python变量里面存的是内存地址,也就是这个值存在内存里面的哪个地方,如果再把这个变量赋值给另一个变量,新的变量通过之前那个变量知道那个变量…...
![](https://img-blog.csdnimg.cn/direct/a7ea45699fa341bfbfe08e680ec81054.png)
从零开始:Spring Boot 中使用 Drools 规则引擎的完整指南
规则引擎作用 规则引擎主要用于将业务逻辑从应用程序代码中分离出来,提高系统的灵活性和可维护性。规则引擎通过预定义的规则来处理输入数据并做出相应的决策,从而实现业务逻辑的自动化和动态调整。 例如 门店信息校验:美团点评在门店信息…...
![](https://img-blog.csdnimg.cn/direct/652557a7dba64016995b040411a8bd84.png)
工业边缘计算网关
1 介绍 HINETG系列边缘计算网关(Linux操作系统),是华辰智通的—款面向工业现场设备接入、数据采集、设备监控的工业级边缘计算网关。采用ARM Cortex-A7 800MHz高性能CPU,拥有以太网、串口、CAN口、IO口等丰富的接口,支持以太网、…...
![](https://img-blog.csdnimg.cn/direct/f7163152327a41f89b398ab1ab2a6375.png)
HTML5的多线程技术:Web Worker API
Web Workers API 是HTML5的一项技术,它允许在浏览器后台独立于主线程运行脚本,即允许进行多线程处理。这对于执行密集型计算任务特别有用,因为它可以防止这些任务阻塞用户界面,从而保持网页的响应性和交互性。Web Workers在自己的…...
![](https://i-blog.csdnimg.cn/direct/592e53e37eea4ac5b2f19c84172d4a76.png)
itext生成pdf文件demo示例
需求 在PDF文件中植入一些信息(pdf模版) 制作模版 可以看到下面红色箭头标注位置,这都是我们需要动态写入数据的表单域,可以使用wps等工具来制作 点击编辑表单,可以给对应空间添加表单域,表单域名称是ke…...
![](https://www.ngui.cc/images/no-images.jpg)
010、GPT-5:AI新纪元的曙光与挑战
目录 GPT-5:AI新纪元的曙光与挑战 1.革命性的个人助理 2.教育领域的变革 3.医疗健康的新篇章 4.科研创新的加速器 5.创意产业的新灵感 6.商业与经济的智能化 7.社会治理的新工具 8.环境保护与可持续发展 9.伦理与社会影响 学术视角:AI发展的前…...
![](https://www.ngui.cc/images/no-images.jpg)
【C++ Primer Plus学习记录】指针和const
可以用两种不同的方式将const关键字用于指针。第一种方法是让指针指向一个常量对象,这样就可以防止使用该指针来修改所指向的值,第二种方法是将指针本身声明为常量,这样可以防止改变指针指向的位置。 首先,声明一个指向常量的指针…...
![](https://www.ngui.cc/images/no-images.jpg)
【面试系列】C#高频面试题
欢迎来到我的博客,很高兴能够在这里和您见面!欢迎订阅相关专栏: ⭐️ 全网最全IT互联网公司面试宝典:收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来:详细讲解AIGC的概念、核心技术、…...
![](https://www.ngui.cc/images/no-images.jpg)
[vue]打包后发布相关问题总结
问题1: 1. history模式下,发布到nginx后,刷新页面或者地址栏回车404问题。 打开nginx的nginx.conf文件,添加注释【2】对应行的代码,可以解决这个问题。 events {worker_connections 1024; }http {include mime.typ…...
![](https://www.ngui.cc/images/no-images.jpg)
魏牌咖啡系列车型OTA升级智能体验再跃升领跑高阶新势力
5月28日,魏牌新能源咖啡系列车型迎来OTA重磅升级。其中,魏牌蓝山迎来极具颠覆性的第6次OTA升级,备受用户关注的语音大模型、卡片式桌面、露营模式等功能全部上车;魏牌高山迎来第2次OTA升级,语音大模型、卡片式桌面、插枪远控等功能全部就位;摩卡、拿铁、玛奇朵则开启第8次…...
![](https://www.ngui.cc/images/no-images.jpg)
每小时电量的计算sql
计算思路,把每小时的电表最大记录取出来,然后用当前小时的最大值减去上个小时的最大值即可。 使用了MYSQL8窗口函数进行计算。 SELECT b.*,b.epimp - b.lastEmimp ecValue FROM ( SELECT a.deviceId,a.ctime,a.epimp, lag(epimp) over (ORDER BY a.dev…...
![](https://img-blog.csdnimg.cn/img_convert/338d92202a9e083b579457cf53d06d97.png)
部署Prometheus + Grafana实现监控数据指标
1.1 Prometheus安装部署 Prometheus监控服务 主机名IP地址系统配置作用Prometheus192.168.110.27/24CentOS 7.94颗CPU 8G内存 100G硬盘Prometheus服务器grafana192.168.110.28/24CentOS 7.94颗CPU 8G内存 100G硬盘grafana服务器 监控机器 主机名IP地址系统配置k8s-master-0…...
![](https://img-blog.csdnimg.cn/direct/3447854d782047cc9ee1a6182e691f19.png)
【前端学习笔记】HTML基础
HTML 一、HTML介绍1.HTML概念2.文档声明3.字符编码4. HTML标签5. HTML属性 二、标签1.meta标签2.语义标签3.布局标签4.列表5.超链接6.图片7.字符实体8.内联格式9.HTML 表格10.HTML 表单 三、HTML5新特性1. 本地存储2. Cookie3. 语义化标签4.多媒体元素5.表单增强6.Canvas7.SVG …...
![](https://img-blog.csdnimg.cn/direct/5fd0cc4bb2f147fe9511bcc33e0e8ea6.png)
NLP(17)--大模型发展(1)
前言 仅记录学习过程,有问题欢迎讨论 大模型的演化: ElMO : 类似双向lstm 结果和词向量拼接 预训练鼻祖 GPT :使用了Transformer 模型 开始使用Token (发现预训练的作用) Bert:认为双向比单向好 MLM(双向) 优于 LT…...
![](https://img-blog.csdnimg.cn/img_convert/a205160de7212f9960b654149b551380.png)
家用超声波清洗机哪个品牌好用?四款众人口口相传的品牌力荐
在追求极致清洁效果的今天,超声波清洗机以其高效、深入和温和的清洁能力,成为了众多行业乃至家庭用户的首选。不论是珍贵的珠宝首饰,精密的医疗器械,还是日常生活中的眼镜和手表,超声波清洗机都能够提供一种安全、快捷…...