工具篇——HorizontalListView(横向滑动的ListView)

代码如下:

package com.wy.test.other;

import android.content.Context;

import android.database.DataSetObserver;

import android.graphics.Rect;

import android.util.AttributeSet;

import android.view.GestureDetector;

import android.view.GestureDetector.OnGestureListener;

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(滑动和点击冲突已经解决)

*/

public class HorizontalListView extends AdapterView {

    float startX;

    float startY;

    public boolean mAlwaysOverrideTouch = true;

    protected ListAdapter mAdapter;

    private int mLeftViewIndex = -1;

    private int mRightViewIndex = 0;

    protected int mCurrentX;

    protected int mNextX;

    private int mMaxX = Integer.MAX_VALUE;

    private int mDisplayOffset = 0;

    protected Scroller mScroller;

    private GestureDetector mGesture;

    private Queue mRemovedViewQueue = new LinkedList();

    private OnItemSelectedListener mOnItemSelected;

    private OnItemClickListener mOnItemClicked;

    private OnItemLongClickListener mOnItemLongClicked;

    private boolean mDataChanged = false;

    public HorizontalListView(Context context, AttributeSet attrs) {

        super(context, attrs);

        initView();

    }

    private synchronized void initView() {

        mLeftViewIndex = -1;

        mRightViewIndex = 0;

        mDisplayOffset = 0;

        mCurrentX = 0;

        mNextX = 0;

        mMaxX = Integer.MAX_VALUE;

        mScroller = new Scroller(getContext());

        mGesture = new GestureDetector(getContext(), mOnGesture);

    }

    @Override

    public void setOnItemSelectedListener(OnItemSelectedListener listener) {

        mOnItemSelected = listener;

    }

    @Override

    public void setOnItemClickListener(OnItemClickListener listener) {

        mOnItemClicked = listener;

    }

    @Override

    public void setOnItemLongClickListener(OnItemLongClickListener listener) {

        mOnItemLongClicked = listener;

    }

    private DataSetObserver mDataObserver = new DataSetObserver() {

        @Override

        public void onChanged() {

            synchronized (HorizontalListView.this) {

                mDataChanged = true;

            }

            invalidate();

            requestLayout();

        }

        @Override

        public void onInvalidated() {

            reset();

            invalidate();

            requestLayout();

        }

    };

    @Override

    public ListAdapter getAdapter() {

        return mAdapter;

    }

    @Override

    public View getSelectedView() {

        return null;

    }

    @Override

    public void setAdapter(ListAdapter adapter) {

        if (mAdapter != null) {

            mAdapter.unregisterDataSetObserver(mDataObserver);

        }

        mAdapter = adapter;

        mAdapter.registerDataSetObserver(mDataObserver);

        reset();

    }

    private synchronized void reset() {

        initView();

        removeAllViewsInLayout();

        requestLayout();

    }

    @Override

    public void setSelection(int position) {

    }

    private void addAndMeasureChild(final View child, int viewPos) {

        LayoutParams params = child.getLayoutParams();

        if (params == null) {

            params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);

        }

        addViewInLayout(child, viewPos, params, true);

