不得不说,作为一名安卓码农,总是会有蛋蛋的忧伤,因为CP常说的就是:你看,人家ios的那个效果好炫酷,比如下面这样的
代码已上Github,部分代码有所调整,以Github上代码为准
作为一名合格的码农,实在不能忍,最后还是实现了这个效果,虽然没有ios的厉害。。。
实现的思路还是不复杂的,主要分两个方向:WheelView类似的思想(github一大堆)、ClipToPadding和ClipChildren取巧。因为我是用的取巧,所以我们下面只谈第二种方法。
难点有两个,一是精确地控制listview的item滚动到悬浮框内。因为大多数时候都不会是某个item刚好在悬浮框内的,但是唯一要考虑的情况也只有一种,即悬浮框内同时出现2个或多个item(设计需要,item的高度都是大于等于悬浮框的高度的,所以悬浮框内最多同时出现2个item,小于悬浮框的高度就会出现问题),取出最适合停留的item(这是个相对概念,大家可以修改代码扩展),我目前需要的就是item中间位置的y值和悬浮视图中间位置的y值最接近的一项,后面的代码里大家会看到如何处理的;
二是当listview只有很少的项时,怎么让listview可以滚动呢?这就需要ClipToPadding和ClipChildren来帮忙了。不知道这两个属性的童鞋可以上网查一查,我相信你会受益良多。ClipToPadding=false,我对这个属性简单理解就是,当你为listview设置了padingTop、padingBottom属性,listview会有pading的效果,但是当你滑动listview到顶部或底部时,listview的顶部或底部却不会出现pading的那部分区域,即listview的内容不再被pading的区域遮盖。ClipChildren=false,就是子视图可以超出父视图区域进行绘制。有了这两个属性,就可以解决listview item比较少时不能滚动的问题了。
由于设计需要滚动时item视图的放大和缩小,所以把处理放到了onscrolllistener的onscroll方法里,最后处理listview精确滚动的代码则要放到onscrolllistener的onScrollStateChanged方法里,这些都是可以修改的。
因为要实现这个效果需要adapter配合,所以代码也比较乱,但是注释还是比较详细的,大家慢慢看。
先看看WheelListView
package com.ykbjson.demo.customview.listview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;
import com.ykbjson.demo.tools.SLog;
/**
* 包名:com.ykbjson.demo.customview.listview
* 描述:类似WheelView的ListView
* 创建者:yankebin
* 日期:2016/6/1
*/
public class WheelListView extends ListView {
private static final int MAX_Y_OVERSCROLL_DISTANCE = 200;
private final Object SCROLL_LOCK = new Object();
//坐标都是相对于手机屏幕
private int topY;//悬浮框顶部坐标
private int middleY;//悬浮框中间坐标
private int bottomY;//悬浮框底部坐标
private int selectPosition;//滚动时adapter当前选中的position
private boolean fromTouch;//当调用smoothScrollToPositionFromTop()方法时也会触发onScroll,需要屏蔽掉
private WheelAdapter wheelAdapter;//数据适配器
private OnSelectCallback callback;//滚动时的回调接口
private int mMaxYOverScrollDistance;
public interface OnSelectCallback {
void onHandleScroll(int selectPosition);
void onHandleIdle(WheelListView wheelListView, int selectPosition);
}
public WheelListView(Context context) {
this(context, null);
}
public WheelListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WheelListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// initBounceListView();
setClipChildren(false);
setClipToPadding(false);
}
/**
* 阻尼效果实现
*/
// private void initBounceListView(){
// //get the density of the screen and do some maths with it on the max overscroll distance
// //variable so that you get similar behaviors no matter what the screen size
// final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
// final float density = metrics.density;
// mMaxYOverScrollDistance = (int) (density * MAX_Y_OVERSCROLL_DISTANCE);
// }
//
// @Override
// protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent){
// //This is where the magic happens, we have replaced the incoming maxOverScrollY with our own custom variable mMaxYOverScrollDistance;
// return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mMaxYOverScrollDistance, isTouchEvent);
// }
public void setAdapter(WheelAdapter adapter) {
super.setAdapter(adapter);
wheelAdapter = adapter;
setCallback(adapter);
}
private void setCallback(OnSelectCallback callback) {
this.callback = callback;
}
/**
* 初始化
*
* @param selectView
* @param rootView
*/
protected void setUp(View selectView, View rootView) {
if (null == selectView || null == rootView) {
return;
}
//让listview现实的区域刚好和悬浮框重合
setPadding(0, selectView.getTop() , 0, rootView.getBottom() - selectView.getBottom() );
int location1[] = new int[2];