package loveworld.listview3d;
import java.util.LinkedList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LightingColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Adapter;
import android.widget.AdapterView;
public class ListView3D extends AdapterView<Adapter> {
// ===========================================================
// Constants
// ===========================================================
// 新添加的所有子视图在当前最当前最后一个子视图后添加的布局模型
private static final int LAYOUT_MODE_BELOW = 0;
// 与LAYOUT_MODE_BELOW相反方向添加的布局模型
private static final int LAYOUT_MODE_ABOVE = 1;
// 初始模式,用户还未接触ListView
private static final int TOUCH_MODE_REST = -1;
// 触摸Down事件模式
private static final int TOUCH_MODE_DOWN = 0;
// 滚动模式
private static final int TOUCH_MODE_SCROLL = 1;
private static final int INVALID_INDEX = -1;
// Item与List的宽度比
private static final float ITEM_WIDTH = 0.85f;
// Item高度因为需要预留给滚动空间,填充后的高度与Item实际高度的比例
private static final float ITEM_VERTICAL_SPACE = 1.45f;
private static final int AMBIENT_LIGHT = 55;
private static final int DIFFUSE_LIGHT = 200;
private static final float SPECULAR_LIGHT = 70;
private static final float SHININESS = 200;
private static final int MAX_INTENSITY = 0xFF;
private static final float SCALE_DOWN_FACTOR = 0.15f;
private static final int DEGREES_PER_SCREEN = 270;
// ===========================================================
// Fields
// ===========================================================
// 视图和数据适配
private Adapter mAdapter;
// 当前显示最后一个Item在Adapter中位置
private int mLastItemPosition = -1;
// 当前显示第一个Item在Adapter中位置
private int mFirstItemPosition;
// 当前顶部第一个item
private int mListTop;
// 当前第一个显示的item与底部第一个item的顶部偏移量
private int mListTopOffset;
// 触摸Down事件时进行记录
private int mListTopStart;
// 记录ListView当前处于哪种模式
private int mTouchMode = TOUCH_MODE_REST;
// 记录上一次触摸X轴
private int mTouchStartX;
// 记录上一次触摸Y轴
private int mTouchStartY;
// 仅记录Down事件时Y轴值
private int mMotionY;
// 触发滚动的最小移动距离
private int mTouchSlop;
// 可反复使用的Rect
private Rect mRect;
// 用于检测是手长按动作
private Runnable mLongPressRunnable;
// View复用当前仅支持一种类型Item视图复用
// 想更多了解ListView视图如何复用可以看AbsListView内部类RecycleBin
private final LinkedList<View> mCachedItemViews = new LinkedList<View>();
private int mListRotation;
private Camera mCamera;
private Matrix mMatrix;
private Paint mPaint;
private boolean mRotationEnabled = true;
private boolean mLightEnabled = true;
// ===========================================================
// Constructors
// ===========================================================
public ListView3D(Context context) {
super(context);
initListView(context);
}
public ListView3D(Context context, AttributeSet attrs) {
super(context, attrs);
initListView(context);
}
public ListView3D(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initListView(context);
}
// ===========================================================
// Setter
// ===========================================================
// ===========================================================
// Getter
// ===========================================================
private int getChildMargin(View child) {
return (int)(child.getMeasuredHeight() * (ITEM_VERTICAL_SPACE - 1) / 2);
}
private int getChildTop(View child) {
return child.getTop() - getChildMargin(child);
}
private int getChildBottom(View child) {
return child.getBottom() + getChildMargin(child);
}
private int getChildHeight(View child) {
return child.getMeasuredHeight() + 2 * getChildMargin(child);
}
public void enableRotation(final boolean enable) {
mRotationEnabled = enable;
if (!mRotationEnabled) {
mListRotation = 0;
}
invalidate();
}
public boolean isRotationEnabled() {
return mRotationEnabled;
}
public void enableLight(final boolean enable) {
mLightEnabled = enable;
if (!mLightEnabled) {
mPaint.setColorFilter(null);
} else {
mPaint.setAlpha(0xFF);
}
invalidate();
}
public boolean isLightEnabled() {
return mLightEnabled;
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public Adapter getAdapter() {
return mAdapter;
}
@Override
public void setAdapter(Adapter adapter) {
mAdapter = adapter;
removeAllViewsInLayout();
requestLayout();
}
@Override
public View getSelectedView() {
throw new UnsupportedOperationException("Not supported");
}
@Override
public void setSelection(int position) {
throw new UnsupportedOperationException("Not supported");
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
// 异常处理
if (mAdapter == null) {
return;
}
// 当前ListView没有任何子视图(Item),所以依次在从上向下填充子视图
if (getChildCount() == 0) {
mLastItemPosition = -1;
// add and measure
fillListDown(mListTop, 0);
} else {
final int offset = mListTop + mListTopOffset - getChildTop(getChildAt(0));
// 移除可视区域的都干掉
removeNonVisibleViews(offset);
fillList(offset);
}
// layout,添加测量完后,获取视图摆放位置
positioinItems();
// draw, 上面子视图都添加完了,重绘布局把子视图绘制出来吧
invalidate();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(ev);
return false;
case MotionEvent.ACTION_HOVER_MOVE:
return startScrollIfNeeded((int)ev.getY());
default:
endTouch();
return false;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (getChildCount() == 0) {
return false;
}
final int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTouch(event);
break;
case MotionEvent.ACTION_MOVE:
if (mTouchMode == TOUCH_MODE_DOWN) {
startScrollIfNeeded(y);
} else if (mTouchMode == TOUCH_MODE_SCROLL) {
scrollList(y);
}
break;
case MotionEvent.ACTION_UP:
// 如果当前触摸没有触发滚动,状态依然是DOWN
// 说明是点击某一个Item
if (mTouchMode == TOUCH_MODE_DOWN) {
clickChildAt((int)event.getX(), y);
}
endTouch();
break;
default:
endTouch();
break;
}
return true;
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
final Bitmap bitmap = child.getDrawingCache();
if (bitmap == null) {
return super.drawChild(canvas, child, drawingTime);
}
// 当前Item左侧顶部坐标值
int left = child.getLeft();
int top = child.getTop();
// 当前Item中部偏移
int centerX = child.getWidth() / 2;
int centerY =
- 1
- 2
前往页