自定义 弧形滑动条 CricleScrollView

来源:互联网 发布:ant for mac 下载 编辑:程序博客网 时间:2024/05/16 11:57
1.最进要做一个需求==>将listView  与 CricleScrollView关联  ,根据ListView 的滑动来控制CricleScrollView滑动块的位置CricleScrollView,类似moto ticWatch主界面的 弧形ScrollView.网上关于弧形ScrollView, 网上的资源可以说少之又少. 其实View的绘制并不难,难点在于如何实时的调整滑动块的位置.


2.CricleScrollView 为了不影响ListView的触摸事件, 需要直接继承View,利用 View的onMeasure()  和 onDraw()方法来完成,利用canvas.drawArc通过两个Paint对象 分别画出滑动条的滑动背景(范围) 和 滑动条


3.那么问题来了,如何去确认背景弧长,活动条弧长呢?其实估计大家都能想到drawArc方法可以通过传入起始角度和活动距离,分别来画出背景和滑动条的长度.


4.那么问题又来了,如何去让CricleScrollView滑动块的位置与listview 的上下拉同步了.刚开始我是想通过滑动总的弧度除以listViewitem的总数,然后通过ListView的滑动监听传入firstVisibleItem 来设置滑动块的起始角度 ,调用View的invalidate()的方法来重新绘制View.然而这种方法存在一个问题就是滑动块不能滑到滑动背景的底部,去掉背景效果会好点,但不美观,而且滑动块一格一格的跳,滑动效果太生硬了.产品经理一定会拍了我肩膀两下,说"小伙子还是老老实实给我弄个有背景又流畅的效果出来吧".


5.ListView本生不具备获取下拉滑动距离的方法,但可以从网上找到不少计算的方法.直接把滑动距离传进去就好了吗?答案当然不行.还需要计算每个弧度单位,类似上面的方法. 注意点:下拉获取的最大滑动距离并不是整个   item的高度 X item个数的距离   所以 每个弧度单位=滑动背景的总弧度应该 /(item X (item的总数-可见item的个数))[这个也只是与最大滑动距离接近而已],然后通过  每次的滑获取的距离 X 弧度单位 来确定 滑动块的起始角度,再通过invalidate()方法重新绘制来设定滑动块的位置.但这样也存在一个问题,因为 每个弧度单位 的获取不是通过 除以最大的滑动距离获得的,滑到底部时会存在要么滑动块超出范围,要么滑动块差一点才能滑到底部.  这时可以通过判断 当滑动距离>(item X (item的总数-可见item的个数)) 时,将滑动距离传入 ,重新计算 每个弧度单位,这样就可以 完美的避免了滑到底部的偏差.


6.滑动的问题是解决了,但往往特殊情况的解决才是重点, 例如item的总个数不满一屏时上面的方法是行不通的,当然通过不显示滑动条可以解决. 但当一屏的可见item是变化的例如总item=4, 可见item个数有3,4两种情况,这种判断条件.那么如何避免这种情况?我的方案是通过集合来收集可见item的情况来判断是否显示CricleScrollView, 条件如下

if(firstVisibleItem==0 && totalItemCount-1<visibleItemCount&&visibleItemNumber.size()==1),满足条件证明总item的个数 不满一屏,不显示CricleScrollView.


7.当写 5 的时候,获取ListView的子View时,在Activity的onCreate中获取是获取不到的,因为ListView获取子View需要在ListView 获取焦点后才能获得子View的, 所以需要将6 的步骤在 ListView 监听焦点变化的方法中初始化.





public class CircelScrollView extends ImageView {


private Context context;
private static Paint paintScroll=new Paint();
private static Paint paintBackgroud=new Paint();
private int mRadiu;//半径
private float mProgress;
private float mPreAngle;//每个item弧度
private float mRecfAngle;//弧长角度
private RectF mRect ;
private int mScrollState;
private int mScrollLength;
private int mScrollStroke;
private int maxListViewScrollY;
private int maxVisibleItemCount;
private boolean isScrollLast=false;
private ArrayList<Integer> visibleItemNumber;

public CircelScrollView(Context context) {
this(context, null);

}


public CircelScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
visibleItemNumber=new ArrayList<Integer>();

}


private void initPaint(int stroke) {
paintScroll.setAntiAlias(true);
paintScroll.setStyle(Paint.Style.STROKE);
paintScroll.setStrokeWidth(stroke);
paintScroll.setColor(0xffee7b1d);
paintBackgroud.setAntiAlias(true);
paintBackgroud.setStyle(Paint.Style.STROKE);
paintBackgroud.setStrokeWidth(stroke);
paintBackgroud.setColor(Color.GRAY);
}


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
}


