1. 程式人生 > >android 頁面容器 下一頁很上一頁view

android 頁面容器 下一頁很上一頁view

乘著中午休息時間,隨便寫點

這裡是一個page容器可以包含多個BasePageView

容器程式碼:

package com.pingyijinren.guider.setting.view;

import java.util.ArrayList;
import java.util.Collection;

import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.Animator.AnimatorListener;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener;
import com.pingyijinren.guider.Constants;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;

/**既可以滑動,又可以翻頁的scrollview<br>
 * 如果在其layout中新增的子view不是規則的,那麼還是按照width來跑一頁<br>
 * 這裡的width值是建構函式設定的。通過{@link #setPageWidth(int)}來設定寬度<br>
 * 在手動滑動的時候,動畫會停止,開啟手動滑動{@link #enableSlide(boolean)}<br>
 * 如果手動滑到了2個頁面之間,那麼在呼叫 {@link #previous()} 或 {@link #next()} 會移動一個scrollX 求 width的摸值 <br>
 * 注意!!!不需要再設定linearLayou,已經預設設定好,通過{@link #addPageView(View)} 來新增page<br>
 * TODO 新增不規則的view可以很好的展現
 * @author WenYF
 *
 */
public class PageLayout extends LinearLayout implements AnimatorListener
, AnimatorUpdateListener{
	private static final String TAG =  "AnimationHorizontalScrollView";
	
	@SuppressWarnings("unused")
	private Context nContext;
	/**
	 * 動畫值發生器
	 */
	private ValueAnimator nPositiveValueAnimator;
	/**
	 * 頁面的寬度
	 */
	private int nPageWidth;
	/**
	 * 是否設定了頁面寬度
	 */
	private boolean nHasSetWidth;
	/**
	 * 頁面數量
	 */
	private int nPageCounts;
	/**
	 * 是否設定了頁面數量
	 */
	private boolean nHasSetPageCounts;
	/**
	 * 當前頁面動畫的開始位置
	 */
	private int nCurrentStartX;
	
	/**
	 * 動畫是否開啟
	 */
	private boolean nIsEnableAnimation;
	/**
	 * 動畫是否結束
	 */
	private boolean nIsEndAnimation;
	
	private Collection<BasePageView> nPageViewsReference;
	/**
	 * 進入的page
	 */
	private BasePageView nInPageView;
	/**
	 * 出去的page
	 */
	private BasePageView nOutPageView;
	
	private OnPageListener nPageListener;
	
	public void setPageListener(OnPageListener listener) {
		nPageListener = listener;
	}
	
	/**只能通過改函式來動態設定本view,counts和width一旦設定不能修改
	 * @param context 上下文
	 * @param pageCounts 子view的數量 如果為-1則交給scroll view自己來監視有多少個view
	 * @param pageWidth 一頁的寬度,-1表示由scroll view來測量自己的寬度
	 */
	public PageLayout(Context context, int pageCounts, int pageWidth) {
		super(context);
		nContext = context;
		initView(pageCounts, pageWidth);
	}
	
	/**使用預設值構造,{@link #PageHorizontalScrollView(Context, int, int)}
	 * @param context
	 */
	public PageLayout(Context context) {
		this(context, null);
	}
	
	/**使用預設值構造,{@link #PageHorizontalScrollView(Context, int, int)}
	 * @param context
	 */
	public PageLayout(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}
	
	/**使用預設值構造,{@link #PageHorizontalScrollView(Context, int, int)}
	 * @param context
	 */
	public PageLayout(Context context, AttributeSet attrs,
			int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		nContext = context;
		initView(-1, -1);
		
	}
	
	private void initView(int counts, int width) {
		nPageViewsReference = new ArrayList<BasePageView>();
		
		ValueAnimator.setFrameDelay(30);
		nPositiveValueAnimator = ValueAnimator.ofInt(0, width);
		nPositiveValueAnimator.setDuration(Constants.ANIMATION_SPEED);
		nPositiveValueAnimator.addListener(this);
		nPositiveValueAnimator.addUpdateListener(this);
		
		nPageWidth = width;
		nHasSetWidth = width != -1;
		
		nPageCounts = counts;
		nHasSetPageCounts = nPageCounts != -1;
		
		nIsEndAnimation = true;
		nIsEnableAnimation = true;
		Log.d(TAG, "width = " + width + ", counts = " + nPageCounts);
		
		setHorizontalScrollBarEnabled(false);
		setHorizontalFadingEdgeEnabled(false);
		
	}
	
	
	/**
	 * @param counts 子view的數量 如果為-1則交給scroll view自己來監視有多少個view
	 */
	public void setPageCounts(int counts) {
		if (counts == -1) {
			nHasSetPageCounts = false;
		} else {
			nHasSetPageCounts = true;
			nPageCounts = counts;
		}
	}
	
	/**
	 * @param width 一頁的寬度,-1表示由scroll view來測量自己的寬度
	 */
	public void setPageWidth(int width) {
		if (width == -1) {
			nHasSetWidth = false;
		} else {
			nHasSetWidth = true;
			nPageWidth = width;
		}
	}
	
	/**開啟或關閉跳轉頁面動畫
	 * @param enable
	 */
	public void enableAnimation(boolean enable) {
		nIsEnableAnimation = enable;
	}
	
	/** 給容器新增view,而不是scroll view
	 * @param page
	 */
	public void addPageView(BasePageView page) {
		addView(page);
		nPageViewsReference.add(page);
	}
	
	/** 給容器新增view,而不是scroll view
	 * @param child
	 * @param index
	 */
	public void addPageView(BasePageView page, int index) {
		addView(page, index);
		nPageViewsReference.add(page);
	}
	
	/** 給容器新增view,而不是scroll view
	 * @param child
	 * @param index
	 * @param params
	 */
	public void addPageView(BasePageView page, int index,
			android.view.ViewGroup.LayoutParams params) {
		addView(page, params);
		nPageViewsReference.add(page);
	}
	
	/**  給容器新增view,而不是scroll view
	 * @param child
	 * @param width
	 * @param height
	 */
	public void addPageView(BasePageView page, int width, int height) {
		addView(page, width, height);
		nPageViewsReference.add(page);
	}
	
	/** 給容器新增view,而不是scroll view
	 * @param child
	 * @param params
	 */
	public void addPageView(BasePageView page, android.view.ViewGroup.LayoutParams params) {
		addView(page, params);
		nPageViewsReference.add(page);
	}
	
	public  Collection<BasePageView> getPageViews() {
		return nPageViewsReference;
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		Log.d(TAG, "onMeasure view width = " + getWidth());
		
		if (!nHasSetWidth) {
			nPositiveValueAnimator.setIntValues(0, getWidth());
			nPageWidth = getWidth();
		}
		
		if (!nHasSetPageCounts) {
			nPageCounts = getChildCount();
			Log.d(TAG, "onMeasure page Counts = " + nPageCounts);
		}
		
	}
	
	/**
	 * 下一頁, 如果動畫沒有結束,呼叫沒有效果<br>
	 * 如果不完整會顯示完整
	 */
	public void next() {
		Log.d(TAG, "next scrollx = " + getScrollX());
		
		if (nPageCounts <= 0 || nPageWidth <= 0) {
			Log.w(TAG, "the values is invalid, page counts = " + ", width = " + nPageWidth);
			return;
		}
		
		// 最後一頁,通知
		if (getScrollX() >= nPageWidth * (nPageCounts - 1) && nPageListener != null) {
			nPageListener.onEnd();
		}
		
		if (getScrollX() < nPageWidth * (nPageCounts - 1) && nIsEndAnimation) {
			int inPageIndex = getScrollX() / nPageWidth + 1;
			int outPageIndex = getScrollX() / nPageWidth;
			
			Log.d(TAG, "inPageIndex = " + inPageIndex);
			Log.d(TAG, "outPageIndex = " + outPageIndex);
			
			nInPageView = (BasePageView) 
					getChildAt(inPageIndex);
			nOutPageView = (BasePageView) 
					getChildAt(outPageIndex);
			
			// 得到要移動的距離
			int deltaX = nPageWidth - (getScrollX() % nPageWidth) != 0 ? 
					nPageWidth - (getScrollX() % nPageWidth) : nPageWidth;
			Log.d(TAG, "next deltaX = " + deltaX);
			
			// 前一頁離開
			nOutPageView.out();
			
			if (!nIsEnableAnimation) {
				nCurrentStartX = getScrollX() + deltaX;
				scrollTo(nCurrentStartX, 0);
				// 後一頁進入
				nInPageView.in();
			} else {
				nCurrentStartX = getScrollX();
				nPositiveValueAnimator.setIntValues(0, deltaX);
				nPositiveValueAnimator.start();
			}
			
			
			
		}
	}
	/**
	 * 上一頁, 如果動畫沒有結束,呼叫沒有效果<br>
	 * 如果不完整會顯示完整
	 */
	public void previous() {
		Log.d(TAG, "previous scrollx = " + getScrollX());
		
		if (nPageCounts <= 0 || nPageWidth <= 0) {
			Log.w(TAG, "the values is invalid, page counts = " + nPageCounts + ", width = " + nPageWidth);
			return;
		}
		
		if (getScrollX() > 0  && nIsEndAnimation) {
			int totleWidth = nPageCounts * nPageWidth;
			int inPageIndex = nPageCounts - 1 - ((totleWidth - (getScrollX() + nPageWidth)) / nPageWidth + 1);
			int outPageIndex = nPageCounts - 1 - (totleWidth - (getScrollX() + nPageWidth)) / nPageWidth;
			
			Log.d(TAG, "inPageIndex = " + inPageIndex);
			Log.d(TAG, "outPageIndex = " + outPageIndex);
			
			nInPageView = (BasePageView) 
					getChildAt(inPageIndex);
			nOutPageView = (BasePageView) 
					getChildAt(outPageIndex);
			
			// 得到要移動的距離
			int deltaX = getScrollX() % nPageWidth != 0 ? getScrollX() % nPageWidth : nPageWidth;
			Log.d(TAG, "previous deltaX = " + deltaX);
			
			// 前一頁離開
			nOutPageView.out();
			
			nCurrentStartX = getScrollX() - deltaX;
			if (!nIsEnableAnimation) {
				scrollTo(nCurrentStartX, 0);
				// 後一頁進入
				nInPageView.in();
			} else {
				nPositiveValueAnimator.setIntValues(0, deltaX);
				nPositiveValueAnimator.reverse();
			}
			
			
		}
	}
	
	@Override
	public void onAnimationStart(Animator animation) {
		nIsEndAnimation = false;
		setEnabled(false);
	}

	@Override
	public void onAnimationEnd(Animator animation) {
		nIsEndAnimation = true;
		
		nInPageView.in();
		
		setEnabled(true);
	}

	@Override
	public void onAnimationCancel(Animator animation) {
		nIsEndAnimation = true;
		setEnabled(true);
	}

	@Override
	public void onAnimationRepeat(Animator animation) {
		
	}

	@Override
	public void onAnimationUpdate(ValueAnimator animation) {
		int values = nCurrentStartX + (Integer) animation.getAnimatedValue();
		Log.v(TAG, "values = " + values);
		scrollTo((int)values, 0);
	}
	
	public interface OnPageListener {
		public void onEnd();
	}
}
這裡是BasePageView

