博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
横向ListView (二)—— 添加快速滚动功能及item相关事件实现
阅读量:5975 次
发布时间:2019-06-19

本文共 12905 字,大约阅读时间需要 43 分钟。

hot3.png

在读本文之前,请先阅读博文

 

之前的文章已经介绍了横向lListView的基础实现逻辑,在这里我将介绍快速滚动实现及item相关事件实现

 

列表的快速滚动的实现主要依赖于android官方提供的android.widget.Scroller类,具体实现需要以下步骤:

    1.捕获快速滑动事件,并启动快速滑动计算(Scroller的功能)

    2.使用Scroller计算一次发生滚动的位移值,刷新视图

    3.如果整体滑动还未停止(即Scroller的滚动计算还未结束),则重复执行步骤2

    4.捕获按下事件,实现当用户按下时停止自动快速滚动操作

如对Scroller的工作原理不了解的,可以参考以下文章:

对于ListView,item需要响应的事件比较重要的就两个,点击和长按,具体实现如下:

    1.点击事件:在OnGestureListener中添加public boolean onSingleTapConfirmed(MotionEvent e)方法的实现,以响应点击事件

    2.长按事件:在OnGestureListener中添加public void onLongPress(MotionEvent e) 方法的实现,以响应长按事件

 

先上完整代码:

