先看一下效果吧:
截图效果不是太好,大家将就着看吧。。。
有些童鞋看了效果第一反应就是用gallery就能实现。最开始我也是用gallery做的。但是gallery存在一些问题,其实我觉得也不是问题,就是设计和小头头觉得不好看,无奈之下就自己用scrollview自己写了一个。。。说多了都是眼泪啊。。。
其实本质上就是一个自定义的linearlayout,外面套了一个scrollview。为啥要套一个scrollview呢?当然是不套的话有问题了,听着像废话。。。具体时间什么问题你可以自己试试,把一个横向的linearlayout放几个view,超过一屏幕就能看出问题了,我也不是太会描述问题。
大概思想就是在onTouch里面通过scrollto和scrollby操作linearlayout的位置,达到显示不同item居中。这个view存在一个大问题,如果item太多,会OOM。我的项目里面内容不多,所以没有进行这方面的考虑。当然肯定还有其它问题,大家就根据需要改动就好了。代码里面还有很多可以优化的地方,自己动手丰衣足食~
下面就边看代码边讲解一下思想:
首先是addview,重写一下:
mContainer是scrollview里面的linearlayout,addview就是网scrollview的linearlayout里面添加view
@Override
public void addView(View child) {
mContainer.addView(child);
}
接下来是onlayout:
mChildRects是一个list,用来存放每个item的left,并且每个item都被setTag,tag就是item的位置。用来获取item位置方便。每个item的监听是执行scrollToView(v)方法,scrollToView方法就是移动到某个view的位置。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mContainer = (LinearLayout) getChildAt(0);
int count = mContainer.getChildCount();
if (count > 0) {
mChildRects.clear();
for (int i = 0; i < count; i++) {
View child = mContainer.getChildAt(i);
child.setTag(i);
mChildRects.add(child.getLeft());
child.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
scrollToView(v);
}
});
}
}
}
scrollToView方法:
这个方法还是很关键的。涉及到一些计算问题。因为上面说到mChildRects存放了所有item的left,当需要移动时,就需要根据item的位置(tag)进行计算移动的距离scrollto到这个位置就好了。具体怎么计算呢?懒一下示意图:
加入红色使我们要移动的item,如果我们要把item移动到屏幕左边,那么直接scrollto至item的left即可,二left存放在mChildRects中。但是我们现在要移到中间。那差了多少呢?就是中间红色的item左边缘到左边item的左边缘,差了多少?不就是scrollview的宽度一半减去item宽度的一半?代码中这部分写错了,不单独上传了,这里提醒一下。
private void scrollToView(View v) {
int currentposition = (Integer) v.getTag();
if (mChangeListener != null && currentposition != mSelectedPosition) {
mChangeListener.onSelectionChange(mSelectedPosition,
mContainer.getChildAt(mSelectedPosition), currentposition,
mContainer.getChildAt(currentposition));
}
mSelectedPosition = currentposition;
int left = mChildRects.get((Integer) v.getTag());
// 滚动到的位置
int scrollTo = left - (getCenterX() - v.getWidth() / 2);
// 当前的x
int currentX = mContainer.getScrollX();
// 需要变化的量
// int deltX = scrollTo - currentX;
// mContainer.scrollBy(deltX, 0);
ValueAnimator value = ValueAnimator.ofInt(currentX, scrollTo);
value.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mContainer.scrollTo((Integer) animation.getAnimatedValue(), 0);
calculateDelt();
scaleView();
}
});
value.setDuration(300);
value.start();
}
然后就是onTouch方法,完全重写了:
这里贴一下代码,就不多说了,大家下了代码自己看吧,注释我写的很清楚,相信看起来不费劲。
@Override
public boolean onTouchEvent(MotionEvent ev) {
int left = mChildRects.get(0);
int right = mChildRects.get(mChildRects.size() - 1);
// 最左边滚动到的位置
int leftScrollTo = left
- (getCenterX() - mContainer.getChildAt(0).getWidth() / 2);
// 最右边滚动到的位置
int rightScrollTo = right
- (getCenterX() - mContainer.getChildAt(mChildRects.size() - 1)
.getWidth() / 2);
calculateDelt();
int currentposition = calculateSelectedPosition();
if (mChangeListener != null && currentposition != mSelectedPosition) {
mChangeListener.onSelectionChange(mSelectedPosition,
mContainer.getChildAt(mSelectedPosition), currentposition,
mContainer.getChildAt(currentposition));
}
mSelectedPosition = currentposition;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = ev.getX();
break;
case MotionEvent.ACTION_MOVE:
if (lastX == -1) {
lastX = ev.getX();
} else {
float x = ev.getX();
float deltX = x - lastX;
if (mContainer.getScrollX() < leftScrollTo && (deltX > 0)) {// 滑到最左端不能继续
return false;
}
if (deltX < 0 && mContainer.getScrollX() > rightScrollTo) {// 滑到最右端不能继续
return false;
}
mContainer.scrollBy((int) (lastX - x), 0);
lastX = x;
scaleView();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
scrollToPosition(mSelectedPosition);
lastX = -1;
break;
}
return false;
}
就写这么多吧,这里写的看起来也不方便。还是直接看代码比较好,代码中我给大家留了一些参数,都是做适配的时候用的,也有详细的注释,哎。。。苦逼啊。。。
代码链接:http://download.csdn.net/detail/lizhengwei1989/9052985
收1分,有时候我也需要积分下一些资料,大家就慷慨支持一下吧