有一點針對業務定製,去掉即可,結構不變:

package com.pingyijinren.guider.setting.view;

import com.pingyijinren.guider.R;

import android.app.Dialog;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;

/**這是一個開機引導中頁面的view基類<br>
 * 它儘可能的對這些頁面進行了抽象,但依舊不是很理想<br>
 * 對整個介面有一個rootView,是一個{@link RelativeLayout}物件<br>
 * 另外還有2個{@link RelativeLayout}物件 分別是上容器和下容器<br>
 * 在上容器裡面定義了2個按鈕,一個是“上一步” 另一個是“跳過”<br>
 * {@link #in()} , {@link #out()} 表示page完成進入和開始退出回撥<br>
 * @author WenYF
 *
 */
public abstract class BasePageView extends RelativeLayout{
	
	/**
	 * page中唯一的一個對話方塊 
	 */
	protected Dialog nDialog;
	/**
	 * 頁面的根view
	 */
	protected RelativeLayout nRootView;
	/**
	 * 上容器
	 */
	protected RelativeLayout nTopViewContainer;
	/**
	 * 下容器
	 */
	protected RelativeLayout nBottomViewContainer;
	
	/**
	 * 上一步按鈕,父view是{@link #nTopViewContainer}
	 */
	protected ImageView nPreviousView;
	/**
	 * 跳過按鈕,父view是{@link #nTopViewContainer}
	 */
	protected ImageView nSkipView;
	/**
	 * 保護此頁面的容器view
	 */
	protected PageLayout nPageControlView;
	/**
	 * top container 用來顯示title的panel
	 */
	protected ImageWithTextView nTopTitlePanal;
	
