新增书籍分享

pull/5/head
fengyuecanzhu 4 years ago
parent b9b87d0e8a
commit 9a135f0590
  1. 4
      app/src/androidTest/java/xyz/fycz/myreader/ExampleInstrumentedTest.java
  2. 4
      app/src/androidTest/java/xyz/fycz/myreader/TestAudioPlay.java
  3. 4
      app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
  4. 813
      app/src/main/java/xyz/fycz/myreader/entity/ReadBookControl.java
  5. 34
      app/src/main/java/xyz/fycz/myreader/source/UserDatabaseHelper.java
  6. 8
      app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
  7. 4
      app/src/main/java/xyz/fycz/myreader/ui/popmenu/CustomizeLayoutMenu.java
  8. 24
      app/src/main/java/xyz/fycz/myreader/util/DipPxUtil.java
  9. 24
      app/src/main/java/xyz/fycz/myreader/util/JsonArrayToObjectArray.java
  10. 81
      app/src/main/java/xyz/fycz/myreader/util/ToastUtils.java
  11. 11
      app/src/main/java/xyz/fycz/myreader/util/help/ChapterContentHelp.java
  12. 1
      app/src/main/java/xyz/fycz/myreader/util/webdav/README.md
  13. 11
      app/src/main/java/xyz/fycz/myreader/webapi/BaseApi.java
  14. 4
      app/src/main/java/xyz/fycz/myreader/webapi/BookStoreApi.java
  15. 5
      app/src/main/java/xyz/fycz/myreader/webapi/HttpDataSource.java
  16. 4
      app/src/main/java/xyz/fycz/myreader/widget/custom/DragFloatBtnT.java
  17. 213
      app/src/main/java/xyz/fycz/myreader/widget/page2/ChapterProvider.java
  18. 401
      app/src/main/java/xyz/fycz/myreader/widget/page2/LocalPageLoader.java
  19. 1866
      app/src/main/java/xyz/fycz/myreader/widget/page2/PageLoader.java
  20. 266
      app/src/main/java/xyz/fycz/myreader/widget/page2/PageLoaderNet.java
  21. 713
      app/src/main/java/xyz/fycz/myreader/widget/page2/PageView.java
  22. 57
      app/src/main/java/xyz/fycz/myreader/widget/page2/TxtChapter.kt
  23. 26
      app/src/main/java/xyz/fycz/myreader/widget/page2/TxtChar.kt
  24. 23
      app/src/main/java/xyz/fycz/myreader/widget/page2/TxtLine.kt
  25. 44
      app/src/main/java/xyz/fycz/myreader/widget/page2/TxtPage.kt
  26. 87
      app/src/main/java/xyz/fycz/myreader/widget/page2/animation/CoverPageAnim.java
  27. 190
      app/src/main/java/xyz/fycz/myreader/widget/page2/animation/HorizonPageAnim.java
  28. 26
      app/src/main/java/xyz/fycz/myreader/widget/page2/animation/NonePageAnim.java
  29. 273
      app/src/main/java/xyz/fycz/myreader/widget/page2/animation/PageAnimation.java
  30. 148
      app/src/main/java/xyz/fycz/myreader/widget/page2/animation/ScrollPageAnim.java
  31. 569
      app/src/main/java/xyz/fycz/myreader/widget/page2/animation/SimulationPageAnim.java
  32. 85
      app/src/main/java/xyz/fycz/myreader/widget/page2/animation/SlidePageAnim.java
  33. 13
      app/src/main/res/layout/gridview_book_detailed_item.xml
  34. BIN
      app/src/main/res/mipmap-xhdpi/ao.png
  35. BIN
      app/src/main/res/mipmap-xhdpi/delete.png
  36. BIN
      app/src/main/res/mipmap-xhdpi/download.png
  37. BIN
      app/src/main/res/mipmap-xhdpi/t3.png
  38. BIN
      app/src/main/res/mipmap-xhdpi/t6.png
  39. BIN
      app/src/main/res/mipmap-xhdpi/z4.png

@ -30,8 +30,8 @@ public class ExampleInstrumentedTest {
@Test @Test
public void test() { public void test() {
ReadService readService = new ReadService(); ReadService readService = new ReadService();
readService.initSynthesizer(); //readService.initSynthesizer();
readService.startSynthesizer("科幻电影是我们从小就爱看的电影题材,我们也都梦想过生活在那样的科幻世界里。科幻片,顾名思义即“科学幻想片”,是“以科学幻想为内容的故事片,其基本特点是从今天已知的科学原理和科学成就出发,对未来的世界或遥远的过去的情景作幻想式的描述。”"); //readService.startSynthesizer("科幻电影是我们从小就爱看的电影题材,我们也都梦想过生活在那样的科幻世界里。科幻片,顾名思义即“科学幻想片”,是“以科学幻想为内容的故事片,其基本特点是从今天已知的科学原理和科学成就出发,对未来的世界或遥远的过去的情景作幻想式的描述。”");
} }
} }

@ -11,8 +11,8 @@ public class TestAudioPlay {
public void test() { public void test() {
ReadService readService = new ReadService(); ReadService readService = new ReadService();
readService.initSynthesizer(); //readService.initSynthesizer();
readService.startSynthesizer("科幻电影是我们从小就爱看的电影题材,我们也都梦想过生活在那样的科幻世界里。科幻片,顾名思义即“科学幻想片”,是“以科学幻想为内容的故事片,其基本特点是从今天已知的科学原理和科学成就出发,对未来的世界或遥远的过去的情景作幻想式的描述。”"); //readService.startSynthesizer("科幻电影是我们从小就爱看的电影题材,我们也都梦想过生活在那样的科幻世界里。科幻片,顾名思义即“科学幻想片”,是“以科学幻想为内容的故事片,其基本特点是从今天已知的科学原理和科学成就出发,对未来的世界或遥远的过去的情景作幻想式的描述。”");
} }
} }

