Parcourir la source

1.修复底部物联设备滚动动画到最后一个设备时闪到第一个动作太生硬,改为反方向继续滑动

JaycePC il y a 2 mois
Parent
commit
b3fcbf8f71

+ 1 - 1
app/build.gradle

@@ -19,7 +19,7 @@ android {
         minSdk 31
         targetSdk 35
         versionCode 2
-        versionName "2.5"
+        versionName "2.6"
 
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         room {

+ 6 - 5
app/src/main/java/xn/xxp/HomeActivity.java

@@ -64,7 +64,6 @@ import xn.xxp.room.dao.DeviceConfigDao;
 import xn.xxp.room.dao.LabConfigDao;
 import xn.xxp.utils.Tool;
 import xn.xxp.widget.ITitleBar;
-import xn.xxp.widget.LooperLayoutManager;
 import xn.xxp.widget.NavViewCompat;
 import xn.xxp.widget.WaringDialog;
 
@@ -136,12 +135,14 @@ public class HomeActivity extends BaseCountDownActivity<ActivityHomeBinding> imp
         fragmentManager = getSupportFragmentManager();
         FragmentUtils.add(fragmentManager, new WindowFragment(), binding.container.getId(), "WindowFragment");
         // 底部物联设备UI
-        binding.bulletinBoardView.setLayoutManager(new LooperLayoutManager(RecyclerView.HORIZONTAL));
-        NoLastLineItemDecoration decoration = new NoLastLineItemDecoration(this, DividerItemDecoration.HORIZONTAL);
-        decoration.setDrawable(Objects.requireNonNull(ContextCompat.getDrawable(this, R.drawable.shape_item_divider_middle)));
-        binding.bulletinBoardView.addItemDecoration(decoration);
         homeBoardAdapter = new HomeBoardAdapter();
         binding.bulletinBoardView.setAdapter(homeBoardAdapter);
+        binding.bulletinBoardView.post(new Runnable() {
+            @Override
+            public void run() {
+                binding.bulletinBoardView.startAutoScroll();
+            }
+        });
 
         // 倒计时业务
         defaultCd = new CountDownTimer(1000, 1000) {

+ 2 - 2
app/src/main/java/xn/xxp/HomeActivityHelp.java

@@ -146,8 +146,8 @@ public class HomeActivityHelp {
                 activity.homeBoardAdapter = new HomeBoardAdapter();
             }
             activity.homeBoardAdapter.setNewInstance(labBulletinBoardVos);
-            activity.binding.bulletinBoardView.initTimer(4, labBulletinBoardVos.size());
-            activity.binding.bulletinBoardView.startAuto(3000);
+//            activity.binding.bulletinBoardView.initTimer(4, labBulletinBoardVos.size());
+//            activity.binding.bulletinBoardView.startAuto(3000);
             queryWarning();
         } else {
             activity.homeBoardAdapter.setEmptyView(R.layout.view_list_empty);

+ 0 - 7
app/src/main/java/xn/xxp/main/MainActivity.kt

@@ -62,7 +62,6 @@ import xn.xxp.room.bean.NoticeSummary
 import xn.xxp.utils.QrTool
 import xn.xxp.utils.Tool
 import xn.xxp.widget.ITitleBar
-import xn.xxp.widget.LooperLayoutManager
 import xn.xxp.widget.NavViewCompat
 import xn.xxp.widget.WaringDialog
 import java.math.BigDecimal
@@ -389,10 +388,6 @@ class MainActivity :
             logoutCountDownFinish()
         })
 
-        binding.bulletinBoardView.layoutManager = LooperLayoutManager(RecyclerView.HORIZONTAL)
-        val decor = NoLastLineItemDecoration(this, DividerItemDecoration.HORIZONTAL)
-        decor.setDrawable(ContextCompat.getDrawable(this, R.drawable.shape_item_divider_middle)!!)
-        binding.bulletinBoardView.addItemDecoration(decor)
         binding.bulletinBoardView.adapter = mBulletinBoardAdapter
 
         binding.reformRecyclerView.layoutManager = LinearLayoutManager(this)
@@ -584,8 +579,6 @@ class MainActivity :
             .subscribe({ data ->
                 dismissLoading()
                 mBulletinBoardAdapter.setNewInstance(data.toMutableList())
-                binding.bulletinBoardView.initTimer(4, data.size)
-                binding.bulletinBoardView.startAuto(SENSOR_SPLASH_TIME)
                 if (data.isNullOrEmpty()) {
                     mBulletinBoardAdapter.setEmptyView(R.layout.view_list_empty)
                 }

+ 109 - 0
app/src/main/java/xn/xxp/widget/AutoScrollRecyclerView.java

@@ -0,0 +1,109 @@
+package xn.xxp.widget;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class AutoScrollRecyclerView extends RecyclerView {
+    private static final int SCROLL_DELAY_MS = 20;
+    private static final int SCROLL_PIXELS = 5;
+    private boolean isScrollingForward = true;
+    private final Handler scrollHandler = new Handler(Looper.getMainLooper());
+    private final Runnable scrollRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (getLayoutManager() == null || getAdapter() == null) {
+                return;
+            }
+
+            LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
+            int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();
+            int lastVisiblePosition = layoutManager.findLastVisibleItemPosition();
+            int itemCount = getAdapter().getItemCount();
+
+            // 检查是否到达边界
+            if (isScrollingForward && lastVisiblePosition == itemCount - 1) {
+                View lastView = layoutManager.findViewByPosition(lastVisiblePosition);
+                if (lastView != null && layoutManager.getDecoratedRight(lastView) <= getWidth()) {
+                    isScrollingForward = false;
+                }
+            } else if (!isScrollingForward && firstVisiblePosition == 0) {
+                View firstView = layoutManager.findViewByPosition(0);
+                if (firstView != null && layoutManager.getDecoratedLeft(firstView) >= 0) {
+                    isScrollingForward = true;
+                }
+            }
+
+            // 根据方向滚动
+            if (isScrollingForward) {
+                smoothScrollBy(SCROLL_PIXELS, 0);
+            } else {
+                smoothScrollBy(-SCROLL_PIXELS, 0);
+            }
+
+            scrollHandler.postDelayed(this, SCROLL_DELAY_MS);
+        }
+    };
+
+    public AutoScrollRecyclerView(@NonNull Context context) {
+        super(context);
+        init();
+    }
+
+    public AutoScrollRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public AutoScrollRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    private void init() {
+        setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent e) {
+        // 禁用用户操作
+        return false;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent e) {
+        // 禁用用户操作
+        return false;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        // 开始自动滚动
+        startAutoScroll();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        // 停止滚动,防止内存泄漏
+        stopAutoScroll();
+    }
+
+    public void startAutoScroll() {
+        stopAutoScroll(); // 先停止之前的滚动
+        scrollHandler.postDelayed(scrollRunnable, SCROLL_DELAY_MS);
+    }
+
+    public void stopAutoScroll() {
+        scrollHandler.removeCallbacks(scrollRunnable);
+    }
+}

+ 0 - 279
app/src/main/java/xn/xxp/widget/LooperLayoutManager.kt

@@ -1,279 +0,0 @@
-package xn.xxp.widget
-
-import android.view.ViewGroup
-import androidx.recyclerview.widget.RecyclerView
-
-/**
- * info
- *
- * @author ReiChin_
- */
-class LooperLayoutManager(
-    @RecyclerView.Orientation
-    private val orientation: Int = RecyclerView.VERTICAL
-) : RecyclerView.LayoutManager() {
-
-    companion object {
-        const val TAG = "LooperLayoutManager"
-    }
-
-    override fun generateDefaultLayoutParams() = RecyclerView.LayoutParams(
-        ViewGroup.LayoutParams.WRAP_CONTENT,
-        ViewGroup.LayoutParams.WRAP_CONTENT
-    )
-
-    override fun isAutoMeasureEnabled() = true
-
-    override fun canScrollHorizontally() = orientation == RecyclerView.HORIZONTAL
-
-    override fun canScrollVertically() = orientation == RecyclerView.VERTICAL
-
-    override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
-        if (itemCount <= 0) {
-            return
-        }
-
-        // preLayout主要支持动画,直接跳过
-        if (state.isPreLayout) {
-            return
-        }
-        // 将视图分离放入scrap缓存中,以准备重新对view进行排版
-        detachAndScrapAttachedViews(recycler)
-        var autalLength = 0
-        for (i in 0 until itemCount) {
-            //初始化,将在屏幕内的view填充
-            val itemView = recycler.getViewForPosition(i)
-            addView(itemView)
-            //测量itemView的宽高
-            measureChildWithMargins(itemView, 0, 0)
-
-            val width = getDecoratedMeasuredWidth(itemView)
-            val height = getDecoratedMeasuredHeight(itemView)
-            //根据itemView的宽高进行布局
-            autalLength += if (orientation == RecyclerView.VERTICAL) {
-                layoutDecorated(itemView, 0, autalLength, width, autalLength + height)
-                height
-            } else {
-                layoutDecorated(itemView, autalLength, 0, autalLength + width, height)
-                width
-            }
-
-            //如果当前布局过的itemView的宽度或高度总和大于RecyclerView的宽(水平)或高(垂直),则不再进行布局
-            if (orientation == RecyclerView.VERTICAL) {
-                if (autalLength > getHeight()) {
-                    break
-                }
-            } else {
-                if (autalLength > getWidth()) {
-                    break
-                }
-            }
-        }
-    }
-
-    override fun scrollHorizontallyBy(
-        dx: Int,
-        recycler: RecyclerView.Recycler,
-        state: RecyclerView.State
-    ): Int {
-        if (orientation == RecyclerView.VERTICAL) {
-            return 0
-        }
-        //1.左右滑动的时候,填充子view
-        val travl = fillHorizontal(dx, recycler, state)
-        if (travl == 0) {
-            return 0
-        }
-
-        //2.滚动
-        offsetChildrenHorizontal(travl * -1)
-
-        //3.回收已经离开界面的
-        recyclerHorizontalHideView(dx, recycler, state)
-        return travl
-    }
-
-    override fun scrollVerticallyBy(
-        dy: Int,
-        recycler: RecyclerView.Recycler,
-        state: RecyclerView.State
-    ): Int {
-        if (orientation == RecyclerView.HORIZONTAL) {
-            return 0
-        }
-
-        //1.上下滑动的时候,填充子view
-        val travl = fillVertical(dy, recycler, state)
-        if (travl == 0) {
-            return 0
-        }
-
-        //2.滚动
-        offsetChildrenVertical(travl * -1)
-
-        //3.回收已经离开界面的
-        recyclerVerticalHideView(dy, recycler, state)
-        return travl
-    }
-
-    /**
-     * 左右滑动的时候,填充
-     */
-    private fun fillHorizontal(
-        dx: Int,
-        recycler: RecyclerView.Recycler,
-        state: RecyclerView.State
-    ): Int {
-        if (dx > 0) {
-            //标注1.向左滚动
-            val lastView = getChildAt(childCount - 1) ?: return 0
-            val lastPos = getPosition(lastView)
-            //标注2.可见的最后一个itemView完全滑进来了,需要补充新的
-            if (lastView.right < width) {
-                //标注3.判断可见的最后一个itemView的索引,
-                // 如果是最后一个,则将下一个itemView设置为第一个,否则设置为当前索引的下一个
-                val scrap = if (lastPos == itemCount - 1) {
-                    recycler.getViewForPosition(0)
-                } else {
-                    recycler.getViewForPosition(lastPos + 1)
-                }
-                //标注4.将新的itemViewadd进来并对其测量和布局
-                addView(scrap)
-                measureChildWithMargins(scrap, 0, 0)
-                val width = getDecoratedMeasuredWidth(scrap)
-                val height = getDecoratedMeasuredHeight(scrap)
-                val insetsWidth = width - scrap.measuredWidth
-                layoutDecorated(
-                    scrap, lastView.right + insetsWidth, 0,
-                    lastView.right + width + insetsWidth, height
-                )
-                return dx
-            }
-        } else {
-            //向右滚动
-            val firstView = getChildAt(0) ?: return 0
-            val firstPos = getPosition(firstView)
-            if (firstView.left >= 0) {
-                val scrap = if (firstPos == 0) {
-                    recycler.getViewForPosition(itemCount - 1)
-                } else {
-                    recycler.getViewForPosition(firstPos - 1)
-                }
-                addView(scrap, 0)
-                measureChildWithMargins(scrap, 0, 0)
-                val width = getDecoratedMeasuredWidth(scrap)
-                val height = getDecoratedMeasuredHeight(scrap)
-                layoutDecorated(
-                    scrap, firstView.left - width, 0,
-                    firstView.left, height
-                )
-            }
-        }
-        return dx
-    }
-
-    /**
-     * 上下滑动的时候,填充
-     */
-    private fun fillVertical(
-        dy: Int,
-        recycler: RecyclerView.Recycler,
-        state: RecyclerView.State
-    ): Int {
-        if (dy > 0) {
-            //标注1.向上滚动
-            val lastView = getChildAt(childCount - 1) ?: return 0
-            val lastPos = getPosition(lastView)
-            //标注2.可见的最后一个itemView完全滑进来了,需要补充新的
-            if (lastView.bottom < height) {
-                //标注3.判断可见的最后一个itemView的索引,
-                // 如果是最后一个,则将下一个itemView设置为第一个,否则设置为当前索引的下一个
-                val scrap = if (lastPos == itemCount - 1) {
-                    recycler.getViewForPosition(0)
-                } else {
-                    recycler.getViewForPosition(lastPos + 1)
-                }
-                //标注4.将新的itemViewadd进来并对其测量和布局
-                addView(scrap)
-                measureChildWithMargins(scrap, 0, 0)
-                val width = getDecoratedMeasuredWidth(scrap)
-                val height = getDecoratedMeasuredHeight(scrap)
-                val insetsHeight = height - scrap.measuredHeight
-                layoutDecorated(
-                    scrap, 0, lastView.bottom + insetsHeight,
-                    width, lastView.bottom + height + insetsHeight
-                )
-                return dy
-            }
-        } else {
-            //向下滚动
-            val firstView = getChildAt(0) ?: return 0
-            val firstPos = getPosition(firstView)
-            if (firstView.top >= 0) {
-                val scrap = if (firstPos == 0) {
-                    recycler.getViewForPosition(itemCount - 1)
-                } else {
-                    recycler.getViewForPosition(firstPos - 1)
-                }
-                addView(scrap, 0)
-                measureChildWithMargins(scrap, 0, 0)
-                val width = getDecoratedMeasuredWidth(scrap)
-                val height = getDecoratedMeasuredHeight(scrap)
-                layoutDecorated(
-                    scrap, 0, firstView.top - height,
-                    width, firstView.top
-                )
-            }
-        }
-        return dy
-    }
-
-    /**
-     * 回收界面不可见的view
-     */
-    private fun recyclerHorizontalHideView(
-        dx: Int,
-        recycler: RecyclerView.Recycler,
-        state: RecyclerView.State
-    ) {
-        for (i in 0 until childCount) {
-            val view = getChildAt(i) ?: continue
-            if (dx > 0) {
-                //向左滚动,移除一个左边不在内容里的view
-                if (view.right < 0) {
-                    removeAndRecycleView(view, recycler)
-                }
-            } else {
-                //向右滚动,移除一个右边不在内容里的view
-                if (view.left > width) {
-                    removeAndRecycleView(view, recycler)
-                }
-            }
-        }
-    }
-
-    /**
-     * 回收界面不可见的view
-     */
-    private fun recyclerVerticalHideView(
-        dy: Int,
-        recycler: RecyclerView.Recycler,
-        state: RecyclerView.State
-    ) {
-        for (i in 0 until childCount) {
-            val view = getChildAt(i) ?: continue
-            if (dy > 0) {
-                //向上滚动,移除一个上边不在内容里的view
-                if (view.bottom < 0) {
-                    removeAndRecycleView(view, recycler)
-                }
-            } else {
-                //向下滚动,移除一个下边不在内容里的view
-                if (view.top > height) {
-                    removeAndRecycleView(view, recycler)
-                }
-            }
-        }
-    }
-
-}