	public BasePageView(Context context) {
		this(context, null);
	}
	public BasePageView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}
	
	public BasePageView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		
		LayoutInflater.from(context).inflate(R.layout.container_view_main, this, true);
		
		nRootView = (RelativeLayout) findViewById(R.id.container_root);
		nTopViewContainer = (RelativeLayout) findViewById(R.id.container_top);
		nTopTitlePanal = (ImageWithTextView) findViewById(R.id.top_title_panal);
		
		nBottomViewContainer = (RelativeLayout) findViewById(R.id.container_bottom);
		nPreviousView = (ImageView) findViewById(R.id.button_previous);
		nSkipView = (ImageView) findViewById(R.id.button_skip);
		
		nPreviousView.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				nPageControlView.previous();
			}
		});
		
		nSkipView.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				nPageControlView.next();
			}
		});
	}
	
	/**設定跳過和上一頁按鈕的visibility
	 * @param visibility
	 */
	public void setButtonViewVisibility(int visibility) {
		nSkipView.setVisibility(visibility);
		nPreviousView.setVisibility(visibility);
	}
	
	/**設定上下兩個容器 Visibility 值
	 * @param visibility {@link View#VISIBLE} or {@link View#INVISIBLE} or {@link View#GONE}
	 */
	public void setContainerViewVisibility(int visibility) {
		nTopViewContainer.setVisibility(visibility);
		nBottomViewContainer.setVisibility(visibility);
	}
	
	/**設定底部的view容器的height
	 * @param height {@link RelativeLayout.LayoutParams} match_parent \ warp_content \ custom height
	 */
	public void setBottomContainerHeight(int height) {
		RelativeLayout.LayoutParams params = (LayoutParams) nBottomViewContainer.getLayoutParams();
		params.height = height;
		nBottomViewContainer.setLayoutParams(params);
	}

	public void setPageControlView(PageLayout view) {
		nPageControlView = view;
	}
	
	/**
	 * 頁面完成進來的時候處理
	 */
	public abstract void in();
	/**
	 * 頁面開始出去的時候處理
	 */
	public abstract void out();
	/**
	 * 釋放view中可能沒有辦法釋放的記憶體
	 */
	public abstract void destory();
}
注意,程式碼裡面用到了http://download.csdn.net/detail/juy19901128/9392637的android動畫開源庫

