在接下来的几篇文章将任老师的博文《您可以下拉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

版权声明:本文博主原创文章。博客,未经同意不得转载。

Android自己定义组件系列【5】——高级实践(1)的更多相关文章

  1. Android自己定义组件系列【7】——进阶实践(4)

    上一篇<Android自己定义组件系列[6]--进阶实践(3)>中补充了关于Android中事件分发的过程知识.这一篇我们接着来分析任老师的<可下拉的PinnedHeaderExpa ...

  2. Android自己定义组件系列【6】——进阶实践(3)

    上一篇<Android自己定义组件系列[5]--进阶实践(2)>继续对任老师的<可下拉的PinnedHeaderExpandableListView的实现>进行了分析,这一篇计 ...

  3. Android自己定义组件系列【5】——进阶实践(2)

    上一篇<Android自己定义组件系列[5]--进阶实践(1)>中对任老师的<可下拉的PinnedHeaderExpandableListView的实现>前一部分进行了实现,这 ...

  4. Android自己定义组件系列【4】——自己定义ViewGroup实现双側滑动

    在上一篇文章<Android自己定义组件系列[3]--自己定义ViewGroup实现側滑>中实现了仿Facebook和人人网的側滑效果,这一篇我们将接着上一篇来实现双面滑动的效果. 1.布 ...

  5. Android自己定义组件系列【3】——自己定义ViewGroup实现側滑

    有关自己定义ViewGroup的文章已经非常多了,我为什么写这篇文章,对于刚開始学习的人或者对自己定义组件比較生疏的朋友尽管能够拿来主义的用了,可是要一步一步的实现和了解当中的过程和原理才干真真脱离别 ...

  6. Android自己定义组件系列【1】——自己定义View及ViewGroup

    View类是ViewGroup的父类,ViewGroup具有View的全部特性.ViewGroup主要用来充当View的容器.将当中的View作为自己孩子,并对其进行管理.当然孩子也能够是ViewGr ...

  7. Android自己定义组件系列【2】——Scroller类

    在上一篇中介绍了View类的scrollTo和scrollBy两个方法,对这两个方法不太了解的朋友能够先看<自己定义View及ViewGroup> scrollTo和scrollBy尽管实 ...

  8. Android自己定义组件系列【9】——Canvas绘制折线图

    有时候我们在项目中会遇到使用折线图等图形,Android的开源项目中为我们提供了非常多插件,可是非常多时候我们须要依据详细项目自己定义这些图表,这一篇文章我们一起来看看怎样在Android中使用Can ...

  9. Android自己定义组件系列【8】——面膜文字动画

    我们掩盖文字动画Flash中非经货共同体共同,由于Android应用程序开发人员做你想要做这个动画在应用程序中去?本文中,我们看的是如何自己的定义ImageView来实现让一张文字图片实现文字的遮罩闪 ...

随机推荐

  1. DB2错误码解释对照

    表 2. SQLSTATE 类代码 类  代码    含义 要获得子代码,  参阅...  00 完全成功完成 表 3  01 警告 表 4  02 无数据 表 5  07 动态 SQL 错误 表 6 ...

  2. hdu 2842 Chinese Rings

    点击打开hdu2842 思路: 矩阵快速幂 分析: 1 题目的意思是给定n个环,和一些规则要把所有的环全部拆下最少需要的步数 2 题目规定如果要拆第n个环,那么第n-1个要挂着,n-2环要被拆下.那么 ...

  3. oracle 之 内存—鞭辟近里(三)

    oracle 之 内存—鞭辟近里(三) 今天是2013-07-08,今天晚上突然接到一个电话,我的外甥问我的qq是多少,我感觉很吃惊,他长大了.在他现在这个年龄就开始接触网络,我难免有少许担心,希望他 ...

  4. mysql视图学习总结

    转自http://www.cnblogs.com/wangtao_20/archive/2011/02/24/1964276.html 一.使用视图的理由是什么? 1.安全性.一般是这样做的:创建一个 ...

  5. xcode中找不到XXX.dylib

    xcode中找不到 XXX.dylib 了,比如libz.tbd 如果要用到 libz.dylib,可以用下面的办法,来自 Stack Overflow. Go to Build Phases > ...

  6. 前后端分离Web项目中,RBAC实现的研究

    在前后端分离Web项目中,RBAC实现的研究   最近手头公司的网站项目终于渐渐走出混沌,走上正轨,任务也轻松了一些,终于有时间整理和总结一下之前做的东西. 以往的项目一般使用模板引擎(如ejs)渲染 ...

  7. 京东商城招聘自动调价系统架构师 T4级别

    岗位级别:T4 岗位职责: 1.负责自动调价系统的架构设计 2.负责自动调价的预测.相关性算法设计 3.核心代码编写,代码review 任职要求: 1.熟悉数据挖掘.机器学习理论和算法 2.熟悉海量数 ...

  8. jeecg 3.5.2 新版本号4种首页风格 【经典风格,shortcut风格,ACE bootstrap风格,云桌面风格】

    [1]经典风格: [2]Shortcut风格: [3]ACE bootsrap风格: [4]云桌面风格: [5]自己定义图表 watermark/2/text/aHR0cDovL2Jsb2cuY3Nk ...

  9. iOS_25彩票_幸运转盘

    终于效果图: 各个view的关系图: 背景圆盘(须要扣图处理)LuckyBaseBackground.png 盖在背景圆盘上面的转盘 LuckyRotateWheel.png watermark/2/ ...

  10. Lua 与C 交换 第一篇

    编译 windows上编译lua源代码 cl /MD /O2 /W3 /c /DLUA_BUILD_AS_DLL *.c del *.o ren lua.obj lua.o ren luac.obj ...