+ 0 - 70
app/src/main/java/xn/xxp/widget/LooperRecyclerView.kt

@@ -1,70 +0,0 @@
-package xn.xxp.widget
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.MotionEvent
-import androidx.recyclerview.widget.RecyclerView
-
-/**
- * info
- *
- * @author ReiChin_
- */
-class LooperRecyclerView @JvmOverloads constructor(
-    context: Context,
-    attrs: AttributeSet? = null,
-    defStyleAttr: Int = 0
-) :
-    RecyclerView(context, attrs, defStyleAttr), Runnable {
-
-    companion object {
-        private const val TIME_AUTO_POLL = 16L
-    }
-
-    private var itemPageCount: Int = 0 // 一页的Item数
-    private var itemAllCount: Int = 0
-    private var scrollTime: Long = 1500
-
-    private var running = false // 标示是否正在自动轮询
-
-    fun initTimer(itemPageCount: Int, itemAllCount: Int) {
-        this.itemPageCount = itemPageCount
-        this.itemAllCount = itemAllCount
-    }
-
-    fun startAuto(scrollTime: Long) {
-        this.scrollTime = scrollTime
-
-        if (running) stopAuto()
-
-        running = true
-        postDelayed(this, scrollTime)
-    }
-
-    fun stopAuto() {
-        running = false
-        removeCallbacks(this)
-    }
-
-    override fun run() {
-        if (itemPageCount < itemAllCount) {
-            if (running) {
-                scrollBy(2, 2)
-                postDelayed(this, TIME_AUTO_POLL)
-            }
-        } else {
-            postDelayed(this, scrollTime)
-        }
-    }
-
-    override fun onTouchEvent(e: MotionEvent?): Boolean {
-        when (e?.action) {
-            MotionEvent.ACTION_DOWN -> if (running) stopAuto()
-            MotionEvent.ACTION_UP,
-            MotionEvent.ACTION_CANCEL,
-            MotionEvent.ACTION_OUTSIDE -> startAuto(scrollTime)
-        }
-        return if (itemPageCount >= itemAllCount) true else super.onTouchEvent(e)
-    }
-
-}