佈局檔案

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.pingyijinren.guider.setting.activity.GuiderSettingActivity" >
    
    <RelativeLayout 
        android:id="@+id/container_top"
        android:layout_width="match_parent"
        android:layout_height="125.33dp"
        android:layout_marginTop="6.67dp"
   		android:layout_marginLeft="6.67dp"
   	 	android:layout_marginRight="6.67dp"
        android:background="@drawable/top">
        
		<ImageView 
       	    android:id="@+id/button_previous"
       	    android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:layout_marginLeft="9.33dp"
        	android:layout_marginTop="9.33dp"
        	android:clickable="true"
        	android:background="@android:color/transparent"
        	android:contentDescription="@string/text_image_view_desc"
       	    android:src="@drawable/selector_button_previous"/>
		
		<ImageView 
       	    android:id="@+id/button_skip"
       	    android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:layout_alignParentRight="true"
        	android:layout_marginRight="9.33dp"
        	android:layout_marginTop="9.33dp"
        	android:clickable="true"
        	android:background="@android:color/transparent"
        	android:contentDescription="@string/text_image_view_desc"
       	    android:src="@drawable/selector_button_skip"/>
		
		<com.pingyijinren.guider.setting.view.ImageWithTextView
	        android:id="@+id/top_title_panal"
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:layout_marginTop="35.33dp"
	        android:layout_centerHorizontal="true" >
    	</com.pingyijinren.guider.setting.view.ImageWithTextView>
		
    </RelativeLayout>
    <RelativeLayout 
        android:id="@+id/container_bottom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/container_top"
        android:layout_marginLeft="6.67dp"
   	 	android:layout_marginRight="6.67dp"
        android:background="@drawable/bottom">

    </RelativeLayout>
    

</RelativeLayout>