上大街网app的个人中心,感觉界面做的还是挺好的,所以仿写了一个。虽然没有加上动画,但是主要的功能还是实现了的。好了看图~~~
android自定义系列(七)–仿大街网个人中心页面
当向上推的时候,上面的TextView隐藏后,listview就会在顶部。当向下拉的时候,又会重新拉出上面的内容。
好了,思路分析~~~
在这里我们继承linearlayout,设置方向为垂直。看看布局文件。
xmlns:tools=“http://schemas.android.com/tools”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:orientation=“vertical”
tools:context=".MainActivity" >
android:id="@+id/tv"
android:text="你好,你猜可以滑动不?"
android:background="#ffeeff"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="300dp" />
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
这个没什么好说的了,接着就是滑动处理了。为了能够让滑动更顺畅,我们用OverScroller来做辅助,当然,onTouchEvent是必须的。
在构造函数初始化OverScroller
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mScroll = new OverScroller(context);
}
我们先来实现,滑动操作(万事开头难,从简单做起),滑动重写撒方法呢~~没错onTouchEvent
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
firstY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
lastY = event.getY();
float dy = lastY - firstY;
if (Math.abs(dy) > mTouchSlop) {
scrollBy(0, (int) -dy);
firstY = lastY;
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return super.onTouchEvent(event);
}
在按下和移动两个地方做文章,计算Y轴的差值,用scrollBy进行移动。好了,一个能移动的View就基本出来了,但是我们会发现,会出现listview会拦消耗我们自定义View的move事件,所以,根据事件分发机制,我们需要拦截掉move事件~~
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
float y = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
firstY = y;
break;
case MotionEvent.ACTION_MOVE:
return true;// 父组件拦截事件,
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
拦截掉事件后,好了又出了一个BUG,当我们不断的向下拉的时候,我们的TextView上面会出现空白。产生的原因是画布的无限性决定的。没办法,我们只有人为去控制了,重写onscrollTo方法~~
@Override
public void scrollTo(int x, int y) {
if (y <= 0) {
y = 0;
}
if (y >= mTopViewHeight) {
y = mTopViewHeight;
}
if (y != getScrollY()) {
super.scrollTo(x, y);
}
}
这 段代码的意思是,当视图的y坐标小于或者等于0的时候,y值为0,所以TextView就不会出现空白了,同理,当TextView向上推的时候,当Y值 超过TextView的高度的时候就设置为TextView的高度。好了,到这里,革命基本完成了一半,但是还有bug,当隐藏TextView的时候, 我们需要不要拦截listview的滑动事件~~这个简单,我们只有找到这个临界点就可以了。这里有三种情况,一种是TextView还没有隐藏,这里我 们要拦截掉、另一种是TextView已经隐藏掉,但是向上拉,由于刚隐藏所以又会被拦截,我们依据dy做依据,如果<0则向上,我们拦截,否则不 拦截。
case MotionEvent.ACTION_MOVE:
float dy = y - firstY;
if (Math.abs(dy) > mTouchSlop && !isHidden) {
return true;// 父组件拦截事件,
}else {
if(firstVisibleItems == 0 && dy > 0)//listView滚动到顶部并且向上滑动
{
return true;
}else
{
return false;
}
}
其中dy是move事件在y方向的差值。到这里,可以说是完成了70%了。为了丰富用户体验,我们用一个速度跟踪器来帮助视图滑动。
在onTouchEvent方法初始化,并将当前事件添加进去
@Override
public boolean onTouchEvent(MotionEvent event) {
if (null == mVelocityTracker) {
mVelocityTracker = VelocityTracker.obtain();// 获得VelocityTracker类实例
}
mVelocityTracker.addMovement(event);
}
然后在手指抬起的时候触获取x和y方向的速度
case MotionEvent.ACTION_UP:
final VelocityTracker tracker = mVelocityTracker;
// 设置maxVelocity值为0.1时,速率大于0.01时,显示的速率都是0.01,速率小于0.01时,显示正常
tracker.computeCurrentVelocity(1, (float) 0.01);
// 设置units的值为1000,意思为一秒时间内运动了多少个像素
tracker.computeCurrentVelocity(1000);
int velocityY = (int) mVelocityTracker.getYVelocity();
int velocityX = (int) mVelocityTracker.getXVelocity();
if (Math.abs(velocityY) > mMinimumVelocity) {
fling(velocityX, -velocityY);
}
当y方向的速度达到一个系统提供定值的时候我们调用fling方法
public void fling(int velocityX, int velocityY) {
// getScrollX、getScrollY得到View移动到的坐标距离原坐标的差值,负值顺着x、y轴方向移动,正值反之。
// getCurrY获取mScroller当前竖直滚动的位置
mScroll.fling(0, getScrollY(), velocityX, velocityY, 0, 0, 0,
mTopViewHeight);
invalidate();
}
其中invalidate会调用draw方法,紧接着draw会调用computeScroll,所以在computeScroll调用invalidate如果没有条件限制的话,其实就是进入了一个死循环。
@Override
public void computeScroll() {
if (mScroll.computeScrollOffset()) {
Log.e("tag", "computeScroll");
scrollTo(mScroll.getCurrX(), mScroll.getCurrY());
invalidate();
}
}
当scroller还没有停的时候不断的移动,一个动画就这样实现了。至于getScrollX这个返回值是撒?引用网友的一个图片吧?通俗易懂~~
android自定义系列(七)–仿大街网个人中心页面
当然,这里还有listview高度的调整,这里不在讨论的范畴,具体的看代码。
最后奉上代码~~
http://download.csdn.net/detail/qq815034762/9388068
有bug欢迎提出~~~
这是我建的群,有兴趣的可以加:207678498