zoukankan      html  css  js  c++  java
  • 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,思路事实上非常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


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

  • 相关阅读:
    软件测试作业1:android手机应用布局之TabActivity
    软件测试作业2:对faulty,error和failure的理解和应用
    python-字符串常用方法、文件简单读写
    python-字典练习题
    python-字典
    python-list 列表 数组
    python基础一循环
    Charles抓包
    Jmeter分布式
    Jmeter如何操作数据库
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4911960.html
Copyright © 2011-2022 走看看