package com.hss.os.horizontallistview.history_version;import android.content.Context;import android.database.DataSetObserver;import android.graphics.Rect;import android.os.Build;import android.support.annotation.RequiresApi;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;import android.widget.AdapterView;import android.widget.ListAdapter;import android.widget.Scroller;import java.util.LinkedList;import java.util.Queue;/** * 为横向ListView添加快速滚动功能及item相关事件实现 * * Created by sxyx on 2017/8/8. */public class HorizontalListView2 extends AdapterView
{ private ListAdapter adapter = null; private GestureDetector mGesture; private Queue
cacheView = new LinkedList<>();//列表项缓存视图 private int firstItemIndex = 0;//显示的第一个子项的下标 private int lastItemIndex = -1;//显示的最后的一个子项的下标 private int scrollValue=0;//列表已经发生有效滚动的位移值 private int hasToScrollValue=0;//接下来列表发生滚动所要达到的位移值 private int maxScrollValue=Integer.MAX_VALUE;//列表发生滚动所能达到的最大位移值(这个由最后显示的列表项决定) private int displayOffset=0;//列表显示的偏移值(用于矫正列表显示的所有子项的显示位置) private Scroller mScroller; private int firstItemLeftEdge=0;//第一个子项的左边界 private int lastItemRightEdge=0;//最后一个子项的右边界 public HorizontalListView2(Context context) { super(context); init(context); } public HorizontalListView2(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public HorizontalListView2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public HorizontalListView2(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context); } private void init(Context context){ mGesture = new GestureDetector(getContext(), mOnGesture); mScroller=new Scroller(context); } private void initParams(){        mScroller.forceFinished(true);//避免在滑动过程中变换视图内容时,出现列表无法滚动的情况 removeAllViewsInLayout(); if(adapter!=null&&lastItemIndex
>>>>left:"+left+" top:"+top+" right:"+right+" bottom:"+bottom); //需要先布局列表项再根据余下的空间布局列表头尾 //布局列表项 /* 1.计算这一次整体滚动偏移量 2.根据偏移量提取需要缓存视图 3.根据偏移量显示新的列表项 4.根据整体偏移值整顿所有列表项位置 5.计算最大滚动位移值,记录已经发生有效滚动的位移值 6.根据显示的最终效果,判断是否要居中显示 */ int dx=calculateScrollValue(); removeNonVisibleItems(dx); showListItem(dx); adjustItems(); calculateMaxScrollValue(); //继续滚动 if(!mScroller.isFinished()){ post(new Runnable(){ @Override public void run() { requestLayout(); } }); } } /** * 计算这一次整体滚动偏移量 * @return */ private int calculateScrollValue(){ int dx=0; if(mScroller.computeScrollOffset()){ hasToScrollValue = mScroller.getCurrX(); } if(hasToScrollValue<=0){ hasToScrollValue=0; mScroller.forceFinished(true); } if(hasToScrollValue >= maxScrollValue) { hasToScrollValue = maxScrollValue; mScroller.forceFinished(true); } dx=hasToScrollValue-scrollValue; scrollValue=hasToScrollValue; return -dx; } /** * 计算最大滚动值 */ private void calculateMaxScrollValue(){ if(getListItemCount()>0) { if(lastItemIndex==adapter.getCount()-1) {
//已经显示了最后一项 if(getChildAt(getChildCount() - 1).getRight()>=getShowEndEdge()) { maxScrollValue = scrollValue + getChildAt(getChildCount() - 1).getRight() - getShowEndEdge(); }else{ maxScrollValue=0; } } }else{ if(adapter!=null&&adapter.getCount()>0){ }else { if (getChildCount() > 0 && getChildAt(getChildCount() - 1).getRight() >= getShowEndEdge()) { maxScrollValue = scrollValue + getChildAt(getChildCount() - 1).getRight() - getShowEndEdge(); } else { maxScrollValue = 0; } } } } /** * 根据偏移量提取需要缓存视图 * @param dx */ private void removeNonVisibleItems(int dx) { if(getListItemCount()>0) { //移除列表头 View child = getChildAt(getStartItemIndex()); while (getListItemCount()>0&&child != null && child.getRight() + dx <= getShowStartEdge()) { displayOffset += child.getMeasuredWidth(); cacheView.offer(child); removeViewInLayout(child); firstItemIndex++; child = getChildAt(getStartItemIndex()); } //移除列表尾 child = getChildAt(getEndItemIndex()); while (getListItemCount()>0&&child != null && child.getLeft() + dx >= getShowEndEdge()) { cacheView.offer(child); removeViewInLayout(child); lastItemIndex--; child = getChildAt(getEndItemIndex()); } } } /** * 根据偏移量显示新的列表项 * @param dx */ private void showListItem(int dx) { if(adapter==null)return; int firstItemEdge = getFirstItemLeftEdge()+dx; int lastItemEdge = getLastItemRightEdge()+dx; displayOffset+=dx;//计算偏移量 //显示列表头视图 while(firstItemEdge > getShowStartEdge() && firstItemIndex-1 >= 0) { firstItemIndex--;//往前显示一个列表项 View child = adapter.getView(firstItemIndex, cacheView.poll(), this); addAndMeasureChild(child, getStartItemIndex()); firstItemEdge -= child.getMeasuredWidth(); displayOffset -= child.getMeasuredWidth(); } //显示列表未视图 while(lastItemEdge < getShowEndEdge() && lastItemIndex+1 < adapter.getCount()) { lastItemIndex++;//往后显示一个列表项 View child = adapter.getView(lastItemIndex, cacheView.poll(), this); addAndMeasureChild(child, getEndItemIndex()+1); lastItemEdge += child.getMeasuredWidth(); } } /** * 调整各个item的位置 */ private void adjustItems() { if(getListItemCount() > 0){ int left = displayOffset+getShowStartEdge(); int top = getPaddingTop(); int endIndex = getEndItemIndex(); int childWidth,childHeight; for(int i=0;i<=endIndex;i++){ View child = getChildAt(i); childWidth = child.getMeasuredWidth(); childHeight = child.getMeasuredHeight(); child.layout(left, top, left + childWidth, top + childHeight); left += childWidth; } firstItemLeftEdge=getChildAt(getStartItemIndex()).getLeft(); lastItemRightEdge=getChildAt(getEndItemIndex()).getRight(); } } //以下八个方法为概念性封装方法,有助于往后的扩展和维护 /** * 获得列表视图中item View的总数 * @return */ private int getListItemCount(){ int itemCount=getChildCount(); return itemCount; } /** * 获得列表视图中第一个item View下标 * @return */ private int getStartItemIndex(){ return 0; } /** * 获得列表视图中最后一个item View下标 * @return */ private int getEndItemIndex(){ return getChildCount()-1; } /** * 获得列表视图中第一个item View左边界值 * @return */ private int getFirstItemLeftEdge(){ if(getListItemCount()>0) { return firstItemLeftEdge; }else{ return 0; } } /** * 获得列表视图中最后一个item View右边界值 * @return */ private int getLastItemRightEdge(){ if(getListItemCount()>0) { return lastItemRightEdge; }else{ return 0; } } /** * 取得视图可见区域的左边界 * @return */ private int getShowStartEdge(){ return getPaddingLeft(); } /** * 取得视图可见区域的右边界 * @return */ private int getShowEndEdge(){ return getWidth()-getPaddingRight(); } /** * 取得视图可见区域的宽度 * @return */ private int getShowWidth(){ return getWidth()-getPaddingLeft()-getPaddingRight(); } /** * 在onTouchEvent处理事件,让子视图优先消费事件 * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { return mGesture.onTouchEvent(event); } private GestureDetector.OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDown(MotionEvent e) { mScroller.forceFinished(true);//点击时停止滚动 return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { mScroller.fling(hasToScrollValue, 0, (int)-velocityX, 0, 0, maxScrollValue, 0, 0); requestLayout(); return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { synchronized(HorizontalListView2.this){ hasToScrollValue += (int)distanceX; } requestLayout(); return true; } @Override public boolean onSingleTapConfirmed(MotionEvent e) { for(int i=0;i

 

列表的快速滚动的实现主要依赖于android官方提供的android.widget.Scroller类,具体实现需要以下步骤:

    1.捕获快速滑动事件,并启动快速滑动计算(Scroller的功能)

@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,                       float velocityY) {    mScroller.fling(hasToScrollValue, 0, (int)-velocityX, 0, 0, maxScrollValue, 0, 0);    requestLayout();    return true;}

    在OnGestureListener中添加public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)方法,以捕获快速滑动事件,调用Scroller.fling()启动快速滑动计算,然后调用requestLayout()要求重新布局界面,已实现快速滑动效果。

    2.使用Scroller计算一次发生滚动的位移值,刷新视图

    在未实现快速滑动之前calculateScrollValue()方法实现如下:

