parent
b9b87d0e8a
commit
9a135f0590
@ -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){ |
||||
|
||||
} |
||||
|
||||
|
||||
} |
@ -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 +0,0 @@ |
||||
## 用于网络备份的WebDav |
@ -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() { |
||||
|
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -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(); |
||||
} |
||||
} |
Before Width: | Height: | Size: 637 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 213 B |
Before Width: | Height: | Size: 119 B |
Before Width: | Height: | Size: 593 B |
Before Width: | Height: | Size: 507 B |
Loading…
Reference in new issue