/*
* HorizontalListView.java v1.5
*
*
* The MIT License
* Copyright (c) 2011 Paul Soucy (paul@dev-smart.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/ import java.util.LinkedList;
import java.util.Queue;
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; public class HorizontalListView extends AdapterView<ListAdapter> { 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<View> mRemovedViewQueue = new LinkedList<View>();
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(AdapterView.OnItemSelectedListener listener) {
mOnItemSelected = listener;
} @Override
public void setOnItemClickListener(AdapterView.OnItemClickListener listener) {
mOnItemClicked = listener;
} @Override
public void setOnItemLongClickListener(AdapterView.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.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
} 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 scrollToZero() {
mCurrentX = 0;
} public synchronized void scrollToScreen(int x) {
mCurrentX += x;
} @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = super.dispatchTouchEvent(ev);
handled |= mGesture.onTouchEvent(ev);
return handled;
} 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());
}
}; public int getmLeftViewIndex() {
return mLeftViewIndex;
} public int getmRightViewIndex() {
return mRightViewIndex;
} }

水平ListView的更多相关文章

  1. 水平ListView类

    package com.hztbc.android.HorizontalListView; /* * HorizontalListView.java v1.5 * *  * The MIT Licen ...

  2. 解决水平ListView在ScrollView中出现的滑动冲突

      解决的问题有两个:  1)实现水平滑动的ListView.重写AdapterView,上代码: package com.liucanwen.horizontallistview.view; imp ...

  3. 多于ListView同步滚动

    简介: 发展过程中可能遇到的2一个或多个其他listview为了用相应的关系保持滚动的情况下一起,本文演示了这种效应为大家. 功效: 实现原理: 在滚动当中不论什么一个ListView的时候,同一时候 ...

  4. [Android Pro] 超能RecyclerView组件使用

    RecyclerView最强大的功能在于秒变功能,只需要改动很少的代码就可以实现ListView,GridView及水平ListViw的切换功能 public class MainActivity e ...

  5. HorizontalListView中使用notifyDataSetChanged()和notifyDataSetInvalidated()

    今天在项目中用到了水平ListView控件HorizontalListView,也是我在网上找的个开源HorizontalListView直接在项目中使用.我是把HorizontalListView放 ...

  6. WPF Bug清单之(13)——应该出现却没有出现的ListView水平滚动条

    转载地址:http://www.cnblogs.com/nankezhishi/archive/2010/03/17/wpfbug13.html 我们知道ListView在内容超出控件本身范围时,默认 ...

  7. [WPF]解决ListView在没有Items时,水平滚动条不出现的问题

    转载地址:http://www.cnblogs.com/nankezhishi/archive/2010/03/19/FixListViewNotScrollHeaderBug.html 在上一篇Bl ...

  8. 07Flutter ListView基础列表组件、水平列表组件、图标组件

    ListView:     ListView:参数     scrollDirection:Axis.horizontal:水平列表.Axis.vertical垂直列表     padding:内边距 ...

  9. ListVIew中包含水平滑动控件,左右滑动时容易触发上下滑动

    自定义ListView import android.content.Context;import android.util.AttributeSet;import android.view.Moti ...

随机推荐

  1. ireport报表,打印时,报表加载失败的解决方法

    1.报表加载失败图示 2.解决方法 原创作者:DSHORE 作者主页:http://www.cnblogs.com/dshore123/ 原文出自:http://www.cnblogs.com/dsh ...

  2. fiddler添加监测请求的 ip地址

    本文转载自:http://www.jackness.org/2014/12/26/%E7%BB%99fiddler%E6%B7%BB%E5%8A%A0%E7%9B%91%E6%B5%8B%E8%AF% ...

  3. Linux 基础——压缩解压命令

    时间过的真快,周末不会学习Linux,每天就是下班后学习.这周工作很忙,要几天总结一次了. 一.处理数据文件 sort dest:将目标文件的内容进行排序,默认是按字符顺序排序的. sort -n d ...

  4. Java学习(构造方法、this关键字、super应用)

    构 造 方 法 定义:对象创建时使用的方法,即在new一个新对象时,对应构造方法,直接对属性赋值. 语句格式:    修饰符(public 等) 构造方法名(必须跟当前类名一样,否则报错)(参数列表) ...

  5. python正则表达式教程

    原文这里,非常实用,转载一下 再来一篇,两篇一起看,美滋滋 本文介绍了Python对于正则表达式的支持,包括正则表达式基础以及Python正则表达式标准库的完整介绍及使用示例.本文的内容不包括如何编写 ...

  6. 04 java 基础:数据类型

    java 数据类型:基本类型与引用类型 基本类型:数值型,其中数值型分为整型.浮点型,整型包括 byte.short .int.long ,默认为 int 类型.浮点类型分为单精度.双精度,分为 fl ...

  7. jsonp原生js跨域拿新浪数据插件封装【可扩展】

    //修改了一个bug,增加了手动释放垃圾 <!DOCTYPE html> <html lang="en"> <head> <meta ch ...

  8. 接口自动化多层嵌套的json数据处理

    最近在做接口自动化测试,响应的内容大多数是多层嵌套的json数据,在对响应数据进行校验的时候,可以通过(key1.key2.key3)形式获取嵌套字典值的方法获取响应值,再和预期值比较 def get ...

  9. JAVAEE学习——hibernate03:多表操作详解、级联、关系维护和练习:添加联系人

    一.一对多|多对一 1.关系表达 表中的表达 实体中的表达 orm元数据中表达 一对多 <!-- 集合,一对多关系,在配置文件中配置 --> <!-- name属性:集合属性名 co ...

  10. JSP中的9大内置对象四大域与servlet里的三大域

    九大内置对象 隐式对象 说明 out 转译后对应JspWriter对象,其内部关联一个PringWriter对象 request 转译后对应HttpServletRequest/ServletRequ ...