編輯:關於Android編程
自己做的一個APP需要用到翻頁閱讀,網上看過立體翻頁效果,不過bug太多了還不兼容。看了一下多看閱讀翻頁是采用平移翻頁的,於是就仿寫了一個平移翻頁的控件。效果如下:
在翻頁時頁面右邊緣繪制了陰影,效果還不錯。要實現這種平移翻頁控件並不難,只需要定義一個布局管理頁面就可以了。具體實現上有以下難點:
1、循環翻頁,頁面的重復利用。
2、在翻頁時過濾掉多點觸碰。
3、采用setAdapter的方式設置頁面布局和數據。
下面就來一一解決這幾個難點。首先看循環翻頁問題,怎麼樣能采用較少的頁面實現這種翻頁呢?由於屏幕上每次只能顯示一張完整的頁面,翻過去的頁面也看不到,所以可以把翻過去的頁面拿來重復利用,不必每次都new一個頁面,所以,我只用了三張頁面實現循環翻頁。要想重復利用頁面,首先要知道頁面在布局中序號和對應的層次關系,比如一個父控件的子view的序號越大就位於越上層。循環利用頁面的原理圖如下:
向右翻頁時狀態圖是這樣的,只用了0、1、2三張頁面,頁面序號為2的位於最上層,我把它隱藏在左邊,所以看到的只有頁面1,頁面0在1下面擋著也看不到,向右翻頁時,頁面2被滑到屏幕中,這時候把頁面0的內容替換成頁面2的前一頁內容,把它放到之前頁面2的位置,這時,狀態又回到了初始狀態,又可以繼續向右翻頁了!
向左翻頁時是這樣的,初始狀態還是一樣,當頁面1被往左翻過時,看到的是頁面0,這時候頁面0下面已經沒有頁面了,而頁面2已經用不到了,這時候把頁面2放到頁面0下面,這時候狀態又回到了初始狀態,就可以繼續往左翻頁了。
類似於這種循環效果的實現我一直用的解決方案都是將選中的置於最中間,比如原理圖中的頁面1,每次翻頁完成後可見的都是頁面1。在滾動選擇器PickerView中也是同樣的方案。這就解決了頁面的重復利用問題了。
解決難點2 翻頁時過濾多點觸碰這個問題在仿淘寶商品浏覽界面中已經解決過了,就是用一個控制變量mEvents過濾掉pointer down或up後到來的第一個move事件。
解決難點3 采用adapter方式設置頁面的布局和數據。這個在Android的AdapterView裡用到的,但是我沒有看它的adapter機制,太復雜了,我就搞了個簡單的adapter,如下:
PageAdapter.java:
package com.jingchen.pagerdemo; import android.view.View; public abstract class PageAdapter { /** * @return 頁面view */ public abstract View getView(); public abstract int getCount(); /** * 將內容添加到view中 * * @param view * 包含內容的view * @param position * 第position頁 */ public abstract void addContent(View view, int position); }
這是一個抽象類,getView()用於返回頁面的布局,getCount()返回數據總共需要多少頁,addContent(View view, int position)這個是每翻過一頁後將會被調用來請求頁面數據的,參數view就是頁面,position是表明第幾頁。待會兒會在自定義布局中定義setAdapter方法設置設配器。
OK,難點都解決了,自定義一個布局叫ScanView繼承自RelativeLayout:
ScanView.java:
package com.jingchen.pagerdemo; import java.util.Timer; import java.util.TimerTask; import android.content.Context; import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.RectF; import android.graphics.Shader.TileMode; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.widget.RelativeLayout; /** * @author chenjing * */ public class ScanView extends RelativeLayout { public static final String TAG = "ScanView"; private boolean isInit = true; // 滑動的時候存在兩頁可滑動,要判斷是哪一頁在滑動 private boolean isPreMoving = true, isCurrMoving = true; // 當前是第幾頁 private int index; private float lastX; // 前一頁,當前頁,下一頁的左邊位置 private int prePageLeft = 0, currPageLeft = 0, nextPageLeft = 0; // 三張頁面 private View prePage, currPage, nextPage; // 頁面狀態 private static final int STATE_MOVE = 0; private static final int STATE_STOP = 1; // 滑動的頁面,只有前一頁和當前頁可滑 private static final int PRE = 2; private static final int CURR = 3; private int state = STATE_STOP; // 正在滑動的頁面右邊位置,用於繪制陰影 private float right; // 手指滑動的距離 private float moveLenght; // 頁面寬高 private int mWidth, mHeight; // 獲取滑動速度 private VelocityTracker vt; // 防止抖動 private float speed_shake = 20; // 當前滑動速度 private float speed; private Timer timer; private MyTimerTask mTask; // 滑動動畫的移動速度 public static final int MOVE_SPEED = 10; // 頁面適配器 private PageAdapter adapter; /** * 過濾多點觸碰的控制變量 */ private int mEvents; public void setAdapter(ScanViewAdapter adapter) { removeAllViews(); this.adapter = adapter; prePage = adapter.getView(); addView(prePage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); adapter.addContent(prePage, index - 1); currPage = adapter.getView(); addView(currPage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); adapter.addContent(currPage, index); nextPage = adapter.getView(); addView(nextPage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); adapter.addContent(nextPage, index + 1); } /** * 向左滑。注意可以滑動的頁面只有當前頁和前一頁 * * @param which */ private void moveLeft(int which) { switch (which) { case PRE: prePageLeft -= MOVE_SPEED; if (prePageLeft < -mWidth) prePageLeft = -mWidth; right = mWidth + prePageLeft; break; case CURR: currPageLeft -= MOVE_SPEED; if (currPageLeft < -mWidth) currPageLeft = -mWidth; right = mWidth + currPageLeft; break; } } /** * 向右滑。注意可以滑動的頁面只有當前頁和前一頁 * * @param which */ private void moveRight(int which) { switch (which) { case PRE: prePageLeft += MOVE_SPEED; if (prePageLeft > 0) prePageLeft = 0; right = mWidth + prePageLeft; break; case CURR: currPageLeft += MOVE_SPEED; if (currPageLeft > 0) currPageLeft = 0; right = mWidth + currPageLeft; break; } } /** * 當往回翻過一頁時添加前一頁在最左邊 */ private void addPrePage() { removeView(nextPage); addView(nextPage, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); // 從適配器獲取前一頁內容 adapter.addContent(nextPage, index - 1); // 交換順序 View temp = nextPage; nextPage = currPage; currPage = prePage; prePage = temp; prePageLeft = -mWidth; } /** * 當往前翻過一頁時,添加一頁在最底下 */ private void addNextPage() { removeView(prePage); addView(prePage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); // 從適配器獲取後一頁內容 adapter.addContent(prePage, index + 1); // 交換順序 View temp = currPage; currPage = nextPage; nextPage = prePage; prePage = temp; currPageLeft = 0; } Handler updateHandler = new Handler() { @Override public void handleMessage(Message msg) { if (state != STATE_MOVE) return; // 移動頁面 // 翻回,先判斷當前哪一頁處於未返回狀態 if (prePageLeft > -mWidth && speed <= 0) { // 前一頁處於未返回狀態 moveLeft(PRE); } else if (currPageLeft < 0 && speed >= 0) { // 當前頁處於未返回狀態 moveRight(CURR); } else if (speed < 0 && index < adapter.getCount()) { // 向左翻,翻動的是當前頁 moveLeft(CURR); if (currPageLeft == (-mWidth)) { index++; // 翻過一頁,在底下添加一頁,把最上層頁面移除 addNextPage(); } } else if (speed > 0 && index > 1) { // 向右翻,翻動的是前一頁 moveRight(PRE); if (prePageLeft == 0) { index--; // 翻回一頁,添加一頁在最上層,隱藏在最左邊 addPrePage(); } } if (right == 0 || right == mWidth) { releaseMoving(); state = STATE_STOP; quitMove(); } ScanView.this.requestLayout(); } }; public ScanView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public ScanView(Context context) { super(context); init(); } public ScanView(Context context, AttributeSet attrs) { super(context, attrs); init(); } /** * 退出動畫翻頁 */ public void quitMove() { if (mTask != null) { mTask.cancel(); mTask = null; } } private void init() { index = 1; timer = new Timer(); mTask = new MyTimerTask(updateHandler); } /** * 釋放動作,不限制手滑動方向 */ private void releaseMoving() { isPreMoving = true; isCurrMoving = true; } @Override public boolean dispatchTouchEvent(MotionEvent event) { if (adapter != null) switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: lastX = event.getX(); try { if (vt == null) { vt = VelocityTracker.obtain(); } else { vt.clear(); } } catch (Exception e) { e.printStackTrace(); } vt.addMovement(event); mEvents = 0; break; case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_POINTER_UP: mEvents = -1; break; case MotionEvent.ACTION_MOVE: // 取消動畫 quitMove(); Log.d("index", "mEvents = " + mEvents + ", isPreMoving = " + isPreMoving + ", isCurrMoving = " + isCurrMoving); vt.addMovement(event); vt.computeCurrentVelocity(500); speed = vt.getXVelocity(); moveLenght = event.getX() - lastX; if ((moveLenght > 0 || !isCurrMoving) && isPreMoving && mEvents == 0) { isPreMoving = true; isCurrMoving = false; if (index == 1) { // 第一頁不能再往右翻,跳轉到前一個activity state = STATE_MOVE; releaseMoving(); } else { // 非第一頁 prePageLeft += (int) moveLenght; // 防止滑過邊界 if (prePageLeft > 0) prePageLeft = 0; else if (prePageLeft < -mWidth) { // 邊界判斷,釋放動作,防止來回滑動導致滑動前一頁時當前頁無法滑動 prePageLeft = -mWidth; releaseMoving(); } right = mWidth + prePageLeft; state = STATE_MOVE; } } else if ((moveLenght < 0 || !isPreMoving) && isCurrMoving && mEvents == 0) { isPreMoving = false; isCurrMoving = true; if (index == adapter.getCount()) { // 最後一頁不能再往左翻 state = STATE_STOP; releaseMoving(); } else { currPageLeft += (int) moveLenght; // 防止滑過邊界 if (currPageLeft < -mWidth) currPageLeft = -mWidth; else if (currPageLeft > 0) { // 邊界判斷,釋放動作,防止來回滑動導致滑動當前頁是前一頁無法滑動 currPageLeft = 0; releaseMoving(); } right = mWidth + currPageLeft; state = STATE_MOVE; } } else mEvents = 0; lastX = event.getX(); requestLayout(); break; case MotionEvent.ACTION_UP: if (Math.abs(speed) < speed_shake) speed = 0; quitMove(); mTask = new MyTimerTask(updateHandler); timer.schedule(mTask, 0, 5); try { vt.clear(); vt.recycle(); } catch (Exception e) { e.printStackTrace(); } break; default: break; } super.dispatchTouchEvent(event); return true; } /* * (非 Javadoc) 在這裡繪制翻頁陰影效果 * * @see android.view.ViewGroup#dispatchDraw(android.graphics.Canvas) */ @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (right == 0 || right == mWidth) return; RectF rectF = new RectF(right, 0, mWidth, mHeight); Paint paint = new Paint(); paint.setAntiAlias(true); LinearGradient linearGradient = new LinearGradient(right, 0, right + 36, 0, 0xffbbbbbb, 0x00bbbbbb, TileMode.CLAMP); paint.setShader(linearGradient); paint.setStyle(Style.FILL); canvas.drawRect(rectF, paint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); if (isInit) { // 初始狀態,一頁放在左邊隱藏起來,兩頁疊在一塊 prePageLeft = -mWidth; currPageLeft = 0; nextPageLeft = 0; isInit = false; } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (adapter == null) return; prePage.layout(prePageLeft, 0, prePageLeft + prePage.getMeasuredWidth(), prePage.getMeasuredHeight()); currPage.layout(currPageLeft, 0, currPageLeft + currPage.getMeasuredWidth(), currPage.getMeasuredHeight()); nextPage.layout(nextPageLeft, 0, nextPageLeft + nextPage.getMeasuredWidth(), nextPage.getMeasuredHeight()); invalidate(); } class MyTimerTask extends TimerTask { Handler handler; public MyTimerTask(Handler handler) { this.handler = handler; } @Override public void run() { handler.sendMessage(handler.obtainMessage()); } } }
代碼中的注釋寫的非常多,原理理解了看代碼就容易看懂了。寫完這個布局後再寫一個ScanViewAdapter繼承PageAdapter:
package com.jingchen.pagerdemo; import java.util.Timer; import java.util.TimerTask; import android.content.Context; import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.RectF; import android.graphics.Shader.TileMode; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.widget.RelativeLayout; /** * @author chenjing * */ public class ScanView extends RelativeLayout { public static final String TAG = "ScanView"; private boolean isInit = true; // 滑動的時候存在兩頁可滑動,要判斷是哪一頁在滑動 private boolean isPreMoving = true, isCurrMoving = true; // 當前是第幾頁 private int index; private float lastX; // 前一頁,當前頁,下一頁的左邊位置 private int prePageLeft = 0, currPageLeft = 0, nextPageLeft = 0; // 三張頁面 private View prePage, currPage, nextPage; // 頁面狀態 private static final int STATE_MOVE = 0; private static final int STATE_STOP = 1; // 滑動的頁面,只有前一頁和當前頁可滑 private static final int PRE = 2; private static final int CURR = 3; private int state = STATE_STOP; // 正在滑動的頁面右邊位置,用於繪制陰影 private float right; // 手指滑動的距離 private float moveLenght; // 頁面寬高 private int mWidth, mHeight; // 獲取滑動速度 private VelocityTracker vt; // 防止抖動 private float speed_shake = 20; // 當前滑動速度 private float speed; private Timer timer; private MyTimerTask mTask; // 滑動動畫的移動速度 public static final int MOVE_SPEED = 10; // 頁面適配器 private PageAdapter adapter; /** * 過濾多點觸碰的控制變量 */ private int mEvents; public void setAdapter(ScanViewAdapter adapter) { removeAllViews(); this.adapter = adapter; prePage = adapter.getView(); addView(prePage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); adapter.addContent(prePage, index - 1); currPage = adapter.getView(); addView(currPage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); adapter.addContent(currPage, index); nextPage = adapter.getView(); addView(nextPage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); adapter.addContent(nextPage, index + 1); } /** * 向左滑。注意可以滑動的頁面只有當前頁和前一頁 * * @param which */ private void moveLeft(int which) { switch (which) { case PRE: prePageLeft -= MOVE_SPEED; if (prePageLeft < -mWidth) prePageLeft = -mWidth; right = mWidth + prePageLeft; break; case CURR: currPageLeft -= MOVE_SPEED; if (currPageLeft < -mWidth) currPageLeft = -mWidth; right = mWidth + currPageLeft; break; } } /** * 向右滑。注意可以滑動的頁面只有當前頁和前一頁 * * @param which */ private void moveRight(int which) { switch (which) { case PRE: prePageLeft += MOVE_SPEED; if (prePageLeft > 0) prePageLeft = 0; right = mWidth + prePageLeft; break; case CURR: currPageLeft += MOVE_SPEED; if (currPageLeft > 0) currPageLeft = 0; right = mWidth + currPageLeft; break; } } /** * 當往回翻過一頁時添加前一頁在最左邊 */ private void addPrePage() { removeView(nextPage); addView(nextPage, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); // 從適配器獲取前一頁內容 adapter.addContent(nextPage, index - 1); // 交換順序 View temp = nextPage; nextPage = currPage; currPage = prePage; prePage = temp; prePageLeft = -mWidth; } /** * 當往前翻過一頁時,添加一頁在最底下 */ private void addNextPage() { removeView(prePage); addView(prePage, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); // 從適配器獲取後一頁內容 adapter.addContent(prePage, index + 1); // 交換順序 View temp = currPage; currPage = nextPage; nextPage = prePage; prePage = temp; currPageLeft = 0; } Handler updateHandler = new Handler() { @Override public void handleMessage(Message msg) { if (state != STATE_MOVE) return; // 移動頁面 // 翻回,先判斷當前哪一頁處於未返回狀態 if (prePageLeft > -mWidth && speed <= 0) { // 前一頁處於未返回狀態 moveLeft(PRE); } else if (currPageLeft < 0 && speed >= 0) { // 當前頁處於未返回狀態 moveRight(CURR); } else if (speed < 0 && index < adapter.getCount()) { // 向左翻,翻動的是當前頁 moveLeft(CURR); if (currPageLeft == (-mWidth)) { index++; // 翻過一頁,在底下添加一頁,把最上層頁面移除 addNextPage(); } } else if (speed > 0 && index > 1) { // 向右翻,翻動的是前一頁 moveRight(PRE); if (prePageLeft == 0) { index--; // 翻回一頁,添加一頁在最上層,隱藏在最左邊 addPrePage(); } } if (right == 0 || right == mWidth) { releaseMoving(); state = STATE_STOP; quitMove(); } ScanView.this.requestLayout(); } }; public ScanView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public ScanView(Context context) { super(context); init(); } public ScanView(Context context, AttributeSet attrs) { super(context, attrs); init(); } /** * 退出動畫翻頁 */ public void quitMove() { if (mTask != null) { mTask.cancel(); mTask = null; } } private void init() { index = 1; timer = new Timer(); mTask = new MyTimerTask(updateHandler); } /** * 釋放動作,不限制手滑動方向 */ private void releaseMoving() { isPreMoving = true; isCurrMoving = true; } @Override public boolean dispatchTouchEvent(MotionEvent event) { if (adapter != null) switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: lastX = event.getX(); try { if (vt == null) { vt = VelocityTracker.obtain(); } else { vt.clear(); } } catch (Exception e) { e.printStackTrace(); } vt.addMovement(event); mEvents = 0; break; case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_POINTER_UP: mEvents = -1; break; case MotionEvent.ACTION_MOVE: // 取消動畫 quitMove(); Log.d("index", "mEvents = " + mEvents + ", isPreMoving = " + isPreMoving + ", isCurrMoving = " + isCurrMoving); vt.addMovement(event); vt.computeCurrentVelocity(500); speed = vt.getXVelocity(); moveLenght = event.getX() - lastX; if ((moveLenght > 0 || !isCurrMoving) && isPreMoving && mEvents == 0) { isPreMoving = true; isCurrMoving = false; if (index == 1) { // 第一頁不能再往右翻,跳轉到前一個activity state = STATE_MOVE; releaseMoving(); } else { // 非第一頁 prePageLeft += (int) moveLenght; // 防止滑過邊界 if (prePageLeft > 0) prePageLeft = 0; else if (prePageLeft < -mWidth) { // 邊界判斷,釋放動作,防止來回滑動導致滑動前一頁時當前頁無法滑動 prePageLeft = -mWidth; releaseMoving(); } right = mWidth + prePageLeft; state = STATE_MOVE; } } else if ((moveLenght < 0 || !isPreMoving) && isCurrMoving && mEvents == 0) { isPreMoving = false; isCurrMoving = true; if (index == adapter.getCount()) { // 最後一頁不能再往左翻 state = STATE_STOP; releaseMoving(); } else { currPageLeft += (int) moveLenght; // 防止滑過邊界 if (currPageLeft < -mWidth) currPageLeft = -mWidth; else if (currPageLeft > 0) { // 邊界判斷,釋放動作,防止來回滑動導致滑動當前頁是前一頁無法滑動 currPageLeft = 0; releaseMoving(); } right = mWidth + currPageLeft; state = STATE_MOVE; } } else mEvents = 0; lastX = event.getX(); requestLayout(); break; case MotionEvent.ACTION_UP: if (Math.abs(speed) < speed_shake) speed = 0; quitMove(); mTask = new MyTimerTask(updateHandler); timer.schedule(mTask, 0, 5); try { vt.clear(); vt.recycle(); } catch (Exception e) { e.printStackTrace(); } break; default: break; } super.dispatchTouchEvent(event); return true; } /* * (非 Javadoc) 在這裡繪制翻頁陰影效果 * * @see android.view.ViewGroup#dispatchDraw(android.graphics.Canvas) */ @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (right == 0 || right == mWidth) return; RectF rectF = new RectF(right, 0, mWidth, mHeight); Paint paint = new Paint(); paint.setAntiAlias(true); LinearGradient linearGradient = new LinearGradient(right, 0, right + 36, 0, 0xffbbbbbb, 0x00bbbbbb, TileMode.CLAMP); paint.setShader(linearGradient); paint.setStyle(Style.FILL); canvas.drawRect(rectF, paint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); if (isInit) { // 初始狀態,一頁放在左邊隱藏起來,兩頁疊在一塊 prePageLeft = -mWidth; currPageLeft = 0; nextPageLeft = 0; isInit = false; } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (adapter == null) return; prePage.layout(prePageLeft, 0, prePageLeft + prePage.getMeasuredWidth(), prePage.getMeasuredHeight()); currPage.layout(currPageLeft, 0, currPageLeft + currPage.getMeasuredWidth(), currPage.getMeasuredHeight()); nextPage.layout(nextPageLeft, 0, nextPageLeft + nextPage.getMeasuredWidth(), nextPage.getMeasuredHeight()); invalidate(); } class MyTimerTask extends TimerTask { Handler handler; public MyTimerTask(Handler handler) { this.handler = handler; } @Override public void run() { handler.sendMessage(handler.obtainMessage()); } } }
這裡只是我的demo裡寫的Adapter,也可以寫成帶更多內容的Adapter。addContent裡帶的參數view就是getView裡面返回的view,這樣就可以根據inflate的布局設置內容了,getView返回的布局page_layout.xml如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/cover" > <TextView android:id="@+id/content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="60dp" android:padding="10dp" android:textColor="#000000" android:textSize="22sp" /> <TextView android:id="@+id/index" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="60dp" android:textColor="#000000" android:textSize="30sp" /> </RelativeLayout>
只包含了兩個TextView,所以在adapter中可以根據id查找到這兩個TextView再給它設置內容。
OK了,MainActivity的布局如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/cover" > <TextView android:id="@+id/content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="60dp" android:padding="10dp" android:textColor="#000000" android:textSize="22sp" /> <TextView android:id="@+id/index" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="60dp" android:textColor="#000000" android:textSize="30sp" /> </RelativeLayout>
很簡單,只包含了ScanView。
MainActivity的代碼:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/cover" > <TextView android:id="@+id/content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="60dp" android:padding="10dp" android:textColor="#000000" android:textSize="22sp" /> <TextView android:id="@+id/index" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="60dp" android:textColor="#000000" android:textSize="30sp" /> </RelativeLayout>
給ScanView設置Adapter就可以了。
好啦,仿多看的平移翻頁就完成了。
希望本文對大家學習Android軟件編程有所幫助。
一:Log日志工具類 一個android應用程序運行後 並不會在 ide的控制台內輸出任何信息. 不能在控制台輸出。但是android提供的Log類。 在程序中輸出日志
2016裡約奧運會馬上就要開始了,QQ新推出了一個有趣的ar火炬傳遞功能,讓所有人都可以體驗點亮火炬的樂趣,非常受大眾的歡迎,但是也有人反映,在qq火炬傳遞
接觸了這麼久的View,總不能一直停留在View裡,現在開始呢,就要學習一個新的知識點:SurfaceView,實際上SurfaceView與View的原理都差不多,只是
官網地址:https://developer.android.com/intl/zh-tw/training/material/theme.html 新的Material