@ -10,10 +10,10 @@ import java.io.File;
public class APPCONST { public class APPCONST {
public static String publicKey = "";//服务端公钥 public static String publicKey = "fyds1.0";//服务端公钥
public static String privateKey;//app私钥 public static String privateKey;//app私钥
public final static String s = "11940364935628058505"; public final static String s = "11940364935628058505";
public static final String KEY = ""; public static final String KEY = "readerByFengyue";
public static final String ALARM_SCHEDULE_MSG = "alarm_schedule_msg"; public static final String ALARM_SCHEDULE_MSG = "alarm_schedule_msg";

@ -1,813 +0,0 @@
//Copyright (c) 2017. 章钦豪. All rights reserved.
package xyz.fycz.myreader.entity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.util.DisplayMetrics;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.utils.BitmapUtil;
import xyz.fycz.myreader.util.utils.MeUtils;
import static xyz.fycz.myreader.widget.page2.PageLoader.DEFAULT_MARGIN_WIDTH;
public class ReadBookControl {
private static final int DEFAULT_BG = 1;
private int textDrawableIndex = DEFAULT_BG;
private List<Map<String, Integer>> textDrawable;
private Bitmap bgBitmap;
private int screenDirection;
private int speechRate;
private boolean speechRateFollowSys;
private int textSize;
private int textColor;
private boolean bgIsColor;
private int bgColor;
private float lineMultiplier;
private float paragraphSize;
private int pageMode;
private Boolean hideStatusBar;
private Boolean hideNavigationBar;
private String fontPath;
private int textConvert;
private int navBarColor;
private Boolean textBold;
private Boolean canClickTurn;
private Boolean canKeyTurn;
private Boolean readAloudCanKeyTurn;
private int CPM;
private Boolean clickAllNext;
private Boolean showTitle;
private Boolean showTimeBattery;
private Boolean showLine;
private Boolean darkStatusIcon;
private int indent;
private int screenTimeOut;
private int paddingLeft;
private int paddingTop;
private int paddingRight;
private int paddingBottom;
private int tipPaddingLeft;
private int tipPaddingTop;
private int tipPaddingRight;
private int tipPaddingBottom;
private float textLetterSpacing;
private boolean canSelectText;
public int minCPM = 200;
public int maxCPM = 2000;
private int defaultCPM = 500;
private SharedPreferences preferences;
private static ReadBookControl readBookControl;
public static ReadBookControl getInstance() {
if (readBookControl == null) {
synchronized (ReadBookControl.class) {
if (readBookControl == null) {
readBookControl = new ReadBookControl();
}
}
}
return readBookControl;
}
private ReadBookControl() {
preferences = SharedPreUtils.getInstance().getSharedReadable();
initTextDrawable();
updateReaderSettings();
}
public void updateReaderSettings() {
this.hideStatusBar = preferences.getBoolean("hide_status_bar", false);
this.hideNavigationBar = preferences.getBoolean("hide_navigation_bar", false);
this.indent = preferences.getInt("indent", 2);
this.textSize = preferences.getInt("textSize", 20);
this.canClickTurn = preferences.getBoolean("canClickTurn", true);
this.canKeyTurn = preferences.getBoolean("canKeyTurn", true);
this.readAloudCanKeyTurn = preferences.getBoolean("readAloudCanKeyTurn", false);
this.lineMultiplier = preferences.getFloat("lineMultiplier", 1);
this.paragraphSize = preferences.getFloat("paragraphSize", 1);
this.CPM = preferences.getInt("CPM", defaultCPM) > maxCPM
? minCPM : preferences.getInt("CPM", defaultCPM);
this.clickAllNext = preferences.getBoolean("clickAllNext", false);
this.fontPath = preferences.getString("fontPath", null);
this.textConvert = preferences.getInt("textConvertInt", 0);
this.textBold = preferences.getBoolean("textBold", false);
this.speechRate = preferences.getInt("speechRate", 10);
this.speechRateFollowSys = preferences.getBoolean("speechRateFollowSys", true);
this.showTitle = preferences.getBoolean("showTitle", true);
this.showTimeBattery = preferences.getBoolean("showTimeBattery", true);
this.showLine = preferences.getBoolean("showLine", true);
this.screenTimeOut = preferences.getInt("screenTimeOut", 0);
this.paddingLeft = preferences.getInt("paddingLeft", DEFAULT_MARGIN_WIDTH);
this.paddingTop = preferences.getInt("paddingTop", 0);
this.paddingRight = preferences.getInt("paddingRight", DEFAULT_MARGIN_WIDTH);
this.paddingBottom = preferences.getInt("paddingBottom", 0);
this.tipPaddingLeft = preferences.getInt("tipPaddingLeft", DEFAULT_MARGIN_WIDTH);
this.tipPaddingTop = preferences.getInt("tipPaddingTop", 0);
this.tipPaddingRight = preferences.getInt("tipPaddingRight", DEFAULT_MARGIN_WIDTH);
this.tipPaddingBottom = preferences.getInt("tipPaddingBottom", 0);
this.pageMode = preferences.getInt("pageMode", 0);
this.screenDirection = preferences.getInt("screenDirection", 0);
this.navBarColor = preferences.getInt("navBarColorInt", 0);
this.textLetterSpacing = preferences.getFloat("textLetterSpacing", 0);
this.canSelectText = preferences.getBoolean("canSelectText", false);
initTextDrawableIndex();
}
//阅读背景
private void initTextDrawable() {
if (null == textDrawable) {
textDrawable = new ArrayList<>();
Map<String, Integer> temp1 = new HashMap<>();
temp1.put("textColor", Color.parseColor("#3E3D3B"));
temp1.put("bgIsColor", 1);
temp1.put("textBackground", Color.parseColor("#F3F3F3"));
temp1.put("darkStatusIcon", 1);
textDrawable.add(temp1);
Map<String, Integer> temp2 = new HashMap<>();
temp2.put("textColor", Color.parseColor("#5E432E"));
temp2.put("bgIsColor", 1);
temp2.put("textBackground", Color.parseColor("#C6BAA1"));
temp2.put("darkStatusIcon", 1);
textDrawable.add(temp2);
Map<String, Integer> temp3 = new HashMap<>();
temp3.put("textColor", Color.parseColor("#22482C"));
temp3.put("bgIsColor", 1);
temp3.put("textBackground", Color.parseColor("#E1F1DA"));
temp3.put("darkStatusIcon", 1);
textDrawable.add(temp3);
Map<String, Integer> temp4 = new HashMap<>();
temp4.put("textColor", Color.parseColor("#FFFFFF"));
temp4.put("bgIsColor", 1);
temp4.put("textBackground", Color.parseColor("#015A86"));
temp4.put("darkStatusIcon", 0);
textDrawable.add(temp4);
Map<String, Integer> temp5 = new HashMap<>();
temp5.put("textColor", Color.parseColor("#808080"));
temp5.put("bgIsColor", 1);
temp5.put("textBackground", Color.parseColor("#000000"));
temp5.put("darkStatusIcon", 0);
textDrawable.add(temp5);
}
}
public void initTextDrawableIndex() {
if (getIsNightTheme()) {
textDrawableIndex = preferences.getInt("textDrawableIndexNight", 4);
} else {
textDrawableIndex = preferences.getInt("textDrawableIndex", DEFAULT_BG);
}
if (textDrawableIndex == -1) {
textDrawableIndex = DEFAULT_BG;
}
initPageStyle();
setTextDrawable();
}
@SuppressWarnings("ConstantConditions")
private void initPageStyle() {
int bgCustom = getBgCustom(textDrawableIndex);
if ((bgCustom == 2 || bgCustom == 3) && getBgPath(textDrawableIndex) != null) {
bgIsColor = false;
String bgPath = getBgPath(textDrawableIndex);
Resources resources = MyApplication.getApplication().getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
int width = dm.widthPixels;
int height = dm.heightPixels;
if (bgCustom == 2) {
bgBitmap = BitmapUtil.getFitSampleBitmap(bgPath, width, height);
} else {
bgBitmap = MeUtils.getFitAssetsSampleBitmap(MyApplication.getApplication().getAssets(), bgPath, width, height);
}
if (bgBitmap != null) {
return;
}
} else if (getBgCustom(textDrawableIndex) == 1) {
bgIsColor = true;
bgColor = getBgColor(textDrawableIndex);
return;
}
bgIsColor = true;
bgColor = textDrawable.get(textDrawableIndex).get("textBackground");
}
private void setTextDrawable() {
darkStatusIcon = getDarkStatusIcon(textDrawableIndex);
textColor = getTextColor(textDrawableIndex);
}
public int getTextColor(int textDrawableIndex) {
if (preferences.getInt("textColor" + textDrawableIndex, 0) != 0) {
return preferences.getInt("textColor" + textDrawableIndex, 0);
} else {
return getDefaultTextColor(textDrawableIndex);
}
}
public void setTextColor(int textDrawableIndex, int textColor) {
preferences.edit()
.putInt("textColor" + textDrawableIndex, textColor)
.apply();
}
@SuppressWarnings("ConstantConditions")
public Drawable getBgDrawable(int textDrawableIndex, Context context, int width, int height) {
int color;
try {
Bitmap bitmap = null;
switch (getBgCustom(textDrawableIndex)) {
case 3:
bitmap = MeUtils.getFitAssetsSampleBitmap(context.getAssets(), getBgPath(textDrawableIndex), width, height);
if (bitmap != null) {
return new BitmapDrawable(context.getResources(), bitmap);
}
case 2:
bitmap = BitmapUtil.getFitSampleBitmap(getBgPath(textDrawableIndex), width, height);
if (bitmap != null) {
return new BitmapDrawable(context.getResources(), bitmap);
}
break;
case 1:
color = getBgColor(textDrawableIndex);
return new ColorDrawable(color);
}
if (textDrawable.get(textDrawableIndex).get("bgIsColor") != 0) {
color = textDrawable.get(textDrawableIndex).get("textBackground");
return new ColorDrawable(color);
} else {
return getDefaultBgDrawable(textDrawableIndex, context);
}
} catch (Exception e) {
if (textDrawable.get(textDrawableIndex).get("bgIsColor") != 0) {
color = textDrawable.get(textDrawableIndex).get("textBackground");
return new ColorDrawable(color);
} else {
return getDefaultBgDrawable(textDrawableIndex, context);
}
}
}
@SuppressWarnings("ConstantConditions")
public Drawable getDefaultBgDrawable(int textDrawableIndex, Context context) {
if (textDrawable.get(textDrawableIndex).get("bgIsColor") != 0) {
return new ColorDrawable(textDrawable.get(textDrawableIndex).get("textBackground"));
} else {
return context.getResources().getDrawable(getDefaultBg(textDrawableIndex));
}
}
public int getBgCustom(int textDrawableIndex) {
return preferences.getInt("bgCustom" + textDrawableIndex, 0);
}
public void setBgCustom(int textDrawableIndex, int bgCustom) {
preferences.edit()
.putInt("bgCustom" + textDrawableIndex, bgCustom)
.apply();
}
public String getBgPath(int textDrawableIndex) {
return preferences.getString("bgPath" + textDrawableIndex, null);
}
public void setBgPath(int textDrawableIndex, String bgUri) {
preferences.edit()
.putString("bgPath" + textDrawableIndex, bgUri)
.apply();
}
@SuppressWarnings("ConstantConditions")
public int getDefaultTextColor(int textDrawableIndex) {
return textDrawable.get(textDrawableIndex).get("textColor");
}
@SuppressWarnings("ConstantConditions")
private int getDefaultBg(int textDrawableIndex) {
return textDrawable.get(textDrawableIndex).get("textBackground");
}
public int getBgColor(int index) {
return preferences.getInt("bgColor" + index, Color.parseColor("#1e1e1e"));
}
public void setBgColor(int index, int bgColor) {
preferences.edit()
.putInt("bgColor" + index, bgColor)
.apply();
}
private boolean getIsNightTheme() {
return MyApplication.getApplication().isNightTheme();
}
public boolean getImmersionStatusBar() {
return preferences.getBoolean("immersionStatusBar", false);
}
public void setImmersionStatusBar(boolean immersionStatusBar) {
preferences.edit()
.putBoolean("immersionStatusBar", immersionStatusBar)
.apply();
}
public int getTextSize() {
return textSize;
}
public void setTextSize(int textSize) {
this.textSize = textSize;
preferences.edit()
.putInt("textSize", textSize)
.apply();
}
public int getTextColor() {
return textColor;
}
public boolean bgIsColor() {
return bgIsColor;
}
public Drawable getTextBackground(Context context) {
if (bgIsColor) {
return new ColorDrawable(bgColor);
}
return new BitmapDrawable(context.getResources(), bgBitmap);
}
public int getBgColor() {
return bgColor;
}
public boolean bgBitmapIsNull() {
return bgBitmap == null || bgBitmap.isRecycled();
}
public Bitmap getBgBitmap() {
return bgBitmap.copy(Bitmap.Config.ARGB_8888, true);
}
public int getTextDrawableIndex() {
return textDrawableIndex;
}
public void setTextDrawableIndex(int textDrawableIndex) {
this.textDrawableIndex = textDrawableIndex;
if (getIsNightTheme()) {
preferences.edit()
.putInt("textDrawableIndexNight", textDrawableIndex)
.apply();
} else {
preferences.edit()
.putInt("textDrawableIndex", textDrawableIndex)
.apply();
}
setTextDrawable();
}
public void setTextConvert(int textConvert) {
this.textConvert = textConvert;
preferences.edit()
.putInt("textConvertInt", textConvert)
.apply();
}
public void setNavBarColor(int navBarColor) {
this.navBarColor = navBarColor;
preferences.edit()
.putInt("navBarColorInt", navBarColor)
.apply();
}
public int getNavBarColor() {
return navBarColor;
}
public void setTextBold(boolean textBold) {
this.textBold = textBold;
preferences.edit()
.putBoolean("textBold", textBold)
.apply();
}
public void setReadBookFont(String fontPath) {
this.fontPath = fontPath;
preferences.edit()
.putString("fontPath", fontPath)
.apply();
}
public String getFontPath() {
return fontPath;
}
public int getTextConvert() {
return textConvert == -1 ? 2 : textConvert;
}
public Boolean getTextBold() {
return textBold;
}
public Boolean getCanKeyTurn(Boolean isPlay) {
if (!canKeyTurn) {
return false;
} else if (readAloudCanKeyTurn) {
return true;
} else {
return !isPlay;
}
}
public Boolean getCanKeyTurn() {
return canKeyTurn;
}
public void setCanKeyTurn(Boolean canKeyTurn) {
this.canKeyTurn = canKeyTurn;
preferences.edit()
.putBoolean("canKeyTurn", canKeyTurn)
.apply();
}
public Boolean getAloudCanKeyTurn() {
return readAloudCanKeyTurn;
}
public void setAloudCanKeyTurn(Boolean canAloudKeyTurn) {
this.readAloudCanKeyTurn = canAloudKeyTurn;
preferences.edit()
.putBoolean("readAloudCanKeyTurn", canAloudKeyTurn)
.apply();
}
public Boolean getCanClickTurn() {
return canClickTurn;
}
public void setCanClickTurn(Boolean canClickTurn) {
this.canClickTurn = canClickTurn;
preferences.edit()
.putBoolean("canClickTurn", canClickTurn)
.apply();
}
public float getTextLetterSpacing() {
return textLetterSpacing;
}
public void setTextLetterSpacing(float textLetterSpacing) {
this.textLetterSpacing = textLetterSpacing;
preferences.edit()
.putFloat("textLetterSpacing", textLetterSpacing)
.apply();
}
public float getLineMultiplier() {
return lineMultiplier;
}
public void setLineMultiplier(float lineMultiplier) {
this.lineMultiplier = lineMultiplier;
preferences.edit()
.putFloat("lineMultiplier", lineMultiplier)
.apply();
}
public float getParagraphSize() {
return paragraphSize;
}
public void setParagraphSize(float paragraphSize) {
this.paragraphSize = paragraphSize;
preferences.edit()
.putFloat("paragraphSize", paragraphSize)
.apply();
}
public int getCPM() {
return CPM;
}
public void setCPM(int cpm) {
if (cpm < minCPM || cpm > maxCPM) cpm = defaultCPM;
this.CPM = cpm;
preferences.edit()
.putInt("CPM", cpm)
.apply();
}
public Boolean getClickAllNext() {
return clickAllNext;
}
public void setClickAllNext(Boolean clickAllNext) {
this.clickAllNext = clickAllNext;
preferences.edit()
.putBoolean("clickAllNext", clickAllNext)
.apply();
}
public int getSpeechRate() {
return speechRate;
}
public void setSpeechRate(int speechRate) {
this.speechRate = speechRate;
preferences.edit()
.putInt("speechRate", speechRate)
.apply();
}
public boolean isSpeechRateFollowSys() {
return speechRateFollowSys;
}
public void setSpeechRateFollowSys(boolean speechRateFollowSys) {
this.speechRateFollowSys = speechRateFollowSys;
preferences.edit()
.putBoolean("speechRateFollowSys", speechRateFollowSys)
.apply();
}
public Boolean getShowTitle() {
return showTitle;
}
public void setShowTitle(Boolean showTitle) {
this.showTitle = showTitle;
preferences.edit()
.putBoolean("showTitle", showTitle)
.apply();
}
public Boolean getShowTimeBattery() {
return showTimeBattery;
}
public void setShowTimeBattery(Boolean showTimeBattery) {
this.showTimeBattery = showTimeBattery;
preferences.edit()
.putBoolean("showTimeBattery", showTimeBattery)
.apply();
}
public Boolean getHideStatusBar() {
return hideStatusBar;
}
public void setHideStatusBar(Boolean hideStatusBar) {
this.hideStatusBar = hideStatusBar;
preferences.edit()
.putBoolean("hide_status_bar", hideStatusBar)
.apply();
}
public Boolean getToLh() {
return preferences.getBoolean("toLh", false);
}
public void setToLh(Boolean toLh) {
preferences.edit()
.putBoolean("toLh", toLh)
.apply();
}
public Boolean getHideNavigationBar() {
return hideNavigationBar;
}
public void setHideNavigationBar(Boolean hideNavigationBar) {
this.hideNavigationBar = hideNavigationBar;
preferences.edit()
.putBoolean("hide_navigation_bar", hideNavigationBar)
.apply();
}
public Boolean getShowLine() {
return showLine;
}
public void setShowLine(Boolean showLine) {
this.showLine = showLine;
preferences.edit()
.putBoolean("showLine", showLine)
.apply();
}
public boolean getDarkStatusIcon() {
return darkStatusIcon;
}
@SuppressWarnings("ConstantConditions")
public boolean getDarkStatusIcon(int textDrawableIndex) {
return preferences.getBoolean("darkStatusIcon" + textDrawableIndex, textDrawable.get(textDrawableIndex).get("darkStatusIcon") != 0);
}
public void setDarkStatusIcon(int textDrawableIndex, Boolean darkStatusIcon) {
preferences.edit()
.putBoolean("darkStatusIcon" + textDrawableIndex, darkStatusIcon)
.apply();
}
public int getScreenTimeOut() {
return screenTimeOut;
}
public void setScreenTimeOut(int screenTimeOut) {
this.screenTimeOut = screenTimeOut;
preferences.edit()
.putInt("screenTimeOut", screenTimeOut)
.apply();
}
public int getPaddingLeft() {
return paddingLeft;
}
public void setPaddingLeft(int paddingLeft) {
this.paddingLeft = paddingLeft;
preferences.edit()
.putInt("paddingLeft", paddingLeft)
.apply();
}
public int getPaddingTop() {
return paddingTop;
}
public void setPaddingTop(int paddingTop) {
this.paddingTop = paddingTop;
preferences.edit()
.putInt("paddingTop", paddingTop)
.apply();
}
public int getPaddingRight() {
return paddingRight;
}
public void setPaddingRight(int paddingRight) {
this.paddingRight = paddingRight;
preferences.edit()
.putInt("paddingRight", paddingRight)
.apply();
}
public int getPaddingBottom() {
return paddingBottom;
}
public void setPaddingBottom(int paddingBottom) {
this.paddingBottom = paddingBottom;
preferences.edit()
.putInt("paddingBottom", paddingBottom)
.apply();
}
public int getTipPaddingLeft() {
return tipPaddingLeft;
}
public void setTipPaddingLeft(int tipPaddingLeft) {
this.tipPaddingLeft = tipPaddingLeft;
preferences.edit()
.putInt("tipPaddingLeft", tipPaddingLeft)
.apply();
}
public boolean isCanSelectText() {
return canSelectText;
}
public void setCanSelectText(boolean canSelectText) {
this.canSelectText = canSelectText;
preferences.edit()
.putBoolean("canSelectText", canSelectText)
.apply();
}
public int getTipPaddingTop() {
return tipPaddingTop;
}
public void setTipPaddingTop(int tipPaddingTop) {
this.tipPaddingTop = tipPaddingTop;
preferences.edit()
.putInt("tipPaddingTop", tipPaddingTop)
.apply();
}
public int getTipPaddingRight() {
return tipPaddingRight;
}
public void setTipPaddingRight(int tipPaddingRight) {
this.tipPaddingRight = tipPaddingRight;
preferences.edit()
.putInt("tipPaddingRight", tipPaddingRight)
.apply();
}
public int getTipPaddingBottom() {
return tipPaddingBottom;
}
public void setTipPaddingBottom(int tipPaddingBottom) {
this.tipPaddingBottom = tipPaddingBottom;
preferences.edit()
.putInt("tipPaddingBottom", tipPaddingBottom)
.apply();
}
public int getPageMode() {
return pageMode;
}
public void setPageMode(int pageMode) {
this.pageMode = pageMode;
preferences.edit()
.putInt("pageMode", pageMode)
.apply();
}
public int getScreenDirection() {
return screenDirection;
}
public void setScreenDirection(int screenDirection) {
this.screenDirection = screenDirection;
preferences.edit()
.putInt("screenDirection", screenDirection)
.apply();
}
public void setIndent(int indent) {
this.indent = indent;
preferences.edit()
.putInt("indent", indent)
.apply();
}
public int getIndent() {
return indent;
}
public int getLight() {
return preferences.getInt("light", getScreenBrightness());
}
public void setLight(int light) {
preferences.edit()
.putInt("light", light)
.apply();
}
public Boolean getLightFollowSys() {
return preferences.getBoolean("lightFollowSys", true);
}
public void setLightFollowSys(boolean isFollowSys) {
preferences.edit()
.putBoolean("lightFollowSys", isFollowSys)
.apply();
}
private int getScreenBrightness() {
int value = 0;
ContentResolver cr = MyApplication.getApplication().getContentResolver();
try {
value = Settings.System.getInt(cr, Settings.System.SCREEN_BRIGHTNESS);
} catch (Settings.SettingNotFoundException ignored) {
}
return value;
}
public boolean disableScrollClickTurn() {
return preferences.getBoolean("disableScrollClickTurn", false);
}
}

@ -1,34 +0,0 @@
package xyz.fycz.myreader.source;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class UserDatabaseHelper extends SQLiteOpenHelper {
private static final String CREATE_USER = "create table UserCache("
+ "id integer primary key autoincrement, "
+ "account varchar, "
+ "password varchar) ";
private Context mContext;
public UserDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version){
super(context,name,factory,version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db){
db.execSQL(CREATE_USER);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVerson){
}
}

@ -82,6 +82,7 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
* @author fengyue * @author fengyue
@ -748,10 +749,15 @@ public class BookDetailedActivity extends BaseActivity {
back.compress(Bitmap.CompressFormat.PNG, 100, fos); back.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush(); fos.flush();
Log.i("tag", "saveBitmap success: " + share.getAbsolutePath()); Log.i("tag", "saveBitmap success: " + share.getAbsolutePath());
back.recycle();
img.recycle();
QRCode.recycle();
return share; return share;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
ToastUtils.showError(e.getLocalizedMessage()); ToastUtils.showError(Objects.requireNonNull(e.getLocalizedMessage()));
return null; return null;
} finally { } finally {
IOUtils.close(fos); IOUtils.close(fos);

@ -361,11 +361,11 @@ public class CustomizeLayoutMenu extends FrameLayout {
holder.mTitle.setText(getFileName(path)); holder.mTitle.setText(getFileName(path));
holder.mTitle.setTextColor(Color.parseColor("#909090")); holder.mTitle.setTextColor(Color.parseColor("#909090"));
try { try {
BitmapDrawable bitmapDrawable = (BitmapDrawable) holder.mImage.getDrawable(); /*BitmapDrawable bitmapDrawable = (BitmapDrawable) holder.mImage.getDrawable();
//如果图片还未回收,先强制回收该图片 //如果图片还未回收,先强制回收该图片
if (bitmapDrawable != null && !bitmapDrawable.getBitmap().isRecycled()) { if (bitmapDrawable != null && !bitmapDrawable.getBitmap().isRecycled()) {
bitmapDrawable.getBitmap().recycle(); bitmapDrawable.getBitmap().recycle();
} }*/
//该变现实的图片 //该变现实的图片
Bitmap bmp = MeUtils.getFitAssetsSampleBitmap(context.getAssets(), getItemAssetsFile(position - 1), 256, 256); Bitmap bmp = MeUtils.getFitAssetsSampleBitmap(context.getAssets(), getItemAssetsFile(position - 1), 256, 256);
holder.mImage.setImageBitmap(bmp); holder.mImage.setImageBitmap(bmp);

@ -1,24 +0,0 @@
package xyz.fycz.myreader.util;
import android.content.Context;
public class DipPxUtil {
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dp2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale+0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
}

@ -1,24 +0,0 @@
package xyz.fycz.myreader.util;
import com.google.gson.Gson;
import org.json.JSONArray;
import java.util.ArrayList;
public class JsonArrayToObjectArray {
public static <T> ArrayList<T> getArray(String json, Class<T> c) throws Exception {
ArrayList<T> arrayList = new ArrayList<>();
JSONArray jsonArray = new JSONArray(json);
for (int i = 0; i < jsonArray.length(); i++) {
arrayList.add(new Gson().fromJson(jsonArray.getString(i), c));
}
return arrayList;
}
}

@ -1,8 +1,8 @@
package xyz.fycz.myreader.util; package xyz.fycz.myreader.util;
import android.os.Build;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
import xyz.fycz.myreader.R; import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication; import xyz.fycz.myreader.application.MyApplication;
@ -18,76 +18,51 @@ public class ToastUtils {
} }
public static void show(@NonNull String msg) { public static void show(@NonNull String msg) {
MyApplication.runOnUiThread(() -> { MyApplication.runOnUiThread(() -> Toasty.custom(MyApplication.getmContext(), msg,
if (showOld(msg)) return; ContextCompat.getDrawable(MyApplication.getmContext(), R.drawable.ic_smile_face),
Toasty.custom(MyApplication.getmContext(), msg, MyApplication.getmContext().getResources().getColor(R.color.toast_default),
MyApplication.getmContext().getDrawable(R.drawable.ic_smile_face), MyApplication.getmContext().getResources().getColor(R.color.white),
MyApplication.getmContext().getColor(R.color.toast_default), Toasty.LENGTH_SHORT, true, true).show());
MyApplication.getmContext().getColor(R.color.white),
Toasty.LENGTH_SHORT, true, true).show();
});
} }
//红色 //红色
public static void showError(@NonNull String msg) { public static void showError(@NonNull String msg) {
MyApplication.runOnUiThread(() -> { MyApplication.runOnUiThread(() -> Toasty.custom(MyApplication.getmContext(), msg,
if (showOld(msg)) return; ContextCompat.getDrawable(MyApplication.getmContext(), R.drawable.ic_error),
Toasty.custom(MyApplication.getmContext(), msg, MyApplication.getmContext().getResources().getColor(R.color.toast_red),
MyApplication.getmContext().getDrawable(R.drawable.ic_error), MyApplication.getmContext().getResources().getColor(R.color.white),
MyApplication.getmContext().getColor(R.color.toast_red), Toasty.LENGTH_SHORT, true, true).show());
MyApplication.getmContext().getColor(R.color.white),
Toasty.LENGTH_SHORT, true, true).show();
});
} }
//绿色 //绿色
public static void showSuccess(@NonNull String msg) { public static void showSuccess(@NonNull String msg) {
MyApplication.runOnUiThread(() -> { MyApplication.runOnUiThread(() -> Toasty.custom(MyApplication.getmContext(), msg,
if (showOld(msg)) return; ContextCompat.getDrawable(MyApplication.getmContext(), R.drawable.ic_success),
Toasty.custom(MyApplication.getmContext(), msg, MyApplication.getmContext().getResources().getColor(R.color.toast_green),
MyApplication.getmContext().getDrawable(R.drawable.ic_success), MyApplication.getmContext().getResources().getColor(R.color.white),
MyApplication.getmContext().getColor(R.color.toast_green), Toasty.LENGTH_SHORT, true, true).show());
MyApplication.getmContext().getColor(R.color.white),
Toasty.LENGTH_SHORT, true, true).show();
});
} }
//蓝色 //蓝色
public static void showInfo(@NonNull String msg) { public static void showInfo(@NonNull String msg) {
MyApplication.runOnUiThread(() -> { MyApplication.runOnUiThread(() -> Toasty.custom(MyApplication.getmContext(), msg,
if (showOld(msg)) return; ContextCompat.getDrawable(MyApplication.getmContext(), R.drawable.ic_smile_face),
Toasty.custom(MyApplication.getmContext(), msg, MyApplication.getmContext().getResources().getColor(R.color.toast_blue),
MyApplication.getmContext().getDrawable(R.drawable.ic_smile_face), MyApplication.getmContext().getResources().getColor(R.color.white),
MyApplication.getmContext().getColor(R.color.toast_blue), Toasty.LENGTH_SHORT, true, true).show());
MyApplication.getmContext().getColor(R.color.white),
Toasty.LENGTH_SHORT, true, true).show();
});
} }
//黄色 //黄色
public static void showWarring(@NonNull String msg) { public static void showWarring(@NonNull String msg) {
MyApplication.runOnUiThread(() -> { MyApplication.runOnUiThread(() -> Toasty.warning(MyApplication.getmContext(), msg, Toasty.LENGTH_SHORT, true).show());
if (showOld(msg)) return;
Toasty.warning(MyApplication.getmContext(), msg, Toasty.LENGTH_SHORT, true).show();
});
} }
public static void showExit(@NonNull String msg) { public static void showExit(@NonNull String msg) {
MyApplication.runOnUiThread(() -> { MyApplication.runOnUiThread(() -> Toasty.custom(MyApplication.getmContext(), msg,
if (showOld(msg)) return; ContextCompat.getDrawable(MyApplication.getmContext(), R.drawable.ic_cry_face),
Toasty.custom(MyApplication.getmContext(), msg, MyApplication.getmContext().getResources().getColor(R.color.toast_blue),
MyApplication.getmContext().getDrawable(R.drawable.ic_cry_face), MyApplication.getmContext().getResources().getColor(R.color.white),
MyApplication.getmContext().getColor(R.color.toast_blue), Toasty.LENGTH_SHORT, true, true).show());
MyApplication.getmContext().getColor(R.color.white),
Toasty.LENGTH_SHORT, true, true).show();
});
} }
private static boolean showOld(@NonNull String msg){
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Toast.makeText(MyApplication.getmContext(), msg, Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
} }

@ -4,7 +4,8 @@ import android.text.TextUtils;
import com.luhuiguo.chinese.ChineseUtils; import com.luhuiguo.chinese.ChineseUtils;
import xyz.fycz.myreader.entity.ReadBookControl; import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.enums.Language;
import xyz.fycz.myreader.greendao.entity.ReplaceRuleBean; import xyz.fycz.myreader.greendao.entity.ReplaceRuleBean;
import xyz.fycz.myreader.model.ReplaceRuleManager; import xyz.fycz.myreader.model.ReplaceRuleManager;
@ -22,14 +23,14 @@ public class ChapterContentHelp {
* 转繁体 * 转繁体
*/ */
private String toTraditional(String content) { private String toTraditional(String content) {
int convertCTS = ReadBookControl.getInstance().getTextConvert(); Language convertCTS = SysManager.getSetting().getLanguage();
switch (convertCTS) { switch (convertCTS) {
case 0: case normal:
break; break;
case 1: case simplified:
content = ChineseUtils.toSimplified(content); content = ChineseUtils.toSimplified(content);
break; break;
case 2: case traditional:
content = ChineseUtils.toTraditional(content); content = ChineseUtils.toTraditional(content);
break; break;
} }

@ -1,22 +1,17 @@
package xyz.fycz.myreader.webapi; package xyz.fycz.myreader.webapi;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.common.ErrorCode; import xyz.fycz.myreader.common.ErrorCode;
import xyz.fycz.myreader.entity.JsonModel; import xyz.fycz.myreader.entity.JsonModel;
import xyz.fycz.myreader.util.HttpUtil; import xyz.fycz.myreader.util.HttpUtil;
import xyz.fycz.myreader.util.JsonArrayToObjectArray;
import com.google.gson.Gson; import com.google.gson.Gson;
import xyz.fycz.myreader.util.utils.GsonExtensionsKt;
import xyz.fycz.myreader.webapi.callback.JsonCallback; import xyz.fycz.myreader.webapi.callback.JsonCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback; import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.source.HttpDataSource;
import xyz.fycz.myreader.util.ToastUtils; import xyz.fycz.myreader.util.ToastUtils;
import java.util.Map; import java.util.Map;
/**
* Created by zhao on 2017/6/20.
*/
public class BaseApi { public class BaseApi {
/** /**
@ -185,7 +180,7 @@ public class BaseApi {
public void onFinish(JsonModel jsonModel) { public void onFinish(JsonModel jsonModel) {
if (jsonModel.isSuccess()) { if (jsonModel.isSuccess()) {
try { try {
callback.onFinish(JsonArrayToObjectArray.getArray(jsonModel.getResult(),c), jsonModel.getError()); callback.onFinish(GsonExtensionsKt.getGSON().fromJson(jsonModel.getResult(), c), jsonModel.getError());
}catch (Exception e){ }catch (Exception e){
callback.onError(e); callback.onError(e);
e.printStackTrace(); e.printStackTrace();

@ -5,10 +5,6 @@ import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.webapi.callback.ResultCallback; import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.webapi.crawler.base.FindCrawler; import xyz.fycz.myreader.webapi.crawler.base.FindCrawler;
/**
* Created by zhao on 2017/7/24.
*/
public class BookStoreApi extends BaseApi{ public class BookStoreApi extends BaseApi{

@ -1,4 +1,4 @@
package xyz.fycz.myreader.source; package xyz.fycz.myreader.webapi;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
@ -23,9 +23,6 @@ import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map; import java.util.Map;
/**
* Created by zhao on 2016/4/16.
*/
public class HttpDataSource { public class HttpDataSource {

@ -7,7 +7,7 @@ import android.view.MotionEvent;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.animation.BounceInterpolator; import android.view.animation.BounceInterpolator;
import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.floatingactionbutton.FloatingActionButton;
import xyz.fycz.myreader.util.DipPxUtil; import xyz.fycz.myreader.util.utils.ScreenUtils;
public class DragFloatBtnT extends FloatingActionButton { public class DragFloatBtnT extends FloatingActionButton {
@ -34,7 +34,7 @@ public class DragFloatBtnT extends FloatingActionButton {
} }
private void init() { private void init() {
padding = DipPxUtil.dp2px(getContext(), 15); padding = ScreenUtils.dpToPx(15);
} }
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")

@ -1,213 +0,0 @@
package xyz.fycz.myreader.widget.page2;
import android.text.Layout;
import android.text.StaticLayout;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.util.help.ChapterContentHelp;
import xyz.fycz.myreader.util.utils.NetworkUtils;
class ChapterProvider {
private PageLoader pageLoader;
private ChapterContentHelp contentHelper = new ChapterContentHelp();
ChapterProvider(PageLoader pageLoader) {
this.pageLoader = pageLoader;
}
TxtChapter dealLoadPageList(Chapter chapter, boolean isPrepare) {
TxtChapter txtChapter = new TxtChapter(chapter.getNumber());
// 判断章节是否存在
if (!isPrepare || pageLoader.noChapterData(chapter)) {
if (pageLoader instanceof PageLoaderNet && !NetworkUtils.isNetWorkAvailable()) {
txtChapter.setStatus(TxtChapter.Status.ERROR);
txtChapter.setMsg("网络连接不可用");
}
return txtChapter;
}
String content;
try {
content = pageLoader.getChapterContent(chapter);
} catch (Exception e) {
txtChapter.setStatus(TxtChapter.Status.ERROR);
txtChapter.setMsg("读取内容出错\n" + e.getLocalizedMessage());
return txtChapter;
}
if (content == null) {
txtChapter.setStatus(TxtChapter.Status.ERROR);
txtChapter.setMsg("缓存文件不存在");
return txtChapter;
}
return loadPageList(chapter, content);
}
/**
* 将章节数据解析成页面列表
*
* @param chapter章节信息
* @param content章节的文本
*/
private TxtChapter loadPageList(Chapter chapter, @NonNull String content) {
//生成的页面
TxtChapter txtChapter = new TxtChapter(chapter.getNumber());
/*if (pageLoader.book.isAudio()) {
txtChapter.setStatus(TxtChapter.Status.FINISH);
txtChapter.setMsg(content);
TxtPage page = new TxtPage(txtChapter.getTxtPageList().size());
page.setTitle(chapter.getDurChapterName());
page.addLine(chapter.getDurChapterName());
page.addLine(content);
page.setTitleLines(1);
txtChapter.addPage(page);
addTxtPageLength(txtChapter, page.getContent().length());
txtChapter.addPage(page);
return txtChapter;
}*/
content = contentHelper.replaceContent(pageLoader.book.getName(), pageLoader.book.getTag(), content, pageLoader.book.getReplaceEnable());
String[] allLine = content.split("\n");
List<String> lines = new ArrayList<>();
List<TxtLine> txtLists = new ArrayList<>();//记录每个字的位置 //pzl
int rHeight = pageLoader.mVisibleHeight - pageLoader.contentMarginHeight * 2;
int titleLinesCount = 0;
boolean showTitle = pageLoader.readBookControl.getShowTitle(); // 是否展示标题
String paragraph = null;
if (showTitle) {
paragraph = contentHelper.replaceContent(pageLoader.book.getName(), pageLoader.book.getTag(), chapter.getTitle(), pageLoader.book.getReplaceEnable());
paragraph = paragraph.trim() + "\n";
}
int i = 1;
while (showTitle || i < allLine.length) {
// 重置段落
if (!showTitle) {
paragraph = allLine[i].replaceAll("\\s", " ").trim();
i++;
if (paragraph.equals("")) continue;
paragraph = pageLoader.indent + paragraph + "\n";
}
addParagraphLength(txtChapter, paragraph.length());
int wordCount;
String subStr;
while (paragraph.length() > 0) {
//当前空间,是否容得下一行文字
if (showTitle) {
rHeight -= pageLoader.mTitlePaint.getTextSize();
} else {
rHeight -= pageLoader.mTextPaint.getTextSize();
}
// 一页已经填充满了,创建 TextPage
if (rHeight <= 0) {
// 创建Page
TxtPage page = new TxtPage(txtChapter.getTxtPageList().size());
page.setTitle(chapter.getTitle());
page.addLines(lines);
page.setTxtLists(new ArrayList<>(txtLists));
page.setTitleLines(titleLinesCount);
txtChapter.addPage(page);
addTxtPageLength(txtChapter, page.getContent().length());
// 重置Lines
lines.clear();
txtLists.clear();//pzl
rHeight = pageLoader.mVisibleHeight - pageLoader.contentMarginHeight * 2;
titleLinesCount = 0;
continue;
}
//测量一行占用的字节数
if (showTitle) {
Layout tempLayout = new StaticLayout(paragraph, pageLoader.mTitlePaint, pageLoader.mVisibleWidth, Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
wordCount = tempLayout.getLineEnd(0);
} else {
Layout tempLayout = new StaticLayout(paragraph, pageLoader.mTextPaint, pageLoader.mVisibleWidth, Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
wordCount = tempLayout.getLineEnd(0);
}
subStr = paragraph.substring(0, wordCount);
if (!subStr.equals("\n")) {
//将一行字节,存储到lines中
lines.add(subStr);
//begin pzl
//记录每个字的位置
char[] cs = subStr.toCharArray();
TxtLine txtList = new TxtLine();//每一行
txtList.setCharsData(new ArrayList<TxtChar>());
for (char c : cs) {
String mesasrustr = String.valueOf(c);
float charwidth = pageLoader.mTextPaint.measureText(mesasrustr);
if (showTitle) {
charwidth = pageLoader.mTitlePaint.measureText(mesasrustr);
}
TxtChar txtChar = new TxtChar();
txtChar.setChardata(c);
txtChar.setCharWidth(charwidth);//字宽
txtChar.setIndex(66);//每页每个字的位置
txtList.getCharsData().add(txtChar);
}
txtLists.add(txtList);
//end pzl
//设置段落间距
if (showTitle) {
titleLinesCount += 1;
rHeight -= pageLoader.mTitleInterval;
} else {
rHeight -= pageLoader.mTextInterval;
}
}
//裁剪
paragraph = paragraph.substring(wordCount);
}
//增加段落的间距
if (!showTitle && lines.size() != 0) {
rHeight = rHeight - pageLoader.mTextPara + pageLoader.mTextInterval;
}
if (showTitle) {
rHeight = rHeight - pageLoader.mTitlePara + pageLoader.mTitleInterval;
showTitle = false;
}
}
if (lines.size() != 0) {
//创建Page
TxtPage page = new TxtPage(txtChapter.getTxtPageList().size());
page.setTitle(chapter.getTitle());
page.addLines(lines);
page.setTxtLists(new ArrayList<>(txtLists));
page.setTitleLines(titleLinesCount);
txtChapter.addPage(page);
addTxtPageLength(txtChapter, page.getContent().length());
//重置Lines
lines.clear();
txtLists.clear();
}
if (txtChapter.getPageSize() > 0) {
txtChapter.setStatus(TxtChapter.Status.FINISH);
} else {
txtChapter.setStatus(TxtChapter.Status.ERROR);
txtChapter.setMsg("未加载到内容");
}
return txtChapter;
}
private void addTxtPageLength(TxtChapter txtChapter, int length) {
if (txtChapter.getTxtPageLengthList().isEmpty()) {
txtChapter.addTxtPageLength(length);
} else {
txtChapter.addTxtPageLength(txtChapter.getTxtPageLengthList().get(txtChapter.getTxtPageLengthList().size() - 1) + length);
}
}
private void addParagraphLength(TxtChapter txtChapter, int length) {
if (txtChapter.getParagraphLengthList().isEmpty()) {
txtChapter.addParagraphLength(length);
} else {
txtChapter.addParagraphLength(txtChapter.getParagraphLengthList().get(txtChapter.getParagraphLengthList().size() - 1) + length);
}
}
}

@ -1,401 +0,0 @@
package xyz.fycz.myreader.widget.page2;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.reactivex.Single;
import io.reactivex.SingleObserver;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.disposables.Disposable;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.service.ChapterService;
import xyz.fycz.myreader.util.IOUtils;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.utils.Charset;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.utils.RxUtils;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.widget.page.BookChapterBean;
import static xyz.fycz.myreader.widget.page.PageLoader.STATUS_PARING;
/**
* Created by newbiechen on 17-7-1.
* 问题:
* 1. 异常处理没有做好
*/
public class LocalPageLoader extends PageLoader {
private static final String TAG = "LocalPageLoader";
//默认从文件中获取数据的长度
private final static int BUFFER_SIZE = 512 * 1024;
//没有标题的时候,每个章节的最大长度
private final static int MAX_LENGTH_WITH_NO_CHAPTER = 10 * 1024;
// "序(章)|前言"
private final static Pattern mPreChapterPattern = Pattern.compile("^(\\s{0,10})((\u5e8f[\u7ae0\u8a00]?)|(\u524d\u8a00)|(\u6954\u5b50))(\\s{0,10})$", Pattern.MULTILINE);
//正则表达式章节匹配模式
// "(第)([0-9零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10})([章节回集卷])(.*)"
private static final String[] CHAPTER_PATTERNS = new String[]{"^(.{0,8})(\u7b2c)([0-9\u96f6\u4e00\u4e8c\u4e24\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u58f9\u8d30\u53c1\u8086\u4f0d\u9646\u67d2\u634c\u7396\u62fe\u4f70\u4edf]{1,10})([\u7ae0\u8282\u56de\u96c6\u5377])(.{0,30})$",
"^(\\s{0,4})([\\(\u3010\u300a]?(\u5377)?)([0-9\u96f6\u4e00\u4e8c\u4e24\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u58f9\u8d30\u53c1\u8086\u4f0d\u9646\u67d2\u634c\u7396\u62fe\u4f70\u4edf]{1,10})([\\.:\uff1a\u0020\f\t])(.{0,30})$",
"^(\\s{0,4})([\\(\uff08\u3010\u300a])(.{0,30})([\\)\uff09\u3011\u300b])(\\s{0,2})$",
"^(\\s{0,4})(\u6b63\u6587)(.{0,20})$",
"^(.{0,4})(Chapter|chapter)(\\s{0,4})([0-9]{1,4})(.{0,30})$"};
//章节解析模式
private Pattern mChapterPattern = null;
//获取书本的文件
private File mBookFile;
//编码类型
private String mCharset;
private Disposable mChapterDisp = null;
private ChapterService mChapterService;
public LocalPageLoader(PageView pageView, Book book, Callback callback) {
super(pageView, book, callback);
this.mChapterService = ChapterService.getInstance();
}
/**
* 未完成的部分:
* 1. 序章的添加
* 2. 章节存在的书本的虚拟分章效果
*/
public List<BookChapterBean> loadChapters() throws IOException {
mBookFile = new File(book.getChapterUrl());
//获取文件编码
mCharset = FileUtils.getFileEncode(mBookFile.getAbsolutePath());
List<BookChapterBean> chapters = new ArrayList<>();
RandomAccessFile bookStream = null;
boolean hasChapter = false;
//获取文件流
bookStream = new RandomAccessFile(mBookFile, "r");
//寻找匹配文章标题的正则表达式,判断是否存在章节名
hasChapter = checkChapterType(bookStream);
//加载章节
byte[] buffer = new byte[BUFFER_SIZE];
//获取到的块起始点,在文件中的位置
long curOffset = 0;
//block的个数
int blockPos = 0;
//读取的长度
int length;
//获取文件中的数据到buffer,直到没有数据为止
while ((length = bookStream.read(buffer, 0, buffer.length)) > 0) {
++blockPos;
//如果存在Chapter
if (hasChapter) {
//将数据转换成String
String blockContent = new String(buffer, 0, length, mCharset);
//当前Block下使过的String的指针
int seekPos = 0;
//进行正则匹配
Matcher matcher = mChapterPattern.matcher(blockContent);
//如果存在相应章节
while (matcher.find()) {
//获取匹配到的字符在字符串中的起始位置
int chapterStart = matcher.start();
//如果 seekPos == 0 && nextChapterPos != 0 表示当前block处前面有一段内容
//第一种情况一定是序章 第二种情况可能是上一个章节的内容
if (seekPos == 0 && chapterStart != 0) {
//获取当前章节的内容
String chapterContent = blockContent.substring(seekPos, chapterStart);
//设置指针偏移
seekPos += chapterContent.length();
//如果当前对整个文件的偏移位置为0的话,那么就是序章
if (curOffset == 0) {
//创建序章
BookChapterBean preChapter = new BookChapterBean();
preChapter.title = "序章";
preChapter.start = 0;
preChapter.end = chapterContent.getBytes(mCharset).length; //获取String的byte值,作为最终值
//如果序章大小大于30才添加进去
if (preChapter.end - preChapter.start > 30) {
chapters.add(preChapter);
}
//创建当前章节
BookChapterBean curChapter = new BookChapterBean();
curChapter.title = matcher.group();
curChapter.start = preChapter.end;
chapters.add(curChapter);
}
//否则就block分割之后,上一个章节的剩余内容
else {
//获取上一章节
BookChapterBean lastChapter = chapters.get(chapters.size() - 1);
//将当前段落添加上一章去
lastChapter.end += chapterContent.getBytes(mCharset).length;
//如果章节内容太小,则移除
if (lastChapter.end - lastChapter.start < 30) {
chapters.remove(lastChapter);
}
//创建当前章节
BookChapterBean curChapter = new BookChapterBean();
curChapter.title = matcher.group();
curChapter.start = lastChapter.end;
chapters.add(curChapter);
}
} else {
//是否存在章节
if (chapters.size() != 0) {
//获取章节内容
String chapterContent = blockContent.substring(seekPos, matcher.start());
seekPos += chapterContent.length();
//获取上一章节
BookChapterBean lastChapter = chapters.get(chapters.size() - 1);
lastChapter.end = lastChapter.start + chapterContent.getBytes(mCharset).length;
//如果章节内容太小,则移除
if (lastChapter.end - lastChapter.start < 30) {
chapters.remove(lastChapter);
}
//创建当前章节
BookChapterBean curChapter = new BookChapterBean();
curChapter.title = matcher.group();
curChapter.start = lastChapter.end;
chapters.add(curChapter);
}
//如果章节不存在则创建章节
else {
BookChapterBean curChapter = new BookChapterBean();
curChapter.title = matcher.group();
curChapter.start = 0;
chapters.add(curChapter);
}
}
}
}
//进行本地虚拟分章
else {
//章节在buffer的偏移量
int chapterOffset = 0;
//当前剩余可分配的长度
int strLength = length;
//分章的位置
int chapterPos = 0;
while (strLength > 0) {
++chapterPos;
//是否长度超过一章
if (strLength > MAX_LENGTH_WITH_NO_CHAPTER) {
//在buffer中一章的终止点
int end = length;
//寻找换行符作为终止点
for (int i = chapterOffset + MAX_LENGTH_WITH_NO_CHAPTER; i < length; ++i) {
if (buffer[i] == Charset.BLANK) {
end = i;
break;
}
}
BookChapterBean chapter = new BookChapterBean();
chapter.title = "第" + blockPos + "章" + "(" + chapterPos + ")";
chapter.start = curOffset + chapterOffset + 1;
chapter.end = curOffset + end;
chapters.add(chapter);
//减去已经被分配的长度
strLength = strLength - (end - chapterOffset);
//设置偏移的位置
chapterOffset = end;
} else {
BookChapterBean chapter = new BookChapterBean();
chapter.title = "第" + blockPos + "章" + "(" + chapterPos + ")";
chapter.start = curOffset + chapterOffset + 1;
chapter.end = curOffset + length;
chapters.add(chapter);
strLength = 0;
}
}
}
//block的偏移点
curOffset += length;
if (hasChapter) {
//设置上一章的结尾
BookChapterBean lastChapter = chapters.get(chapters.size() - 1);
lastChapter.end = curOffset;
}
//当添加的block太多的时候,执行GC
if (blockPos % 15 == 0) {
System.gc();
System.runFinalization();
}
}
IOUtils.close(bookStream);
System.gc();
System.runFinalization();
return chapters;
}
public void loadChapters(final ResultCallback resultCallback) {
// 通过RxJava异步处理分章事件
Single.create((SingleOnSubscribe<List<BookChapterBean>>) e -> {
e.onSuccess(loadChapters());
}).compose(RxUtils::toSimpleSingle).subscribe(new SingleObserver<List<BookChapterBean>>() {
@Override
public void onSubscribe(Disposable d) {
mChapterDisp = d;
}
@Override
public void onSuccess(List<BookChapterBean> chapters) {
mChapterDisp = null;
isChapterListPrepare = true;
List<Chapter> mChapters = new ArrayList<>();
int i = 0;
for (BookChapterBean bookChapterBean : chapters) {
Chapter chapter = new Chapter();
chapter.setBookId(book.getId());
chapter.setTitle(bookChapterBean.getTitle().replace("\n", "").replaceAll("^\\s+|\\s+$", ""));
chapter.setId(StringHelper.getStringRandom(25));
String content = getChapterContent(bookChapterBean);
if (StringHelper.isEmpty(content)) {
continue;
}
chapter.setNumber(i++);
mChapterService.saveOrUpdateChapter(chapter, content);
mChapters.add(chapter);
}
if (resultCallback != null) {
resultCallback.onFinish(mChapters, 1);
}
}
@Override
public void onError(Throwable e) {
resultCallback.onError((Exception) e);
durDhapterError(e.getMessage());
Log.d(TAG, "file load error:" + e.toString());
}
});
}
/**
* 从文件中提取一章的内容
*
* @param chapter
* @return
*/
private String getChapterContent(BookChapterBean chapter) {
RandomAccessFile bookStream = null;
try {
bookStream = new RandomAccessFile(mBookFile, "r");
bookStream.seek(chapter.start);
int extent = (int) (chapter.end - chapter.start);
byte[] content = new byte[extent];
bookStream.read(content, 0, extent);
return new String(content, mCharset);
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.close(bookStream);
}
return null;
}
/**
* 1. 检查文件中是否存在章节名
* 2. 判断文件中使用的章节名类型的正则表达式
*
* @return 是否存在章节名
*/
private boolean checkChapterType(RandomAccessFile bookStream) throws IOException {
//首先获取128k的数据
byte[] buffer = new byte[BUFFER_SIZE / 4];
int length = bookStream.read(buffer, 0, buffer.length);
//进行章节匹配
for (String str : CHAPTER_PATTERNS) {
Pattern pattern = Pattern.compile(str, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(new String(buffer, 0, length, mCharset));
//如果匹配存在,那么就表示当前章节使用这种匹配方式
if (matcher.find()) {
mChapterPattern = pattern;
//重置指针位置
bookStream.seek(0);
return true;
}
}
//重置指针位置
bookStream.seek(0);
return false;
}
@Override
public void closeBook() {
super.closeBook();
if (mChapterDisp != null) {
mChapterDisp.dispose();
mChapterDisp = null;
}
}
@Override
public void refreshChapterList() {
// 对于文件是否存在,或者为空的判断,不作处理。 ==> 在文件打开前处理过了。
mBookFile = new File(book.getChapterUrl());
// 判断文件是否已经加载过,并具有缓存
if (book.getChapterTotalNum() != 0) {
List<Chapter> chapters = mChapterService.findBookAllChapterByBookId(book.getId());
isChapterListPrepare = true;
// 目录加载完成,执行回调操作。
if (!chapters.isEmpty()) {
callback.onCategoryFinish(chapters);
}
// 打开章节
skipToChapter(book.getHisttoryChapterNum(), book.getLastReadPosition());
}
}
@Override
public String getChapterContent(Chapter chapter) {
File file = new File(APPCONST.BOOK_CACHE_PATH + book.getId()
+ File.separator + chapter.getTitle() + FileUtils.SUFFIX_FY);
if (!file.exists()) return null;
byte[] contentByte = FileUtils.getBytes(file);
return new String(contentByte, StandardCharsets.UTF_8);
}
@Override
protected boolean noChapterData(Chapter chapter) {
return !ChapterService.isChapterCached(book.getId(), chapter.getTitle());
}
@Override
public void updateChapter() {
}
}

@ -1,266 +0,0 @@
package xyz.fycz.myreader.widget.page2;
import android.annotation.SuppressLint;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.service.ChapterService;
import xyz.fycz.myreader.util.utils.FileUtils;
/**
* 网络页面加载器
*/
public class PageLoaderNet extends PageLoader {
private static final String TAG = "PageLoaderNet";
private List<String> downloadingChapterList = new ArrayList<>();
PageLoaderNet(PageView pageView, Book bookShelfBean, Callback callback) {
super(pageView, bookShelfBean, callback);
}
@Override
public void refreshChapterList() {
if (!callback.getChapterList().isEmpty()) {
isChapterListPrepare = true;
// 打开章节
skipToChapter(book.getHisttoryChapterNum(), book.getLastReadPosition());
} else {
/*WebBookModel.getInstance().getChapterList(book)
.compose(RxUtils::toSimpleSingle)
.subscribe(new MyObserver<List<BookChapterBean>>() {
@Override
public void onSubscribe(Disposable d) {
compositeDisposable.add(d);
}
@Override
public void onNext(List<BookChapterBean> chapterBeanList) {
isChapterListPrepare = true;
// 目录加载完成
if (!chapterBeanList.isEmpty()) {
BookshelfHelp.delChapterList(book.getNoteUrl());
callback.onCategoryFinish(chapterBeanList);
}
// 加载并显示当前章节
skipToChapter(book.getDurChapter(), book.getDurChapterPage());
}
@Override
public void onError(Throwable e) {
if (e instanceof WebBook.NoSourceThrowable) {
mPageView.autoChangeSource();
} else {
durDhapterError(e.getMessage());
}
}
});*/
}
}
public void changeSourceFinish(Book bookShelfBean) {
if (bookShelfBean == null) {
openChapter(book.getHisttoryChapterNum());
} else {
this.book = bookShelfBean;
refreshChapterList();
}
}
/*@SuppressLint("DefaultLocale")
private synchronized void loadContent(final int chapterIndex) {
if (downloadingChapterList.size() >= 20) return;
if (chapterIndex >= callback.getChapterList().size()
|| DownloadingList(listHandle.CHECK, callback.getChapterList().get(chapterIndex).getUrl()))
return;
if (null != book && callback.getChapterList().size() > 0) {
Observable.create((ObservableOnSubscribe<Integer>) e -> {
if (shouldRequestChapter(chapterIndex)) {
DownloadingList(listHandle.ADD, callback.getChapterList().get(chapterIndex).getUrl());
e.onNext(chapterIndex);
}
e.onComplete();
})
.flatMap(index -> WebBookModel.getInstance().getBookContent(book, callback.getChapterList().get(chapterIndex), null))
.subscribeOn(scheduler)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new MyObserver<BookContentBean>() {
@Override
public void onSubscribe(Disposable d) {
compositeDisposable.add(d);
}
@SuppressLint("DefaultLocale")
@Override
public void onNext(BookContentBean bookContentBean) {
DownloadingList(listHandle.REMOVE, bookContentBean.getDurChapterUrl());
finishContent(bookContentBean.getDurChapterIndex());
}
@Override
public void onError(Throwable e) {
DownloadingList(listHandle.REMOVE, callback.getChapterList().get(chapterIndex).getDurChapterUrl());
if (chapterIndex == book.getDurChapter()) {
if (e instanceof WebBook.NoSourceThrowable) {
mPageView.autoChangeSource();
} else if (e instanceof VipThrowable) {
callback.vipPop();
} else {
durDhapterError(e.getMessage());
}
}
}
});
}
}*/
/**
* 编辑下载列表
*/
private synchronized boolean DownloadingList(listHandle editType, String value) {
if (editType == listHandle.ADD) {
downloadingChapterList.add(value);
return true;
} else if (editType == listHandle.REMOVE) {
downloadingChapterList.remove(value);
return true;
} else {
return downloadingChapterList.indexOf(value) != -1;
}
}
/**
* 章节下载完成
*/
private void finishContent(int chapterIndex) {
if (chapterIndex == mCurChapterPos) {
super.parseCurChapter();
}
if (chapterIndex == mCurChapterPos - 1) {
super.parsePrevChapter();
}
if (chapterIndex == mCurChapterPos + 1) {
super.parseNextChapter();
}
}
/**
* 刷新当前章节
*/
@SuppressLint("DefaultLocale")
/*public void refreshDurChapter() {
if (callback.getChapterList().isEmpty()) {
updateChapter();
return;
}
if (callback.getChapterList().size() - 1 < mCurChapterPos) {
mCurChapterPos = callback.getChapterList().size() - 1;
}
BookshelfHelp.delChapter(BookshelfHelp.getCachePathName(book.getBookInfoBean().getName(), book.getTag()),
mCurChapterPos, callback.getChapterList().get(mCurChapterPos).getDurChapterName());
skipToChapter(mCurChapterPos, 0);
}*/
@Override
protected String getChapterContent(Chapter chapter) {
File file = new File(APPCONST.BOOK_CACHE_PATH + book.getId()
+ File.separator + chapter.getTitle() + FileUtils.SUFFIX_FY);
if (!file.exists()) return null;
byte[] contentByte = FileUtils.getBytes(file);
return new String(contentByte, StandardCharsets.UTF_8);
//return BookshelfHelp.getChapterCache(book, chapter);
}
@SuppressLint("DefaultLocale")
@Override
protected boolean noChapterData(Chapter chapter) {
//return !BookshelfHelp.isChapterCached(book.getBookInfoBean().getName(), book.getTag(), chapter, book.isAudio());
return !ChapterService.isChapterCached(book.getId(), chapter.getTitle());
}
/*private boolean shouldRequestChapter(Integer chapterIndex) {
return NetworkUtils.isNetWorkAvailable() && noChapterData(callback.getChapterList().get(chapterIndex));
}*/
// 装载上一章节的内容
@Override
void parsePrevChapter() {
/*if (mCurChapterPos >= 1) {
loadContent(mCurChapterPos - 1);
}*/
super.parsePrevChapter();
}
// 装载当前章内容。
@Override
void parseCurChapter() {
/*for (int i = mCurChapterPos; i < Math.min(mCurChapterPos + 5, book.getChapterTotalNum()); i++) {
loadContent(i);
}*/
super.parseCurChapter();
}
// 装载下一章节的内容
@Override
void parseNextChapter() {
/*for (int i = mCurChapterPos; i < Math.min(mCurChapterPos + 5, book.getChapterTotalNum()); i++) {
loadContent(i);
}*/
super.parseNextChapter();
}
@Override
public void updateChapter() {
/*mPageView.getActivity().toast("目录更新中");
WebBookModel.getInstance().getChapterList(book)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<BookChapterBean>>() {
@Override
public void onSubscribe(Disposable d) {
compositeDisposable.add(d);
}
@Override
public void onNext(List<BookChapterBean> chapterBeanList) {
isChapterListPrepare = true;
if (chapterBeanList.size() > callback.getChapterList().size()) {
mPageView.getActivity().toast("更新完成,有新章节");
callback.onCategoryFinish(chapterBeanList);
} else {
mPageView.getActivity().toast("更新完成,无新章节");
}
// 加载并显示当前章节
skipToChapter(book.getDurChapter(), book.getDurChapterPage());
}
@Override
public void onError(Throwable e) {
durDhapterError(e.getMessage());
}
@Override
public void onComplete() {
}
});*/
}
@Override
public void closeBook() {
super.closeBook();
}
public enum listHandle {
ADD, REMOVE, CHECK
}
}

@ -1,713 +0,0 @@
package xyz.fycz.myreader.widget.page2;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import com.gyf.immersionbar.ImmersionBar;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import xyz.fycz.myreader.entity.ReadBookControl;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.ui.activity.ReadActivity;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.utils.ScreenUtils;
import xyz.fycz.myreader.util.utils.SnackbarUtils;
import xyz.fycz.myreader.widget.page2.animation.CoverPageAnim;
import xyz.fycz.myreader.widget.page2.animation.HorizonPageAnim;
import xyz.fycz.myreader.widget.page2.animation.NonePageAnim;
import xyz.fycz.myreader.widget.page2.animation.PageAnimation;
import xyz.fycz.myreader.widget.page2.animation.ScrollPageAnim;
import xyz.fycz.myreader.widget.page2.animation.SimulationPageAnim;
import xyz.fycz.myreader.widget.page2.animation.SlidePageAnim;
import static xyz.fycz.myreader.util.utils.ScreenUtils.getDisplayMetrics;
/**
* 绘制页面显示内容的类
*/
public class PageView extends View implements PageAnimation.OnPageChangeListener {
private ReadActivity activity;
private int mViewWidth = 0; // 当前View的宽
private int mViewHeight = 0; // 当前View的高
private int statusBarHeight = 0; //状态栏高度
private boolean actionFromEdge = false;
// 初始化参数
private ReadBookControl readBookControl = ReadBookControl.getInstance();
private boolean isPrepare;
// 动画类
private PageAnimation mPageAnim;
//点击监听
private TouchListener mTouchListener;
//内容加载器
private PageLoader mPageLoader;
//文字选择画笔
private Paint mTextSelectPaint = null;
//文字选择画笔颜色
private int TextSelectColor = Color.parseColor("#77fadb08");
private Path mSelectTextPath = new Path();
//触摸到起始位置
private int mStartX = 0;
private int mStartY = 0;
// 是否发触了长按事件
private boolean isLongPress = false;
//第一个选择的文字
private TxtChar firstSelectTxtChar = null;
//最后选择的一个文字
private TxtChar lastSelectTxtChar = null;
//选择模式
private SelectMode selectMode = SelectMode.Normal;
//文本高度
private float textHeight = 0;
// 唤醒菜单的区域
private RectF mCenterRect = null;
//是否在移动
private boolean isMove = false;
//长按的runnable
private Runnable mLongPressRunnable;
//长按时间
private static final int LONG_PRESS_TIMEOUT = 1000;
//选择的列
private List<TxtLine> mSelectLines = new ArrayList<TxtLine>();
public PageView(Context context) {
this(context, null);
}
public PageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
//初始化画笔
mTextSelectPaint = new Paint();
mTextSelectPaint.setAntiAlias(true);
mTextSelectPaint.setTextSize(19);
mTextSelectPaint.setColor(TextSelectColor);
mLongPressRunnable = () -> {
if (mPageLoader == null) return;
performLongClick();
if (mStartX > 0 && mStartY > 0) {// 说明还没释放,是长按事件
isLongPress = true;//长按
TxtChar p = mPageLoader.detectPressTxtChar(mStartX, mStartY);//找到长按的点
firstSelectTxtChar = p;//设置开始位置字符
lastSelectTxtChar = p;//设置结束位置字符
selectMode = SelectMode.PressSelectText;//设置模式为长按选择
mTouchListener.onLongPress();//响应长按事件,供上层调用
}
};
}
@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
super.onSizeChanged(width, height, oldWidth, oldHeight);
mViewWidth = width;
mViewHeight = height;
isPrepare = true;
if (mPageLoader != null) {
mPageLoader.prepareDisplay(width, height);
}
//设置中间区域范围
mCenterRect = new RectF(mViewWidth / 3f, mViewHeight / 3f,
mViewWidth * 2f / 3, mViewHeight * 2f / 3);
}
//设置翻页的模式
void setPageMode(PageAnimation.Mode pageMode, int marginTop, int marginBottom) {
//视图未初始化的时候,禁止调用
if (mViewWidth == 0 || mViewHeight == 0 || mPageLoader == null) return;
if (!readBookControl.getHideStatusBar()) {
marginTop = marginTop + statusBarHeight;
}
switch (pageMode) {
case COVER:
mPageAnim = new CoverPageAnim(mViewWidth, mViewHeight, this, this);
break;
case SLIDE:
mPageAnim = new SlidePageAnim(mViewWidth, mViewHeight, this, this);
break;
case NONE:
mPageAnim = new NonePageAnim(mViewWidth, mViewHeight, this, this);
break;
case SCROLL:
mPageAnim = new ScrollPageAnim(mViewWidth, mViewHeight, 0,
marginTop, marginBottom, this, this);
break;
default:
mPageAnim = new SimulationPageAnim(mViewWidth, mViewHeight, this, this);
}
}
public ReadActivity getActivity() {
return activity;
}
public int getStatusBarHeight() {
return statusBarHeight;
}
public Bitmap getBgBitmap(int pageOnCur) {
if (mPageAnim == null) return null;
return mPageAnim.getBgBitmap(pageOnCur);
}
public void autoPrevPage() {
if (mPageAnim instanceof ScrollPageAnim) {
((ScrollPageAnim) mPageAnim).startAnim(PageAnimation.Direction.PREV);
} else {
startHorizonPageAnim(PageAnimation.Direction.PREV);
}
}
public void autoNextPage() {
if (mPageAnim instanceof ScrollPageAnim) {
((ScrollPageAnim) mPageAnim).startAnim(PageAnimation.Direction.NEXT);
} else {
startHorizonPageAnim(PageAnimation.Direction.NEXT);
}
}
private synchronized void startHorizonPageAnim(PageAnimation.Direction direction) {
if (mTouchListener == null) return;
//结束动画
mPageAnim.abortAnim();
if (direction == PageAnimation.Direction.NEXT) {
int x = mViewWidth;
int y = mViewHeight;
//初始化动画
mPageAnim.setStartPoint(x, y);
//设置点击点
mPageAnim.setTouchPoint(x, y);
//设置方向
boolean hasNext = hasNextPage(0);
mPageAnim.setDirection(direction);
if (!hasNext) {
((HorizonPageAnim) mPageAnim).setNoNext(true);
return;
}
} else if (direction == PageAnimation.Direction.PREV) {
int x = 0;
int y = mViewHeight;
//初始化动画
mPageAnim.setStartPoint(x, y);
//设置点击点
mPageAnim.setTouchPoint(x, y);
mPageAnim.setDirection(direction);
//设置方向方向
boolean hashPrev = hasPrevPage();
if (!hashPrev) {
((HorizonPageAnim) mPageAnim).setNoNext(true);
return;
}
} else {
return;
}
((HorizonPageAnim) mPageAnim).setNoNext(false);
((HorizonPageAnim) mPageAnim).setCancel(false);
mPageAnim.startAnim();
}
public void drawPage(int pageOnCur) {
if (!isPrepare) return;
if (mPageLoader != null) {
mPageLoader.drawPage(getBgBitmap(pageOnCur), pageOnCur);
}
invalidate();
}
/**
* 绘制滚动背景
*/
@Override
public void drawBackground(Canvas canvas) {
if (!isPrepare) return;
if (mPageLoader != null) {
mPageLoader.drawBackground(canvas);
}
}
/**
* 绘制滚动内容
*/
@Override
public void drawContent(Canvas canvas, float offset) {
if (!isPrepare) return;
if (mPageLoader != null) {
mPageLoader.drawContent(canvas, offset);
}
}
/**
* 绘制横翻背景
*/
public void drawBackground(int pageOnCur) {
if (!isPrepare) return;
if (mPageLoader != null) {
mPageLoader.drawPage(getBgBitmap(pageOnCur), pageOnCur);
}
invalidate();
}
/**
* 绘制横翻内容
*
* @param pageOnCur 相对当前页的位置
*/
public void drawContent(int pageOnCur) {
if (!isPrepare) return;
if (mPageLoader != null) {
mPageLoader.drawPage(getBgBitmap(pageOnCur), pageOnCur);
}
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
if (mPageAnim instanceof ScrollPageAnim)
super.onDraw(canvas);
//绘制动画
if (mPageAnim != null) {
mPageAnim.draw(canvas);
}
if (selectMode != SelectMode.Normal && !isRunning() && !isMove) {
DrawSelectText(canvas);
}
}
private void DrawSelectText(Canvas canvas) {
if (selectMode == SelectMode.PressSelectText) {
drawPressSelectText(canvas);
} else if (selectMode == SelectMode.SelectMoveForward) {
drawMoveSelectText(canvas);
} else if (selectMode == SelectMode.SelectMoveBack) {
drawMoveSelectText(canvas);
}
}
private void drawPressSelectText(Canvas canvas) {
if (lastSelectTxtChar != null) {// 找到了选择的字符
mSelectTextPath.reset();
mSelectTextPath.moveTo(firstSelectTxtChar.getTopLeftPosition().x, firstSelectTxtChar.getTopLeftPosition().y);
mSelectTextPath.lineTo(firstSelectTxtChar.getTopRightPosition().x, firstSelectTxtChar.getTopRightPosition().y);
mSelectTextPath.lineTo(firstSelectTxtChar.getBottomRightPosition().x, firstSelectTxtChar.getBottomRightPosition().y);
mSelectTextPath.lineTo(firstSelectTxtChar.getBottomLeftPosition().x, firstSelectTxtChar.getBottomLeftPosition().y);
canvas.drawPath(mSelectTextPath, mTextSelectPaint);
}
}
public String getSelectStr() {
if (mSelectLines.size() == 0) {
return String.valueOf(firstSelectTxtChar.getChardata());
}
StringBuilder sb = new StringBuilder();
for (TxtLine l : mSelectLines) {
//Log.e("selectline", l.getLineData() + "");
sb.append(l.getLineData());
}
return sb.toString();
}
private void drawMoveSelectText(Canvas canvas) {
if (firstSelectTxtChar == null || lastSelectTxtChar == null)
return;
getSelectData();
drawSelectLines(canvas);
}
List<TxtLine> mLinseData = null;
private void getSelectData() {
TxtPage txtPage = mPageLoader.curChapter().txtChapter.getPage(mPageLoader.getCurPagePos());
if (txtPage != null) {
mLinseData = txtPage.getTxtLists();
Boolean Started = false;
Boolean Ended = false;
mSelectLines.clear();
// 找到选择的字符数据,转化为选择的行,然后将行选择背景画出来
for (TxtLine l : mLinseData) {
TxtLine selectline = new TxtLine();
selectline.setCharsData(new ArrayList<>());
for (TxtChar c : l.getCharsData()) {
if (!Started) {
if (c.getIndex() == firstSelectTxtChar.getIndex()) {
Started = true;
selectline.getCharsData().add(c);
if (c.getIndex() == lastSelectTxtChar.getIndex()) {
Ended = true;
break;
}
}
} else {
if (c.getIndex() == lastSelectTxtChar.getIndex()) {
Ended = true;
if (!selectline.getCharsData().contains(c)) {
selectline.getCharsData().add(c);
}
break;
} else {
selectline.getCharsData().add(c);
}
}
}
mSelectLines.add(selectline);
if (Started && Ended) {
break;
}
}
}
}
public SelectMode getSelectMode() {
return selectMode;
}
public void setSelectMode(SelectMode mCurrentMode) {
this.selectMode = mCurrentMode;
}
private void drawSelectLines(Canvas canvas) {
drawOaleSeletLinesBg(canvas);
}
public void clearSelect() {
firstSelectTxtChar = null;
lastSelectTxtChar = null;
selectMode = SelectMode.Normal;
mSelectTextPath.reset();
invalidate();
}
//根据当前坐标返回文字
public TxtChar getCurrentTxtChar(float x, float y) {
return mPageLoader.detectPressTxtChar(x, y);
}
private void drawOaleSeletLinesBg(Canvas canvas) {// 绘制椭圆型的选中背景
for (TxtLine l : mSelectLines) {
if (l.getCharsData() != null && l.getCharsData().size() > 0) {
TxtChar fistchar = l.getCharsData().get(0);
TxtChar lastchar = l.getCharsData().get(l.getCharsData().size() - 1);
float fw = fistchar.getCharWidth();
float lw = lastchar.getCharWidth();
RectF rect = new RectF(fistchar.getTopLeftPosition().x, fistchar.getTopLeftPosition().y,
lastchar.getTopRightPosition().x, lastchar.getBottomRightPosition().y);
canvas.drawRoundRect(rect, fw / 2,
textHeight / 2, mTextSelectPaint);
}
}
}
public TxtChar getFirstSelectTxtChar() {
return firstSelectTxtChar;
}
public void setFirstSelectTxtChar(TxtChar firstSelectTxtChar) {
this.firstSelectTxtChar = firstSelectTxtChar;
}
public TxtChar getLastSelectTxtChar() {
return lastSelectTxtChar;
}
public void setLastSelectTxtChar(TxtChar lastSelectTxtChar) {
this.lastSelectTxtChar = lastSelectTxtChar;
}
@Override
public void computeScroll() {
//进行滑动
if (mPageAnim != null) {
mPageAnim.scrollAnim();
}
super.computeScroll();
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (mPageAnim == null) return true;
if (mPageLoader == null) return true;
Paint.FontMetrics fontMetrics = mPageLoader.mTextPaint.getFontMetrics();
textHeight = Math.abs(fontMetrics.ascent) + Math.abs(fontMetrics.descent);
if (actionFromEdge) {
if (event.getAction() == MotionEvent.ACTION_UP)
actionFromEdge = false;
return true;
}
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPageAnim.initTouch(x, y);
if (event.getEdgeFlags() != 0 || event.getRawY() < ScreenUtils.dpToPx(5) || event.getRawY() > getDisplayMetrics().heightPixels - ScreenUtils.dpToPx(5)) {
actionFromEdge = true;
return true;
}
mStartX = x;
mStartY = y;
isMove = false;
//
if (readBookControl.isCanSelectText() && mPageLoader.getPageStatus() == TxtChapter.Status.FINISH) {
postDelayed(mLongPressRunnable, LONG_PRESS_TIMEOUT);
}
//
isLongPress = false;
mTouchListener.onTouch();
mPageAnim.onTouchEvent(event);
selectMode = SelectMode.Normal;
mTouchListener.onTouchClearCursor();
break;
case MotionEvent.ACTION_MOVE:
mPageAnim.initTouch(x, y);
// 判断是否大于最小滑动值。
int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
if (!isMove) {
isMove = Math.abs(mStartX - event.getX()) > slop || Math.abs(mStartY - event.getY()) > slop;
}
// 如果滑动了,且不是长按,则进行翻页。
if (isMove) {
if (readBookControl.isCanSelectText()) {
removeCallbacks(mLongPressRunnable);
}
mPageAnim.onTouchEvent(event);
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mPageAnim.initTouch(x, y);
mPageAnim.setTouchInitFalse();
if (!isMove) {
if (readBookControl.isCanSelectText()) {
removeCallbacks(mLongPressRunnable);
}
//是否点击了中间
if (mCenterRect.contains(x, y)) {
if (firstSelectTxtChar == null) {
if (mTouchListener != null) {
mTouchListener.center();
}
} else {
if (mSelectTextPath != null) {//长安选择删除选中状态
if (!isLongPress) {
firstSelectTxtChar = null;
mSelectTextPath.reset();
invalidate();
}
}
//清除移动选择状态
}
return true;
}
if (!readBookControl.getCanClickTurn()) {
return true;
}
if (mPageAnim instanceof ScrollPageAnim && readBookControl.disableScrollClickTurn()) {
return true;
}
}
if (firstSelectTxtChar == null || isMove) {//长安选择删除选中状态
mPageAnim.onTouchEvent(event);
} else {
if (!isLongPress) {
//释放了
if (LONG_PRESS_TIMEOUT != 0) {
removeCallbacks(mLongPressRunnable);
}
firstSelectTxtChar = null;
mSelectTextPath.reset();
invalidate();
}
}
break;
}
return true;
}
/**
* 判断是否存在上一页
*/
private boolean hasPrevPage() {
if (mPageLoader.hasPrev()) {
return true;
} else {
showSnackBar("已经是第一页了");
return false;
}
}
/**
* 判断是否下一页存在
*/
private boolean hasNextPage(int pageOnCur) {
if (mPageLoader.hasNext(pageOnCur)) {
return true;
} else {
showSnackBar("已经是最后一页了");
return false;
}
}
public boolean isRunning() {
return mPageAnim != null && mPageAnim.isRunning();
}
public boolean isPrepare() {
return isPrepare;
}
public void setTouchListener(TouchListener mTouchListener) {
this.mTouchListener = mTouchListener;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mPageAnim != null) {
mPageAnim.abortAnim();
mPageAnim.clear();
}
mPageLoader = null;
mPageAnim = null;
}
@Override
public void resetScroll() {
mPageLoader.resetPageOffset();
}
@Override
public boolean hasPrev() {
return PageView.this.hasPrevPage();
}
@Override
public boolean hasNext(int pageOnCur) {
return PageView.this.hasNextPage(pageOnCur);
}
@Override
public void changePage(PageAnimation.Direction direction) {
mPageLoader.pagingEnd(direction);
}
/**
* 获取 PageLoader
*/
public PageLoader getPageLoader(ReadActivity activity, Book bookShelfBean, PageLoader.Callback callback) {
this.activity = activity;
this.statusBarHeight = ImmersionBar.getStatusBarHeight(activity);
// 判是否已经存在
if (mPageLoader != null) {
return mPageLoader;
}
// 根据书籍类型,获取具体的加载器
//if (!Objects.equals(bookShelfBean.getTag(), BookShelfBean.LOCAL_TAG)) {
if (!"本地书籍".equals(bookShelfBean.getType())) {
mPageLoader = new PageLoaderNet(this, bookShelfBean, callback);
} else {
/*String fileSuffix = FileUtils.getFileSuffix(bookShelfBean.getChapterUrl());
if (fileSuffix.equalsIgnoreCase(FileUtils.SUFFIX_EPUB)) {
mPageLoader = new PageLoaderEpub(this, bookShelfBean, callback);
} else {
mPageLoader = new PageLoaderText(this, bookShelfBean, callback);
}*/
}
// 判断是否 PageView 已经初始化完成
if (mViewWidth != 0 || mViewHeight != 0) {
// 初始化 PageLoader 的屏幕大小
mPageLoader.prepareDisplay(mViewWidth, mViewHeight);
}
return mPageLoader;
}
public void autoChangeSource() {
mPageLoader.setStatus(TxtChapter.Status.CHANGE_SOURCE);
//activity.autoChangeSource();
}
/**
* 显示tips
*
* @param msg
*/
public void showSnackBar(String msg) {
SnackbarUtils.show(this, msg);
}
public enum SelectMode {
Normal, PressSelectText, SelectMoveForward, SelectMoveBack
}
public interface TouchListener {
void onTouch();
void onTouchClearCursor();
void onLongPress();
void center();
}
}

@ -1,57 +0,0 @@
package xyz.fycz.myreader.widget.page2
import java.util.*
import kotlin.math.max
import kotlin.math.min
/**
* 章节
*/
class TxtChapter internal constructor(val position: Int) {
val txtPageList = ArrayList<TxtPage>()
val txtPageLengthList = ArrayList<Int>()
val paragraphLengthList = ArrayList<Int>()
var status = Status.LOADING
var msg: String? = null
val pageSize: Int
get() = txtPageList.size
fun addPage(txtPage: TxtPage) {
txtPageList.add(txtPage)
}
fun getPage(page: Int): TxtPage? {
return if (txtPageList.isNotEmpty()) {
txtPageList[max(0, min(page, txtPageList.size - 1))]
} else null
}
fun getPageLength(position: Int): Int {
return if (position >= 0 && position < txtPageLengthList.size) {
txtPageLengthList[position]
} else -1
}
fun addTxtPageLength(length: Int) {
txtPageLengthList.add(length)
}
fun addParagraphLength(length: Int) {
paragraphLengthList.add(length)
}
fun getParagraphIndex(length: Int): Int {
for (i in paragraphLengthList.indices) {
if ((i == 0 || paragraphLengthList[i - 1] < length) && length <= paragraphLengthList[i]) {
return i
}
}
return -1
}
enum class Status {
LOADING, FINISH, ERROR, EMPTY, CATEGORY_EMPTY, CHANGE_SOURCE
}
}

@ -1,26 +0,0 @@
package xyz.fycz.myreader.widget.page2
import android.graphics.Point
class TxtChar {
var chardata: Char = ' '//字符数据
var selected: Boolean? = false//当前字符是否被选中
//记录文字的左上右上左下右下四个点坐标
var topLeftPosition: Point? = null//左上
var topRightPosition: Point? = null//右上
var bottomLeftPosition: Point? = null//左下
var bottomRightPosition: Point? = null//右下
var charWidth = 0f//字符宽度
var Index = 0//当前字符位置
override fun toString(): String {
return ("ShowChar [chardata=" + chardata + ", Selected=" + selected + ", TopLeftPosition=" + topLeftPosition
+ ", TopRightPosition=" + topRightPosition + ", BottomLeftPosition=" + bottomLeftPosition
+ ", BottomRightPosition=" + bottomRightPosition + ", charWidth=" + charWidth + ", Index=" + Index
+ "]");
}
}

@ -1,23 +0,0 @@
package xyz.fycz.myreader.widget.page2
class TxtLine {
var charsData: List<TxtChar>? = null
fun getLineData(): String {
var linedata = ""
if (charsData == null) return linedata
charsData?.let {
if (it.isEmpty()) return linedata
for (c in it) {
linedata += c.chardata
}
}
return linedata
}
override fun toString(): String {
return "ShowLine [Linedata=" + getLineData() + "]"
}
}

@ -1,44 +0,0 @@
package xyz.fycz.myreader.widget.page2
import java.util.*
/**
* 页面
*/
class TxtPage(val position: Int) {
var title: String? = null
var titleLines: Int = 0 //当前 lines 中为 title 的行数。
private val lines = ArrayList<String>()
//存放每个字的位置
var txtLists: List<TxtLine>? = null
val content: String
get() {
val s = StringBuilder()
for (i in lines.indices) {
s.append(lines[i])
}
return s.toString()
}
fun addLine(line: String) {
lines.add(line)
}
fun addLines(lines: List<String>) {
this.lines.addAll(lines)
}
fun getLine(i: Int): String {
return lines[i]
}
fun getLines(): List<String> {
return lines
}
fun size(): Int {
return lines.size
}
}

@ -1,87 +0,0 @@
package xyz.fycz.myreader.widget.page2.animation;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.view.View;
/**
* 覆盖翻页
*/
public class CoverPageAnim extends HorizonPageAnim {
private Rect mSrcRect, mDestRect;
private GradientDrawable mBackShadowDrawableLR;
public CoverPageAnim(int w, int h, View view, OnPageChangeListener listener) {
super(w, h, view, listener);
mSrcRect = new Rect(0, 0, mViewWidth, mViewHeight);
mDestRect = new Rect(0, 0, mViewWidth, mViewHeight);
int[] mBackShadowColors = new int[]{0x66000000, 0x00000000};
mBackShadowDrawableLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, mBackShadowColors);
mBackShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);
}
@Override
public void drawMove(Canvas canvas) {
int dis;
if (mDirection == Direction.NEXT) {
dis = (int) (mViewWidth - mStartX + mTouchX);
if (dis > mViewWidth) {
dis = mViewWidth;
}
//计算bitmap截取的区域
mSrcRect.left = mViewWidth - dis;
//计算bitmap在canvas显示的区域
mDestRect.right = dis;
canvas.drawBitmap(bitmapList.get(2), 0, 0, null);
canvas.drawBitmap(bitmapList.get(1), mSrcRect, mDestRect, null);
addShadow(dis, canvas);
} else {
dis = (int) (mTouchX - mStartX);
if (dis > mViewWidth) {
dis = mViewWidth;
}
mSrcRect.left = mViewWidth - dis;
mDestRect.right = dis;
canvas.drawBitmap(bitmapList.get(1), 0, 0, null);
canvas.drawBitmap(bitmapList.get(0), mSrcRect, mDestRect, null);
addShadow(dis, canvas);
}
}
//添加阴影
private void addShadow(int left, Canvas canvas) {
mBackShadowDrawableLR.setBounds(left, 0, left + 30, mScreenHeight);
mBackShadowDrawableLR.draw(canvas);
}
@Override
public void startAnim() {
int dx;
if (mDirection == Direction.NEXT) {
if (isCancel) {
int dis = (int) ((mViewWidth - mStartX) + mTouchX);
if (dis > mViewWidth) {
dis = mViewWidth;
}
dx = mViewWidth - dis;
} else {
dx = (int) -(mTouchX + (mViewWidth - mStartX));
}
} else {
if (isCancel) {
dx = (int) -(mTouchX - mStartX);
} else {
dx = (int) (mViewWidth - (mTouchX - mStartX));
}
}
//滑动速度保持一致
int duration = (animationSpeed * Math.abs(dx)) / mViewWidth;
mScroller.startScroll((int) mTouchX, 0, dx, 0, duration);
super.startAnim();
}
}

@ -1,190 +0,0 @@
package xyz.fycz.myreader.widget.page2.animation;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 横向动画的模板
*/
public abstract class HorizonPageAnim extends PageAnimation {
private static final String TAG = "HorizonPageAnim";
List<Bitmap> bitmapList = new ArrayList<>();
HorizonPageAnim(int w, int h, View view, OnPageChangeListener listener) {
super(w, h, view, listener);
//创建图片
for (int i = 0; i < 3; i++) {
bitmapList.add(Bitmap.createBitmap(mViewWidth, mViewHeight, Bitmap.Config.ARGB_8888));
}
}
/**
* 转换页面在显示下一章的时候必须首先调用此方法
*/
@Override
public boolean changePage() {
if (isCancel) return false;
switch (mDirection) {
case NEXT:
Collections.swap(bitmapList, 0, 1);
Collections.swap(bitmapList, 1, 2);
break;
case PREV:
Collections.swap(bitmapList, 1, 2);
Collections.swap(bitmapList, 0, 1);
break;
default:
return false;
}
return true;
}
public abstract void drawMove(Canvas canvas);
@Override
public void onTouchEvent(MotionEvent event) {
abortAnim();
final int slop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
//获取点击位置
int x = (int) event.getX();
int y = (int) event.getY();
//设置触摸点
setTouchPoint(x, y);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
//判断是否移动了
if (!isMove) {
isMove = Math.abs(mStartX - x) > slop || Math.abs(mStartY - y) > slop;
}
if (isMove) {
//判断是否是准备移动的状态(将要移动但是还没有移动)
if (mMoveX == 0 && mMoveY == 0) {
//判断翻得是上一页还是下一页
if (x - mStartX > 0) {
//上一页的参数配置
isNext = false;
boolean hasPrev = mListener.hasPrev();
setDirection(Direction.PREV);
//如果上一页不存在
if (!hasPrev) {
noNext = true;
return;
}
} else {
//进行下一页的配置
isNext = true;
//判断是否下一页存在
boolean hasNext = mListener.hasNext(0);
//如果存在设置动画方向
setDirection(Direction.NEXT);
//如果不存在表示没有下一页了
if (!hasNext) {
noNext = true;
return;
}
}
} else {
//判断是否取消翻页
isCancel = isNext ? x - mMoveX > 0 : x - mMoveX < 0;
}
mMoveX = x;
mMoveY = y;
isRunning = true;
mView.invalidate();
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
isRunning = false;
if (!isMove) {
if (!readBookControl.getCanClickTurn()) {
return;
}
isNext = x > mScreenWidth / 2 || readBookControl.getClickAllNext();
if (isNext) {
//判断是否下一页存在
boolean hasNext = mListener.hasNext(0);
//设置动画方向
if (!hasNext) {
return;
}
setDirection(Direction.NEXT);
} else {
boolean hasPrev = mListener.hasPrev();
if (!hasPrev) {
return;
}
setDirection(Direction.PREV);
}
} else {
isCancel = Math.abs(mLastX - mStartX) < slop * 3 || isCancel;
}
// 开启翻页效果
if (!noNext) {
startAnim();
}
mView.invalidate();
break;
}
}
@Override
public void draw(Canvas canvas) {
if (isRunning && !noNext) {
drawMove(canvas);
} else {
canvas.drawBitmap(getBgBitmap(0), 0, 0, null);
isCancel = true;
}
}
@Override
public void abortAnim() {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
if (changePage()) {
mListener.changePage(mDirection);
setDirection(PageAnimation.Direction.NONE);
}
movingFinish();
setTouchPoint(mScroller.getFinalX(), mScroller.getFinalY());
mView.invalidate();
}
}
@Override
public Bitmap getBgBitmap(int pageOnCur) {
if (pageOnCur < 0) {
return bitmapList.get(0);
} else if (pageOnCur > 0) {
return bitmapList.get(2);
}
return bitmapList.get(1);
}
public void setCancel(boolean cancel) {
isCancel = cancel;
}
public void setNoNext(boolean noNext) {
this.noNext = noNext;
}
}

@ -1,26 +0,0 @@
package xyz.fycz.myreader.widget.page2.animation;
import android.graphics.Canvas;
import android.view.View;
/**
* 无动画翻页
*/
public class NonePageAnim extends HorizonPageAnim {
public NonePageAnim(int w, int h, View view, OnPageChangeListener listener) {
super(w, h, view, listener);
}
@Override
public void drawMove(Canvas canvas) {
canvas.drawBitmap(bitmapList.get(1), 0, 0, null);
}
@Override
public void startAnim() {
super.startAnim();
isRunning = false;
}
}

@ -1,273 +0,0 @@
package xyz.fycz.myreader.widget.page2.animation;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.Scroller;
import androidx.annotation.NonNull;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.entity.ReadBookControl;
/**
* 翻页动画抽象类
*/
public abstract class PageAnimation {
//动画速度
static final int animationSpeed = 300;
//正在使用的View
protected View mView;
protected ReadBookControl readBookControl = ReadBookControl.getInstance();
//滑动装置
Scroller mScroller;
//监听器
protected OnPageChangeListener mListener;
//移动方向
Direction mDirection = Direction.NONE;
//屏幕的尺寸
int mScreenWidth;
int mScreenHeight;
int mMarginTop;
//视图的尺寸
int mViewWidth;
int mViewHeight;
//起始点
float mStartX;
float mStartY;
//触碰点
float mTouchX;
float mTouchY;
//上一个触碰点
float mLastX;
float mLastY;
private boolean isMoving = false;
boolean isRunning = false;
private boolean touchInit = false;
//是否取消翻页
boolean isCancel = false;
//可以使用 mLast代替
int mMoveX = 0;
int mMoveY = 0;
//是否移动了
boolean isMove = false;
//是否翻阅下一页。true表示翻到下一页,false表示上一页。
boolean isNext = false;
//是否没下一页或者上一页
boolean noNext = false;
PageAnimation(int w, int h, View view, OnPageChangeListener listener) {
this(w, h, 0, 0, 0, view, listener);
}
PageAnimation(int w, int h, int marginWidth, int marginTop, int marginBottom, View view, OnPageChangeListener listener) {
mScreenWidth = w;
mScreenHeight = h;
//屏幕的间距
mMarginTop = marginTop;
mViewWidth = mScreenWidth - marginWidth * 2;
mViewHeight = mScreenHeight - mMarginTop - marginBottom;
mView = view;
mListener = listener;
mScroller = new Scroller(mView.getContext(), new LinearInterpolator());
}
public Scroller getScroller() {
return mScroller;
}
public void setStartPoint(float x, float y) {
mStartX = x;
mStartY = y;
mLastX = mStartX;
mLastY = mStartY;
}
public void setTouchPoint(float x, float y) {
mLastX = mTouchX;
mLastY = mTouchY;
mTouchX = x;
mTouchY = y;
}
public void setTouchInitFalse() {
touchInit = false;
}
public void initTouch(int x, int y) {
if (!touchInit) {
//移动的点击位置
mMoveX = 0;
mMoveY = 0;
//是否移动
isMove = false;
//是否存在下一章
noNext = false;
//是下一章还是前一章
isNext = false;
//是否正在执行动画
isRunning = false;
//取消
isCancel = false;
//设置起始位置的触摸点
setStartPoint(x, y);
touchInit = true;
}
}
public boolean isRunning() {
return isRunning;
}
void movingFinish() {
isMoving = false;
isRunning = false;
}
/**
* 开启翻页动画
*/
public void startAnim() {
isRunning = true;
isMoving = true;
mView.invalidate();
}
public void setDirection(Direction direction) {
mDirection = direction;
}
public void clear() {
mView = null;
}
/**
* 点击事件的处理
*/
public abstract void onTouchEvent(MotionEvent event);
/**
* 绘制图形
*/
public abstract void draw(Canvas canvas);
/**
* 滚动动画
* 必须放在computeScroll()方法中执行
*/
public void scrollAnim() {
if (mScroller.computeScrollOffset()) {
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
setTouchPoint(x, y);
mView.postInvalidate();
} else if (isMoving) {
if (changePage()) {
mListener.changePage(mDirection);
setDirection(Direction.NONE);
}
movingFinish();
}
}
/**
* 取消动画
*/
public abstract void abortAnim();
public abstract boolean changePage();
/**
* 获取背景板
* pageOnCur: 位于当前页的位置, 小于0上一页, 0 当前页, 大于0下一页
*/
public abstract Bitmap getBgBitmap(int pageOnCur);
/**
* 翻页模式
*/
public enum Mode {
COVER(MyApplication.getApplication().getString(R.string.page_mode_COVER)),
SIMULATION(MyApplication.getApplication().getString(R.string.page_mode_SIMULATION)),
SLIDE(MyApplication.getApplication().getString(R.string.page_mode_SLIDE)),
SCROLL(MyApplication.getApplication().getString(R.string.page_mode_SCROLL)),
NONE(MyApplication.getApplication().getString(R.string.page_mode_NONE));
private String name;
Mode(String name) {
this.name = name;
}
public static Mode getPageMode(int pageMode) {
switch (pageMode) {
case 0:
return COVER;
case 1:
return SIMULATION;
case 2:
return SLIDE;
case 3:
return SCROLL;
case 4:
return NONE;
default:
return COVER;
}
}
public static String[] getAllPageMode() {
return new String[]{COVER.name, SIMULATION.name, SLIDE.name, SCROLL.name, NONE.name};
}
@NonNull
@Override
public String toString() {
return this.name;
}
}
/**
* 翻页方向
*/
public enum Direction {
NONE(true), NEXT(true), PREV(true);
public final boolean isHorizontal;
Direction(boolean isHorizontal) {
this.isHorizontal = isHorizontal;
}
}
public interface OnPageChangeListener {
void resetScroll();
boolean hasPrev();
boolean hasNext(int pageOnCur);
void drawContent(Canvas canvas, float offset);
void drawBackground(Canvas canvas);
void changePage(Direction direction);
}
}

@ -1,148 +0,0 @@
package xyz.fycz.myreader.widget.page2.animation;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
public class ScrollPageAnim extends PageAnimation {
private static final String TAG = "ScrollAnimation";
// 滑动追踪的时间
private static final int VELOCITY_DURATION = 1000;
private VelocityTracker mVelocity;
// 整个Bitmap的背景显示
private Bitmap mBgBitmap;
public ScrollPageAnim(int w, int h, int marginWidth, int marginTop, int marginBottom, View view, OnPageChangeListener listener) {
super(w, h, marginWidth, marginTop, marginBottom, view, listener);
mListener.resetScroll();
mBgBitmap = Bitmap.createBitmap(mScreenWidth, mScreenHeight, Bitmap.Config.ARGB_8888);
}
@Override
public void onTouchEvent(MotionEvent event) {
final int slop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
int x = (int) event.getX();
int y = (int) event.getY();
// 初始化速度追踪器
if (mVelocity == null) {
mVelocity = VelocityTracker.obtain();
}
mVelocity.addMovement(event);
// 设置触碰点
setTouchPoint(x, y);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isMove = false;
isRunning = false;
// 设置起始点
setStartPoint(x, y);
// 停止动画
abortAnim();
break;
case MotionEvent.ACTION_MOVE:
if (!isMove) {
isMove = Math.abs(mStartX - x) > slop || Math.abs(mStartY - y) > slop;
}
mVelocity.computeCurrentVelocity(VELOCITY_DURATION);
isRunning = true;
// 进行刷新
mView.postInvalidate();
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
isRunning = false;
if (!isMove) {
if (!readBookControl.getCanClickTurn() || readBookControl.disableScrollClickTurn()) {
return;
}
//是否翻阅下一页。true表示翻到下一页,false表示上一页。
boolean isNext = x > mScreenWidth / 2 || readBookControl.getClickAllNext();
if (isNext) {
startAnim(Direction.NEXT);
} else {
startAnim(Direction.PREV);
}
} else {
// 开启动画
startAnim();
}
// 删除检测器
if (mVelocity != null) {
mVelocity.recycle();
mVelocity = null;
}
break;
}
}
@Override
public void draw(Canvas canvas) {
//进行布局
float offset = mLastY - mTouchY;
//绘制背景
mListener.drawBackground(canvas);
//绘制内容
canvas.save();
//移动位置
canvas.translate(0, mMarginTop);
//裁剪显示区域
canvas.clipRect(0, 0, mViewWidth, mViewHeight);
mListener.drawContent(canvas, offset);
canvas.restore();
}
@Override
public synchronized void startAnim() {
super.startAnim();
//惯性滚动
mScroller.fling(0, (int) mTouchY, 0, (int) mVelocity.getYVelocity(),
0, 0, -10 * mViewHeight, 10 * mViewHeight);
}
/**
* 翻页动画
*/
public void startAnim(Direction direction) {
setStartPoint(0, 0);
setTouchPoint(0, 0);
switch (direction) {
case NEXT:
super.startAnim();
mScroller.startScroll(0, 0, 0, -mViewHeight + 300, animationSpeed);
break;
case PREV:
super.startAnim();
mScroller.startScroll(0, 0, 0, mViewHeight - 300, animationSpeed);
break;
}
}
@Override
public void abortAnim() {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
isRunning = false;
}
}
@Override
public boolean changePage() {
return false;
}
@Override
public Bitmap getBgBitmap(int pageOnCur) {
return mBgBitmap;
}
}

@ -1,569 +0,0 @@
package xyz.fycz.myreader.widget.page2.animation;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Region;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.view.View;
/**
* 仿真翻页
*/
public class SimulationPageAnim extends HorizonPageAnim {
private static final String TAG = "SimulationPageAnim";
private int mCornerX = 1; // 拖拽点对应的页脚
private int mCornerY = 1;
private Path mPath0;
private Path mPath1;
private PointF mBezierStart1 = new PointF(); // 贝塞尔曲线起始点
private PointF mBezierControl1 = new PointF(); // 贝塞尔曲线控制点
private PointF mBezierVertex1 = new PointF(); // 贝塞尔曲线顶点
private PointF mBezierEnd1 = new PointF(); // 贝塞尔曲线结束点
private PointF mBezierStart2 = new PointF(); // 另一条贝塞尔曲线
private PointF mBezierControl2 = new PointF();
private PointF mBezierVertex2 = new PointF();
private PointF mBezierEnd2 = new PointF();
private float mMiddleX;
private float mMiddleY;
private float mDegrees;
private float mTouchToCornerDis;
private ColorMatrixColorFilter mColorMatrixFilter;
private Matrix mMatrix;
private float[] mMatrixArray = {0, 0, 0, 0, 0, 0, 0, 0, 1.0f};
private boolean mIsRT_LB; // 是否属于右上左下
private float mMaxLength;
private int[] mBackShadowColors;// 背面颜色组
private int[] mFrontShadowColors;// 前面颜色组
private GradientDrawable mBackShadowDrawableLR; // 有阴影的GradientDrawable
private GradientDrawable mBackShadowDrawableRL;
private GradientDrawable mFolderShadowDrawableLR;
private GradientDrawable mFolderShadowDrawableRL;
private GradientDrawable mFrontShadowDrawableHBT;
private GradientDrawable mFrontShadowDrawableHTB;
private GradientDrawable mFrontShadowDrawableVLR;
private GradientDrawable mFrontShadowDrawableVRL;
private Paint mPaint;
public SimulationPageAnim(int w, int h, View view, OnPageChangeListener listener) {
super(w, h, view, listener);
mPath0 = new Path();
mPath1 = new Path();
mMaxLength = (float) Math.hypot(mScreenWidth, mScreenHeight);
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
createDrawable();
ColorMatrix cm = new ColorMatrix();//设置颜色数组
float[] array = {1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0};
cm.set(array);
mColorMatrixFilter = new ColorMatrixColorFilter(cm);
mMatrix = new Matrix();
mTouchX = 0.01f; // 不让x,y为0,否则在点计算时会有问题
mTouchY = 0.01f;
}
@Override
public void setStartPoint(float x, float y) {
super.setStartPoint(x, y);
calcCornerXY(x, y);
}
@Override
public void setTouchPoint(float x, float y) {
super.setTouchPoint(x, y);
//触摸y中间位置吧y变成屏幕高度
if ((mStartY > mScreenHeight / 3.0 && mStartY < mScreenHeight * 2 / 3.0) || mDirection.equals(Direction.PREV)) {
mTouchY = mScreenHeight;
}
if (mStartY > mScreenHeight / 3.0 && mStartY < mScreenHeight / 2.0 && mDirection.equals(Direction.NEXT)) {
mTouchY = 1;
}
}
@Override
public void setDirection(Direction direction) {
super.setDirection(direction);
switch (direction) {
case PREV:
//上一页滑动不出现对角
if (mStartX > mScreenWidth / 2.0) {
calcCornerXY(mStartX, mScreenHeight);
} else {
calcCornerXY(mScreenWidth - mStartX, mScreenHeight);
}
break;
case NEXT:
if (mScreenWidth / 2.0 > mStartX) {
calcCornerXY(mScreenWidth - mStartX, mStartY);
}
break;
}
}
@Override
public void startAnim() {
int dx, dy;
// dx 水平方向滑动的距离,负值会使滚动向左滚动
// dy 垂直方向滑动的距离,负值会使滚动向上滚动
if (isCancel) {
if (mCornerX > 0 && mDirection.equals(Direction.NEXT)) {
dx = (int) (mScreenWidth - mTouchX);
} else {
dx = -(int) mTouchX;
}
if (!mDirection.equals(Direction.NEXT)) {
dx = (int) -(mScreenWidth + mTouchX);
}
if (mCornerY > 0) {
dy = (int) (mScreenHeight - mTouchY);
} else {
dy = -(int) mTouchY; // 防止mTouchY最终变为0
}
} else {
if (mCornerX > 0 && mDirection.equals(Direction.NEXT)) {
dx = -(int) (mScreenWidth + mTouchX);
} else {
dx = (int) (mScreenWidth - mTouchX + mScreenWidth);
}
if (mCornerY > 0) {
dy = (int) (mScreenHeight - mTouchY);
} else {
dy = (int) (1 - mTouchY); // 防止mTouchY最终变为0
}
}
mScroller.startScroll((int) mTouchX, (int) mTouchY, dx, dy, animationSpeed);
super.startAnim();
}
@Override
public void drawMove(Canvas canvas) {
if (mDirection == Direction.NEXT) {
calcPoints();
drawCurrentPageArea(canvas, bitmapList.get(1));//绘制翻页时的正面页
drawNextPageAreaAndShadow(canvas, bitmapList.get(2));
drawCurrentPageShadow(canvas);
drawCurrentBackArea(canvas, bitmapList.get(1));
} else {
calcPoints();
drawCurrentPageArea(canvas, bitmapList.get(0));
drawNextPageAreaAndShadow(canvas, bitmapList.get(1));
drawCurrentPageShadow(canvas);
drawCurrentBackArea(canvas, bitmapList.get(0));
}
}
/**
* 创建阴影的GradientDrawable
*/
private void createDrawable() {
int[] color = {0x333333, 0xb0333333};
mFolderShadowDrawableRL = new GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, color);
mFolderShadowDrawableRL
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFolderShadowDrawableLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, color);
mFolderShadowDrawableLR
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mBackShadowColors = new int[]{0xff111111, 0x111111};
mBackShadowDrawableRL = new GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, mBackShadowColors);
mBackShadowDrawableRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mBackShadowDrawableLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, mBackShadowColors);
mBackShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFrontShadowColors = new int[]{0x80111111, 0x111111};
mFrontShadowDrawableVLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, mFrontShadowColors);
mFrontShadowDrawableVLR
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFrontShadowDrawableVRL = new GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, mFrontShadowColors);
mFrontShadowDrawableVRL
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFrontShadowDrawableHTB = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, mFrontShadowColors);
mFrontShadowDrawableHTB
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFrontShadowDrawableHBT = new GradientDrawable(
GradientDrawable.Orientation.BOTTOM_TOP, mFrontShadowColors);
mFrontShadowDrawableHBT
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
}
/**
* 绘制翻起页背面
*/
private void drawCurrentBackArea(Canvas canvas, Bitmap bitmap) {
int i = (int) (mBezierStart1.x + mBezierControl1.x) / 2;
float f1 = Math.abs(i - mBezierControl1.x);
int i1 = (int) (mBezierStart2.y + mBezierControl2.y) / 2;
float f2 = Math.abs(i1 - mBezierControl2.y);
float f3 = Math.min(f1, f2);
mPath1.reset();
mPath1.moveTo(mBezierVertex2.x, mBezierVertex2.y);
mPath1.lineTo(mBezierVertex1.x, mBezierVertex1.y);
mPath1.lineTo(mBezierEnd1.x, mBezierEnd1.y);
mPath1.lineTo(mTouchX, mTouchY);
mPath1.lineTo(mBezierEnd2.x, mBezierEnd2.y);
mPath1.close();
GradientDrawable mFolderShadowDrawable;
int left;
int right;
if (mIsRT_LB) {
left = (int) (mBezierStart1.x - 1);
right = (int) (mBezierStart1.x + f3 + 1);
mFolderShadowDrawable = mFolderShadowDrawableLR;
} else {
left = (int) (mBezierStart1.x - f3 - 1);
right = (int) (mBezierStart1.x + 1);
mFolderShadowDrawable = mFolderShadowDrawableRL;
}
canvas.save();
try {
canvas.clipPath(mPath0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas.clipPath(mPath1);
} else {
canvas.clipPath(mPath1, Region.Op.INTERSECT);
}
} catch (Exception ignored) {
}
mPaint.setColorFilter(mColorMatrixFilter);
float dis = (float) Math.hypot(mCornerX - mBezierControl1.x,
mBezierControl2.y - mCornerY);
float f8 = (mCornerX - mBezierControl1.x) / dis;
float f9 = (mBezierControl2.y - mCornerY) / dis;
mMatrixArray[0] = 1 - 2 * f9 * f9;
mMatrixArray[1] = 2 * f8 * f9;
mMatrixArray[3] = mMatrixArray[1];
mMatrixArray[4] = 1 - 2 * f8 * f8;
mMatrix.reset();
mMatrix.setValues(mMatrixArray);
mMatrix.preTranslate(-mBezierControl1.x, -mBezierControl1.y);
mMatrix.postTranslate(mBezierControl1.x, mBezierControl1.y);
canvas.drawBitmap(bitmap, mMatrix, mPaint);
mPaint.setColorFilter(null);
canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
mFolderShadowDrawable.setBounds(
left, (int) mBezierStart1.y,
right, (int) (mBezierStart1.y + mMaxLength)
);
mFolderShadowDrawable.draw(canvas);
canvas.restore();
}
/**
* 绘制翻起页的阴影
*/
private void drawCurrentPageShadow(Canvas canvas) {
double degree;
if (mIsRT_LB) {
degree = Math.PI / 4 - Math.atan2(mBezierControl1.y - mTouchY, mTouchX - mBezierControl1.x);
} else {
degree = Math.PI / 4 - Math.atan2(mTouchY - mBezierControl1.y, mTouchX - mBezierControl1.x);
}
// 翻起页阴影顶点与touch点的距离
double d1 = (float) 25 * 1.414 * Math.cos(degree);
double d2 = (float) 25 * 1.414 * Math.sin(degree);
float x = (float) (mTouchX + d1);
float y;
if (mIsRT_LB) {
y = (float) (mTouchY + d2);
} else {
y = (float) (mTouchY - d2);
}
mPath1.reset();
mPath1.moveTo(x, y);
mPath1.lineTo(mTouchX, mTouchY);
mPath1.lineTo(mBezierControl1.x, mBezierControl1.y);
mPath1.lineTo(mBezierStart1.x, mBezierStart1.y);
mPath1.close();
canvas.save();
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas.clipOutPath(mPath0);
} else {
canvas.clipPath(mPath0, Region.Op.XOR);
}
canvas.clipPath(mPath1, Region.Op.INTERSECT);
} catch (Exception ignored) {
}
int leftx;
int rightx;
GradientDrawable mCurrentPageShadow;
if (mIsRT_LB) {
leftx = (int) (mBezierControl1.x);
rightx = (int) mBezierControl1.x + 25;
mCurrentPageShadow = mFrontShadowDrawableVLR;
} else {
leftx = (int) (mBezierControl1.x - 25);
rightx = (int) mBezierControl1.x + 1;
mCurrentPageShadow = mFrontShadowDrawableVRL;
}
float rotateDegrees;
rotateDegrees = (float) Math.toDegrees(Math.atan2(mTouchX - mBezierControl1.x, mBezierControl1.y - mTouchY));
canvas.rotate(rotateDegrees, mBezierControl1.x, mBezierControl1.y);
mCurrentPageShadow.setBounds(
leftx, (int) (mBezierControl1.y - mMaxLength),
rightx, (int) (mBezierControl1.y));
mCurrentPageShadow.draw(canvas);
canvas.restore();
mPath1.reset();
mPath1.moveTo(x, y);
mPath1.lineTo(mTouchX, mTouchY);
mPath1.lineTo(mBezierControl2.x, mBezierControl2.y);
mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);
mPath1.close();
canvas.save();
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas.clipOutPath(mPath0);
} else {
canvas.clipPath(mPath0, Region.Op.XOR);
}
canvas.clipPath(mPath1);
} catch (Exception ignored) {
}
if (mIsRT_LB) {
leftx = (int) (mBezierControl2.y);
rightx = (int) (mBezierControl2.y + 25);
mCurrentPageShadow = mFrontShadowDrawableHTB;
} else {
leftx = (int) (mBezierControl2.y - 25);
rightx = (int) (mBezierControl2.y + 1);
mCurrentPageShadow = mFrontShadowDrawableHBT;
}
rotateDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl2.y - mTouchY, mBezierControl2.x - mTouchX));
canvas.rotate(rotateDegrees, mBezierControl2.x, mBezierControl2.y);
float temp;
if (mBezierControl2.y < 0) temp = mBezierControl2.y - mScreenHeight;
else temp = mBezierControl2.y;
int hmg = (int) Math.hypot(mBezierControl2.x, temp);
if (hmg > mMaxLength)
mCurrentPageShadow.setBounds(
(int) (mBezierControl2.x - 25) - hmg, leftx,
(int) (mBezierControl2.x + mMaxLength) - hmg, rightx
);
else
mCurrentPageShadow.setBounds(
(int) (mBezierControl2.x - mMaxLength), leftx,
(int) (mBezierControl2.x), rightx);
mCurrentPageShadow.draw(canvas);
canvas.restore();
}
private void drawNextPageAreaAndShadow(Canvas canvas, Bitmap bitmap) {
mPath1.reset();
mPath1.moveTo(mBezierStart1.x, mBezierStart1.y);
mPath1.lineTo(mBezierVertex1.x, mBezierVertex1.y);
mPath1.lineTo(mBezierVertex2.x, mBezierVertex2.y);
mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);
mPath1.lineTo(mCornerX, mCornerY);
mPath1.close();
mDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl1.x - mCornerX,
mBezierControl2.y - mCornerY));
int leftx;
int rightx;
GradientDrawable mBackShadowDrawable;
if (mIsRT_LB) { //左下及右上
leftx = (int) (mBezierStart1.x);
rightx = (int) (mBezierStart1.x + mTouchToCornerDis / 4);
mBackShadowDrawable = mBackShadowDrawableLR;
} else {
leftx = (int) (mBezierStart1.x - mTouchToCornerDis / 4);
rightx = (int) mBezierStart1.x;
mBackShadowDrawable = mBackShadowDrawableRL;
}
canvas.save();
try {
canvas.clipPath(mPath0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas.clipPath(mPath1);
} else {
canvas.clipPath(mPath1, Region.Op.INTERSECT);
}
//canvas.clipPath(mPath1, Region.Op.INTERSECT);
} catch (Exception ignored) {
}
canvas.drawBitmap(bitmap, 0, 0, null);
canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
mBackShadowDrawable.setBounds(
leftx, (int) mBezierStart1.y,
rightx, (int) (mMaxLength + mBezierStart1.y));//左上及右下角的xy坐标值,构成一个矩形
mBackShadowDrawable.draw(canvas);
canvas.restore();
}
private void drawCurrentPageArea(Canvas canvas, Bitmap bitmap) {
mPath0.reset();
mPath0.moveTo(mBezierStart1.x, mBezierStart1.y);
mPath0.quadTo(mBezierControl1.x, mBezierControl1.y, mBezierEnd1.x, mBezierEnd1.y);
mPath0.lineTo(mTouchX, mTouchY);
mPath0.lineTo(mBezierEnd2.x, mBezierEnd2.y);
mPath0.quadTo(mBezierControl2.x, mBezierControl2.y, mBezierStart2.x, mBezierStart2.y);
mPath0.lineTo(mCornerX, mCornerY);
mPath0.close();
canvas.save();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas.clipOutPath(mPath0);
} else {
canvas.clipPath(mPath0, Region.Op.XOR);
}
canvas.drawBitmap(bitmap, 0, 0, null);
try {
canvas.restore();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 计算拖拽点对应的拖拽脚
*/
private void calcCornerXY(float x, float y) {
if (x <= mScreenWidth / 2.0) {
mCornerX = 0;
} else {
mCornerX = mScreenWidth;
}
if (y <= mScreenHeight / 2.0) {
mCornerY = 0;
} else {
mCornerY = mScreenHeight;
}
mIsRT_LB = (mCornerX == 0 && mCornerY == mScreenHeight)
|| (mCornerX == mScreenWidth && mCornerY == 0);
}
private void calcPoints() {
mMiddleX = (mTouchX + mCornerX) / 2;
mMiddleY = (mTouchY + mCornerY) / 2;
mBezierControl1.x =
mMiddleX - (mCornerY - mMiddleY) * (mCornerY - mMiddleY) / (mCornerX - mMiddleX);
mBezierControl1.y = mCornerY;
mBezierControl2.x = mCornerX;
if (mCornerY - mMiddleY == 0) {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / 0.1f;
} else {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
* (mCornerX - mMiddleX) / (mCornerY - mMiddleY);
}
mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2;
mBezierStart1.y = mCornerY;
// 固定左边上下两个点
if (mTouchX > 0 && mTouchX < mScreenWidth) {
if (mBezierStart1.x < 0 || mBezierStart1.x > mScreenWidth) {
if (mBezierStart1.x < 0)
mBezierStart1.x = mScreenWidth - mBezierStart1.x;
float f1 = Math.abs(mCornerX - mTouchX);
float f2 = mScreenWidth * f1 / mBezierStart1.x;
mTouchX = Math.abs(mCornerX - f2);
float f3 = Math.abs(mCornerX - mTouchX) * Math.abs(mCornerY - mTouchY) / f1;
mTouchY = Math.abs(mCornerY - f3);
mMiddleX = (mTouchX + mCornerX) / 2;
mMiddleY = (mTouchY + mCornerY) / 2;
mBezierControl1.x =
mMiddleX - (mCornerY - mMiddleY) * (mCornerY - mMiddleY) / (mCornerX - mMiddleX);
mBezierControl1.y = mCornerY;
mBezierControl2.x = mCornerX;
float f5 = mCornerY - mMiddleY;
if (f5 == 0) {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
* (mCornerX - mMiddleX) / 0.1f;
} else {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
* (mCornerX - mMiddleX) / (mCornerY - mMiddleY);
}
mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2;
}
}
mBezierStart2.x = mCornerX;
mBezierStart2.y = mBezierControl2.y - (mCornerY - mBezierControl2.y) / 2;
mTouchToCornerDis = (float) Math.hypot((mTouchX - mCornerX), (mTouchY - mCornerY));
mBezierEnd1 =
getCross(new PointF(mTouchX, mTouchY), mBezierControl1, mBezierStart1, mBezierStart2);
mBezierEnd2 =
getCross(new PointF(mTouchX, mTouchY), mBezierControl2, mBezierStart1, mBezierStart2);
mBezierVertex1.x = (mBezierStart1.x + 2 * mBezierControl1.x + mBezierEnd1.x) / 4;
mBezierVertex1.y = (2 * mBezierControl1.y + mBezierStart1.y + mBezierEnd1.y) / 4;
mBezierVertex2.x = (mBezierStart2.x + 2 * mBezierControl2.x + mBezierEnd2.x) / 4;
mBezierVertex2.y = (2 * mBezierControl2.y + mBezierStart2.y + mBezierEnd2.y) / 4;
}
/**
* 求解直线P1P2和直线P3P4的交点坐标
*/
private PointF getCross(PointF P1, PointF P2, PointF P3, PointF P4) {
PointF CrossP = new PointF();
// 二元函数通式: y=ax+b
float a1 = (P2.y - P1.y) / (P2.x - P1.x);
float b1 = ((P1.x * P2.y) - (P2.x * P1.y)) / (P1.x - P2.x);
float a2 = (P4.y - P3.y) / (P4.x - P3.x);
float b2 = ((P3.x * P4.y) - (P4.x * P3.y)) / (P3.x - P4.x);
CrossP.x = (b2 - b1) / (a1 - a2);
CrossP.y = a1 * CrossP.x + b1;
return CrossP;
}
}

