乘着中午休息時間,随便寫點
這裡是一個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>