/** * 计算这一次整体滚动偏移量 * @return */private int calculateScrollValue(){    int dx=0;    hasToScrollValue=hasToScrollValue<0? 0:hasToScrollValue;    hasToScrollValue=hasToScrollValue>maxScrollValue? maxScrollValue:hasToScrollValue;    dx=hasToScrollValue-scrollValue;    scrollValue=hasToScrollValue;    return -dx;}

    以下代码是为了实现快速滑动而做出的修改:

/** * 计算这一次整体滚动偏移量 * @return */private int calculateScrollValue(){    int dx=0;    if(mScroller.computeScrollOffset()){        hasToScrollValue = mScroller.getCurrX();    }    if(hasToScrollValue<=0){        hasToScrollValue=0;        mScroller.forceFinished(true);    }    if(hasToScrollValue >= maxScrollValue) {        hasToScrollValue = maxScrollValue;        mScroller.forceFinished(true);    }    dx=hasToScrollValue-scrollValue;    scrollValue=hasToScrollValue;    return -dx;}

    主要是调用Scroller计算需要发生滚动的位移值,以及在滚动到边界上的时候,让Scroller停止计算

    3.如果整体滑动还未停止(即Scroller的滚动计算还未结束),则重复执行步骤2

        这一步只需要在protected void onLayout(boolean changed, int left, int top, int right, int bottom)方法中添加以下代码即可

//继续滚动if(!mScroller.isFinished()){    post(new Runnable(){        @Override        public void run() {            requestLayout();        }    });}

       Scroller的整体滚动计算还未完成则调用 requestLayout()不断重复刷新界面,直到整体滚动完成

    4.捕获按下事件,实现当用户按下时停止自动快速滚动操作

@Overridepublic boolean onDown(MotionEvent e) {    mScroller.forceFinished(true);//点击时停止滚动    return true;}

    在OnGestureListener的public boolean onDown(MotionEvent e) 方法中添加mScroller.forceFinished(true); 用于告诉Scroller整体滚动计算需要停止,借此停止整个界面的滑动

对于ListView,item需要响应的事件比较重要的就两个,点击和长按,具体实现如下:

    1.点击事件:在OnGestureListener中添加public boolean onSingleTapConfirmed(MotionEvent e)方法的实现,以响应点击事件

@Overridepublic boolean onSingleTapConfirmed(MotionEvent e) {    for(int i=0;i

    2.长按事件:在OnGestureListener中添加public void onLongPress(MotionEvent e) 方法的实现,以响应长按事件

@Overridepublic void onLongPress(MotionEvent e) {    int childCount = getChildCount();    for (int i = 0; i < childCount; i++) {        View child = getChildAt(i);        if (isEventWithinView(e, child)) {            int position=firstItemIndex + i;            if (getOnItemLongClickListener() != null) {                getOnItemLongClickListener().onItemLongClick(HorizontalListView2.this, child, position, adapter.getItemId(position));            }            break;        }    }}

 

 

 

 

转载于:https://my.oschina.net/u/3614895/blog/1504435

你可能感兴趣的文章
循序渐进Python3(十)-- 4 -- paramiko
查看>>
codeforces Rockethon 2015 C Second price auction [想法]
查看>>
[1]区分event对象中的[clientX,offsetX,screenX,pageX]
查看>>
发送 email 过程
查看>>
城市里的间谍B901
查看>>
sass方式实现颜色平铺(红色--->紫色)
查看>>
JS入门
查看>>
每天几条java(7)
查看>>
【leetcode】3 SUM
查看>>
根据经纬度坐标计算两点间几何距离 - 椰子树下 - CSDN博客
查看>>
(转)Thread.setDaemon设置说明
查看>>
vue自定义轮播图组件 swiper
查看>>
百度AI开放平台 UNIT平台开发在线客服 借助百度的人工智能如何开发一个在线客服系统...
查看>>
完成注册功能
查看>>
[导入]让你的WAP网站有更好的兼容性
查看>>
.NET Exceptionless 本地部署踩坑记录
查看>>
TOMCAT 的 CONTEXT
查看>>
航电OJ-2544最短路
查看>>
CF772E Verifying Kingdom
查看>>
POJ 3417 Network
查看>>