        child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),

                MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));

    }

    @Override

    protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {

        super.onLayout(changed, left, top, right, bottom);

        if (mAdapter == null) {

            return;

        }

        if (mDataChanged) {

            int oldCurrentX = mCurrentX;

            initView();

            removeAllViewsInLayout();

            mNextX = oldCurrentX;

            mDataChanged = false;

        }

        if (mScroller.computeScrollOffset()) {

            int scrollx = mScroller.getCurrX();

            mNextX = scrollx;

        }

        if (mNextX <= 0) {

            mNextX = 0;

            mScroller.forceFinished(true);

        }

        if (mNextX >= mMaxX) {

            mNextX = mMaxX;

            mScroller.forceFinished(true);

        }

        int dx = mCurrentX - mNextX;

        removeNonVisibleItems(dx);

        fillList(dx);

        positionItems(dx);

        mCurrentX = mNextX;

        if (!mScroller.isFinished()) {

            post(new Runnable() {

                @Override

                public void run() {

                    requestLayout();

                }

            });

        }

    }

    private void fillList(final int dx) {

        int edge = 0;

        View child = getChildAt(getChildCount() - 1);

        if (child != null) {

            edge = child.getRight();

        }

        fillListRight(edge, dx);

        edge = 0;

        child = getChildAt(0);

        if (child != null) {

            edge = child.getLeft();

        }

        fillListLeft(edge, dx);

    }

    private void fillListRight(int rightEdge, final int dx) {

        while (rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {

            View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);

            addAndMeasureChild(child, -1);

            rightEdge += child.getMeasuredWidth();

            if (mRightViewIndex == mAdapter.getCount() - 1) {

                mMaxX = mCurrentX + rightEdge - getWidth();

            }

            if (mMaxX < 0) {

                mMaxX = 0;

            }

            mRightViewIndex++;

        }

    }

    private void fillListLeft(int leftEdge, final int dx) {

        while (leftEdge + dx > 0 && mLeftViewIndex >= 0) {

            View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);

            addAndMeasureChild(child, 0);

            leftEdge -= child.getMeasuredWidth();

            mLeftViewIndex--;

            mDisplayOffset -= child.getMeasuredWidth();

        }

    }

    private void removeNonVisibleItems(final int dx) {

        View child = getChildAt(0);

        while (child != null && child.getRight() + dx <= 0) {

            mDisplayOffset += child.getMeasuredWidth();

            mRemovedViewQueue.offer(child);

            removeViewInLayout(child);

            mLeftViewIndex++;

            child = getChildAt(0);

        }

        child = getChildAt(getChildCount() - 1);

        while (child != null && child.getLeft() + dx >= getWidth()) {

            mRemovedViewQueue.offer(child);

            removeViewInLayout(child);

            mRightViewIndex--;

            child = getChildAt(getChildCount() - 1);

        }

    }

    private void positionItems(final int dx) {

        if (getChildCount() > 0) {

            mDisplayOffset += dx;

            int left = mDisplayOffset;

            for (int i = 0; i < getChildCount(); i++) {

                View child = getChildAt(i);

                int childWidth = child.getMeasuredWidth();

                child.layout(left, 0, left + childWidth, child.getMeasuredHeight());

                left += childWidth + child.getPaddingRight();

            }

        }

    }

    public synchronized void scrollTo(int x) {

        mScroller.startScroll(mNextX, 0, x - mNextX, 0);

        requestLayout();

    }

    /**

     * 上下滑动需要拦截,

     */

    @Override

    public boolean dispatchTouchEvent(MotionEvent ev) {

        boolean handled = super.dispatchTouchEvent(ev);

        handled |= mGesture.onTouchEvent(ev);

        getParent().requestDisallowInterceptTouchEvent(true);

        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:

                startX = (int) ev.getX();

                startY = (int) ev.getY();

                break;

            case MotionEvent.ACTION_MOVE:

                int endX = (int) ev.getX();

                int endY = (int) ev.getY();

                int dx = (int) (endX - startX);

                int dy = (int) (endY - startY);

                if (Math.abs(dx) + 50 > Math.abs(dy)) {

                } else {

                    // 上下滑动,需要拦截

                    getParent().requestDisallowInterceptTouchEvent(false);

                }

                break;

            case MotionEvent.ACTION_UP:

                break;

        }

        return handled | super.dispatchTouchEvent(ev);

    }

    protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,

                              float velocityY) {

        synchronized (HorizontalListView.this) {

            mScroller.fling(mNextX, 0, (int) -velocityX, 0, 0, mMaxX, 0, 0);

        }

        requestLayout();

        return true;

    }

    protected boolean onDown(MotionEvent e) {

        mScroller.forceFinished(true);

        return true;

    }

    private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {

        @Override

        public boolean onDown(MotionEvent e) {

            return HorizontalListView.this.onDown(e);

        }

        @Override

        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,

                               float velocityY) {

            return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY);

        }

        @Override

        public boolean onScroll(MotionEvent e1, MotionEvent e2,

                                float distanceX, float distanceY) {

            synchronized (HorizontalListView.this) {

                mNextX += (int) distanceX;

            }

            requestLayout();

            return true;

        }

        @Override

        public boolean onSingleTapConfirmed(MotionEvent e) {

            for (int i = 0; i < getChildCount(); i++) {

                View child = getChildAt(i);

                if (isEventWithinView(e, child)) {

                    if (mOnItemClicked != null) {

                        mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));

                    }

                    if (mOnItemSelected != null) {

                        mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));

                    }

                    break;

                }

            }

            return true;

        }

        @Override

        public void onLongPress(MotionEvent e) {

            int childCount = getChildCount();

            for (int i = 0; i < childCount; i++) {

                View child = getChildAt(i);

                if (isEventWithinView(e, child)) {

                    if (mOnItemLongClicked != null) {

                        mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));

                    }

                    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());

        }

    };

    //解决滑动和点击冲突问题

    @Override

    public boolean onInterceptTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:

                startX = ev.getX();

                startY = ev.getY();

                break;

            case MotionEvent.ACTION_MOVE:

                float endX = ev.getX();

                float endY = ev.getY();

                float dx = endX - startX;

                float dy = endY - startY;

                if (Math.abs(dx) - 10 > Math.abs(dy)) {

                    return true;

                } else if (Math.abs(dx) < Math.abs(dy) - 10) {

                    return true;

                }

                break;

            case MotionEvent.ACTION_UP:

                break;

        }

        return super.onInterceptTouchEvent(ev);

    }

}

