Android-ui效果自定义控件自定义View系列

悬浮导航栏StickyNavLayout的实现--适配任意数据量

2016-05-28  本文已影响3197人  皮球二二

致敬鸿洋,这篇文章是基于他的项目改造而成的

本次主题将分成3个部分进行讲解,前2部分基本上是对鸿洋的代码解读以及一些小bug的处理,第三部分是针对鸿洋Android-StickyNavLayout不支持的功能进行优化处理。本文对应的项目地址在Github

重头戏即将登场

通过之前2章的学习,貌似我们的功能是完美的。这里我做出了一点点小改变

changeItems(4);  
private void changeItems(int num) {    
    strings.clear();    
    for (int i=0;i<num;i++) {        
        strings.add("");    
    }    
    adapter.notifyDataSetChanged();
}

我在初始化的时候只赋值4条数据进去,来看看什么情况

bug来了

这是什么鬼!?虽然说他是bug也好像没那么严重,只能说不是那么合理吧。我们应该达到什么效果
1 总数据高度连列表高度都没有的时候,不能滑动
2 总数据高度达到列表高度但是没有达到之前可以悬浮的效果时,可以滑动列表到最底端
3 数据足够多的时候,我们继续刚才那种效果
这三点就是我们本次主题的最终需求

第一种情况的实现

其实思路非常简单

startListViewHeight=getMeasuredHeight()-id_indicatorview.getMeasuredHeight()-id_topview.getMeasuredHeight();  

startListViewHeight获取listview最开始的高度
然后我们得到总数据高度(总数据高度大家在各自项目中自行去计算,这里仅仅做一个范例,把item的高度写死成50dp了)

public void setContentHeight(int item) {    
    this.contentHeight=item*dp2px(context, 50);
}

真正滑动的地方刚才我们也看到了,是重写的ScrollTo方法,我们只要加上一句

if (contentHeight<startListViewHeight) {    
    y = 0;
}

即可。这样就划不动啦

第二种情况的实现

同样在重写的ScrollTo方法中加一句限制

if (y>(contentHeight-startListViewHeight) && (contentHeight-startListViewHeight)>0) {   
      y=contentHeight-startListViewHeight;
}
滑动限制

数据动态切换

刚才貌似初次加载都不会有什么问题了,但是一旦数据切换呢,比如我直接从14条变成4条,会什么样呢?

id_indicatorview= (LinearLayout) findViewById(R.id.id_indicatorview);
    id_indicatorview.setOnClickListener(new View.OnClickListener() {    
    @Override    
    public void onClick(View v) {        
        Toast.makeText(MainActivity.this, "click2", Toast.LENGTH_SHORT).show();        
        changeItems(4);  
    }
});

你想想看不做处理肯定是不行的,你一开始滑动到最顶部悬浮在那边,然后什么都不处理,那4条肯定孤零零的悬在那边,而且ScrollTo方法重写了,滑也滑不动了。。。

我们首先得确定当前是何种状态

public int getState() {    
    int state=-1;    
    if (getScrollY()==0) {        
        state=1;    //没有进行过滚动
    }    
    if (getScrollY()<mTopViewHeight) {        
        state=2;    //进行过滚动但是没有达到悬停的条件
    }   
    if (getScrollY()>=mTopViewHeight) {        
        state=3;    //已经悬停了
    }    
    return state;
}

只可能存在这三种状态。
同样,改变数据源之后新状态也是这3种状态

第一种情况 之前滚动了多少,原样滚回去

第二种情况 这种情况比较复杂。如果之前是第一种状态,则回滚到现在可以滚动的最大值去;如果之前是第二种状态,则什么都不需要做;如果之前是第三种状态,那么就要判断一下现在已经滚动的距离是不是已经超过了当前最大滚动范围,没有超过,则无需处理,如果已经超过,就要把超过的部分滚回来

第三种情况 无需改变,不管你之前是什么样的状态,我都能完美的适配你

public void reset() {    
    isReset=true;    
    //之前滚动的状态    
    int state=getState();    
    //第一种情况
    if (this.contentHeight<startListViewHeight) {        
        scrollBy(0, -getScrollY());       
        return;    
    }    
    //第二种情况    
    if (contentHeight-startListViewHeight<mTopViewHeight) {        
        if (state==3) {          
            if (startListViewHeight+getScrollY()>contentHeight) {                
                scrollTo(0, (contentHeight-startListViewHeight));            
            }        
        }       
        else if (state==2) {       
            if (startListViewHeight+getScrollY()>contentHeight) {                
                scrollBy(0, -(startListViewHeight+getScrollY()-contentHeight));           
            }        
        }    
    }    
    //第三种情况
    if (contentHeight-startListViewHeight>=mTopViewHeight) {    
    }
}

这边有个isReset标志位,是用来告诉ScrollTo方法你直接滚动就行了,不要参与什么判断

@Override
public void scrollTo(int x, int y) {    
if (isReset) {        
    super.scrollTo(x, y);        
    isReset=false;        
    isTopHidden=false;        
    return;    
}

至此,整个悬停功能就完美的实现了,没有什么不合理的地方,看看最终效果


最终效果

测试的时候直接点击中间导航条即可更改adapter数据源数量

上一篇 下一篇

猜你喜欢

热点阅读