@ -1,85 +0,0 @@
package xyz.fycz.myreader.widget.page2.animation;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.View;
/**
* 滑动翻页
*/
public class SlidePageAnim extends HorizonPageAnim {
private Rect mSrcRect, mDestRect, mNextSrcRect, mNextDestRect;
public SlidePageAnim(int w, int h, View view, OnPageChangeListener listener) {
super(w, h, view, listener);
mSrcRect = new Rect(0, 0, mViewWidth, mViewHeight);
mDestRect = new Rect(0, 0, mViewWidth, mViewHeight);
mNextSrcRect = new Rect(0, 0, mViewWidth, mViewHeight);
mNextDestRect = new Rect(0, 0, mViewWidth, mViewHeight);
}
@Override
public void drawMove(Canvas canvas) {
int dis;
if (mDirection == Direction.NEXT) {//左半边的剩余区域
dis = (int) (mScreenWidth - mStartX + mTouchX);
if (dis > mScreenWidth) {
dis = mScreenWidth;
}
//计算bitmap截取的区域
mSrcRect.left = mScreenWidth - dis;
//计算bitmap在canvas显示的区域
mDestRect.right = dis;
//计算下一页截取的区域
mNextSrcRect.right = mScreenWidth - dis;
//计算下一页在canvas显示的区域
mNextDestRect.left = dis;
canvas.drawBitmap(bitmapList.get(2), mNextSrcRect, mNextDestRect, null);
canvas.drawBitmap(bitmapList.get(1), mSrcRect, mDestRect, null);
} else {
dis = (int) (mTouchX - mStartX);
if (dis < 0) {
dis = 0;
mStartX = mTouchX;
}
mSrcRect.left = mScreenWidth - dis;
mDestRect.right = dis;
//计算下一页截取的区域
mNextSrcRect.right = mScreenWidth - dis;
//计算下一页在canvas显示的区域
mNextDestRect.left = dis;
canvas.drawBitmap(bitmapList.get(1), mNextSrcRect, mNextDestRect, null);
canvas.drawBitmap(bitmapList.get(0), mSrcRect, mDestRect, null);
}
}
@Override
public void startAnim() {
int dx;
if (mDirection == Direction.NEXT) {
if (isCancel) {
int dis = (int) ((mScreenWidth - mStartX) + mTouchX);
if (dis > mScreenWidth) {
dis = mScreenWidth;
}
dx = mScreenWidth - dis;
} else {
dx = (int) -(mTouchX + (mScreenWidth - mStartX));
}
} else {
if (isCancel) {
dx = (int) -Math.abs(mTouchX - mStartX);
} else {
dx = (int) (mScreenWidth - (mTouchX - mStartX));
}
}
//滑动速度保持一致
int duration = (animationSpeed * Math.abs(dx)) / mScreenWidth;
mScroller.startScroll((int) mTouchX, 0, dx, 0, duration);
super.startAnim();
}
}

@ -140,18 +140,7 @@
style="?android:attr/progressBarStyleSmall" style="?android:attr/progressBarStyleSmall"
/> />
<ImageView <View
android:id="@+id/iv_delete"
android:layout_width="38dp"
android:layout_height="38dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingBottom="10dp"
android:visibility="gone"
app:srcCompat="@mipmap/delete"
android:layout_alignParentRight="true"
/>
<View
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="0.5dp" android:layout_height="0.5dp"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 507 B

Loading…
Cancel
Save