横向ListView(六) —— 将自定义的横向ListView改成竖向

 横向转竖向比较容易步骤如下:

        1.把相应的left、right、width、height的值改成top、bottom、height、width的值

        2.将addAndMeasureChild()方法改为以下代码:

private void addAndMeasureChild(View child, int viewIndex) {
    LayoutParams params = child.getLayoutParams();
    params = params==null ? new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT):params;

    addViewInLayout(child, viewIndex, params, true);
    child.measure(MeasureSpec.makeMeasureSpec(getWidth()-getPaddingLeft()-getPaddingRight(), MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.UNSPECIFIED));
}

        3.修改ScrollBar的调用,把mScrollBar.showHorizontal()改成mScrollBar.showVertical()

if(canShowScrollBar) {
    //布局完所有的视图之后添加上滚动条的显示
    addAndMeasureChild(mScrollBar, getChildCount());
    if (adapter != null) {
        mScrollBar.showVertical(this, firstItemIndex, lastItemIndex, adapter.getCount(), headViewHeight, footViewHeight);
    } else {
        mScrollBar.showVertical(this, 0, 0, 0, headViewHeight, footViewHeight);
    }
}

以下是完整代码:

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.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.Scroller;

import com.hss.os.horizontallistview.ScrollBar;

import java.util.LinkedList;
import java.util.Queue;

/**
 * Created by sxyx on 2017/8/11.
 */

public class VerticalListView extends AdapterView<ListAdapter> {

    private Queue<View> cacheView = new LinkedList<>();//列表项缓存视图
    private ListAdapter adapter = null;
    private GestureDetector mGesture;

    private int firstItemIndex = 0;//显示的第一个子项的下标
    private int lastItemIndex = -1;//显示的最后的一个子项的下标
    private int scrollValue=0;//列表已经发生有效滚动的位移值
    private int hasToScrollValue=0;//接下来列表发生滚动所要达到的位移值
    private int maxScrollValue=Integer.MAX_VALUE;//列表发生滚动所能达到的最大位移值(这个由最后显示的列表项决定)
    private int minScrollValue=Integer.MIN_VALUE;//列表发生滚动所能达到的最小位移值(值为0表示不能发生类似下拉刷新的操作,负值表示可以)
    private int displayOffset=0;//列表显示的偏移值(用于矫正列表显示的所有子项的显示位置)

    private Scroller mScroller;
    private int firstItemTopEdge=0;//第一个子项的左边界
    private int lastItemBottomEdge=0;//最后一个子项的右边界

    private View headView;
    private View footView;
    private boolean hasHeadView=false;
    private boolean hasFootView=false;

    private ScrollBar mScrollBar;
    private int headViewHeight=0;//头视图宽度
    private int footViewHeight=0;//尾视图宽度
    private boolean canShowScrollBar=true;//是否需要显示滚动条

    private boolean canPullUp=true;//可以下拉
    private boolean canPullDown=true;//可以上拉


    public VerticalListView(Context context) {
        super(context);
        init(context);
    }

    public VerticalListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public VerticalListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public VerticalListView(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);
        mScrollBar=new ScrollBar(context);
    }



    private void initParams(){
        mScroller.forceFinished(true);//避免在滑动过程中变换视图内容时,出现列表无法滚动的情况
        removeAllViewsInLayout();
        if(adapter!=null&&lastItemIndex<adapter.getCount())
            hasToScrollValue=scrollValue;//保持显示位置不变
        else hasToScrollValue=0;//滚动到列表头
        scrollValue=0;//列表已经发生有效滚动的位移值
        firstItemIndex = 0;//显示的第一个子项的下标
        lastItemIndex = -1;//显示的最后的一个子项的下标
        maxScrollValue=Integer.MAX_VALUE;//列表发生滚动所能达到的最大位移值(这个由最后显示的列表项决定)
        // 列表发生滚动所能达到的最小位移值(值为0表示不能发生类似下拉刷新的操作,负值表示可以)
        if(!isCanPullDown()) minScrollValue = 0;
        else  minScrollValue = Integer.MIN_VALUE;
        displayOffset=0;//列表显示的偏移值(用于矫正列表显示的所有子项的显示位置)
        firstItemTopEdge=0;//第一个子项的左边界
        lastItemBottomEdge=0;//最后一个子项的右边界
        if(hasHeadView||hasFootView) {
            if (hasHeadView) {
                scrollValue = headView.getMeasuredHeight();
                headView.layout(0, 0, 0, 0);
                setHeadView(headView);
            }
            if (hasFootView) {
                footView.layout(0, 0, 0, 0);
                setFootView(footView);
            }
        }else  requestLayout();
    }


    private DataSetObserver mDataObserver = new DataSetObserver() {

        @Override
        public void onChanged() {
            //执行Adapter数据改变时的逻辑
            initParams();
        }

        @Override
        public void onInvalidated() {
            //执行Adapter数据失效时的逻辑
            initParams();
        }

    };

    @Override
    public ListAdapter getAdapter() {
        return adapter;
    }

    @Override
    public void setAdapter(ListAdapter adapter) {
        if(adapter!=null){
            adapter.registerDataSetObserver(mDataObserver);
        }
        if(this.adapter!=null){
            this.adapter.unregisterDataSetObserver(mDataObserver);
        }
        this.adapter=adapter;
        requestLayout();
    }

    @Override
    public View getSelectedView() {
        return null;
    }

    @Override
    public void setSelection(int i) {

    }

    private void addAndMeasureChild(View child, int viewIndex) {
        LayoutParams params = child.getLayoutParams();
        params = params==null ? new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT):params;

        addViewInLayout(child, viewIndex, params, true);
        child.measure(MeasureSpec.makeMeasureSpec(getWidth()-getPaddingLeft()-getPaddingRight(), MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.UNSPECIFIED));
    }



    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        //在执行布局之前需要先移除滚动条,以免影响其它视图的显示运算
        mScrollBar.remove(this);

        //需要先布局列表项再根据余下的空间布局列表头尾
        //布局列表项
        /*
 1.计算这一次整体滚动偏移量
 2.根据偏移量提取需要缓存视图
 3.根据偏移量显示新的列表项
 4.根据整体偏移值整顿所有列表项位置
 5.计算最大滚动位移值,记录已经发生有效滚动的位移值
 6.根据显示的最终效果,判断是否要居中显示
 */

        int dx=calculateScrollValue();
        removeNonVisibleItems(dx);
        showListItem(dx);
        adjustItems();
        //布局列表头、尾
        adjustHeadAndFootView(dx);
        calculateMaxScrollValue();

        if(canShowScrollBar) {
            //布局完所有的视图之后添加上滚动条的显示
            addAndMeasureChild(mScrollBar, getChildCount());
            if (adapter != null) {
                mScrollBar.showVertical(this, firstItemIndex, lastItemIndex, adapter.getCount(), headViewHeight, footViewHeight);
            } else {
                mScrollBar.showVertical(this, 0, 0, 0, headViewHeight, footViewHeight);
            }
        }

        //继续滚动
        if(!mScroller.isFinished()){
            //在onLayout中requestLayout()方法必须按以下形式调用,否则没有效果
            post(new Runnable(){
                @Override
                public void run() {
                    requestLayout();
                }
            });
        }else if(isRemoveFootView){//表示所有的计算都已经完成
            isRemoveFootView=false;
            //这是修补因移除尾视图而出现列表头空白的bug
            int startIndex=0;
            if(canShowScrollBar) startIndex=1;
            if(getChildCount()>startIndex){
                if(getChildAt(0).getTop()>getShowStartEdge()){
                    //表示列表头有出现空白
                    hasToScrollValue += getChildAt(0).getTop()-getShowStartEdge();
                    //在onLayout中requestLayout()方法必须按以下形式调用,否则没有效果
                    post(new Runnable(){
                        @Override
                        public void run() {
                            requestLayout();
                        }
                    });
                }
            }
        }
    }

    /**
 * 计算这一次整体滚动偏移量
 * @return
 */
    private int calculateScrollValue(){
        int dx=0;

        if(mScroller.computeScrollOffset()){
            hasToScrollValue = mScroller.getCurrY();
        }

        if(hasToScrollValue <= minScrollValue){
            hasToScrollValue = minScrollValue;
            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).getBottom()>=getShowEndEdge()) {
                    setMaxScrollValue(scrollValue + getChildAt(getChildCount() - 1).getBottom() - getShowEndEdge());
                }else{
                    setMaxScrollValue(0);
                }
            }else{
                setMaxScrollValue(Integer.MAX_VALUE);
            }
        }else{
            if(adapter!=null&&adapter.getCount()>0){
                // 修补bug:当尾视图足够宽能够占据所有可视区域时,
                // maxScrollValue的值没有改变,而导致滚动出现空白区域
                setMaxScrollValue(scrollValue + getChildAt(getChildCount() - 1).getBottom() - getShowEndEdge());
            }else {
                if (getChildCount() > 0
                        && getChildAt(getChildCount() - 1).getBottom() >= getShowEndEdge()) {
                    setMaxScrollValue(scrollValue + getChildAt(getChildCount() - 1).getBottom() - getShowEndEdge());
                }else{
                    setMaxScrollValue(0);
                }
            }
        }
    }

    /**
 * 根据偏移量提取需要缓存视图
 * @param dx
 */
    private void removeNonVisibleItems(int dx) {
        if(getListItemCount()>0) {
            //移除列表头
            View child = getChildAt(getStartItemIndex());
            while (getListItemCount()>0&&child != null && child.getBottom() + dx <= getShowStartEdge()) {
                displayOffset += child.getMeasuredHeight();
                cacheView.offer(child);
                removeViewInLayout(child);
                firstItemIndex++;
                child = getChildAt(getStartItemIndex());
            }

            //移除列表尾
            child = getChildAt(getEndItemIndex());
            while (getListItemCount()>0&&child != null && child.getTop() + dx >= getShowEndEdge()) {
                cacheView.offer(child);
                removeViewInLayout(child);
                lastItemIndex--;
                child = getChildAt(getEndItemIndex());
            }
        }
    }

    /**
 * 根据偏移量显示新的列表项
 * @param dx
 */
    private void showListItem(int dx) {
        if(adapter==null)return;

        int firstItemEdge = getFirstItemTopEdge()+dx;
        int lastItemEdge = getLastItemBottomEdge()+dx;
        displayOffset+=dx;//计算偏移量
        //显示列表头视图
        while(firstItemEdge > getShowStartEdge() && firstItemIndex-1 >= 0) {
            firstItemIndex--;//往前显示一个列表项
            View child = adapter.getView(firstItemIndex, cacheView.poll(), this);
            addAndMeasureChild(child, getStartItemIndex());
            firstItemEdge -= child.getMeasuredHeight();
            displayOffset -= child.getMeasuredHeight();
        }
        //显示列表未视图
        while(lastItemEdge < getShowEndEdge() && lastItemIndex+1 < adapter.getCount()) {
            lastItemIndex++;//往后显示一个列表项
            View child = adapter.getView(lastItemIndex, cacheView.poll(), this);
            addAndMeasureChild(child, getEndItemIndex()+1);
            lastItemEdge += child.getMeasuredHeight();
        }
    }

    /**
 * 调整各个item的位置
 */
    private void adjustItems() {
        if(getListItemCount() > 0){
            int top = displayOffset+getShowStartEdge();
            int left = getPaddingLeft();
            int endIndex = getEndItemIndex();
            int startIndex = getStartItemIndex();

            int childWidth,childHeight;
            for(int i=startIndex;i<=endIndex;i++){
                View child = getChildAt(i);
                childWidth = child.getMeasuredWidth();
                childHeight = child.getMeasuredHeight();
                child.layout(left, top, left + childWidth, top + childHeight);
                top += childHeight;
            }

            firstItemTopEdge=getChildAt(getStartItemIndex()).getTop();
            lastItemBottomEdge=getChildAt(getEndItemIndex()).getBottom();
        }
    }

    /**
 * 调整列表头、尾
 */
    private void adjustHeadAndFootView(int dx){
        headViewHeight=footViewHeight=0;
        if(hasHeadView){
            int top,bottom;
            if(getListItemCount()>0){
                bottom=firstItemTopEdge;
            }else{
                bottom=headView.getBottom()+dx;
            }
            top=bottom-headView.getMeasuredHeight();
            headView.layout(getPaddingLeft(), top, headView.getMeasuredWidth()+getPaddingLeft(), bottom);
            headViewHeight=headView.getMeasuredHeight();
        }

        if(hasFootView){
            int top,bottom;
            if(getListItemCount()>0){
                top=getChildAt(getEndItemIndex()).getBottom();
            }else{
                //添加headView.getRight()>=getShowStartEdge()条件用于修补原先的bug
                if(hasHeadView&&headView.getBottom()>=getShowStartEdge())
                    top=headView.getBottom();
                else {
                    if(footView.getTop()==0&&dx==0){//第一次赋值
                        top=getShowStartEdge();
                    }else{
                        top=footView.getTop()+dx;
                    }
                    //调整以修补显示bug
                    if(hasHeadView) {//重新调整headView的位置
                        headView.layout(getPaddingLeft(),top-headView.getMeasuredWidth(), headView.getMeasuredWidth() + getPaddingLeft(),top);
                    }
                }
            }
            bottom=top+footView.getMeasuredHeight();
            footView.layout(getPaddingLeft(), top, footView.getMeasuredWidth()+getPaddingLeft(),bottom);
            footViewHeight=footView.getMeasuredHeight();
        }

    }




    //以下八个方法为概念性封装方法,有助于往后的扩展和维护


    /**
 * 获得列表视图中item View的总数
 * @return
 */
    private int getListItemCount(){
        int itemCount=getChildCount();
        if(hasHeadView)itemCount-=1;
        if(hasFootView)itemCount-=1;
        return itemCount;
    }
    /**
 * 获得列表视图中第一个item View下标
 * @return
 */
    private int getStartItemIndex(){
        if(hasHeadView) return 1;
        return 0;
    }
    /**
 * 获得列表视图中最后一个item View下标
 * @return
 */
    private int getEndItemIndex(){
        if(hasFootView) return getChildCount()-2;
        return getChildCount()-1;
    }
    /**
 * 获得列表视图中第一个item View上边界值
 * @return
 */
    private int getFirstItemTopEdge(){
        if(getListItemCount()>0) {
            return firstItemTopEdge;
        }else{
            if(hasHeadView) return headView.getBottom();
            else return getShowStartEdge();
        }
    }
    /**
 * 获得列表视图中最后一个item View下边界值
 * @return
 */
    private int getLastItemBottomEdge(){
        if(getListItemCount()>0) {
            return lastItemBottomEdge;
        }else{
            if(hasFootView) return footView.getTop();
            else return getShowStartEdge();
        }
    }
    /**
 * 取得视图可见区域的左边界
 * @return
 */
    private int getShowStartEdge(){
        return getPaddingTop();
    }
    /**
 * 取得视图可见区域的右边界
 * @return
 */
    private int getShowEndEdge(){
        return getHeight()-getPaddingBottom();
    }
    /**
 * 取得视图可见区域的高度
 * @return
 */
    private int getShowHeight(){
        return getHeight()-getPaddingTop()-getPaddingBottom();
    }


    public void setMaxScrollValue(int maxScrollValue) {
        if(isCanPullUp()&&maxScrollValue!=Integer.MAX_VALUE) {
            if(isSpringBack){//在执行回弹操作
                this.maxScrollValue = Integer.MAX_VALUE;
            }else if(mScroller.isFinished()){//手动执行滚动操作
                this.maxScrollValue = Integer.MAX_VALUE;
            }else{
                if(maxScrollValue<0)this.maxScrollValue=0;
                else this.maxScrollValue = maxScrollValue;
            }
        }else{
            if(maxScrollValue<0)this.maxScrollValue=0;
            else this.maxScrollValue = maxScrollValue;
        }
    }







    public void setHeadView(View view){
        if(view!=null) {
            int headBottom=-1;
            int height=0;
            if (hasHeadView&&headView!=null) {
                headBottom=headView.getBottom();
                height=headView.getHeight();
                removeViewInLayout(headView);
            }
            hasHeadView = true;
            headView=view;
            addAndMeasureChild(headView, 0);

            if(getListItemCount()>0) {//有列表内容
                if (headBottom == -1) {
                    //新增列表头
                    if (firstItemIndex == 0) {//第一个显示的是第一个列表项
                        //滚动整个列表,让其显示完整列表头(让列表往回滚)
                        scrollValue = headView.getMeasuredHeight()
                                + getShowStartEdge() - firstItemTopEdge;
                        hasToScrollValue=0;
                    } else {//不是显示第一个列表项
                        //不滚动列表项,增加历史滚动值
                        hasToScrollValue += headView.getMeasuredHeight();
                        scrollValue = hasToScrollValue;
                    }
                } else {
                    //替换列表头
                    hasToScrollValue += headView.getMeasuredHeight()-height;
                }
            }
            setMaxScrollValue(Integer.MAX_VALUE);
            requestLayout();
        }
    }
    public void removeHeadView(){
        if(hasHeadView&&headView!=null){
            hasHeadView=false;
            int top=headView.getTop();
            int height=headView.getMeasuredHeight();
            removeViewInLayout(headView);

            if(headView.getBottom()>=getShowStartEdge()) {//列表头有显示
                scrollValue = -(height+top-getShowStartEdge());
                hasToScrollValue=0;
            }else{
                scrollValue-=height;
                hasToScrollValue-=height;
            }
            requestLayout();
        }else{
            hasHeadView=false;
        }
    }

    public void setFootView(View view){
        if(view!=null) {
            if (hasFootView&&footView!=null) {
                removeViewInLayout(footView);
            }
            hasFootView=true;
            footView=view;
            addAndMeasureChild(footView, -1);
            requestLayout();
        }
    }
    private boolean isRemoveFootView=false;
    public void removeFootView(){
        if(hasFootView&&footView!=null){
            hasFootView=false;
            int top=footView.getTop();
            mScroller.forceFinished(true);//用于修补由于快速华东的过程中移除尾视图而出现bug
            removeViewInLayout(footView);

            if(top<getShowEndEdge()) {//表示需要回退
                hasToScrollValue -= getShowEndEdge()-top;
                isRemoveFootView=true;
            }
            requestLayout();
        }else{
            hasFootView=false;
        }
    }

    /**
 * 在onTouchEvent处理事件,让子视图优先消费事件
 * @param event
 * @return
 */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(dealTouchEvent(event)) return true;
        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) {
            minScrollValue=0;//在快速滑动时,不能出现类似下拉刷新的操作
            mScroller.fling(0, scrollValue, 0, (int)-velocityY, 0, 0, 0, maxScrollValue);
            requestLayout();
            return true;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2,
 float distanceX, float distanceY) {
            if(isCanPullDown()) {
                minScrollValue = Integer.MIN_VALUE;//手动滑动时可以出现类似下拉刷新的操作
            }else{
                minScrollValue = 0;
            }
            synchronized(VerticalListView.this){
                hasToScrollValue += (int)calDistanceVal(distanceY);
            }
            requestLayout();
            return true;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            int startIndex=0;
            if(canShowScrollBar) startIndex=1;
            for(int i=0;i<getChildCount();i++){
                View child = getChildAt(i);
                if(child==mScrollBar) break;
                if (isEventWithinView(e, child)) {
                    if(hasHeadView&&i==0){
                        //点击列表头
                        if(OnHeadViewClickListener!=null)
                            OnHeadViewClickListener.onClick(child);
                    }else if(hasFootView&&i==getChildCount()-(1+startIndex)){
                        //点击列表尾
                        if(OnFootViewClickListener!=null)
                            OnFootViewClickListener.onClick(child);
                    }else {
                        int position=firstItemIndex + i;
                        if(hasHeadView) position--;
                        if (getOnItemClickListener() != null) {
                            getOnItemClickListener().onItemClick(VerticalListView.this, child, position, adapter.getItemId(position));
                        }
                        if (getOnItemSelectedListener() != null) {
                            getOnItemSelectedListener().onItemSelected(VerticalListView.this, child, position, adapter.getItemId(position));
                        }
                    }
                    break;
                }

            }
            return true;
        }

        @Override
        public void onLongPress(MotionEvent e) {
            int startIndex=0;//这个值用于处理滚动条的影响
            if(canShowScrollBar) startIndex=1;
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                if(child==mScrollBar) return;
                if (isEventWithinView(e, child)) {
                    if(hasHeadView&&i==0){
                        //长按列表头
                        if(OnHeadViewLongClickListener!=null)
                            OnHeadViewLongClickListener.onLongClick(child);
                    }else if(hasFootView&&i==getChildCount()-(1+startIndex)){
                        //长按列表尾
                        if(OnFootViewLongClickListener!=null)
                            OnFootViewLongClickListener.onLongClick(child);
                    } else {
                        int position=firstItemIndex + i;
                        if(hasHeadView) position--;
                        if (getOnItemLongClickListener() != null) {
                            getOnItemLongClickListener().onItemLongClick(VerticalListView.this, child, position, adapter.getItemId(position));
                        }
                    }
                    break;
                }

            }
        }

        private boolean isEventWithinView(MotionEvent e, View child) {
            Rect viewRect = new Rect();
            int[] childPosition = new int[2];
            child.getLocationOnScreen(childPosition);
            int left = childPosition[0];
            int right = left + child.getWidth();
            int top = childPosition[1];
            int bottom = top + child.getHeight();
            viewRect.set(left, top, right, bottom);
            return viewRect.contains((int) e.getRawX(), (int) e.getRawY());
        }
    };



    private boolean isEventDownIntercept=false;
    private boolean isSpringBack=false;
    private boolean isCanPullDownInthis=true;
    /**
 * 在手势解析器之前拦截事件
 * @param event
 * @return
 */
    private boolean dealTouchEvent(MotionEvent event){
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                isCanPullDownInthis=true;
                int startIndex=0;
                if(canShowScrollBar) startIndex=1;
                if(getChildCount()==startIndex){
                    isCanPullDownInthis=false;
                }else{
                    if(getChildAt(getChildCount()-(1+startIndex)).getBottom()<getShowEndEdge()){
                        isCanPullDownInthis=false;
                    }
                }
                isEventDownIntercept=springBack();
                if (isEventDownIntercept) return true;
                break;
            case MotionEvent.ACTION_MOVE:
                return isEventDownIntercept;
            case MotionEvent.ACTION_UP:
                return springBack();
        }
        return false;
    }

    /**
 * 采用阻尼式算法,重新提取移动值
 * @param distanceVal
 * @return
 */
    private float calDistanceVal(float distanceVal){
        if(!isCanPullDownInthis&&distanceVal>0){
            return 0;
        }
        int startIndex=0;//这个值用于处理滚动条的影响
        if(canShowScrollBar) startIndex=1;
        if(getChildCount()>startIndex){
            int dis=getChildAt(0).getTop()-getShowStartEdge();
            if(dis>0){//执行头部拉伸阻尼式计算
                if(distanceVal>=0) distanceVal = distanceVal/2;
                else {
                    float sc = 1 - dis / (getShowHeight() * 0.4f);
                    sc = sc < 0 ? 0 : sc;
                    distanceVal = distanceVal * sc;
                }
                if(onPullDownListener!=null)
                    onPullDownListener.onScroll(distanceVal);
            }else {
                dis = getShowEndEdge() - getChildAt(getChildCount() - (1 + startIndex)).getBottom();
                if (dis > 0 && getFirstItemTopEdge() < getShowStartEdge()) {
                    //执行尾部拉伸阻尼式计算
                    if (distanceVal <= 0) distanceVal = distanceVal / 2;
                    else {
                        float sc = 1 - dis / (getShowHeight() * 0.4f);
                        sc = sc < 0 ? 0 : sc;
                        distanceVal = distanceVal * sc;
                    }
                    if (onPullUpListener != null)
                        onPullUpListener.onScroll(distanceVal);
                }
            }
        }
        return distanceVal;
    }

    /**
 * 执行回滚操作
 * @return
 */
    private boolean springBack(){
        isSpringBack=false;
        int startIndex=0;
        if(canShowScrollBar) startIndex=1;
        if(getChildCount()>startIndex){
            int dis=getChildAt(0).getTop()-getShowStartEdge();
            if(dis>0){//执行头部回滚操作
                mScroller.startScroll(0, scrollValue, 0, dis, 500);
                isSpringBack=true;
                requestLayout();
                if(onPullDownListener!=null
                        &&(dis>getShowHeight()/5||dis>120))
                    onPullDownListener.onPullEvent();
                return true;
            }
            dis=getShowEndEdge()-getChildAt(getChildCount()-(1+startIndex)).getBottom();
            if(dis>0&&getFirstItemTopEdge()<getShowStartEdge()){
                //执行尾部回滚操作
                mScroller.startScroll(0, scrollValue, 0, -dis, 500);
                isSpringBack=true;
                requestLayout();
                if(onPullUpListener!=null
                        &&(dis>getShowHeight()/5||dis>120))
                    onPullUpListener.onPullEvent();
                return true;
            }
        }

        return false;
    }

    private OnPullListener onPullDownListener = null;
    private OnPullListener onPullUpListener = null;


    private OnClickListener OnHeadViewClickListener;
    private OnClickListener OnFootViewClickListener;
    private OnLongClickListener OnHeadViewLongClickListener;
    private OnLongClickListener OnFootViewLongClickListener;

    public void setOnPullDownListener(OnPullListener onPullDownListener) {
        this.onPullDownListener = onPullDownListener;
    }

    public void setOnPullUpListener(OnPullListener onPullUpListener) {
        this.onPullUpListener = onPullUpListener;
    }

    public void setOnHeadViewClickListener(OnClickListener onHeadViewClickListener) {
        OnHeadViewClickListener = onHeadViewClickListener;
    }

    public void setOnFootViewClickListener(OnClickListener onFootViewClickListener) {
        OnFootViewClickListener = onFootViewClickListener;
    }

    public void setOnHeadViewLongClickListener(OnLongClickListener onHeadViewLongClickListener) {
        OnHeadViewLongClickListener = onHeadViewLongClickListener;
    }

    public void setOnFootViewLongClickListener(OnLongClickListener onFootViewLongClickListener) {
        OnFootViewLongClickListener = onFootViewLongClickListener;
    }

    public interface OnPullListener{
        /**
 * 滚动时执行
 * @param dis 移动的位移值
 */
        void onScroll(float dis);

        /**
 * 事件确认(类似下拉刷新事件触发)
 */
        void onPullEvent();
    }



    public synchronized void scrollTo(int x) {
        mScroller.startScroll(hasToScrollValue, 0, x - hasToScrollValue, 0);
        requestLayout();
    }


    public boolean isCanShowScrollBar() {
        return canShowScrollBar;
    }

    public void setCanShowScrollBar(boolean canShowScrollBar) {
        this.canShowScrollBar = canShowScrollBar;
    }

    public boolean isCanPullDown() {
        return canPullDown;
    }

    public void setCanPullDown(boolean canPullDown) {
        this.canPullDown = canPullDown;
    }

    public boolean isCanPullUp() {
        return canPullUp;
    }

    public void setCanPullUp(boolean canPullUp) {
        this.canPullUp = canPullUp;
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在 Xamarin 中,ListView 是一个非常常见的控件,用于显示一个竖直滚动的列表。但如果我们需要实现一个横向滚动的列表,该怎么办呢?这时候,我们可以使用自定义的 ItemsControl 来实现这个功能。 ItemsControl 是一个用于显示一个集合的控件,它的每个元素都可以自定义显示方式。在本文中,我们会通过自定义 ItemsControl,实现一个横向滚动的列表。 首先,我们需要创建一个自定义控件,继承自 ItemsControl。在这个控件中,我们需要对 ItemContainerStyle 和 ItemsPanel 进行定义。 ItemContainerStyle 用于定义每个元素的样式,我们可以设置元素的大小、边距等属性。在本例中,我们设置元素宽度为 100,高度为自适应,并设置左右边距为 5。 ItemsPanel 用于定义元素的排列方式。在本例中,我们使用一个 StackPanel,设置 Orientation 为 Horizontal,这样子项就会水平排列。 下面是完整的代码实现: ```csharp using Xamarin.Forms; namespace MyNamespace { public class HorizontalItemsControl : ItemsControl { public HorizontalItemsControl() { // 设置样式 ItemContainerStyle = new Style(typeof(ViewCell)) { Setters = { new Setter { Property = ViewCell.WidthRequestProperty, Value = 100 }, new Setter { Property = ViewCell.MarginProperty, Value = new Thickness(5, 0) } } }; // 设置子项排列方式 ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Horizontal); } } } ``` 使用起来也非常简单,只需要在 XAML 中声明这个控件即可: ```xml <local:HorizontalItemsControl ItemsSource="{Binding MyItems}"> <local:HorizontalItemsControl.ItemTemplate> <DataTemplate> <!-- 这里放置每个元素的显示内容 --> </DataTemplate> </local:HorizontalItemsControl.ItemTemplate> </local:HorizontalItemsControl> ``` 其中,MyItems 指定了要显示的数据源,ItemTemplate 指定了每个元素的显示方式。 这样,我们就实现了一个简单的横向滚动列表。当然,这只是一个简单的示例,实际使用中可能还需要进行更多的定制和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xxpr_ybgg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值