Android編程實(shí)現(xiàn)小說閱讀器滑動(dòng)效果的方法
本文實(shí)例講述了Android編程實(shí)現(xiàn)小說閱讀器滑動(dòng)效果的方法。分享給大家供大家參考,具體如下:
看過小說都知道小說閱讀器翻頁有好多種效果,比如仿真翻頁,滑動(dòng)翻頁,等等。由于某種原因,突然想寫一個(gè)簡(jiǎn)單點(diǎn)的滑動(dòng)翻頁效果。在這里寫出來也沒有什么意圖,希望大家可以根據(jù)這個(gè)效果舉一反三,寫出其他的效果。圖就不上了。
下面是代碼:大家理解onTouch事件即可
package com.example.testscroll.view; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.Scroller; public class FlipperLayout extends ViewGroup { private Scroller mScroller; private VelocityTracker mVelocityTracker; private int mVelocityValue = 0; /** 商定這個(gè)滑動(dòng)是否有效的距離 */ private int limitDistance = 0; private int screenWidth = 0; /** 手指移動(dòng)的方向 */ private static final int MOVE_TO_LEFT = 0; private static final int MOVE_TO_RIGHT = 1; private static final int MOVE_NO_RESULT = 2; /** 最后觸摸的結(jié)果方向 */ private int mTouchResult = MOVE_NO_RESULT; /** 一開始的方向 */ private int mDirection = MOVE_NO_RESULT; /** 觸摸的模式 */ private static final int MODE_NONE = 0; private static final int MODE_MOVE = 1; private int mMode = MODE_NONE; /** 滑動(dòng)的view */ private View mScrollerView = null; /** 最上層的view(處于邊緣的,看不到的) */ private View currentTopView = null; /** 顯示的view,顯示在屏幕 */ private View currentShowView = null; /** 最底層的view(看不到的) */ private View currentBottomView = null; public FlipperLayout(Context context) { super(context); init(context); } public FlipperLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } public FlipperLayout(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void init(Context context) { mScroller = new Scroller(context); screenWidth = context.getResources().getDisplayMetrics().widthPixels; limitDistance = screenWidth / 3; } /*** * * @param listener * @param currentBottomView * 最底層的view,初始狀態(tài)看不到 * @param currentShowView * 正在顯示的View * @param currentTopView * 最上層的View,初始化時(shí)滑出屏幕 */ public void initFlipperViews(TouchListener listener, View currentBottomView, View currentShowView, View currentTopView) { this.currentBottomView = currentBottomView; this.currentShowView = currentShowView; this.currentTopView = currentTopView; setTouchResultListener(listener); addView(currentBottomView); addView(currentShowView); addView(currentTopView); /** 默認(rèn)將最上層的view滑動(dòng)的邊緣(用于查看上一頁) */ currentTopView.scrollTo(-screenWidth, 0); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); int height = child.getMeasuredHeight(); int width = child.getMeasuredWidth(); child.layout(0, 0, width, height); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(width, height); for (int i = 0; i < getChildCount(); i++) { getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); } } private int startX = 0; @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: if (!mScroller.isFinished()) { break; } startX = (int) ev.getX(); break; } return super.dispatchTouchEvent(ev); } @SuppressWarnings("deprecation") @Override public boolean onTouchEvent(MotionEvent event) { obtainVelocityTracker(event); switch (event.getAction()) { case MotionEvent.ACTION_MOVE: if (!mScroller.isFinished()) { return super.onTouchEvent(event); } if (startX == 0) { startX = (int) event.getX(); } final int distance = startX - (int) event.getX(); if (mDirection == MOVE_NO_RESULT) { if (mListener.whetherHasNextPage() && distance > 0) { mDirection = MOVE_TO_LEFT; } else if (mListener.whetherHasPreviousPage() && distance < 0) { mDirection = MOVE_TO_RIGHT; } } if (mMode == MODE_NONE && ((mDirection == MOVE_TO_LEFT && mListener.whetherHasNextPage()) || (mDirection == MOVE_TO_RIGHT && mListener .whetherHasPreviousPage()))) { mMode = MODE_MOVE; } if (mMode == MODE_MOVE) { if ((mDirection == MOVE_TO_LEFT && distance <= 0) || (mDirection == MOVE_TO_RIGHT && distance >= 0)) { mMode = MODE_NONE; } } if (mDirection != MOVE_NO_RESULT) { if (mDirection == MOVE_TO_LEFT) { if (mScrollerView != currentShowView) { mScrollerView = currentShowView; } } else { if (mScrollerView != currentTopView) { mScrollerView = currentTopView; } } if (mMode == MODE_MOVE) { mVelocityTracker.computeCurrentVelocity(1000, ViewConfiguration.getMaximumFlingVelocity()); if (mDirection == MOVE_TO_LEFT) { mScrollerView.scrollTo(distance, 0); } else { mScrollerView.scrollTo(screenWidth + distance, 0); } } else { final int scrollX = mScrollerView.getScrollX(); if (mDirection == MOVE_TO_LEFT && scrollX != 0 && mListener.whetherHasNextPage()) { mScrollerView.scrollTo(0, 0); } else if (mDirection == MOVE_TO_RIGHT && mListener.whetherHasPreviousPage() && screenWidth != Math.abs(scrollX)) { mScrollerView.scrollTo(-screenWidth, 0); } } } break; case MotionEvent.ACTION_UP: if (mScrollerView == null) { return super.onTouchEvent(event); } final int scrollX = mScrollerView.getScrollX(); mVelocityValue = (int) mVelocityTracker.getXVelocity(); // scroll左正,右負(fù)(),(startX + dx)的值如果為0,即復(fù)位 /* * android.widget.Scroller.startScroll( int startX, int startY, int * dx, int dy, int duration ) */ int time = 500; if (mMode == MODE_MOVE && mDirection == MOVE_TO_LEFT) { if (scrollX > limitDistance || mVelocityValue < -time) { // 手指向左移動(dòng),可以翻屏幕 mTouchResult = MOVE_TO_LEFT; if (mVelocityValue < -time) { time = 200; } mScroller.startScroll(scrollX, 0, screenWidth - scrollX, 0, time); } else { mTouchResult = MOVE_NO_RESULT; mScroller.startScroll(scrollX, 0, -scrollX, 0, time); } } else if (mMode == MODE_MOVE && mDirection == MOVE_TO_RIGHT) { if ((screenWidth - scrollX) > limitDistance || mVelocityValue > time) { // 手指向右移動(dòng),可以翻屏幕 mTouchResult = MOVE_TO_RIGHT; if (mVelocityValue > time) { time = 250; } mScroller.startScroll(scrollX, 0, -scrollX, 0, time); } else { mTouchResult = MOVE_NO_RESULT; mScroller.startScroll(scrollX, 0, screenWidth - scrollX, 0, time); } } resetVariables(); postInvalidate(); break; } return true; } private void resetVariables() { mDirection = MOVE_NO_RESULT; mMode = MODE_NONE; startX = 0; releaseVelocityTracker(); } private TouchListener mListener; private void setTouchResultListener(TouchListener listener) { this.mListener = listener; } @Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()) { mScrollerView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } else if (mScroller.isFinished() && mListener != null && mTouchResult != MOVE_NO_RESULT) { if (mTouchResult == MOVE_TO_LEFT) { if (currentTopView != null) { removeView(currentTopView); } currentTopView = mScrollerView; currentShowView = currentBottomView; if (mListener.currentIsLastPage()) { final View newView = mListener.createView(mTouchResult); currentBottomView = newView; addView(newView, 0); } else { currentBottomView = new View(getContext()); currentBottomView.setVisibility(View.GONE); addView(currentBottomView, 0); } } else { if (currentBottomView != null) { removeView(currentBottomView); } currentBottomView = currentShowView; currentShowView = mScrollerView; if (mListener.currentIsFirstPage()) { final View newView = mListener.createView(mTouchResult); currentTopView = newView; currentTopView.scrollTo(-screenWidth, 0); addView(currentTopView); } else { currentTopView = new View(getContext()); currentTopView.scrollTo(-screenWidth, 0); currentTopView.setVisibility(View.GONE); addView(currentTopView); } } mTouchResult = MOVE_NO_RESULT; } } private void obtainVelocityTracker(MotionEvent event) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } private void releaseVelocityTracker() { if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } /*** * 用來實(shí)時(shí)回調(diào)觸摸事件回調(diào) * * @author freeson */ public interface TouchListener { /** 手指向左滑動(dòng),即查看下一章節(jié) */ final int MOVE_TO_LEFT = 0; /** 手指向右滑動(dòng),即查看上一章節(jié) */ final int MOVE_TO_RIGHT = 1; /** * 創(chuàng)建一個(gè)承載Text的View * * @param direction * {@link MOVE_TO_LEFT,MOVE_TO_RIGHT} * @return */ public View createView(final int direction); /*** * 當(dāng)前頁是否是第一頁 * * @return */ public boolean currentIsFirstPage(); /*** * 當(dāng)前頁是否是最后一頁 * * @return */ public boolean currentIsLastPage(); /** * 當(dāng)前頁是否有上一頁(用來判斷可滑動(dòng)性) * * @return */ public boolean whetherHasPreviousPage(); /*** * 當(dāng)前頁是否有下一頁(用來判斷可滑動(dòng)性) * * @return */ public boolean whetherHasNextPage(); } }
Activity測(cè)試文件:
package com.example.testscroll; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import android.app.Activity; import android.content.res.AssetManager; import android.os.Bundle; import android.os.Handler; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.widget.TextView; import com.example.testscroll.view.FlipperLayout; import com.example.testscroll.view.FlipperLayout.TouchListener; import com.example.testscrollactivity.R; public class MainActivity extends Activity implements OnClickListener, TouchListener { private String text = ""; private int textLenght = 0; private static final int COUNT = 400; private int currentTopEndIndex = 0; private int currentShowEndIndex = 0; private int currentBottomEndIndex = 0; private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { FlipperLayout rootLayout = (FlipperLayout) findViewById(R.id.container); View recoverView = LayoutInflater.from(MainActivity.this).inflate(R.layout.view_new, null); View view1 = LayoutInflater.from(MainActivity.this).inflate(R.layout.view_new, null); View view2 = LayoutInflater.from(MainActivity.this).inflate(R.layout.view_new, null); rootLayout.initFlipperViews(MainActivity.this, view2, view1, recoverView); textLenght = text.length(); System.out.println("----textLenght----->" + textLenght); TextView textView = (TextView) view1.findViewById(R.id.textview); if (textLenght > COUNT) { textView.setText(text.subSequence(0, COUNT)); textView = (TextView) view2.findViewById(R.id.textview); if (textLenght > (COUNT << 1)) { textView.setText(text.subSequence(COUNT, COUNT * 2)); currentShowEndIndex = COUNT; currentBottomEndIndex = COUNT << 1; } else { textView.setText(text.subSequence(COUNT, textLenght)); currentShowEndIndex = textLenght; currentBottomEndIndex = textLenght; } } else { textView.setText(text.subSequence(0, textLenght)); currentShowEndIndex = textLenght; currentBottomEndIndex = textLenght; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new ReadingThread().start(); } @Override public void onClick(View v) { } @Override public View createView(final int direction) { String txt = ""; if (direction == TouchListener.MOVE_TO_LEFT) { currentTopEndIndex = currentShowEndIndex; final int nextIndex = currentBottomEndIndex + COUNT; currentShowEndIndex = currentBottomEndIndex; if (textLenght > nextIndex) { txt = text.substring(currentBottomEndIndex, nextIndex); currentBottomEndIndex = nextIndex; } else { txt = text.substring(currentBottomEndIndex, textLenght); currentBottomEndIndex = textLenght; } } else { currentBottomEndIndex = currentShowEndIndex; currentShowEndIndex = currentTopEndIndex; currentTopEndIndex = currentTopEndIndex - COUNT; txt = text.substring(currentTopEndIndex - COUNT, currentTopEndIndex); } View view = LayoutInflater.from(this).inflate(R.layout.view_new, null); TextView textView = (TextView) view.findViewById(R.id.textview); textView.setText(txt); System.out.println("-top->" + currentTopEndIndex + "-show->" + currentShowEndIndex + "--bottom-->" + currentBottomEndIndex); return view; } @Override public boolean whetherHasPreviousPage() { return currentShowEndIndex > COUNT; } @Override public boolean whetherHasNextPage() { return currentShowEndIndex < textLenght; } @Override public boolean currentIsFirstPage() { boolean should = currentTopEndIndex > COUNT; if (!should) { currentBottomEndIndex = currentShowEndIndex; currentShowEndIndex = currentTopEndIndex; currentTopEndIndex = currentTopEndIndex - COUNT; } return should; } @Override public boolean currentIsLastPage() { boolean should = currentBottomEndIndex < textLenght; if (!should) { currentTopEndIndex = currentShowEndIndex; final int nextIndex = currentBottomEndIndex + COUNT; currentShowEndIndex = currentBottomEndIndex; if (textLenght > nextIndex) { currentBottomEndIndex = nextIndex; } else { currentBottomEndIndex = textLenght; } } return should; } private class ReadingThread extends Thread { public void run() { AssetManager am = getAssets(); InputStream response; try { response = am.open("text.txt"); if (response != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int i = -1; while ((i = response.read()) != -1) { baos.write(i); } text = new String(baos.toByteArray(), "UTF-8"); baos.close(); response.close(); handler.sendEmptyMessage(0); } } catch (IOException e) { e.printStackTrace(); } } } }
xml布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <TextView android:id="@+id/textview" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1.0" android:background="#666666" android:gravity="center" android:text="新建的View" android:textColor="@android:color/white" android:textSize="16sp" android:visibility="visible" /> <View android:layout_width="5dp" android:layout_height="match_parent" android:background="#FFFF00" android:gravity="center" android:textSize="25sp" android:visibility="visible" /> </LinearLayout>
activity布局文件:
<com.example.testscroll.view.FlipperLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" > </com.example.testscroll.view.FlipperLayout>
備注:上面為什么加一個(gè)速率計(jì)算器呢,其實(shí)只是為了識(shí)別這個(gè)動(dòng)作是不是快速滑動(dòng)的動(dòng)作,就算滑動(dòng)的距離不到屏幕的1/3,但是只要速率滿足都可以判定改滑動(dòng)是一個(gè)翻頁的動(dòng)作。
注意哦:這只是其中一個(gè)滑動(dòng)的效果而已啊,不包括小說分章節(jié)的邏輯哦。雖然有些粗糙,但是還是有可以值得學(xué)習(xí)的地方,大家如果還有什么好的解決方案,可以一起討論。
附上demo下載地址 點(diǎn)擊下載demo。
希望本文所述對(duì)大家Android程序設(shè)計(jì)有所幫助。
- Android WebView如何判定網(wǎng)頁加載的錯(cuò)誤
- Android webView字體突然變小的原因及解決
- Android 解決WebView多進(jìn)程崩潰的方法
- Android 中 WebView 的基本用法詳解
- 在Android環(huán)境下WebView中攔截所有請(qǐng)求并替換URL示例詳解
- 解決Android webview設(shè)置cookie和cookie丟失的問題
- Android 如何從零開始寫一款書籍閱讀器的示例
- Android實(shí)現(xiàn)閱讀進(jìn)度記憶功能
- android閱讀器長(zhǎng)按選擇文字功能實(shí)現(xiàn)代碼
- android仿新聞閱讀器菜單彈出效果實(shí)例(附源碼DEMO下載)
- Android實(shí)現(xiàn)閱讀APP平移翻頁效果
- Android使用WebView實(shí)現(xiàn)離線閱讀功能
相關(guān)文章
Android編程之菜單的實(shí)現(xiàn)方法實(shí)例詳解
這篇文章主要介紹了Android編程之菜單的實(shí)現(xiàn)方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了上下文菜單、選項(xiàng)菜單和子菜單的實(shí)現(xiàn)技巧,需要的朋友可以參考下2015-11-11Android App多個(gè)入口的實(shí)現(xiàn)方法
這篇文章主要介紹了Android App多個(gè)入口的實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-02-02Android嚴(yán)苛模式StrictMode使用詳解
StrictMode類是Android 2.3 (API 9)引入的一個(gè)工具類,可以用來幫助開發(fā)者發(fā)現(xiàn)代碼中的一些不規(guī)范的問題,以達(dá)到提升應(yīng)用響應(yīng)能力的目的2018-01-01基于Manifest.xml中不要出現(xiàn)重復(fù)的uses permission的說明
本篇文章對(duì)Manifest.xml中不要出現(xiàn)重復(fù)的uses permission進(jìn)行了介紹。需要的朋友參考下2013-05-05Android多線程學(xué)習(xí)實(shí)例詳解
這篇文章主要介紹了Android多線程,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android多線程的概念、使用方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2016-10-10Android實(shí)現(xiàn)聯(lián)動(dòng)下拉框二級(jí)地市聯(lián)動(dòng)下拉框功能
這篇文章主要介紹了Android實(shí)現(xiàn)聯(lián)動(dòng)下拉框二級(jí)地市聯(lián)動(dòng)下拉框功能,本文給大家分享思路步驟,給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-12-12