在接下来的几篇文章将任老师的博文《您可以下拉PinnedHeaderExpandableListView实现》骤来具体实现。来学习一下大神的代码并记录一下。
原文出处:http://blog.csdn.net/singwhatiwanna/article/details/25546871
先看一下终于效果:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2luZ3doYXRpd2FubmE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
新建一个activity_main.xml文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.example.testexpandablelistview.ui.StickyLayout
android:id="@+id/sticky_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="0dp"
android:orientation="vertical">
<LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center"
android:background="#78a524"
android:orientation="vertical"> </LinearLayout>
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> </LinearLayout>
</com.example.testexpandablelistview.ui.StickyLayout>
</RelativeLayout>
上面的StickyLayout类就是我们自己定义的LinearLayout,思路事实上非常easy,先获取StickyLayout中的header和content两个View,代码例如以下:
private void initData(){
//使用getIdentifier()方法能够方便的获各应用包下的指定资源ID。
//具体请看:http://blog.sina.com.cn/s/blog_5da93c8f0100zlrx.html
int headerId = getResources().getIdentifier("header", "id", getContext().getPackageName());
int contentId = getResources().getIdentifier("content", "id", getContext().getPackageName());
if(headerId != 0 && contentId != 0){
mHeader = findViewById(headerId);
mContent = findViewById(contentId);
mOriginalHeaderHeight = mHeader.getMeasuredHeight();
mHeaderHeight = mOriginalHeaderHeight;
//是一个距离,表示滑动的时候,手的移动要大于这个距离才開始移动控件。 mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
Log.d(TAG, "mTouchSlop = " + mTouchSlop);
}else{
throw new NoSuchElementException("Did your view with \"header\" or \"content\" exist? ");
}
}
再处理屏幕的监听函数
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int intercepted = 0;
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastXIntercept = x;
mLastYIntercept = y;
mLastX = x;
mLastY = y;
intercepted = 0;
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
if(mStatus == STATUS_EXPANDED && deltaY <= -mTouchSlop){
intercepted = 1;
}else if(mGiveUpTouchEventListener != null){
if(mGiveUpTouchEventListener.giveUpTouchEvent(event) && deltaY >= mTouchSlop){
intercepted = 1;
}
}
break;
case MotionEvent.ACTION_UP:{
intercepted = 0;
mLastXIntercept = mLastYIntercept = 0;
break;
}
default:
break;
}
Log.d(TAG, "intercepted = " + intercepted);
//假设为1则返回true,传递给当前的onTouchEvent。假设为0则返回false,传递给子控件
return intercepted != 0;
}
onInterceptTouchEvent是在ViewGroup里面定义的,用于拦截手势事件,每一个手势事件都会先调用onInterceptTouchEvent,假设该方法返回true则拦截到事件,当前的onTouchEvent会触发,假设返回false则传递给子控件。
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
Log.d(TAG, "x=" + x + " y=" + y + " mlastY=" + mLastY);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastX;
int deltaY = y - mLastY;
Log.d(TAG, "mHeaderHeight=" + mHeaderHeight + " deltaY=" + deltaY + " mlastY=" + mLastY);
mHeaderHeight +=deltaY;
setHeaderHeight(mHeaderHeight);
break;
case MotionEvent.ACTION_UP:
int destHeight = 0;
if(mHeaderHeight <= mOriginalHeaderHeight * 0.5){
destHeight = 0;
mStatus = STATUS_COLLAPSED;
}else{
destHeight = mOriginalHeaderHeight;
mStatus = STATUS_EXPANDED;
}
//慢慢滑向终点
this.smoothSetHeaderHeight(mHeaderHeight, destHeight, 500);
break;
default:
break;
}
mLastX = x;
mLastY = y;
return true;
}
滑动的时候将事件传递给onTouchEvent
滑动事件中setHeaderHeight改变了上面的header部分的高度,当抬起时会推断是否改变了原始高度的一般,再慢慢滑向终点。
/*
* 改变header的高度
*/
private void setHeaderHeight(int height) {
if(height < 0){
height = 0;
} else if (height > mOriginalHeaderHeight) {
height = mOriginalHeaderHeight;
}
if(mHeaderHeight != height || true){
mHeaderHeight = height;
mHeader.getLayoutParams().height = mHeaderHeight;
mHeader.requestLayout();
}
}
public void smoothSetHeaderHeight(final int from, final int to, long duration) {
final int frameCount = (int) (duration / 1000f * 30) + 1;
final float partation = (to - from) / (float) frameCount;
new Thread("Thread#smoothSetHeaderHeight") {
public void ruan(){
for(int i = 0; i < frameCount; i++) {
final int height;
if(i == frameCount - 1){
height = to;
}else{
height = (int)(from + partation * i);
}
post(new Runnable() { @Override
public void run() {
setHeaderHeight(height);
}
});
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
上面的View.post(Runnable)方法的作用是将Runnable对象加入到UI线程中执行,从而改变header部分的高度。
StickyLayout类的完整代码例如以下:
package com.example.testexpandablelistview.ui; import java.util.NoSuchElementException; import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.LinearLayout; /**
* 自己定义LinearLayout
* @author 转自:http://blog.csdn.net/singwhatiwanna/article/details/25546871
*
*/
public class StickyLayout extends LinearLayout{ private static final String TAG = "StickyLayout"; public interface OnGiveUpTouchEnventListener{
public boolean giveUpTouchEvent(MotionEvent event);
} private View mHeader; //上面部分。以下成为Header
private View mContent; //以下部分
private OnGiveUpTouchEnventListener mGiveUpTouchEventListener; private int mTouchSlop; //移动的距离 //header的高度 单位:px
private int mOriginalHeaderHeight; //Header部分的原始高度
private int mHeaderHeight; //Header部分如今的实际高度(随着手势滑动会变化) private int mStatus = STATUS_EXPANDED; //当前的状态
public static final int STATUS_EXPANDED = 1; //展开状态
public static final int STATUS_COLLAPSED = 2; //闭合状态 //分别记录上次滑动的坐标
private int mLastX = 0;
private int mLastY = 0; //分别记录上次滑动的坐标(onInterceptTouchEvent)
private int mLastXIntercept = 0;
private int mLastYIntercept = 0; /*
* 构造函数1
*/
public StickyLayout(Context context){
super(context);
} /*
* 构造函数2
*/
public StickyLayout(Context context, AttributeSet attrs) {
super(context, attrs);
} /* 构造函数3
* TargetApi 标签的作用是使高版本号的api代码在低版本号sdk不报错
*/ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
public StickyLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
} /**
* onWindowFocusChanged方法用于监听一个activity是否载入完成。Activity生命周期中,
* onStart, onResume, onCreate都不是真正visible的时间点,真正的visible时间点是onWindowFocusChanged()函数被执行时。
*/
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
//假设是activity载入完成,mHeader和mContent未被初始化。则执行初始化方法。
if(hasWindowFocus && (mHeader == null || mContent == null)){
initData();
}
} private void initData(){
//使用getIdentifier()方法能够方便的获各应用包下的指定资源ID。
//具体请看:http://blog.sina.com.cn/s/blog_5da93c8f0100zlrx.html
int headerId = getResources().getIdentifier("header", "id", getContext().getPackageName());
int contentId = getResources().getIdentifier("content", "id", getContext().getPackageName());
if(headerId != 0 && contentId != 0){
mHeader = findViewById(headerId);
mContent = findViewById(contentId);
mOriginalHeaderHeight = mHeader.getMeasuredHeight();
mHeaderHeight = mOriginalHeaderHeight;
//是一个距离。表示滑动的时候,手的移动要大于这个距离才開始移动控件。
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
Log.d(TAG, "mTouchSlop = " + mTouchSlop);
}else{
throw new NoSuchElementException("Did your view with \"header\" or \"content\" exist?");
}
} @Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int intercepted = 0;
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastXIntercept = x;
mLastYIntercept = y;
mLastX = x;
mLastY = y;
intercepted = 0;
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
if(mStatus == STATUS_EXPANDED && deltaY <= -mTouchSlop){
intercepted = 1;
}else if(mGiveUpTouchEventListener != null){
if(mGiveUpTouchEventListener.giveUpTouchEvent(event) && deltaY >= mTouchSlop){
intercepted = 1;
}
}
break;
case MotionEvent.ACTION_UP:{
intercepted = 0;
mLastXIntercept = mLastYIntercept = 0;
break;
}
default:
break;
}
Log.d(TAG, "intercepted = " + intercepted);
//假设为1则返回true,传递给当前的onTouchEvent。假设为0则返回false,传递给子控件
return intercepted != 0;
} @Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
Log.d(TAG, "x=" + x + " y=" + y + " mlastY=" + mLastY);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastX;
int deltaY = y - mLastY;
Log.d(TAG, "mHeaderHeight=" + mHeaderHeight + " deltaY=" + deltaY + " mlastY=" + mLastY);
mHeaderHeight +=deltaY;
setHeaderHeight(mHeaderHeight);
break;
case MotionEvent.ACTION_UP:
int destHeight = 0;
if(mHeaderHeight <= mOriginalHeaderHeight * 0.5){
destHeight = 0;
mStatus = STATUS_COLLAPSED;
}else{
destHeight = mOriginalHeaderHeight;
mStatus = STATUS_EXPANDED;
}
//慢慢滑向终点
this.smoothSetHeaderHeight(mHeaderHeight, destHeight, 500);
break;
default:
break;
}
mLastX = x;
mLastY = y;
return true;
} public void smoothSetHeaderHeight(final int from, final int to, long duration) {
final int frameCount = (int) (duration / 1000f * 30) + 1;
final float partation = (to - from) / (float) frameCount;
new Thread("Thread#smoothSetHeaderHeight") {
public void ruan(){
for(int i = 0; i < frameCount; i++) {
final int height;
if(i == frameCount - 1){
height = to;
}else{
height = (int)(from + partation * i);
}
post(new Runnable() { @Override
public void run() {
setHeaderHeight(height);
}
});
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
} /*
* 改变header的高度
*/
private void setHeaderHeight(int height) {
if(height < 0){
height = 0;
} else if (height > mOriginalHeaderHeight) {
height = mOriginalHeaderHeight;
}
if(mHeaderHeight != height || true){
mHeaderHeight = height;
mHeader.getLayoutParams().height = mHeaderHeight;
mHeader.requestLayout();
}
}
}
MainActivity.java
package com.example.testexpandablelistview; import android.app.Activity;
import android.os.Bundle; import com.example.testexpandablelistview.ui.StickyLayout; public class MainActivity extends Activity{ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
执行效果:
原文地址:http://blog.csdn.net/singwhatiwanna/article/details/25546871
版权声明:本文博主原创文章。博客,未经同意不得转载。