+ 10 - 1
app/src/main/res/layout/activity_home.xml

@@ -450,7 +450,16 @@
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent" />
 
-        <xn.xxp.widget.LooperRecyclerView
+<!--        <xn.xxp.widget.LooperRecyclerView-->
+<!--            android:id="@+id/bulletinBoardView"-->
+<!--            android:layout_width="0dp"-->
+<!--            android:layout_height="61dp"-->
+<!--            android:layout_marginStart="20dp"-->
+<!--            android:layout_marginEnd="50dp"-->
+<!--            app:layout_constraintBottom_toBottomOf="@id/bulletin_bg"-->
+<!--            app:layout_constraintEnd_toEndOf="@id/bulletin_bg"-->
+<!--            app:layout_constraintStart_toStartOf="@id/bulletin_bg" />-->
+        <xn.xxp.widget.AutoScrollRecyclerView
             android:id="@+id/bulletinBoardView"
             android:layout_width="0dp"
             android:layout_height="61dp"

+ 1 - 1
app/src/main/res/layout/activity_main.xml

@@ -263,7 +263,7 @@
             app:layout_constraintEnd_toStartOf="@id/back"
             app:layout_constraintStart_toStartOf="parent" />
 
-        <xn.xxp.widget.LooperRecyclerView
+        <xn.xxp.widget.AutoScrollRecyclerView
             android:id="@+id/bulletinBoardView"
             android:layout_width="0dp"
             android:layout_height="61dp"