Android自己定义组件系列【5】——高级实践(1)
在接下来的几篇文章将任老师的博文《您可以下拉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)的更多相关文章
- Android自己定义组件系列【7】——进阶实践(4)
上一篇<Android自己定义组件系列[6]--进阶实践(3)>中补充了关于Android中事件分发的过程知识.这一篇我们接着来分析任老师的<可下拉的PinnedHeaderExpa ...
- Android自己定义组件系列【6】——进阶实践(3)
上一篇<Android自己定义组件系列[5]--进阶实践(2)>继续对任老师的<可下拉的PinnedHeaderExpandableListView的实现>进行了分析,这一篇计 ...
- Android自己定义组件系列【5】——进阶实践(2)
上一篇<Android自己定义组件系列[5]--进阶实践(1)>中对任老师的<可下拉的PinnedHeaderExpandableListView的实现>前一部分进行了实现,这 ...
- Android自己定义组件系列【4】——自己定义ViewGroup实现双側滑动
在上一篇文章<Android自己定义组件系列[3]--自己定义ViewGroup实现側滑>中实现了仿Facebook和人人网的側滑效果,这一篇我们将接着上一篇来实现双面滑动的效果. 1.布 ...
- Android自己定义组件系列【3】——自己定义ViewGroup实现側滑
有关自己定义ViewGroup的文章已经非常多了,我为什么写这篇文章,对于刚開始学习的人或者对自己定义组件比較生疏的朋友尽管能够拿来主义的用了,可是要一步一步的实现和了解当中的过程和原理才干真真脱离别 ...
- Android自己定义组件系列【1】——自己定义View及ViewGroup
View类是ViewGroup的父类,ViewGroup具有View的全部特性.ViewGroup主要用来充当View的容器.将当中的View作为自己孩子,并对其进行管理.当然孩子也能够是ViewGr ...
- Android自己定义组件系列【2】——Scroller类
在上一篇中介绍了View类的scrollTo和scrollBy两个方法,对这两个方法不太了解的朋友能够先看<自己定义View及ViewGroup> scrollTo和scrollBy尽管实 ...
- Android自己定义组件系列【9】——Canvas绘制折线图
有时候我们在项目中会遇到使用折线图等图形,Android的开源项目中为我们提供了非常多插件,可是非常多时候我们须要依据详细项目自己定义这些图表,这一篇文章我们一起来看看怎样在Android中使用Can ...
- Android自己定义组件系列【8】——面膜文字动画
我们掩盖文字动画Flash中非经货共同体共同,由于Android应用程序开发人员做你想要做这个动画在应用程序中去?本文中,我们看的是如何自己的定义ImageView来实现让一张文字图片实现文字的遮罩闪 ...
随机推荐
- 解决Andriod使用HttpURLConnection 失败问题
在Android的Activity中使用HttpURLConnection连接到服务端时抛出异常,Access denied.第一个想到是权限问题.然后就尝试将INTERNET权限加上:在Manife ...
- Mybatis 3 返回布尔值,需要注意的地方
在Mybatis中,有时候需要返回布尔值 ,来确定某个记录行是否存在. 例如: <select id="isExistCode" parameterType="st ...
- POJ 3415 Max Sum of Max-K-sub-sequence (线段树+dp思想)
Max Sum of Max-K-sub-sequence Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K ...
- uva-211-The Domino Effect
http://uva.onlinejudge.org/external/2/211.html http://uva.onlinejudge.org/external/2/211.pdf 题意:每一种骨 ...
- UVA1450-Airport
题目链接 题意:有一个飞机场.有两条待飞跑到w和e.一条起飞跑道.每一时刻仅仅能起飞一架飞机,然后有w[i]和e[i]架飞机进入w和e跑道.飞机编号从0開始,问说怎样安排起飞能够使得飞机编号的最大值最 ...
- Unity3D ITween!
percentage +=0.001f; iTween.PutOnPath(gameObject,path,percentage); //You can cause the object to ori ...
- 怎样通过git协作开发
近期iOS群里的一些小伙伴刚刚毕业,刚參加工作的小伙伴们,对于怎样进行git下的一个写作开发抱有较大的疑惑.今天小汤我就给大家分享个git下协作开发的小技巧. 怎样通过git协作开发? 当两个开发人员 ...
- MongoDB--Getting Started with Java Driver
原文链接 http://docs.mongodb.org/ecosystem/tutorial/getting-started-with-java-driver/ 介绍 本文的目的是让你对怎样使用M ...
- cocos2d-x 3.0游戏实例学习笔记《卡牌塔防》第二步---编辑器(1)--触摸加入点
/* 说明: **1.本次游戏实例是<cocos2d-x游戏开发之旅>上的最后一个游戏,这里用3.0重写并做下笔记 **2.我也问过木头本人啦,他说:随便写,第一别全然照搬代码:第二能够说 ...
- Spring实战笔记2---Bean的装配
创建应用对象之间协作关系的行为通常成为装配,该篇的主要内容有两个,一个Spring装配Bean的几种方式以及Spring表达式,事实上这两者是分不开的,在Spring中,对象无需自己负责查找或者创建与 ...