編輯:關於Android編程
現在幾乎所有的APP在首次使用的時候都會有引導頁,引導頁的內容也都是和App相關的內容。如何去簡單去設計擴展性好的引導頁是很重要的。今天就為大家提供一種結合ViewPager引導頁的設計方式,很簡單。
首先創建引導頁布局文件activity_guide.xml
上面的布局中有一個自定義的CirclePageIndicator類,這是引導頁的索引類,是第三方提供的。
CirclePageIndicator
/* * Copyright (C) 2011 Patrik Akerfeldt * Copyright (C) 2011 Jake Wharton * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.syz.guidepage.demo; import static android.graphics.Paint.ANTI_ALIAS_FLAG; import static android.widget.LinearLayout.HORIZONTAL; import static android.widget.LinearLayout.VERTICAL; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.ViewConfigurationCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; /** * Draws circles (one for each view). The current view position is filled and * others are only stroked. */ public class CirclePageIndicator extends View implements PageIndicator { private static final int INVALID_POINTER = -1; private float mRadius; private final Paint mPaintPageFill = new Paint(ANTI_ALIAS_FLAG); private final Paint mPaintStroke = new Paint(ANTI_ALIAS_FLAG); private final Paint mPaintFill = new Paint(ANTI_ALIAS_FLAG); private ViewPager mViewPager; private ViewPager.OnPageChangeListener mListener; private int mCurrentPage; private int mSnapPage; private float mPageOffset; private int mScrollState; private int mOrientation; private boolean mCentered; private boolean mSnap; private int mTouchSlop; private float mLastMotionX = -1; private int mActivePointerId = INVALID_POINTER; private boolean mIsDragging; public CirclePageIndicator(Context context) { this(context, null); } public CirclePageIndicator(Context context, AttributeSet attrs) { this(context, attrs, R.attr.vpiCirclePageIndicatorStyle); } public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); if (isInEditMode()) return; //Load defaults from resources final Resources res = getResources(); final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color); final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color); final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation); final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color); final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width); final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius); final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered); final boolean defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap); //Retrieve styles attributes TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, 0); mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered); mOrientation = a.getInt(R.styleable.CirclePageIndicator_android_orientation, defaultOrientation); mPaintPageFill.setStyle(Style.FILL); mPaintPageFill.setColor(a.getColor(R.styleable.CirclePageIndicator_pageColor, defaultPageColor)); mPaintStroke.setStyle(Style.STROKE); mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor)); mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth)); mPaintFill.setStyle(Style.FILL); mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor)); mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius); mSnap = a.getBoolean(R.styleable.CirclePageIndicator_snap, defaultSnap); Drawable background = a.getDrawable(R.styleable.CirclePageIndicator_android_background); if (background != null) { setBackgroundDrawable(background); } a.recycle(); final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); } public ViewPager.OnPageChangeListener getmListener() { return mListener; } public void setmListener(ViewPager.OnPageChangeListener mListener) { this.mListener = mListener; } public void setCentered(boolean centered) { mCentered = centered; invalidate(); } public boolean isCentered() { return mCentered; } public void setPageColor(int pageColor) { mPaintPageFill.setColor(pageColor); invalidate(); } public int getPageColor() { return mPaintPageFill.getColor(); } public void setFillColor(int fillColor) { mPaintFill.setColor(fillColor); invalidate(); } public int getFillColor() { return mPaintFill.getColor(); } public void setOrientation(int orientation) { switch (orientation) { case HORIZONTAL: case VERTICAL: mOrientation = orientation; requestLayout(); break; default: throw new IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL."); } } public int getOrientation() { return mOrientation; } public void setStrokeColor(int strokeColor) { mPaintStroke.setColor(strokeColor); invalidate(); } public int getStrokeColor() { return mPaintStroke.getColor(); } public void setStrokeWidth(float strokeWidth) { mPaintStroke.setStrokeWidth(strokeWidth); invalidate(); } public float getStrokeWidth() { return mPaintStroke.getStrokeWidth(); } public void setRadius(float radius) { mRadius = radius; invalidate(); } public float getRadius() { return mRadius; } public void setSnap(boolean snap) { mSnap = snap; invalidate(); } public boolean isSnap() { return mSnap; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mViewPager == null) { return; } final int count = mViewPager.getAdapter().getCount(); if (count == 0) { return; } if (mCurrentPage >= count) { setCurrentItem(count - 1); return; } int longSize; int longPaddingBefore; int longPaddingAfter; int shortPaddingBefore; if (mOrientation == HORIZONTAL) { longSize = getWidth(); longPaddingBefore = getPaddingLeft(); longPaddingAfter = getPaddingRight(); shortPaddingBefore = getPaddingTop(); } else { longSize = getHeight(); longPaddingBefore = getPaddingTop(); longPaddingAfter = getPaddingBottom(); shortPaddingBefore = getPaddingLeft(); } final float threeRadius = mRadius * 3; final float shortOffset = shortPaddingBefore + mRadius; float longOffset = longPaddingBefore + mRadius; if (mCentered) { longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) - ((count * threeRadius) / 2.0f); } float dX; float dY; float pageFillRadius = mRadius; if (mPaintStroke.getStrokeWidth() > 0) { pageFillRadius -= mPaintStroke.getStrokeWidth() / 2.0f; } //Draw stroked circles for (int iLoop = 0; iLoop < count; iLoop++) { float drawLong = longOffset + (iLoop * threeRadius); if (mOrientation == HORIZONTAL) { dX = drawLong; dY = shortOffset; } else { dX = shortOffset; dY = drawLong; } // Only paint fill if not completely transparent if (mPaintPageFill.getAlpha() > 0) { canvas.drawCircle(dX, dY, pageFillRadius, mPaintPageFill); } // Only paint stroke if a stroke width was non-zero if (pageFillRadius != mRadius) { canvas.drawCircle(dX, dY, mRadius, mPaintStroke); } } //Draw the filled circle according to the current scroll float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius; if (!mSnap) { cx += mPageOffset * threeRadius; } if (mOrientation == HORIZONTAL) { dX = longOffset + cx; dY = shortOffset; } else { dX = shortOffset; dY = longOffset + cx; } canvas.drawCircle(dX, dY, mRadius, mPaintFill); } @SuppressLint("ClickableViewAccessibility") public boolean onTouchEvent(android.view.MotionEvent ev) { if (super.onTouchEvent(ev)) { return true; } if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) { return false; } final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; switch (action) { case MotionEvent.ACTION_DOWN: mActivePointerId = MotionEventCompat.getPointerId(ev, 0); mLastMotionX = ev.getX(); break; case MotionEvent.ACTION_MOVE: { final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float x = MotionEventCompat.getX(ev, activePointerIndex); final float deltaX = x - mLastMotionX; if (!mIsDragging) { if (Math.abs(deltaX) > mTouchSlop) { mIsDragging = true; } } if (mIsDragging) { mLastMotionX = x; if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) { mViewPager.fakeDragBy(deltaX); } } break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (!mIsDragging) { final int count = mViewPager.getAdapter().getCount(); final int width = getWidth(); final float halfWidth = width / 2f; final float sixthWidth = width / 6f; if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) { if (action != MotionEvent.ACTION_CANCEL) { mViewPager.setCurrentItem(mCurrentPage - 1); } return true; } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) { if (action != MotionEvent.ACTION_CANCEL) { mViewPager.setCurrentItem(mCurrentPage + 1); } return true; } } mIsDragging = false; mActivePointerId = INVALID_POINTER; if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag(); break; case MotionEventCompat.ACTION_POINTER_DOWN: { final int index = MotionEventCompat.getActionIndex(ev); mLastMotionX = MotionEventCompat.getX(ev, index); mActivePointerId = MotionEventCompat.getPointerId(ev, index); break; } case MotionEventCompat.ACTION_POINTER_UP: final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) { final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); } mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId)); break; } return true; } @Override public void setViewPager(ViewPager view) { if (mViewPager == view) { return; } if (mViewPager != null) { mViewPager.setOnPageChangeListener(null); } if (view.getAdapter() == null) { throw new IllegalStateException("ViewPager does not have adapter instance."); } mViewPager = view; mViewPager.setOnPageChangeListener(this); invalidate(); } @Override public void setViewPager(ViewPager view, int initialPosition) { setViewPager(view); setCurrentItem(initialPosition); } @Override public void setCurrentItem(int item) { if (mViewPager == null) { throw new IllegalStateException("ViewPager has not been bound."); } mViewPager.setCurrentItem(item); mCurrentPage = item; invalidate(); } @Override public void notifyDataSetChanged() { invalidate(); } @Override public void onPageScrollStateChanged(int state) { mScrollState = state; if (mListener != null) { mListener.onPageScrollStateChanged(state); } } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { mCurrentPage = position; mPageOffset = positionOffset; invalidate(); if (mListener != null) { mListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } } @Override public void onPageSelected(int position) { if (mSnap || mScrollState == ViewPager.SCROLL_STATE_IDLE) { mCurrentPage = position; mSnapPage = position; invalidate(); } if (mListener != null) { mListener.onPageSelected(position); } } @Override public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { mListener = listener; } /* * (non-Javadoc) * * @see android.view.View#onMeasure(int, int) */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mOrientation == HORIZONTAL) { setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec)); } else { setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec)); } } /** * Determines the width of this view * * @param measureSpec * A measureSpec packed into an int * @return The width of the view, honoring constraints from measureSpec */ private int measureLong(int measureSpec) { int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) { //We were told how big to be result = specSize; } else { //Calculate the width according the views count final int count = mViewPager.getAdapter().getCount(); result = (int)(getPaddingLeft() + getPaddingRight() + (count * 2 * mRadius) + (count - 1) * mRadius + 1); //Respect AT_MOST value if that was what is called for by measureSpec if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } /** * Determines the height of this view * * @param measureSpec * A measureSpec packed into an int * @return The height of the view, honoring constraints from measureSpec */ private int measureShort(int measureSpec) { int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { //We were told how big to be result = specSize; } else { //Measure the height result = (int)(2 * mRadius + getPaddingTop() + getPaddingBottom() + 1); //Respect AT_MOST value if that was what is called for by measureSpec if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState savedState = (SavedState)state; super.onRestoreInstanceState(savedState.getSuperState()); mCurrentPage = savedState.currentPage; mSnapPage = savedState.currentPage; requestLayout(); } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState savedState = new SavedState(superState); savedState.currentPage = mCurrentPage; return savedState; } static class SavedState extends BaseSavedState { int currentPage; public SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); currentPage = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(currentPage); } public static final Parcelable.CreatorCREATOR = new Parcelable.Creator () { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } }
再創建一個用於存放引導頁的布局guide_view_pager.xml
接著創建主界面,主界面中什麼都不用,只是在TextView中寫上一句話。
MainActivity
package com.syz.guidepage.demo; import android.app.Activity; import android.os.Bundle; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }activity_main.xml布局
最後在GuideActivity中實現
package com.syz.guidepage.demo; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; /** * @author syz * @date 2016-4-20 */ public class GuideActivity extends Activity implements OnTouchListener, OnClickListener { private ViewPager mViewPager; private ViewPageAdapter adapter; private int flaggingWidth; private int size = 0; private int lastX = 0; private int currentIndex = 0; private CirclePageIndicator indicator; private boolean locker = true; private Listviews = new ArrayList (); private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE);//隱藏應用程序的標題欄 //隱藏系統標題欄 this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_guide); DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); flaggingWidth = dm.widthPixels / 2; initViewPager(); } protected void initViewPager() { mViewPager = (ViewPager) findViewById(R.id.pager_splash_ad); View view = LayoutInflater.from(this).inflate(R.layout.guide_view_pager, null); ImageView iv_ad = (ImageView) view.findViewById(R.id.pic_view); // iv_ad.setImageResource(R.drawable.guide1);//極其耗內存,圖片資源過大,很容易出現內存OOM // ImageLoaderUtil.loadImage(null, iv_ad, R.drawable.guide1); Bitmap bitmap = readBitmap(R.drawable.guide1); iv_ad.setImageBitmap(bitmap); views.add(view); View view1 = LayoutInflater.from(this).inflate(R.layout.guide_view_pager, null); ImageView iv_ad1 = (ImageView) view1.findViewById(R.id.pic_view); Bitmap bitmap1 = readBitmap(R.drawable.guide2); iv_ad1.setImageBitmap(bitmap1); views.add(view1); View view2 = LayoutInflater.from(this).inflate(R.layout.guide_view_pager, null); ImageView iv_ad2 = (ImageView) view2.findViewById(R.id.pic_view); mTextView = (TextView) view2.findViewById(R.id.goto_main); mTextView.setVisibility(View.VISIBLE); mTextView.setOnClickListener(this); Bitmap bitmap2 = readBitmap(R.drawable.guide3); iv_ad2.setImageBitmap(bitmap2); views.add(view2); size = views.size(); adapter = new ViewPageAdapter(views); mViewPager.setAdapter(adapter); indicator = (CirclePageIndicator) findViewById(R.id.viewflowindic); indicator.setmListener(new MypageChangeListener()); indicator.setViewPager(mViewPager); mViewPager.setOnTouchListener(this); } protected Bitmap readBitmap(int resId) { BitmapFactory.Options opt = new BitmapFactory.Options(); // 對像素要求不高的盡量使用RGB_565 // opt.inPreferredConfig = Bitmap.Config.RGB_565; // 該配置比上面的RGB_565要強一點,支持透明的方式 opt.inPreferredConfig = Bitmap.Config.ARGB_8888; opt.inPurgeable = true;// 表示可回收 opt.inInputShareable = true;// 可共享,與上面配合使用 // 獲取資源圖片 InputStream is = this.getResources().openRawResource(resId); return BitmapFactory.decodeStream(is, null, opt); } @Override public void onClick(View v) { if (v.getId() == R.id.goto_main) { gotoMain(); } } @Override protected void onDestroy() { super.onDestroy(); } /** * view適配器 */ protected class ViewPageAdapter extends PagerAdapter { List list = new ArrayList (); public ViewPageAdapter(List list) { this.list = list; } @Override public int getCount() { return list.size(); } @Override public Object instantiateItem(ViewGroup container, int position) { ((ViewPager) container).addView(list.get(position), 0); return list.get(position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { ((ViewPager) container).removeView(list.get(position)); } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1;// 官方提示這樣寫 } } private void gotoMain() { startActivity(new Intent(this, MainActivity.class)); finish(); overridePendingTransition(R.anim.alpha_in_anim, R.anim.alpha_out_anim); } @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = (int) event.getX(); break; case MotionEvent.ACTION_MOVE: if ((lastX - event.getX()) > flaggingWidth && (currentIndex == size - 1) && locker) { locker = false; gotoMain(); } break; default: break; } return false; } private class MypageChangeListener implements OnPageChangeListener { @Override public void onPageScrollStateChanged(int position) { } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageSelected(int arg0) { currentIndex = arg0; } } }
alpha_in_anim.xml
alpha_out_anim.xml
效果展示:
Demo地址:http://download.csdn.net/detail/loveyaozu/9497226
上一篇文章中我們講解了android中內存對象的序列化方式。由於android開發涉及到不同Activity的數據傳遞,對於基本數據類型數據的傳遞是沒有問題的,但是一旦涉
android讓人頭疼的適配問題。 --------- Android 中的單位大概有這些: 常用的dip、sp,有時候用到px。 DisplayMetrics
定位(Location) 和 傳感器(Sensors)API充分發揮了移動設備的優勢,您可以調用這些API,制作出交互性較高的應用程序,比如使用設備自帶的GPS模塊定位、
先聲明授人與魚不如授人與漁,只能提供一個思路,當然需要源碼的同學可以私下有償問我要源碼:QQ:508181017 工作了將近三年時間了,一直沒正兒八