在项目中的应用:

1.在xml布局文件中直接使用;


2.填充数据(跟普通的listview一样);

// 获取屏幕宽高

int screenWidth = getWindowManager().getDefaultDisplay().getWidth(); // 屏幕宽(像素,如:480px)

final String[] strings = new String[]{"零售", "零退", "批销", "批退", "调剂"};

HorizontalListViewAdapter horizontalListViewAdapter = new HorizontalListViewAdapter(getApplicationContext(), screenWidth, strings);

hlv.setAdapter(horizontalListViewAdapter);

hlv.setOnItemClickListener(new AdapterView.OnItemClickListener() {

    @Override

    public void onItemClick(AdapterView parent, View view, int position, long id) {

        ToastUtil.makeText(getApplicationContext(),strings[position]);

    }

});

2.1.adapter的代码

public class HorizontalListViewAdapter extends BaseAdapter {

    private final Context context;

    private final int screenWidth;

    private final String[] strings;

    public HorizontalListViewAdapter(Context applicationContext, int screenWidth, String[] strings) {

        this.context = applicationContext;

        this.screenWidth = screenWidth;

        this.strings = strings;

    }

    @Override

    public int getCount() {

        return strings.length;

    }

    @Override

    public Object getItem(int position) {

        return strings[position];

    }

    @Override

    public long getItemId(int position) {

        return position;

    }

    @Override

    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder holder;

        if (convertView == null) {

            convertView = View.inflate(getApplicationContext(), R.layout.item_horizontallistview, null);

            holder = new ViewHolder(convertView);

            convertView.setTag(holder);

        }else {

            holder = (ViewHolder) convertView.getTag();

        }

        //计算每条的宽度

        ViewGroup.LayoutParams layoutParams = holder.rl.getLayoutParams();

        layoutParams.width = screenWidth / 7 * 2;

        holder.rl.setLayoutParams(layoutParams);

        holder.tv.setText(strings[position]);

        return convertView;

    }

}

static class ViewHolder {

    @InjectView(R.id.tv)

    TextView tv;

    @InjectView(R.id.rl)

    RelativeLayout rl;

    ViewHolder(View view) {

        ButterKnife.inject(this, view);

    }

}

早计划,早准备,早完成。 欢迎关注!交流!Star!

GitHub:https://github.com/wangyang0313

微信公众号:一个灵活的胖子MrWang

CSDN:https://blog.csdn.net/qq941263013

20200430145304899.jpg

你可能感兴趣的:(工具篇——HorizontalListView(横向滑动的ListView))