优化阅读文字选择

pull/5/head
fengyuecanzhu 3 years ago
parent 72acb8e74d
commit 8c91b9afa4
  1. 5
      app/src/main/assets/updatelog.fy
  2. 18
      app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
  3. 72
      app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
  4. 12
      app/src/main/java/xyz/fycz/myreader/widget/page/EpubPageLoader.java
  5. 6
      app/src/main/java/xyz/fycz/myreader/widget/page/LocalPageLoader.java
  6. 70
      app/src/main/java/xyz/fycz/myreader/widget/page/PageLoader.java
  7. 15
      app/src/main/java/xyz/fycz/myreader/widget/page/PageView.java
  8. 4
      app/src/main/java/xyz/fycz/myreader/widget/page/TxtLine.kt
  9. 2
      app/src/main/java/xyz/fycz/myreader/widget/page/TxtPage.java
  10. 2
      app/src/main/res/layout/activity_read.xml
  11. 56
      app/src/main/res/layout/dialog_book_source.xml
  12. 4
      app/version_code.properties

@ -1,3 +1,8 @@
2021.06.14
风月读书v2.1.3
更新内容:
1、优化阅读文字选择
2021.06.13
风月读书v2.1.2
更新内容:

@ -22,18 +22,17 @@ import javax.script.ScriptEngineManager;
public class APPCONST {
public static String publicKey = "";//服务端公钥
public static String privateKey;//app私钥
public static final String KEY = "";
public static final String[] STORAGE_PERMISSIONS = new String[]{Permission.WRITE_EXTERNAL_STORAGE,
Permission.READ_EXTERNAL_STORAGE};
public static final String FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/";
public static final String SHARE_FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/share/";
public static String LOG_DIR = FileUtils.getCachePath() + "/log/";
public static String COVER_DIR = FileUtils.getCachePath() + "/covers/";
public static final String BG_FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/bg/";
public static String TEM_FILE_DIR = FileUtils.getCachePath() + "/tem/";
public static final String FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/";
public static final String SHARE_FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/share/";
public static String LOG_DIR = FileUtils.getCachePath() + "/log/";
public static String COVER_DIR = FileUtils.getCachePath() + "/covers/";
public static final String BG_FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/bg/";
public static String TEM_FILE_DIR = FileUtils.getCachePath() + "/tem/";
public static final String BACKUP_FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/backup/";
public static final String TXT_BOOK_DIR = Environment.getExternalStorageDirectory() + "/FYReader/noveltxt/";
public static final String FONT_BOOK_DIR = Environment.getExternalStorageDirectory() + "/FYReader/font/";
@ -41,9 +40,9 @@ public class APPCONST {
public static final String QQ_DATA_DIR = Environment.getExternalStorageDirectory() + "/tencent/MobileQQ/data/";
//BookCachePath (因为getCachePath引用了Context,所以必须是静态变量,不能够是静态常量)
public static String BOOK_CACHE_PATH = FileUtils.getCachePath() + File.separator
+ "book_cache"+ File.separator ;
+ "book_cache" + File.separator;
public static String HTML_CACHE_PATH = FileUtils.getCachePath() + File.separator
+ "html_cache"+ File.separator ;
+ "html_cache" + File.separator;
public static long exitTime;
public static final int exitConfirmTime = 2000;
@ -120,6 +119,7 @@ public class APPCONST {
public static final Pattern JS_PATTERN = Pattern.compile("(<js>[\\w\\W]*?</js>|@js:[\\w\\W]*$)", Pattern.CASE_INSENSITIVE);
public static final Pattern EXP_PATTERN = Pattern.compile("\\{\\{([\\w\\W]*?)\\}\\}");
// public static final Pattern IMG_PATTERN = Pattern.compile("<img .*?src.*?=.*?\"(.*?(?:,\\{.*\\})?)\".*?>", Pattern.CASE_INSENSITIVE);
public static final ScriptEngine SCRIPT_ENGINE = new ScriptEngineManager().getEngineByName("rhino");

@ -28,6 +28,7 @@ import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.SeekBar;
import androidx.annotation.NonNull;
@ -111,6 +112,7 @@ import xyz.fycz.myreader.widget.BubblePopupView;
import xyz.fycz.myreader.widget.page.PageLoader;
import xyz.fycz.myreader.widget.page.PageMode;
import xyz.fycz.myreader.widget.page.PageView;
import xyz.fycz.myreader.widget.page.TxtChar;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@ -1741,6 +1743,9 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
longPressMenu.hidePopupListWindow();
break;
case MotionEvent.ACTION_MOVE:
ImageView left = binding.getRoot().findViewWithTag("left");
ImageView right = binding.getRoot().findViewWithTag("right");
int dx = (int) event.getRawX() - lastX;
int dy = (int) event.getRawY() - lastY;
int l = v.getLeft() + dx;
@ -1759,17 +1764,52 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
int hh = binding.cursorLeft.getHeight();
int ww = binding.cursorLeft.getWidth();
if (v.getId() == R.id.cursor_left) {
binding.readPvContent.setFirstSelectTxtChar(binding.readPvContent.getCurrentTxtChar(lastX + ww, lastY - hh));
if (binding.readPvContent.getFirstSelectTxtChar() != null) {
binding.cursorLeft.setX(binding.readPvContent.getFirstSelectTxtChar().getTopLeftPosition().x - ww);
binding.cursorLeft.setY(binding.readPvContent.getFirstSelectTxtChar().getBottomLeftPosition().y);
if (v.getId() == left.getId()) {
TxtChar first = binding.readPvContent.getCurrentTxtChar(lastX + ww, lastY - hh, true);
if (first != null) {
binding.readPvContent.setFirstSelectTxtChar(first);
}
left.setX(binding.readPvContent.getFirstSelectTxtChar().getBottomLeftPosition().x - ww);
left.setY(binding.readPvContent.getFirstSelectTxtChar().getBottomLeftPosition().y);
} else {
binding.readPvContent.setLastSelectTxtChar(binding.readPvContent.getCurrentTxtChar(lastX - ww, lastY - hh));
if (binding.readPvContent.getLastSelectTxtChar() != null) {
binding.cursorRight.setX(binding.readPvContent.getLastSelectTxtChar().getBottomRightPosition().x);
binding.cursorRight.setY(binding.readPvContent.getLastSelectTxtChar().getBottomRightPosition().y);
TxtChar last = binding.readPvContent.getCurrentTxtChar(lastX - ww, lastY - hh, false);
if (last != null) {
binding.readPvContent.setLastSelectTxtChar(last);
}
right.setX(binding.readPvContent.getLastSelectTxtChar().getBottomRightPosition().x);
right.setY(binding.readPvContent.getLastSelectTxtChar().getBottomRightPosition().y);
}
float leftX = left.getX();
float leftY = left.getY();
float rightX = right.getX();
float rightY = right.getY();
if ((leftY == rightY && leftX > rightX) || leftY > rightY) {
left.setTag("right");
left.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_cursor_right));
right.setTag("left");
right.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_cursor_left));
if (v.getId() == left.getId()) {
TxtChar last = binding.readPvContent.getLastSelectTxtChar();
last = binding.readPvContent.getNextTxtChar(last);
binding.readPvContent.setFirstSelectTxtChar(last);
if (last != null) {
right.setX(last.getBottomLeftPosition().x - ww);
right.setY(last.getBottomLeftPosition().y);
} else {
return true;
}
} else {
TxtChar first = binding.readPvContent.getFirstSelectTxtChar();
first = binding.readPvContent.getLastTxtChar(first);
binding.readPvContent.setLastSelectTxtChar(first);
if (first != null) {
left.setX(first.getBottomRightPosition().x);
left.setY(first.getBottomRightPosition().y);
}
}
}
@ -1826,15 +1866,17 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
* 显示选择
*/
private void cursorShow() {
binding.cursorLeft.setVisibility(View.VISIBLE);
binding.cursorRight.setVisibility(View.VISIBLE);
ImageView left = binding.getRoot().findViewWithTag("left");
ImageView right = binding.getRoot().findViewWithTag("right");
left.setVisibility(View.VISIBLE);
right.setVisibility(View.VISIBLE);
int hh = binding.cursorLeft.getHeight();
int ww = binding.cursorLeft.getWidth();
if (binding.readPvContent.getFirstSelectTxtChar() != null) {
binding.cursorLeft.setX(binding.readPvContent.getFirstSelectTxtChar().getTopLeftPosition().x - ww);
binding.cursorLeft.setY(binding.readPvContent.getFirstSelectTxtChar().getBottomLeftPosition().y);
binding.cursorRight.setX(binding.readPvContent.getFirstSelectTxtChar().getBottomRightPosition().x);
binding.cursorRight.setY(binding.readPvContent.getFirstSelectTxtChar().getBottomRightPosition().y);
left.setX(binding.readPvContent.getFirstSelectTxtChar().getTopLeftPosition().x - ww);
left.setY(binding.readPvContent.getFirstSelectTxtChar().getBottomLeftPosition().y);
right.setX(binding.readPvContent.getFirstSelectTxtChar().getBottomRightPosition().x);
right.setY(binding.readPvContent.getFirstSelectTxtChar().getBottomRightPosition().y);
}
}

@ -53,6 +53,7 @@ public class EpubPageLoader extends PageLoader {
EpubPageLoader(PageView pageView, xyz.fycz.myreader.greendao.entity.Book bookShelfBean, Setting setting) {
super(pageView, bookShelfBean, setting);
mStatus = STATUS_PARING;
}
@Override
@ -91,13 +92,17 @@ public class EpubPageLoader extends PageLoader {
@Override
public void onNext(xyz.fycz.myreader.greendao.entity.Book bookShelfBean) {
isChapterListPrepare = true;
//提示目录加载完成
if (mPageChangeListener != null) {
mPageChangeListener.onCategoryFinish(mChapterList);
}
// 加载并显示当前章节
openChapter();
}
@Override
public void onError(Throwable e) {
error(STATUS_CATEGORY_ERROR, e.getMessage());
error(STATUS_PARSE_ERROR, e.getMessage());
}
});
}
@ -249,9 +254,8 @@ public class EpubPageLoader extends PageLoader {
}
private Observable<xyz.fycz.myreader.greendao.entity.Book> checkChapterList(xyz.fycz.myreader.greendao.entity.Book collBook) {
if (mCollBook.getChapterTotalNum() != 0) {
mChapterList = ChapterService.getInstance().findBookAllChapterByBookId(mCollBook.getId());
mPageChangeListener.onCategoryFinish(mChapterList);
mChapterList = ChapterService.getInstance().findBookAllChapterByBookId(mCollBook.getId());
if (!mChapterList.isEmpty()) {
return Observable.just(collBook);
} else {
return Observable.create((ObservableOnSubscribe<List<Chapter>>) e -> {

@ -69,15 +69,15 @@ public class LocalPageLoader extends PageLoader {
// 对于文件是否存在,或者为空的判断,不作处理。 ==> 在文件打开前处理过了。
mBookFile = new File(mCollBook.getChapterUrl());
if (!mBookFile.exists()) {
error(STATUS_CATEGORY_ERROR, "书籍源文件不存在\n" + mCollBook.getChapterUrl());
error(STATUS_PARSE_ERROR, "书籍源文件不存在\n" + mCollBook.getChapterUrl());
return;
}
mCharset = mCollBook.getInfoUrl();
// 判断文件是否已经加载过,并具有缓存
if (mCollBook.getChapterTotalNum() != 0) {
mChapterList = mChapterService.findBookAllChapterByBookId(mCollBook.getId());
if (!mChapterList.isEmpty()) {
mChapterList = mChapterService.findBookAllChapterByBookId(mCollBook.getId());
isChapterListPrepare = true;
//提示目录加载完成

@ -32,13 +32,11 @@ import xyz.fycz.myreader.model.audio.ReadAloudService;
import xyz.fycz.myreader.util.IOUtils;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.help.ChapterContentHelp;
import xyz.fycz.myreader.util.help.StringHelper;
import xyz.fycz.myreader.util.utils.BitmapUtil;
import xyz.fycz.myreader.util.utils.MeUtils;
import xyz.fycz.myreader.util.utils.RxUtils;
import xyz.fycz.myreader.util.utils.ScreenUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
import xyz.fycz.myreader.widget.page2.TxtLine;
import java.io.BufferedReader;
import java.io.File;
@ -46,6 +44,7 @@ import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Created by fengyue on 20-11-21
@ -785,7 +784,7 @@ public abstract class PageLoader {
error(STATUS_ERROR, msg);
}
public void error(int status, String msg){
public void error(int status, String msg) {
//加载错误
mStatus = status;
errorMsg = msg;
@ -1044,10 +1043,10 @@ public abstract class PageLoader {
tip = "章节内容为空";
break;
case STATUS_PARING:
tip = "正在拆分章节请等待...";
tip = "正在解析文件请等待...";
break;
case STATUS_PARSE_ERROR:
tip = "文件解析错误";
tip = "文件解析错误\n" + errorMsg;
break;
case STATUS_CATEGORY_EMPTY:
tip = "目录列表为空";
@ -1056,7 +1055,8 @@ public abstract class PageLoader {
tip = "目录加载失败\n" + errorMsg;
break;
}
if (mStatus == STATUS_ERROR || mStatus == STATUS_CATEGORY_ERROR) {
if (mStatus == STATUS_ERROR || mStatus == STATUS_CATEGORY_ERROR
|| mStatus == STATUS_PARSE_ERROR) {
drawErrorMsg(canvas, tip, 0);
} else {
//将提示语句放到正中间
@ -2061,6 +2061,10 @@ public abstract class PageLoader {
}
}
enum Detect {
None, Left, Right
}
/**
* --------------------
* 检测获取按压坐标所在位置的字符没有的话返回null
@ -2068,7 +2072,7 @@ public abstract class PageLoader {
* author: huangwei
* 2017年7月4日上午10:23:19
*/
TxtChar detectPressTxtChar(float down_X2, float down_Y2) {
TxtChar detectPressTxtChar(float down_X2, float down_Y2, Detect detect) {
TxtPage txtPage = mCurPage;
if (txtPage == null) return null;
List<TxtLine> txtLines = txtPage.txtLists;
@ -2076,14 +2080,24 @@ public abstract class PageLoader {
for (TxtLine l : txtLines) {
List<TxtChar> txtChars = l.getCharsData();
if (txtChars != null) {
for (TxtChar c : txtChars) {
for (int i = 0; i < txtChars.size(); i++) {
TxtChar c = txtChars.get(i);
Point leftPoint = c.getBottomLeftPosition();
Point rightPoint = c.getBottomRightPosition();
if (leftPoint != null && down_Y2 > leftPoint.y) {
break;// 说明是在下一行
}
if (leftPoint != null && rightPoint != null && down_X2 >= leftPoint.x && down_X2 <= rightPoint.x) {
return c;
if (leftPoint != null && rightPoint != null) {
boolean flag = down_X2 >= leftPoint.x && down_X2 <= rightPoint.x;
switch (detect) {
case Left:
flag = flag || (i == 0 && down_X2 < leftPoint.x);
break;
case Right:
flag = flag || (i == txtChars.size() - 1 && down_X2 > rightPoint.x);
break;
}
if (flag) return c;
}
}
@ -2092,6 +2106,42 @@ public abstract class PageLoader {
return null;
}
TxtChar detectLastTxtChar(TxtChar txtChar) {
TxtChar last = txtChar;
TxtPage txtPage = mCurPage;
if (txtPage == null) return txtChar;
List<TxtLine> txtLines = txtPage.txtLists;
if (txtLines == null) return txtChar;
for (TxtLine l : txtLines) {
List<TxtChar> txtChars = l.getCharsData();
if (txtChars != null) {
for (TxtChar c : txtChars) {
if (c != null && c.getIndex() == txtChar.getIndex()) return last;
last = c;
}
}
}
return txtChar;
}
TxtChar detectNextTxtChar(TxtChar txtChar) {
TxtPage txtPage = mCurPage;
boolean isNext = false;
if (txtPage == null) return txtChar;
List<TxtLine> txtLines = txtPage.txtLists;
if (txtLines == null) return txtChar;
for (TxtLine l : txtLines) {
List<TxtChar> txtChars = l.getCharsData();
if (txtChars != null) {
for (TxtChar c : txtChars) {
if (isNext) return c;
if (c != null && c.getIndex() == txtChar.getIndex()) isNext = true;
}
}
}
return txtChar;
}
public boolean isPrev() {
return isPrev;
}

@ -26,7 +26,6 @@ import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.utils.SnackbarUtils;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.widget.animation.*;
import xyz.fycz.myreader.widget.page2.TxtLine;
/**
* Created by Administrator on 2016/8/29 0029.
@ -125,7 +124,7 @@ public class PageView extends View {
performLongClick();
if (mStartX > 0 && mStartY > 0) {// 说明还没释放,是长按事件
isLongPress = true;//长按
TxtChar p = mPageLoader.detectPressTxtChar(mStartX, mStartY);//找到长按的点
TxtChar p = mPageLoader.detectPressTxtChar(mStartX, mStartY, PageLoader.Detect.None);//找到长按的点
firstSelectTxtChar = p;//设置开始位置字符
lastSelectTxtChar = p;//设置结束位置字符
selectMode = SelectMode.PressSelectText;//设置模式为长按选择
@ -628,8 +627,16 @@ public class PageView extends View {
}
//根据当前坐标返回文字
public TxtChar getCurrentTxtChar(float x, float y) {
return mPageLoader.detectPressTxtChar(x, y);
public TxtChar getCurrentTxtChar(float x, float y, boolean left) {
return mPageLoader.detectPressTxtChar(x, y, left ? PageLoader.Detect.Left : PageLoader.Detect.Right);
}
public TxtChar getLastTxtChar(TxtChar txtChar){
return mPageLoader.detectLastTxtChar(txtChar);
}
public TxtChar getNextTxtChar(TxtChar txtChar){
return mPageLoader.detectNextTxtChar(txtChar);
}
private void drawOaleSeletLinesBg(Canvas canvas) {// 绘制选中背景

@ -1,6 +1,4 @@
package xyz.fycz.myreader.widget.page2
import xyz.fycz.myreader.widget.page.TxtChar
package xyz.fycz.myreader.widget.page
class TxtLine {

@ -2,8 +2,6 @@ package xyz.fycz.myreader.widget.page;
import java.util.List;
import xyz.fycz.myreader.widget.page2.TxtLine;
public class TxtPage {
int position;
String title;

@ -82,6 +82,7 @@
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/cursor_left"
android:tag="left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
@ -91,6 +92,7 @@
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/cursor_right"
android:tag="right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:background="@color/colorBackground">
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorBackground"
android:orientation="vertical">
<!--<TextView
@ -15,34 +16,39 @@
android:padding="8dp"
android:text="神墓(辰东)"/>-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include
android:id="@+id/it"
layout="@layout/toolbar"/>
layout="@layout/toolbar" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_refresh_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_alignParentEnd="true"
android:padding="8dp"
app:srcCompat="@drawable/ic_refresh"/>
android:id="@+id/iv_refresh_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerInParent="true"
android:padding="8dp"
android:background="?android:attr/selectableItemBackground"
app:srcCompat="@drawable/ic_refresh" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_stop_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_toStartOf="@+id/iv_refresh_search"
android:padding="8dp"
app:srcCompat="@drawable/ic_stop_black_24dp"/>
android:id="@+id/iv_stop_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="?android:attr/selectableItemBackground"
android:layout_toStartOf="@+id/iv_refresh_search"
android:padding="8dp"
app:srcCompat="@drawable/ic_stop_black_24dp" />
</RelativeLayout>
<xyz.fycz.myreader.widget.RefreshProgressBar
android:id="@+id/rpb"
android:layout_width="match_parent"
android:layout_height="3dp"
android:visibility="visible" />
android:id="@+id/rpb"
android:layout_width="match_parent"
android:layout_height="3dp"
android:visibility="visible" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/dialog_rv_content"

@ -1,2 +1,2 @@
#Sun Jun 06 19:04:52 CST 2021
VERSION_CODE=211
#Mon Jun 14 12:41:11 CST 2021
VERSION_CODE=212

Loading…
Cancel
Save