美文网首页AndroidAndroid技术知识Android知识
仿京东商品列表页码提示效果

仿京东商品列表页码提示效果

作者: 小编 | 来源:发表于2016-12-22 15:56 被阅读1331次
    我是图片

    如图,此功能需要注意两个状态:

    1. 当手指滑动,并且不离开屏幕的时候PageCountView才显示;
    2. 当手指快速滑动,列表还在滑翔状态时,PageCountView也显示。

    XML布局

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/content_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
        <TextView
            android:id="@+id/tv_page_count"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:layout_margin="15dp"
            android:background="@drawable/bg_page_count"
            android:text="1/140"
            android:textColor="@android:color/white"
            android:textSize="12sp" />
    </RelativeLayout>
    

    PageCountView其实就是一个TextView,它的背景是自己写的一个Drawable,即bg_page_count.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
        <solid android:color="#66000000" />
        <padding
            android:bottom="2dp"
            android:left="5dp"
            android:right="5dp"
            android:top="2dp" />
        <corners android:radius="10dp" />
    </shape>
    
    效果图

    核心代码

    主要给 RecyclerView添加滚动监听onScrollListener,监听onScrolled方法和onScrollStateChanged方法。前一个方法处理页码值的计算和更新操作,后一个方法处理View的显示和隐藏。

    在动画显示的时候会定义一个回调方法ViewPropertyAnimatorListener,记录onAnimationEnd方法的状态,用于在IDLE状态下并且显示动画执行完毕后,再执行隐藏动画。否则会出现类似闪烁的问题。

    MyAdapter的代码就不贴了,自己随意封装。

    public class MainActivity extends AppCompatActivity {
    
        private static final int PAGE_PER = 20;//每页显示数量
        private static final int COUNT = 100;//总数量;实际开发中总数量是从网络获取的。
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            final TextView tvPageCount = (TextView) findViewById(R.id.tv_page_count);
            //初始状态隐藏PageCountView。当然也可以在xml中控制
            tvPageCount.post(new Runnable() {
                @Override
                public void run() {
                    AnimUtils.hide(tvPageCount);
                }
            });
    
            RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
            recyclerView.setAdapter(new MyAdapter());
            recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    
                    RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                    if (layoutManager instanceof LinearLayoutManager) {
                        int lastVisiblePosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
                        int page;
                        if (lastVisiblePosition % PAGE_PER == 0) {
                            page = lastVisiblePosition / PAGE_PER;
                        } else {
                            page = lastVisiblePosition / PAGE_PER + 1;
                        }
                        tvPageCount.setText(page + "/" + (COUNT / PAGE_PER));
                    }
                }
    
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    if (newState == RecyclerView.SCROLL_STATE_IDLE && isAnimFinish) {
                        //如果是IDLE状态,并且显示动画执行完毕,再执行隐藏动画,避免出现动画闪烁
                        AnimUtils.hide(tvPageCount);
                    } else if (tvPageCount.getVisibility() != View.VISIBLE) {
                        AnimUtils.show(tvPageCount, listener);
                    }
                }
            });
        }
    
        private boolean isAnimFinish = false;//记录动画执行完成的状态
        private ViewPropertyAnimatorListener listener = new ViewPropertyAnimatorListener() {
            @Override
            public void onAnimationStart(View view) {
                isAnimFinish = false;
            }
    
            @Override
            public void onAnimationEnd(View view) {
                isAnimFinish = true;
            }
    
            @Override
            public void onAnimationCancel(View view) {
            }
        };
    }
    

    动画工具类AnimUtils

    public class AnimUtils {
    
        private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
    
        public static void hide(final View view) {
            ViewCompat.animate(view)
                    .scaleX(0.0f)
                    .scaleY(0.0f)
                    .setInterpolator(INTERPOLATOR)
                    .setDuration(300)
                    .setListener(new ViewPropertyAnimatorListener() {
                        @Override
                        public void onAnimationStart(View view) {
                        }
                        @Override
                        public void onAnimationEnd(View view) {
                            view.setVisibility(View.GONE);
                        }
                        @Override
                        public void onAnimationCancel(View view) {
                        }
                    })
                    .start();
        }
    
        public static void show(final View view, ViewPropertyAnimatorListener listener) {
            view.setVisibility(View.VISIBLE);
            ViewCompat.animate(view)
                    .scaleX(1.0f)
                    .scaleY(1.0f)
                    .setInterpolator(INTERPOLATOR)
                    .setDuration(300)
                    .setListener(listener)
                    .start();
        }
    }
    

    到此,基本效果已经实现。
    但是,当快速滑动一小段距离,立马停止触摸屏幕的时候,View虽然显示了,但是不执行隐藏方法。可能导致的原因是动画执行一次需要300ms,如果在此时间内,回调方法onScrollStateChanged执行,处于IDLE状态,但是动画未执行完毕,isAnimFinish仍然为false,那么就不会隐藏。

    完善代码

    定义全局变量newState,在onScrollStateChanged记录newState的状态值,在显示动画监听器的onAnimationEnd方法中判断,如果newState是IDLE状态,再去执行隐藏动画。

    //定义全局变量
    private int newState;
    
    //onScrollStateChanged方法
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        MainActivity.this.newState = newState;
        if (newState == RecyclerView.SCROLL_STATE_IDLE && !isAnimFinish) {
            //如果是IDLE状态,并且显示动画执行完毕,再执行隐藏动画,避免出现动画闪烁
            //如果快速简短滑动,可能导致出现IDLE状态,但是动画未执行完成。因此无法执行隐藏动画。所以保存当前newState,在onAnimationEnd中增加判断。
            AnimUtils.hide(tvPageCount);
        } else if (tvPageCount.getVisibility() != View.VISIBLE) {
            AnimUtils.show(tvPageCount, listener);
        }
    }
    
    //onAnimationEnd方法
    @Override
    public void onAnimationEnd(View view) {
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
            //IDLE状态下隐藏,其余状态保持View显示
            AnimUtils.hide(view);
        }
        isAnimFinish = false;
    }
    

    工匠精神

    作为一名Coder,代码如此杂乱是在是sou不鸟。我们可以封装一下动画监听器。

    public class AnimListenerBuilder {
    
        private int newState;
        private boolean isAnimFinish = false;
    
        private ViewPropertyAnimatorListener listener;
    
        public AnimListenerBuilder() {
            listener = new ViewPropertyAnimatorListener() {
                @Override
                public void onAnimationStart(View view) {
                    isAnimFinish = false;
                }
    
                @Override
                public void onAnimationEnd(View view) {
                    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                        //IDLE状态下隐藏,其余状态保持View显示
                        AnimUtils.hide(view);
                    }
                    isAnimFinish = true;
                }
    
                @Override
                public void onAnimationCancel(View view) {
    
                }
            };
        }
    
        public ViewPropertyAnimatorListener build() {
            return listener;
        }
    
        public void setNewState(int newState) {
            this.newState = newState;
        }
    
        public boolean isAnimFinish() {
            return isAnimFinish;
        }
        
    }
    

    MainActivity则干净许多

    public class MainActivity extends AppCompatActivity {
    
        private static final int PAGE_PER = 20;//每页显示数量
        private static final int COUNT = 100;//总数量
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            final TextView tvPageCount = (TextView) findViewById(R.id.tv_page_count);
            //初始状态,隐藏View
            tvPageCount.post(new Runnable() {
                @Override
                public void run() {
                    AnimUtils.hide(tvPageCount);
                }
            });
    
            final AnimListenerBuilder builder = new AnimListenerBuilder();
    
            RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
            recyclerView.setAdapter(new MyAdapter());
            recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    
                    RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                    if (layoutManager instanceof LinearLayoutManager) {
                        int lastVisiblePosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
                        int page;
                        if (lastVisiblePosition % PAGE_PER == 0) {
                            page = lastVisiblePosition / PAGE_PER;
                        } else {
                            page = lastVisiblePosition / PAGE_PER + 1;
                        }
                        tvPageCount.setText(page + "/" + (COUNT / PAGE_PER));
                    }
                }
    
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    builder.setNewState(newState);
                    if (newState == RecyclerView.SCROLL_STATE_IDLE && builder.isAnimFinish()) {
                        //如果是IDLE状态,并且显示动画执行完毕,再执行隐藏动画,避免出现动画闪烁
                        //如果快速简短滑动,可能导致出现IDLE状态,但是动画未执行完成。因此无法执行隐藏动画。所以保存当前newState,在onAnimationEnd中增加判断。
                        AnimUtils.hide(tvPageCount);
                    } else if (tvPageCount.getVisibility() != View.VISIBLE) {
                        AnimUtils.show(tvPageCount, builder.build());
                    }
                }
            });
        }
    }
    

    相关文章

      网友评论

      本文标题:仿京东商品列表页码提示效果

      本文链接:https://www.haomeiwen.com/subject/ucxhvttx.html