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