/**
* 初始化弧长
* @param listViewHeightlistView下拉可达到的最大值
* @param recfAngle弧长角度
* @param radiu圆弧半径
* @param scrollLength   滑动条长度弧长对应的角度
*/
private void initCirculScroll(float listViewHeight,float recfAngle,int radiu,int scrollLength,int scrollStroke)
{
mRecfAngle=recfAngle;
if(listViewHeight<=0)
{
mPreAngle=recfAngle/180;
}
else
{
mPreAngle=recfAngle/listViewHeight;
}
mRadiu=radiu;
mScrollLength=scrollLength;
mScrollStroke=scrollStroke;
//初始化画笔
initPaint(scrollStroke);
invalidate();
}

/**
* 重新定义  
* @param scrollY
*/
private void remusePreAngle(int scrollY)
{
mPreAngle=mRecfAngle/scrollY;
}

/**
* 初始化CircelScrollView
* @param lstView   
* @param recfAngle    总弧长对应的角度
* @param radiu半径
* @param scrollLength  滑动条长度
* @param scrollStroke  滑动条粗细
*/
public void initCirculScroll(ListView lstView,float recfAngle,int radiu,int scrollLength,int scrollStroke)
{
int height=0;
int count = lstView.getCount();
View childAt = lstView.getChildAt(0);
if(childAt!=null)
{
height=childAt.getHeight();
}
if(!isScrollLast)
{
maxListViewScrollY=(count-maxVisibleItemCount)*height;
}
initCirculScroll(maxListViewScrollY,recfAngle,radiu
,scrollLength,scrollStroke);
}


/**
* 设置 实时滑动距离
* @param progress
* @param scrollState
*/
private void setScrollDistance(float progress,int scrollState)
{
this.mProgress=progress;
mScrollState=scrollState;
invalidate();
}

/**
* 设置 scroll滑动 位置,
* @param view   
* @param firstVisibleItem
* @param visibleItemCount
* @param totalItemCount
*/
public void setScrollDistance(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if(maxVisibleItemCount<visibleItemCount )
    {
    maxVisibleItemCount=visibleItemCount;
    visibleItemNumber.add(visibleItemCount);
    }
   
    if(totalItemCount==0 )
    {
    setScrollDistance(0, 0);
    return;
    }
    if(firstVisibleItem==0 && totalItemCount-1<visibleItemCount&&visibleItemNumber.size()==1)
    {
    setScrollDistance(0, 0);
    return;
    }


    View c = ((ListView)view).getChildAt(0);
   
        int firstVisiblePosition = view.getFirstVisiblePosition();
        int top = c.getTop();


        int headerHeight=0;
        if (firstVisiblePosition >= 1) {
            headerHeight = view.getHeight();
        }
        
        int scrollY = -top + firstVisibleItem * c.getHeight();
        
        if(scrollY>maxListViewScrollY&&firstVisibleItem!=0)
    {
    remusePreAngle(scrollY);
    }


        setScrollDistance(scrollY, 1);



@Override
protected void onDraw(Canvas canvas) {


//绘制圆弧
canvas.drawColor(Color.TRANSPARENT);

if(mRect==null)
{
mRect = new RectF(getWidth()/2 - mRadiu-mScrollStroke/2, getWidth()/2 - mRadiu-mScrollStroke/2, getWidth()/2
+ mRadiu-mScrollStroke/2, getWidth()/2 + mRadiu-mScrollStroke/2);
}
if(mScrollState==1)
{
canvas.drawArc(mRect,-mRecfAngle/2-mScrollLength/2, mRecfAngle+mScrollLength, false, paintBackgroud);
canvas.drawArc(mRect, mProgress*mPreAngle-mRecfAngle/2-mScrollLength/2, mScrollLength, false, paintScroll);
// canvas.drawArc(mRect, mProgress*mPreAngle-mRecfAngle/2, mProgress==0?(mProgress*mPreAngle)+30-mRecfAngle/2:(mProgress*mPreAngle)/(mPreAngle)+30-mRecfAngle/2, false, paint);
}
super.onDraw(canvas);
}


private int measureWidth(int widthMeasureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);


if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = Utils.dip2px(context, 120);
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}


private int measureHeight(int measuredHeight) {
int result = 0;
int specMode = MeasureSpec.getMode(measuredHeight);
int specSize = MeasureSpec.getSize(measuredHeight);


if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = Utils.dip2px(context, 120);
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}





}




 
0 0
原创粉丝点击