package com.banking.xc.utils.ui;
import skytv_com.banking.enjoymovie.MyApplication;
import com.banking.xc.utils.DPIUtil;
import com.banking.xc.utils.Log;
import com.banking.xc.utils.MyActivity;
import com.banking.xc.utils.MySimpleAdapter;
import android.content.Context;
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
public class MyHorizontalImagesView extends AdapterView<ListAdapter> implements GestureDetector.OnGestureListener {
private static final String TAG = "ProductImagesView";
private MySimpleAdapter mAdapter;
/**
* 当前坐标偏移量
*/
private int offset;
/**
* 目的坐标偏移量(动画用)
*/
private int targetOffset;
/**
* 最后一页的区域坐标(头一页的区域坐标是0因此不用定义和计算)
*/
private int lastPageArea;
/**
* 当前区域坐标
*/
private int area = 0;
/**
* 防止多次测量
*/
private boolean alreadyMeasureChild;
/**
* 左右留白最小需要多少
*/
private int minPaddingLeft;
/**
* 左右留白最小需要多少
*/
private int minPaddingRight;
/**
* 总个数
*/
private int childCount;
/**
* 旧个数
*/
private int oldCount;
/**
* 每页个数
*/
private int pageSize;
/**
* 元素与其背景宽高
*/
private int childWidthWithPadding;
/**
* 元素与其背景宽高
*/
private int childHeightWithPadding;
/**
* 元素与其间隔宽度,用于排版
*/
private int unitWidth;
private GestureDetector mGestureDetector;// 手势识别器
/**
* 前冲或后冲标记
*/
private int flingFlag;
private BorderListener borderListener;// 边界监听器
private Border border = new Border();
public void setOnBorderListener(BorderListener borderListener) {
this.borderListener = borderListener;
}
public MyHorizontalImagesView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initProductImagesView();
}
public MyHorizontalImagesView(Context context, AttributeSet attrs) {
super(context, attrs);
initProductImagesView();
}
public MyHorizontalImagesView(Context context) {
super(context);
initProductImagesView();
}
/**
* Common code for different constructor flavors
*/
private void initProductImagesView() {
mGestureDetector = new GestureDetector(this.getContext(), this);
mGestureDetector.setIsLongpressEnabled(true);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (null == mAdapter || mAdapter.getCount() < 1) {
return true;
}
if (null == thread) {
thread = new Thread(animationDelegate);
thread.start();
}
boolean retValue = mGestureDetector.onTouchEvent(event);
int action = event.getAction();
if (action == MotionEvent.ACTION_UP) {
onUp();
} else if (action == MotionEvent.ACTION_CANCEL) {
onCancel();
}
return retValue;
}
private Thread thread;
private AnimationDelegate animationDelegate = new AnimationDelegate();
private class AnimationDelegate implements Runnable {
private boolean fit = true;
private static final int NONE = 0;
private static final int LEFT = 1;
private static final int RIGHT = 2;
@Override
public void run() {
int vx = 0;
while (true) {
isFit();
vx += (targetOffset - offset) * 0.4f;// spring为弹性系数
offset += (vx *= 0.3f);// friction为摩擦力
if (Math.abs(targetOffset - offset) < 9) {
offset = targetOffset;
vx = 0;
}
fit = offset == targetOffset;
if (Log.D) {
Log.d(TAG, "offset -->> " + offset);
}
// 刷新界面
((MyActivity) getContext()).post(new Runnable() {
@Override
public void run() {
requestLayout();
}
});
try {
Thread.sleep(30);// 帧数
} catch (InterruptedException e) {
}
}
}
private synchronized void isFit() {
if (fit) {
try {
if (Log.D) {
Log.d(TAG, "product image wait start -->> ");
}
wait();
if (Log.D) {
Log.d(TAG, "product image wait end -->> ");
}
} catch (InterruptedException e) {
isFit();
}
}
}
public synchronized void adjust(int flag) {
if (Log.D) {
Log.d(TAG, "adjust() -->> " + flag);
}
switch (flag) {
case RIGHT:
area -= pageSize;
if (area <= -lastPageArea) {
area = -lastPageArea;
}
break;
case LEFT:
area += pageSize;
if (area >= 0) {
area = 0;
}
break;
case NONE:
area = Math.round((float) offset / unitWidth);
break;
}
if (Log.D) {
Log.d(TAG, "adjust() -->> area : " + area);
}
if (Log.D) {
Log.d(TAG, "adjust() -->> targetOffset before : " + targetOffset);
}
if (childCount == pageSize) {
targetOffset = 0;
} else {
targetOffset = area * unitWidth;
}
if (Log.D) {
Log.d(TAG, "adjust() -->> targetOffset after : " + targetOffset);
}
notify();
}
};
private void borderChange() {
if (null != borderListener) {
borderListener.onChange(border);
}
}
@Override
public ListAdapter getAdapter() {
return mAdapter;
}
private DataSetObserver dataSetObserver = new DataSetObserver() {
@Override
public void onChanged() {
if (Log.D) {
Log.d(TAG, "onChanged() -->> adapter count:" + mAdapter.getCount());
}
MyActivity myActivity = MyApplication.getInstance().getCurrentMyActivity();
if(1==2){
myActivity.post(new Runnable() {
public void run() {
if(mAdapter.getCount() < 1){
border.left = 0;
border.right = 0;
borderChange();
}
if(mAdapter.getCount() != oldCount){
oldCount = mAdapter.getCount();
alreadyMeasureChild = false;
detachAllViewsFromParent();
for (int i = 0; i < mAdapter.getCount(); i++) {
View child = mAdapter.getView(i, null, MyHorizontalImagesView.this);
addViewInLayout(child, i, child.getLayoutParams());
}
}else{
for (int i = 0; i < mAdapter.getCount(); i++) {
View child = mAdapter.getView(i, getChildAt(i), MyHorizontalImagesView.this);
}
}
}
});
}
}
};
@Override
public void setAdapter(ListAdapter adapter) {
if (Log.D) {
Log.d(TAG, "setAdapter() -->> ");
}
mAdapter = (MySimpleAdapter) adapter;
mAdapter.registerDataSetObserver(dataSetObserver);
mAdapter.notifyDataSetChanged();
}
@Override
public View getSelectedView() {
return null;
}
@Override
public void setSelection(int position) {
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),//
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec) + getPaddingTop() + getPaddingBottom());
}
// 测量元素
private void measureChild(View child) {
// 防止多次测量
if (alreadyMeasureChild) {
return;
}
if (Log.D) {
Log.d(TAG, "measureChild() -->> ");
}
// 为左右留白保留原始数值
if (0 == minPaddingLeft) {
minPaddingLeft = getPaddingLeft();
}
// 为左右留白保留原始数值
if (0 == minPaddingRight) {
minPaddingRight = getPaddingRight();
}
LayoutParams p = child.getLayoutParams();
childWidthWithPadding = p.width + child.getPaddingLeft() + child.getPaddingRight();
childHeightWithPadding = p.height + child.getPaddingTop() + child.getPaddingBottom();
int visualRangeWidth = getMeasuredWidth() - minPaddingLeft - minPaddingRight;// 可视范围
int minSpacing = DPIUtil.dip2px(10);// 最起码的间隔
// 可视范围除以,每个元素的宽度加上最起码的间距,计算出可视范围能容纳的元素个数
pageSize = visualRangeWidth / (childWidthWithPadding + minSpacing);
// 虽然说可以容纳10个,未必就有10个以上的元素
if (pageSize > childCount) {
pageSize = childCount;
}
if (Log.D) {
Log.d(TAG, "measureChild() -->> childCount:" + childCount);
Log.d(TAG, "measureChild() -->> pageSize:" + pageSize);
}
// 可视范围去掉元素所占宽度后,计算出剩余的可分配的间隔空间
int visualSpacingTotal = visualRangeWidth - pageSize * childWidthWithPadding;
// 计算出平均的间隙
int spacing = 0;
if (pageSize - 1 == 0) {// 页面只显示一个元素的时候需要特殊处理
spacing = 0;
setPadding(minPaddingLeft + visualSpacingTotal / 2, getPaddingTop(), minPaddingRight + visualSpacingTotal / 2, getPaddingBottom());
} else {
spacing = visualSpacingTotal / (pageSize - 1);
setPadding(minPaddingLeft, getPaddingTop(), minPaddingRight, getPaddingBottom());
}
// 避免左右两边间隔小,图片之间间隔大,因此再进行调整。
int spacingLR = minPaddingLeft + minPaddingRight;
if (spacing > spacingLR) {
spacing = (spacingLR + visualSpacingTotal) / (pageSize + 1);//
setPadding(spacing, getPaddingTop(), spacing, getPaddingBottom());
}
// 一个元素加上间隔的宽度,用于排版
unitWidth = childWidthWithPadding + spacing;
// 最后一页的坐标
lastPageArea = childCount - pageSize;
// 如果是后来更改了数据,应该检查修正位置
if (Log.D) {
Log.d(TAG, "area -->> " + area);
}
if (Log.D) {
Log.d(TAG, "lastPageArea -->> " + lastPageArea);
}
if (area <= -lastPageArea) {
area = -lastPageArea;
animationDelegate.adjust(-1);
}
// 标记已经测量过
alreadyMeasureChild = true;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (Log.D) {
Log.d(TAG, "onLayout() -->> ");
}
childCount = getChildCount();// 总个数
for (int i = 0; i < childCount; i++) {// 迭代
View child = getChildAt(i);
// 尝试测量
measureChild(child);
int left = 0;
int top = 0;
// 通知左右箭头的隐藏与显示
if (childCount == pageSize) {
// 通知
border.left = 0;
border.right = 0;
borderChange();
} else {
// 通知
if (area >= 0) {// 左边去到尽头
border.left = 0;
} else {
border.left = 1;
}
if (area <= -lastPageArea) {// 右边去到尽头
border.right = 0;
} else {
border.right = 1;
}
borderChange();
}
left = getPaddingLeft() + offset + i * unitWidth;
top = getPaddingTop();
child.layout(left, top, left + childWidthWithPadding, top + childHeightWithPadding);
}
}
public boolean onSingleTapUp(MotionEvent e) {// 松开
if (Log.D) {
Log.d(TAG, "onSingleTapUp -->> ");
}
int index = (-Math.round(((float) offset - e.getX()) / unitWidth - 0.5f)) - 1;
if (Log.D) {
Log.d(TAG, "index -->> " + index);
}
if (index > getChildCount() - 1) {
index = getChildCount() - 1;
} else if (index < 0) {
index = 0;
}
View child = getChildAt(index);
if(child != null){
performItemClick(child, index, child.getId());
}
/*
* if (mDownTouchPosition >= 0) {
*
* // An item tap should make it selected, so scroll to this child. scrollToChild(mDownTouchPosition - mFirstPosition);
*
* // Also pass the click so the client knows, if it wants to. if (mShouldCallbackOnUnselectedItemClick || mDownTouchPosition == mSelectedPosition) { performItemClick(mDownTouchView,
* mDownTouchPosition, mAdapterf .getItemId(mDownTouchPosition)); }
*
* return true; }
*/
return false;
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (velocityX > 300) {
flingFlag = AnimationDelegate.LEFT;
} else if (velocityX < -300) {
flingFlag = AnimationDelegate.RIGHT;
} else {
flingFlag = AnimationDelegate.NONE;
}
/*
* if (!mShouldCallbackDuringFling) { // We want to suppress selection changes
*
* // Remove any future code to set mSuppressSelectionChanged = false removeCallbacks(mDisableSuppressSelectionChangedRunnable);
*
* // This will get reset once we scroll into slots if (!mSuppressSelectionChanged) mSuppressSelectionChanged = true; }
*
* // Fling the gallery! mFlingRunnable.startUsingVelocity((int) -velocityX);
*/
return true;
}
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (childCount == pageSize) {
return true;
}
getParent().requestDisallowInterceptTouchEvent(true);
// if (localLOGV) if(Log.V){Log.v(TAG, String.valueOf(e2.getX() -
// e1.getX()));}
/*
* Now's a good time to tell our parent to stop intercepting our events! The user has moved more than the slop amount, since GestureDetector ensures this before calling this method. Also, if a
* parent is more interested in this touch's events than we are, it would have intercepted them by now (for example, we can assume when a Gallery is in the ListView, a vertical scroll would
* not end up in this method since a ListView would have intercepted it by now).
*/
// As the user scrolls, we want to callback selection changes so
// related-
// info on the screen is up-to-date with the gallery's selection
/*
* if (!mShouldCallbackDuringFling) { if (mIsFirstScroll) {
*
* We're not notifying the client of selection changes during the fling, and this scroll could possibly be a fling. Don't do selection changes until we're sure it is not a fling.
*
* if (!mSuppressSelectionChanged) mSuppressSelectionChanged = true; postDelayed(mDisableSuppressSelectionChangedRunnable, SCROLL_TO_FLING_UNCERTAINTY_TIMEOUT); } } else { if
* (mSuppressSelectionChanged) mSuppressSelectionChanged = false; }
*/
// Track the motion
/*
* trackMotionScroll(-1 * (int) distanceX);
*
* mIsFirstScroll = false;
*/
int tempOffset = offset + Math.round(-distanceX);
if (tempOffset > 0) {
offset = 0;
} else if (tempOffset < lastPageArea * unitWidth * -1) {
offset = lastPageArea * unitWidth * -1;
} else {
offset = tempOffset;
}
requestLayout();
flingFlag = AnimationDelegate.NONE;
return true;
}
public boolean onDown(MotionEvent e) {
// 初始化
flingFlag = AnimationDelegate.NONE;
if (Log.D) {
Log.d(TAG, "onDown -->> ");
}
// Kill any existing fling/scroll 停止缓冲动画或调整动画
// mFlingRunnable.stop(false);
// Get the item's view that was touched 找到点击位置的子 View 以及 显示为点中状态
/*
* mDownTouchPosition = pointToPosition((int) e.getX(), (int) e.getY());
*
* if (mDownTouchPosition >= 0) { mDownTouchView = getChildAt(mDownTouchPosition - mFirstPosition); mDownTouchView.setPressed(true); }
*/
// Reset the multiple-scroll tracking state 标记开始滚动
/* mIsFirstScroll = true; */
// Must return true to get matching events for this down event.
return true;
}
/**
* Called when a touch event's action is MotionEvent.ACTION_UP.
*/
void onUp() {
animationDelegate.adjust(flingFlag);
/*
* if (mFlingRunnable.mScroller.isFinished()) { scrollIntoSlots(); }
*
* dispatchUnpress();
*/
}
/**
* Called when a touch event's action is MotionEvent.ACTION_CANCEL.
*/
void onCancel() {
onUp();
}
public void onLongPress(MotionEvent e) {// 长按
if (Log.D) {
Log.d(TAG, "onLongPress -->> ");
}
int index = (-Math.round(((float) offset - e.getX()) / unitWidth - 0.5f)) - 1;
if (Log.D) {
Log.d(TAG, "index -->> " + index);
}
if (index > getChildCount() - 1) {
index = getChildCount() - 1;
} else if (index < 0) {
index = 0;
}
View child = getChildAt(index);
performItemLongClick(child, index, child.getId());
/*
* if (mDownTouchPosition < 0) { return; }
*
* performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); long id = getItemIdAtPosition(mDownTouchPosition); dispatchLongPress(mDownTouchView, mDownTouchPosition, id);
*/
}
private boolean performItemLongClick(View view, int position, long id) {
boolean handled = false;
OnItemLongClickListener l = getOnItemLongClickListener();
if (l != null) {
handled = l.onItemLongClick(this, view, position, id);
}
if (handled) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
return handled;
}
public void onShowPress(MotionEvent e) {
if (Log.D) {
Log.d(TAG, "onShowPress -->> ");
}
}
public static class Border {
public int left;// 0:到达边界,1:还可以前进
public int right;// 0:到达边界,1:还可以前进
}
public interface BorderListener {
void onChange(Border border);
}
}