Android programming method to realize the sliding effect of novel reader

  • 2020-10-07 18:53:09
  • OfStack

This paper illustrates the Android programming method to realize the sliding effect of novel readers. To share for your reference, the details are as follows:

Anyone who has read a novel knows that a novel reader has a variety of effects, such as simulated page turning, sliding page turning, and so on. For some reason, I suddenly want to write a simple slide page turning effect. I don't really want to write it here, but I hope you can pick 1 to 3 and write the other effects. I'm not going to draw it.

Here's the code: Just understand the onTouch event


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; 
 /**  Agree on whether the slip is valid or not  */ 
 private int limitDistance = 0; 
 private int screenWidth = 0; 
 /**  The direction of the finger  */ 
 private static final int MOVE_TO_LEFT = 0; 
 private static final int MOVE_TO_RIGHT = 1; 
 private static final int MOVE_NO_RESULT = 2; 
 /**  The final direction of the result of touch  */ 
 private int mTouchResult = MOVE_NO_RESULT; 
 /** 1 Starting direction  */ 
 private int mDirection = MOVE_NO_RESULT; 
 /**  Mode of touch  */ 
 private static final int MODE_NONE = 0; 
 private static final int MODE_MOVE = 1; 
 private int mMode = MODE_NONE; 
 /**  The sliding view */ 
 private View mScrollerView = null; 
 /**  The top of the view (on the edge, not visible)  */ 
 private View currentTopView = null; 
 /**  According to the view , displayed on the screen  */ 
 private View currentShowView = null; 
 /**  At the bottom of the view (Not visible)  */ 
 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 
  *   At the bottom of the view You can't see the initial state  
  * @param currentShowView 
  *   In display View 
  * @param currentTopView 
  *   The top of the View , slides off the screen when initialized  
  */ 
 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); 
  /**  Default will be uppermost view Slide edge (used to view on 1 Page)  */ 
  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 Positive left, negative right (),(startX + dx) If the value of 0 , the reset  
   /* 
    * 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) { 
     //  By moving your finger to the left, you can flip the screen  
     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) { 
     //  Move your finger to the right to flip the screen  
     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; 
  } 
 } 
 /*** 
  *  Used for real-time callback of touch event callbacks  
  * 
  * @author freeson 
  */ 
 public interface TouchListener { 
  /**  Slide your finger to the left to check 1 chapter  */ 
  final int MOVE_TO_LEFT = 0; 
  /**  Swipe to the right to view up 1 chapter  */ 
  final int MOVE_TO_RIGHT = 1; 
  /** 
   *  create 1 A bearing Text the View 
   * 
   * @param direction 
   *   {@link MOVE_TO_LEFT,MOVE_TO_RIGHT} 
   * @return 
   */ 
  public View createView(final int direction); 
  /*** 
   *  Is the current page the first 1 page  
   * 
   * @return 
   */ 
  public boolean currentIsFirstPage(); 
  /*** 
   *  Is the current page last 1 page  
   * 
   * @return 
   */ 
  public boolean currentIsLastPage(); 
  /** 
   *  Is the current page on 1 Page (to determine slidability)  
   * 
   * @return 
   */ 
  public boolean whetherHasPreviousPage(); 
  /*** 
   *  Whether the current page is under 1 Page (to determine slidability)  
   * 
   * @return 
   */ 
  public boolean whetherHasNextPage(); 
 } 
}

Activity test file:


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 layout file:


<?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=" The new 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 layout file:


<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>

Note: Why add a rate calculator above? In fact, it is just to identify whether this action is a fast sliding action. Even if the distance of sliding is less than 1/3 of the screen, as long as the rate meets, it can be determined that sliding is a page-turning action.

Note: This is just one of the sliding effects, not including the chapter logic. Although a little rough, but there are still worth learning, if you have any good solutions, you can discuss 1.

Attach the demo download address and click here to download demo.

I hope this article has been helpful in Android programming.


Related articles: