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

来源:互联网 发布:python和ruby的区别 编辑:程序博客网 时间:2024/05/22 05:29

接下来几篇文章将对任老师的博文《可下拉的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.htmlint 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?");}}
再处理屏幕的监听函数

@Overridepublic 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则传递给子控件。

@Overridepublic 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() {@Overridepublic 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;   //上面部分,下面成为Headerprivate View mContent;  //下面部分private OnGiveUpTouchEnventListener mGiveUpTouchEventListener;private int mTouchSlop;  //移动的距离//header的高度   单位:pxprivate 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()函数被执行时。 */@Overridepublic 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.htmlint 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?");}}@Overridepublic 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;}@Overridepublic 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() {@Overridepublic 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{@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}}
运行效果:

原文地址:http://blog.csdn.net/singwhatiwanna/article/details/25546871


19 0
原创粉丝点击