当前位置: 代码迷 >> Android >> Android自定义组件系列【五】——进阶实践(1)
  详细解决方案

Android自定义组件系列【五】——进阶实践(1)

热度:12   发布时间:2016-04-28 05:39:42.0
Android自定义组件系列【5】——进阶实践(1)

接下来几篇文章将对任老师的博文《可下拉的PinnedHeaderExpandableListView的实现》分步骤来详细实现,来学习一下大神的代码并记录一下。

原文出处:http://blog.csdn.net/singwhatiwanna/article/details/25546871

先看一下最终效果:

新建一个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,思路其实很简单,先获取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


  相关解决方案