自定义控件代码:
package com.example.drawable_ui.view;
import com.example.drawable_ui.R;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.OverScroller;
/**
* 练习:拥有head的listview , 可以将header隐藏的layout
* 知识点:1.Scroller中的 srollby / srollto / fling 函数的调用 2.速度控制 3.事件分发,拦截
* 4.view中onFinishInflate() ,onMeasure ,onTouchEvent,onInterceptTouchEvent 的调用时机
*/
public class StickyLayout extends LinearLayout {
private ViewGroup mHeaderView;
private ListView mListView;
private ViewGroup mDividerView;
private int mScaledSlop;
private int mMaxFlingVelocity;
private int mMinFlingVelocity;
private OverScroller mScroller;
private float lastY;
private VelocityTracker mVelocityTracker;
public StickyLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(LinearLayout.VERTICAL);
mScroller = new OverScroller(context);
//判断是否是合法点击的帮助类
mScaledSlop = ViewConfiguration.get(context).getScaledTouchSlop();
//fling最大的速度
mMaxFlingVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity();
//fling最小的速度
mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
}
@Override
protected void onFinishInflate() {
mHeaderView = (ViewGroup) findViewById(R.id.id_sticky_header);
mDividerView = (ViewGroup) findViewById(R.id.id_sticky_divider);
mListView = (ListView) findViewById(R.id.id_sticky_listview);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//计算listview的高
ViewGroup.LayoutParams params = mListView.getLayoutParams();
params.height = getMeasuredHeight() - mDividerView.getMeasuredHeight();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
initVelocityTracker();
mVelocityTracker.addMovement(event);
switch (action) {
case MotionEvent.ACTION_DOWN:
lastY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
float downY = event.getY();
float dY = downY - lastY;
int scrollY = getScrollY();
//计算滑动后的位置,从而计算滑动之后是否超过了边界范围
float currScrollY = scrollY - dY;
if(currScrollY >= mHeaderView.getMeasuredHeight()){
scrollTo(0, mHeaderView.getMeasuredHeight());
}else if(currScrollY <= 0){
scrollTo(0, 0);
}else {
Log.e("TAG", "scrollBy");
scrollBy(0, (int)-dY);
}
lastY = downY;
break;
case MotionEvent.ACTION_UP:
//计算滑动速度,如果大于可fling的最小值, 就让其fling起来
mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
float velocityY = mVelocityTracker.getYVelocity();
Log.e("TAG", "UP_velocityY : " + velocityY + ", mMinFlingVelocity :" + mMinFlingVelocity);
if(Math.abs(velocityY) > mMinFlingVelocity){
fling(-velocityY);
}
releaseVelocityTracker();
break;
default:
break;
}
return super.onTouchEvent(event);
}
private void fling(float velocityY) {
//mHeaderView.getMeasuredHeight() 表示最后停止的位置
mScroller.fling(0, getScrollY(), 0, (int)velocityY, 0, 0, 0, mHeaderView.getMeasuredHeight());
invalidate();
}
@Override
public void computeScroll() {
if(mScroller.computeScrollOffset()){
scrollTo(0, mScroller.getCurrY());
invalidate();
}
}
private void initVelocityTracker(){
if(mVelocityTracker == null){
mVelocityTracker = VelocityTracker.obtain();
}
}
private void releaseVelocityTracker(){
if(mVelocityTracker != null){
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
lastY = ev.getY();
break;
//事件拦截: 1.headview影藏,不拦截 2.headview没有影藏,拦截 3.lisview第一个item显示,拦截
case MotionEvent.ACTION_MOVE:
initVelocityTracker();
mVelocityTracker.addMovement(ev);
mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
float yVelocity = mVelocityTracker.getYVelocity();
Log.e("TAG", "yVelocity : " +yVelocity);
int scrollY = getScrollY();
if(scrollY < mHeaderView.getMeasuredHeight()){//2.headview没有影藏,拦截
return true;
}else {
if(mListView.getFirstVisiblePosition() == 0 && yVelocity > 0){
return true;
}else{
return false;
}
}
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
}
xml文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.drawable_ui.MainActivity" >
<com.example.drawable_ui.view.StickyLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:id="@id/id_sticky_header"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#faaaaa" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="详情界面"
android:textColor="#000000"
android:textSize="20sp" />
</RelativeLayout>
<LinearLayout
android:id="@id/id_sticky_divider"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#55cc77"
android:orientation="horizontal" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="内容标题"
android:textColor="#000000"
android:layout_gravity="center_vertical"
android:textSize="12sp" />
</LinearLayout>
<ListView
android:id="@+id/id_sticky_listview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
</com.example.drawable_ui.view.StickyLayout>
</RelativeLayout>
values/ids.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="id_sticky_header" type="id"></item>
<item name="id_sticky_divider" type="id"></item>
<item name="id_sticky_listview" type="id"></item>
</resources>