书籍长按菜单优化

pull/5/head
fengyuecanzhu 4 years ago
parent 1848beef19
commit 8b30c0a72b
  1. 4
      .idea/assetWizardSettings.xml
  2. 2
      DialogX/src/main/java/com/kongzue/dialogx/style/MaterialStyle.java
  3. 4
      app/src/main/assets/updatelog.fy
  4. 2
      app/src/main/java/xyz/fycz/myreader/application/App.java
  5. 2
      app/src/main/java/xyz/fycz/myreader/base/adapter/BaseListAdapter.java
  6. 4
      app/src/main/java/xyz/fycz/myreader/base/adapter/IViewHolder.java
  7. 199
      app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
  8. 26
      app/src/main/java/xyz/fycz/myreader/ui/adapter/BookSourceAdapter.java
  9. 238
      app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseAdapter.java
  10. 95
      app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseDetailedAdapter.java
  11. 111
      app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseDragAdapter.java
  12. 18
      app/src/main/java/xyz/fycz/myreader/ui/adapter/helper/IItemTouchHelperViewHolder.java
  13. 40
      app/src/main/java/xyz/fycz/myreader/ui/adapter/helper/ItemTouchCallback.java
  14. 16
      app/src/main/java/xyz/fycz/myreader/ui/adapter/helper/OnStartDragListener.java
  15. 41
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/BookSourceHolder.java
  16. 16
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/BookStoreBookHolder.java
  17. 3
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/CatalogHolder.java
  18. 5
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/FileHolder.java
  19. 4
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/LocalSourceHolder.java
  20. 4
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/ReadRecordHolder.java
  21. 3
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/ReplaceRuleHolder.java
  22. 4
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/SearchBookHolder.java
  23. 3
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/SourceEditHolder.kt
  24. 5
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/SourceExchangeHolder.java
  25. 57
      app/src/main/java/xyz/fycz/myreader/ui/dialog/BookGroupDialog.java
  26. 78
      app/src/main/java/xyz/fycz/myreader/ui/dialog/MyAlertDialog.java
  27. 11
      app/src/main/java/xyz/fycz/myreader/ui/fragment/DIYSourceFragment.java
  28. 72
      app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java
  29. 1
      app/src/main/java/xyz/fycz/myreader/util/help/JsExtensions.java
  30. 225
      app/src/main/java/xyz/fycz/myreader/util/utils/ShareBookUtil.java
  31. 18
      app/src/main/res/drawable/ic_leaderboard.xml
  32. 15
      app/src/main/res/drawable/ic_top.xml
  33. 14
      app/src/main/res/drawable/menu_book_detail.xml
  34. 5
      app/src/main/res/drawable/selector_btn_input_finish.xml
  35. 45
      app/src/main/res/layout/dialog_input.xml
  36. 16
      app/src/main/res/layout/edit_dialog.xml
  37. 16
      app/src/main/res/layout/edit_text.xml
  38. 1
      app/src/main/res/layout/fragment_book_list.xml
  39. 2
      app/src/main/res/layout/fragment_find.xml
  40. 11
      app/src/main/res/layout/item_book_source.xml
  41. 12
      app/src/main/res/layout/item_read_record.xml
  42. 233
      app/src/main/res/layout/menu_book.xml
  43. 124
      app/src/main/res/layout/menu_book_local.xml
  44. 2
      app/src/main/res/values/strings.xml
  45. 12
      app/src/main/res/values/styles.xml
  46. 4
      app/version_code.properties

@ -14,8 +14,8 @@
<option name="values">
<map>
<entry key="assetSourceType" value="FILE" />
<entry key="outputName" value="ic_more" />
<entry key="sourceFile" value="F:\SVG图标\更多.svg" />
<entry key="outputName" value="ic_top" />
<entry key="sourceFile" value="F:\SVG图标\置顶.svg" />
</map>
</option>
</PersistentState>

@ -161,7 +161,7 @@ public class MaterialStyle implements DialogXStyle {
@Override
public int overrideMenuTextColor(boolean light) {
return light ? R.color.black90 : R.color.white90;
return light ? R.color.black90 : R.color.dialogxMIUITextDark;
}
@Override

@ -1,3 +1,7 @@
风月读书v2.0.8
更新内容:
1、优化书籍、书源拖拽移动效果
2021.06.03
风月读书v2.0.7
更新内容:

@ -26,6 +26,7 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import com.kongzue.dialogx.DialogX;
import com.kongzue.dialogx.style.MaterialStyle;
import com.liulishuo.filedownloader.FileDownloader;
import com.liulishuo.filedownloader.connection.FileDownloadUrlConnection;
@ -112,6 +113,7 @@ public class App extends Application {
private void initDialogX() {
DialogX.init(this);
DialogX.DEBUGMODE = debug;
DialogX.globalStyle = MaterialStyle.style();
}
public void initNightTheme() {

@ -43,7 +43,7 @@ public abstract class BaseListAdapter<T> extends RecyclerView.Adapter<RecyclerVi
throw new IllegalArgumentException("The ViewHolder item must extend BaseViewHolder");
IViewHolder<T> iHolder = ((BaseViewHolder) holder).holder;
iHolder.onBind(getItem(position),position);
iHolder.onBind(holder, getItem(position), position);
//设置点击事件
holder.itemView.setOnClickListener((v)->{

@ -3,6 +3,8 @@ package xyz.fycz.myreader.base.adapter;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
/**
* @author fengyue
* @date 2020/8/12 20:02
@ -11,6 +13,6 @@ import android.view.ViewGroup;
public interface IViewHolder<T> {
View createItemView(ViewGroup parent);
void initView();
void onBind(T data,int pos);
void onBind(RecyclerView.ViewHolder holder, T data, int pos);
void onClick();
}

@ -77,6 +77,7 @@ import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.utils.GsonExtensionsKt;
import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.util.utils.RxUtils;
import xyz.fycz.myreader.util.utils.ShareBookUtil;
import xyz.fycz.myreader.util.utils.StringUtils;
import xyz.fycz.myreader.webapi.BookApi;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
@ -640,7 +641,7 @@ public class BookDetailedActivity extends BaseActivity {
mSourceDialog.show();
break;
case R.id.action_share:
shareBook();
ShareBookUtil.shareBook(this, mBook, binding.ih.bookDetailIvCover);
break;
case R.id.action_edit:
Intent editIntent = new Intent(this, BookInfoEditActivity.class);
@ -660,17 +661,7 @@ public class BookDetailedActivity extends BaseActivity {
startActivity(intent);
break;
case R.id.action_group_setting:
mBookGroupDia.addGroup(mBook, new BookGroupDialog.OnGroup() {
@Override
public void change() {
}
@Override
public void addGroup() {
}
});
mBookGroupDia.addGroup(mBook, null);
break;
case R.id.action_edit_source:
BookSource source = BookSourceManager.getBookSourceByStr(mBook.getSource());
@ -738,190 +729,6 @@ public class BookDetailedActivity extends BaseActivity {
}
}
/**
* 分享书籍
*/
private void shareBook() {
if ("本地书籍".equals(mBook.getType())) {
File file = new File(mBook.getChapterUrl());
if (!file.exists()) {
ToastUtils.showWarring("书籍源文件不存在,无法分享!");
return;
}
try {
ShareUtils.share(this, file, mBook.getName() + ".txt", "text/plain");
} catch (Exception e) {
String dest = APPCONST.SHARE_FILE_DIR + File.separator + mBook.getName() + ".txt";
FileUtils.copy(mBook.getChapterUrl(), dest);
ShareUtils.share(this, new File(dest), mBook.getName() + ".txt", "text/plain");
}
return;
}
ToastUtils.showInfo("正在生成分享图片");
Single.create((SingleOnSubscribe<File>) emitter -> {
// 使用url
String url = SharedPreUtils.getInstance().getString(getString(R.string.downloadLink), URLCONST.LAN_ZOUS_URL);
if (url == null)
url = "";
int maxLength = 1273 - 1 - url.length();
SharedBook sharedBook = SharedBook.bookToSharedBook(mBook);
url = url + "#" + GsonExtensionsKt.getGSON().toJson(sharedBook);
Log.d("QRcode", "Length=" + url.length() + "\n" + url);
Bitmap bitmap;
QRCodeEncoder.HINTS.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
bitmap = QRCodeEncoder.syncEncodeQRCode(url, 360);
QRCodeEncoder.HINTS.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
File share = makeShareFile(bitmap);
if (share == null) {
ToastUtils.showError("分享图片生成失败");
return;
}
emitter.onSuccess(share);
}).compose(RxUtils::toSimpleSingle)
.subscribe(new MySingleObserver<File>() {
@Override
public void onSuccess(@NonNull File File) {
share(File);
}
});
}
/**
* 生成分享图片
*
* @param QRCode
* @return
*/
private File makeShareFile(Bitmap QRCode) {
FileOutputStream fos = null;
try {
Bitmap back = BitmapFactory.decodeStream(getResources().getAssets().open("share.png")).copy(Bitmap.Config.ARGB_8888, true);
int backWidth = back.getWidth();
int backHeight = back.getHeight();
int margin = 60;
int marginTop = 24;
binding.ih.bookDetailIvCover.setDrawingCacheEnabled(true);
Bitmap img = Bitmap.createBitmap(binding.ih.bookDetailIvCover.getDrawingCache()).copy(Bitmap.Config.ARGB_8888, true);
binding.ih.bookDetailIvCover.setDrawingCacheEnabled(false);
img = BitmapUtil.getBitmap(img, 152, 209);
Canvas cv = new Canvas(back);
cv.drawBitmap(img, margin, margin + marginTop * 2, null);
TextPaint textPaint = new TextPaint();
textPaint.setAntiAlias(true);
textPaint.setFilterBitmap(true);
textPaint.setColor(Color.BLACK);
textPaint.setTextSize(40);
String name = TextUtils.ellipsize(mBook.getName(), textPaint, backWidth - margin + marginTop * 3 - img.getWidth(), TextUtils.TruncateAt.END).toString();
cv.drawText(name, margin + marginTop + img.getWidth(), margin + marginTop * 4, textPaint);
textPaint.setColor(getResources().getColor(R.color.origin));
textPaint.setTextSize(32);
cv.drawText(mBook.getAuthor(), margin + marginTop + img.getWidth(), margin + marginTop * 6, textPaint);
textPaint.setColor(Color.BLACK);
cv.drawText(mBook.getType() == null ? "" : mBook.getType(), margin + marginTop + img.getWidth(), margin + marginTop * 8, textPaint);
cv.drawText("书源:" + BookSourceManager.getSourceNameByStr(mBook.getSource()), margin + marginTop + img.getWidth(), margin + marginTop * 10, textPaint);
int textSize = 35;
int textInterval = textSize / 2;
textPaint.setTextSize(textSize);
drawDesc(getDescLines(backWidth - margin * 2, textPaint), textPaint, cv, margin + marginTop * 4 + img.getHeight(), margin, textInterval);
cv.drawBitmap(QRCode, backWidth - QRCode.getWidth(), backHeight - QRCode.getHeight(), null);
cv.save();// 保存
cv.restore();// 存储
File share = FileUtils.getFile(APPCONST.SHARE_FILE_DIR + mBook.getName() + "_share.png");
fos = new FileOutputStream(share);
back.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
Log.i("tag", "saveBitmap success: " + share.getAbsolutePath());
back.recycle();
img.recycle();
QRCode.recycle();
return share;
} catch (Exception e) {
e.printStackTrace();
ToastUtils.showError(e.getLocalizedMessage() + "");
return null;
} finally {
IOUtils.close(fos);
}
}
/**
* 分享生成的图片
*
* @param share
*/
private void share(File share) {
ShareUtils.share(this, share, "分享书籍", "image/png");
}
/**
* 绘制简介
*
* @param lines
* @param textPaint
* @param canvas
* @param top
* @param left
* @param textInterval
*/
private void drawDesc(List<String> lines, TextPaint textPaint, Canvas canvas, int top, int left, int textInterval) {
float interval = textInterval + textPaint.getTextSize();
for (String line : lines) {
canvas.drawText(line, left, top, textPaint);
top += interval;
}
}
/**
* 生成简介lines
*
* @param width
* @param textPaint
* @return
*/
private List<String> getDescLines(int width, TextPaint textPaint) {
List<String> lines = new ArrayList<>();
String desc = StringUtils.halfToFull(" ") + mBook.getDesc();
int i = 0;
int wordCount = 0;
String subStr = null;
while (desc.length() > 0) {
if (i == 9) {
lines.add(TextUtils.ellipsize(desc, textPaint, width / 1.8f, TextUtils.TruncateAt.END).toString());
break;
}
wordCount = textPaint.breakText(desc, true, width, null);
subStr = desc.substring(0, wordCount);
lines.add(subStr);
desc = desc.substring(wordCount);
i++;
}
return lines;
}
private boolean isThirdSource() {
return mReadCrawler instanceof ThirdCrawler;

@ -7,10 +7,12 @@ import androidx.fragment.app.FragmentActivity;
import java.util.Collections;
import xyz.fycz.myreader.application.App;
import xyz.fycz.myreader.base.adapter.IViewHolder;
import xyz.fycz.myreader.greendao.DbManager;
import xyz.fycz.myreader.greendao.entity.rule.BookSource;
import xyz.fycz.myreader.ui.adapter.helper.ItemTouchCallback;
import xyz.fycz.myreader.ui.adapter.helper.OnStartDragListener;
import xyz.fycz.myreader.ui.adapter.holder.BookSourceHolder;
/**
@ -21,6 +23,7 @@ import xyz.fycz.myreader.ui.adapter.holder.BookSourceHolder;
public class BookSourceAdapter extends BaseSourceAdapter {
private final FragmentActivity activity;
private final OnSwipeListener onSwipeListener;
private OnStartDragListener onStartDragListener;
private boolean mEditState;
private final ItemTouchCallback.OnItemTouchListener itemTouchListener = new ItemTouchCallback.OnItemTouchListener() {
@Override
@ -32,26 +35,41 @@ public class BookSourceAdapter extends BaseSourceAdapter {
public boolean onMove(int srcPosition, int targetPosition) {
Collections.swap(mList, srcPosition, targetPosition);
notifyItemMoved(srcPosition, targetPosition);
notifyItemChanged(srcPosition);
/*notifyItemChanged(srcPosition);
notifyItemChanged(targetPosition);
AsyncTask.execute(() -> {
for (int i = 1; i <= mList.size(); i++) {
mList.get(i - 1).setOrderNum(i);
}
DbManager.getDaoSession().getBookSourceDao().insertOrReplaceInTx(mList);
});
});*/
return true;
}
@Override
public void onEnd() {
App.getHandler().postDelayed(() -> notifyDataSetChanged(), 500);
AsyncTask.execute(() -> {
for (int i = 1; i <= mList.size(); i++) {
mList.get(i - 1).setOrderNum(i);
}
DbManager.getDaoSession().getBookSourceDao().insertOrReplaceInTx(mList);
});
}
};
public BookSourceAdapter(FragmentActivity activity, OnSwipeListener onSwipeListener) {
public BookSourceAdapter(FragmentActivity activity, OnSwipeListener onSwipeListener,
OnStartDragListener onStartDragListener) {
this.activity = activity;
this.onSwipeListener = onSwipeListener;
this.onStartDragListener = onStartDragListener;
}
@Override
protected IViewHolder<BookSource> createViewHolder(int viewType) {
return new BookSourceHolder(activity, this, onSwipeListener);
return new BookSourceHolder(activity, this, onSwipeListener, onStartDragListener);
}
public ItemTouchCallback.OnItemTouchListener getItemTouchListener() {

@ -1,14 +1,20 @@
package xyz.fycz.myreader.ui.adapter;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import android.view.View;
import android.widget.*;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.SwitchCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.kongzue.dialogx.dialogs.BottomDialog;
import com.kongzue.dialogx.dialogs.BottomMenu;
import com.kongzue.dialogx.interfaces.OnBindView;
import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener;
import com.kongzue.dialogx.interfaces.OnMenuItemSelectListener;
@ -22,12 +28,29 @@ import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import io.reactivex.Single;
import io.reactivex.SingleOnSubscribe;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.App;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BitIntentDataManager;
import xyz.fycz.myreader.base.observer.MySingleObserver;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.greendao.entity.rule.BookSource;
import xyz.fycz.myreader.model.sourceAnalyzer.BookSourceManager;
import xyz.fycz.myreader.ui.activity.BookDetailedActivity;
import xyz.fycz.myreader.ui.activity.BookInfoEditActivity;
import xyz.fycz.myreader.ui.activity.SourceEditActivity;
import xyz.fycz.myreader.ui.adapter.helper.IItemTouchHelperViewHolder;
import xyz.fycz.myreader.ui.adapter.helper.ItemTouchCallback;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.ui.dialog.SourceExchangeDialog;
import xyz.fycz.myreader.util.help.StringHelper;
import xyz.fycz.myreader.util.utils.RxUtils;
import xyz.fycz.myreader.util.utils.ShareBookUtil;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.widget.CoverImageView;
import xyz.fycz.myreader.widget.custom.DragAdapter;
import xyz.fycz.myreader.greendao.entity.Book;
@ -359,8 +382,211 @@ public abstract class BookcaseAdapter extends RecyclerView.Adapter<BookcaseAdapt
}
}
public void showBookMenu(Book book, int pos) {
BottomDialog.show(new BookMenuDialog(book, pos));
}
class BookMenuDialog extends OnBindView<BottomDialog> {
private BottomDialog dialog;
private Book mBook;
private int pos;
private RelativeLayout rlBookDetail;
private CoverImageView ivBookImg;
private TextView tvBookName;
private TextView tvBookAuthor;
private TextView tvTop;
private SwitchCompat scIsUpdate;
private TextView tvDownload;
private TextView tvExport;
private TextView tvChangeSource;
private TextView tvSetGroup;
private TextView tvShare;
private TextView tvRemove;
private TextView tvEdit;
private TextView tvRefresh;
private TextView tvLink;
private TextView tvEditSource;
public BookMenuDialog(Book book, int pos) {
super("本地书籍".equals(book.getType()) ? R.layout.menu_book_local : R.layout.menu_book);
mBook = book;
this.pos = pos;
}
@Override
public void onBind(BottomDialog dialog, View v) {
this.dialog = dialog;
bindLocalView(v);
if (!"本地书籍".equals(mBook.getType())) {
bindView(v);
}
}
private void bindView(View v) {
scIsUpdate = v.findViewById(R.id.sc_is_update);
tvDownload = v.findViewById(R.id.tv_download);
tvExport = v.findViewById(R.id.tv_export_cathe);
tvChangeSource = v.findViewById(R.id.tv_change_source);
tvShare = v.findViewById(R.id.tv_share);
tvRefresh = v.findViewById(R.id.tv_refresh);
tvLink = v.findViewById(R.id.tv_link);
tvEditSource = v.findViewById(R.id.tv_edit_source);
bindEvent();
}
private void bindEvent() {
scIsUpdate.setChecked(!mBook.getIsCloseUpdate());
scIsUpdate.setOnCheckedChangeListener((buttonView, isChecked) -> {
mBook.setIsCloseUpdate(!mBook.getIsCloseUpdate());
mBookService.updateEntity(mBook);
});
tvDownload.setOnClickListener(v -> {
dialog.dismiss();
downloadBook(mBook);
});
tvExport.setOnClickListener(v -> {
dialog.dismiss();
Single.create((SingleOnSubscribe<Boolean>) emitter -> {
emitter.onSuccess(unionChapterCathe(mBook));
}).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver<Boolean>() {
@Override
public void onSuccess(@NotNull Boolean aBoolean) {
if (aBoolean) {
DialogCreator.createTipDialog(mContext,
"缓存导出成功,导出目录:"
+ APPCONST.TXT_BOOK_DIR);
} else {
DialogCreator.createTipDialog(mContext,
"章节目录为空或未找到缓存文件,缓存导出失败!");
}
}
static class ViewHolder extends RecyclerView.ViewHolder {
@Override
public void onError(Throwable e) {
e.printStackTrace();
DialogCreator.createTipDialog(mContext,
"章节目录为空或未找到缓存文件,缓存导出失败!");
}
});
});
tvChangeSource.setOnClickListener(v -> {
this.dialog.dismiss();
SourceExchangeDialog dialog = new SourceExchangeDialog((Activity) mContext, mBook);
dialog.setOnSourceChangeListener((bean, pos) -> {
Book bookTem = (Book) mBook.clone();
bookTem.setChapterUrl(bean.getChapterUrl());
bookTem.setInfoUrl(bean.getInfoUrl());
if (!StringHelper.isEmpty(bean.getImgUrl())) {
bookTem.setImgUrl(bean.getImgUrl());
}
if (!StringHelper.isEmpty(bean.getType())) {
bookTem.setType(bean.getType());
}
if (!StringHelper.isEmpty(bean.getDesc())) {
bookTem.setDesc(bean.getDesc());
}
if (!StringHelper.isEmpty(bean.getUpdateDate())) {
bookTem.setUpdateDate(bean.getUpdateDate());
}
if (!StringHelper.isEmpty(bean.getWordCount())) {
bookTem.setWordCount(bean.getWordCount());
}
if (!StringHelper.isEmpty(bean.getStatus())) {
bookTem.setStatus(bean.getStatus());
}
bookTem.setSource(bean.getSource());
mBookService.updateBook(mBook, bookTem);
mBook = bookTem;
list.set(this.pos, mBook);
mBookcasePresenter.refreshBook(mBook, true);
});
dialog.show();
});
tvShare.setOnClickListener(v -> {
dialog.dismiss();
ShareBookUtil.shareBook(mContext, mBook, ivBookImg);
});
tvRefresh.setOnClickListener(v -> {
dialog.dismiss();
mBookcasePresenter.refreshBook(mBook, false);
});
tvLink.setOnClickListener(v -> {
ReadCrawler rc = ReadCrawlerUtil.getReadCrawler(mBook.getSource());
Uri uri = Uri.parse(NetworkUtils.getAbsoluteURL(rc.getNameSpace(), mBook.getChapterUrl()));
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
App.getHandler().postDelayed(() -> mContext.startActivity(intent), 300);
dialog.dismiss();
});
tvEditSource.setOnClickListener(v -> {
BookSource source = BookSourceManager.getBookSourceByStr(mBook.getSource());
if (!TextUtils.isEmpty(source.getSourceEName())) {
ToastUtils.showWarring("内置书源无法编辑!");
} else {
Intent sourceIntent = new Intent(mContext, SourceEditActivity.class);
sourceIntent.putExtra(APPCONST.BOOK_SOURCE, source);
App.getHandler().postDelayed(() -> mContext.startActivity(sourceIntent), 300);
dialog.dismiss();
}
});
}
private void bindLocalView(View v) {
rlBookDetail = v.findViewById(R.id.rl_book_detail);
ivBookImg = v.findViewById(R.id.iv_book_img);
tvBookName = v.findViewById(R.id.tv_book_name);
tvBookAuthor = v.findViewById(R.id.tv_book_author);
tvEdit = v.findViewById(R.id.tv_edit);
tvTop = v.findViewById(R.id.tv_top);
tvSetGroup = v.findViewById(R.id.tv_set_group);
tvRemove = v.findViewById(R.id.tv_remove);
bindLocalEvent();
}
private void bindLocalEvent() {
rlBookDetail.setOnClickListener(v -> {
Intent intent = new Intent(mContext, BookDetailedActivity.class);
BitIntentDataManager.getInstance().putData(intent, mBook);
App.getHandler().postDelayed(() -> mContext.startActivity(intent), 300);
dialog.dismiss();
});
if (!App.isDestroy((Activity) mContext)) {
ReadCrawler rc = ReadCrawlerUtil.getReadCrawler(mBook.getSource());
ivBookImg.load(NetworkUtils.getAbsoluteURL(rc.getNameSpace(), mBook.getImgUrl()), mBook.getName(), mBook.getAuthor());
}
tvBookName.setText(mBook.getName());
tvBookAuthor.setText(mBook.getAuthor());
tvEdit.setOnClickListener(v -> {
Intent editIntent = new Intent(mContext, BookInfoEditActivity.class);
BitIntentDataManager.getInstance().putData(editIntent, mBook);
App.getHandler().postDelayed(() -> mContext.startActivity(editIntent), 300);
dialog.dismiss();
});
tvTop.setOnClickListener(v -> {
if (!isGroup) {
mBook.setSortCode(0);
} else {
mBook.setGroupSort(0);
}
mBookService.updateEntity(mBook);
mBookcasePresenter.init();
ToastUtils.showSuccess("书籍《" + mBook.getName() + "》移至顶部成功!");
dialog.dismiss();
});
tvSetGroup.setOnClickListener(v -> {
dialog.dismiss();
mBookcasePresenter.addGroup(mBook);
});
tvRemove.setOnClickListener(v -> {
dialog.dismiss();
showDeleteBookDialog(mBook);
});
}
}
static class ViewHolder extends RecyclerView.ViewHolder implements IItemTouchHelperViewHolder {
CheckBox cbBookChecked;
CoverImageView ivBookImg;
TextView tvBookName;
@ -370,6 +596,16 @@ public abstract class BookcaseAdapter extends RecyclerView.Adapter<BookcaseAdapt
public ViewHolder(@NonNull @NotNull View itemView) {
super(itemView);
}
@Override
public void onItemSelected() {
itemView.setTranslationZ(10);
}
@Override
public void onItemClear() {
itemView.setTranslationZ(0);
}
}
public void setOnBookCheckedListener(OnBookCheckedListener listener) {

@ -56,10 +56,16 @@ public class BookcaseDetailedAdapter extends BookcaseAdapter {
public boolean onMove(int srcPosition, int targetPosition) {
Collections.swap(list, srcPosition, targetPosition);
notifyItemMoved(srcPosition, targetPosition);
notifyItemChanged(srcPosition);
notifyItemChanged(targetPosition);
/*notifyItemChanged(srcPosition);
notifyItemChanged(targetPosition);*/
return true;
}
@Override
public void onEnd() {
App.getHandler().postDelayed(() -> notifyDataSetChanged(), 500);
}
};
}
@ -151,90 +157,7 @@ public class BookcaseDetailedAdapter extends BookcaseAdapter {
});
viewHolder.llBookRead.setOnLongClickListener(v -> {
if (!ismEditState()) {
/*AlertDialog bookDialog = MyAlertDialog.build(mContext)
.setTitle(book.getName())
.setItems(menu, (dialog, which) -> {
switch (which) {
case 0:
if (!isGroup) {
book.setSortCode(0);
}else {
book.setGroupSort(0);
}
mBookService.updateEntity(book);
mBookcasePresenter.init();
ToastUtils.showSuccess("书籍《" + book.getName() + "》移至顶部成功!");
break;
case 1:
downloadBook(book);
break;
case 2:
App.getApplication().newThread(() -> {
try {
if (unionChapterCathe(book)) {
DialogCreator.createTipDialog(mContext,
"缓存导出成功,导出目录:"
+ APPCONST.TXT_BOOK_DIR);
} else {
DialogCreator.createTipDialog(mContext,
"章节目录为空或未找到缓存文件,缓存导出失败!");
}
} catch (IOException e) {
e.printStackTrace();
DialogCreator.createTipDialog(mContext,
"章节目录为空或未找到缓存文件,缓存导出失败!");
}
});
break;
case 3:
showDeleteBookDialog(book);
break;
}
})
.setNegativeButton(null, null)
.setPositiveButton(null, null)
.create();
bookDialog.show();*/
BottomMenu.show(book.getName(), menu)
.setOnMenuItemClickListener((dialog, text, which) -> {
switch (which) {
case 0:
if (!isGroup) {
book.setSortCode(0);
} else {
book.setGroupSort(0);
}
mBookService.updateEntity(book);
mBookcasePresenter.init();
ToastUtils.showSuccess("书籍《" + book.getName() + "》移至顶部成功!");
break;
case 1:
downloadBook(book);
break;
case 2:
App.getApplication().newThread(() -> {
try {
if (unionChapterCathe(book)) {
DialogCreator.createTipDialog(mContext,
"缓存导出成功,导出目录:"
+ APPCONST.TXT_BOOK_DIR);
} else {
DialogCreator.createTipDialog(mContext,
"章节目录为空或未找到缓存文件,缓存导出失败!");
}
} catch (IOException e) {
e.printStackTrace();
DialogCreator.createTipDialog(mContext,
"章节目录为空或未找到缓存文件,缓存导出失败!");
}
});
break;
case 3:
showDeleteBookDialog(book);
break;
}
return false;
});
showBookMenu(book, position);
return true;
}
return false;

@ -57,15 +57,21 @@ public class BookcaseDragAdapter extends BookcaseAdapter {
list.remove(srcPosition);
list.add(targetPosition, shelfBean);
notifyItemMoved(srcPosition, targetPosition);
int start = srcPosition;
/*int start = srcPosition;
int end = targetPosition;
if (start > end) {
start = targetPosition;
end = srcPosition;
}
notifyItemRangeChanged(start, end - start + 1);
notifyItemRangeChanged(start, end - start + 1);*/
return true;
}
@Override
public void onEnd() {
App.getHandler().postDelayed(() -> notifyDataSetChanged(), 500);
}
};
}
@ -136,106 +142,7 @@ public class BookcaseDragAdapter extends BookcaseAdapter {
});
viewHolder.ivBookImg.setOnLongClickListener(v -> {
if (!ismEditState()) {
/*AlertDialog bookDialog = MyAlertDialog.build(mContext)
.setTitle(book.getName())
.setItems(menu, (dialog, which) -> {
switch (which) {
case 0:
Intent intent = new Intent(mContext, BookDetailedActivity.class);
BitIntentDataManager.getInstance().putData(intent, book);
mContext.startActivity(intent);
break;
case 1:
if (!isGroup) {
book.setSortCode(0);
}else {
book.setGroupSort(0);
}
mBookService.updateEntity(book);
mBookcasePresenter.init();
ToastUtils.showSuccess("书籍《" + book.getName() + "》移至顶部成功!");
break;
case 2:
downloadBook(book);
break;
case 3:
App.getApplication().newThread(new Runnable() {
@Override
public void run() {
try {
if (unionChapterCathe(book)) {
DialogCreator.createTipDialog(mContext,
"缓存导出成功,导出目录:"
+ APPCONST.TXT_BOOK_DIR);
} else {
DialogCreator.createTipDialog(mContext,
"章节目录为空或未找到缓存文件,缓存导出失败!");
}
} catch (IOException e) {
e.printStackTrace();
DialogCreator.createTipDialog(mContext,
"章节目录为空或未找到缓存文件,缓存导出失败!");
}
}
});
break;
case 4:
showDeleteBookDialog(book);
break;
}
})
.setNegativeButton(null, null)
.setPositiveButton(null, null)
.create();
bookDialog.show();*/
BottomMenu.show(book.getName(), menu)
.setOnMenuItemClickListener((dialog, text, which) -> {
switch (which) {
case 0:
Intent intent = new Intent(mContext, BookDetailedActivity.class);
BitIntentDataManager.getInstance().putData(intent, book);
mContext.startActivity(intent);
break;
case 1:
if (!isGroup) {
book.setSortCode(0);
} else {
book.setGroupSort(0);
}
mBookService.updateEntity(book);
mBookcasePresenter.init();
ToastUtils.showSuccess("书籍《" + book.getName() + "》移至顶部成功!");
break;
case 2:
downloadBook(book);
break;
case 3:
App.getApplication().newThread(new Runnable() {
@Override
public void run() {
try {
if (unionChapterCathe(book)) {
DialogCreator.createTipDialog(mContext,
"缓存导出成功,导出目录:"
+ APPCONST.TXT_BOOK_DIR);
} else {
DialogCreator.createTipDialog(mContext,
"章节目录为空或未找到缓存文件,缓存导出失败!");
}
} catch (IOException e) {
e.printStackTrace();
DialogCreator.createTipDialog(mContext,
"章节目录为空或未找到缓存文件,缓存导出失败!");
}
}
});
break;
case 4:
showDeleteBookDialog(book);
break;
}
return false;
});
showBookMenu(book, position);
return true;
}
return false;

@ -0,0 +1,18 @@
package xyz.fycz.myreader.ui.adapter.helper;
/**
* @author fengyue
* @date 2021/6/3 17:47
*/
public interface IItemTouchHelperViewHolder {
/**
* item被选中在侧滑或拖拽过程中更新状态
*/
void onItemSelected();
/**
* item的拖拽或侧滑结束恢复默认的状态
*/
void onItemClear();
}

@ -9,6 +9,9 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.viewpager.widget.ViewPager;
import org.jetbrains.annotations.NotNull;
/**
* @author fengyue
* @date 2021/2/9 10:08
@ -44,11 +47,11 @@ public class ItemTouchCallback extends ItemTouchHelper.Callback {
}
/**
* 设置是否可以被拖拽
* 设置是否可以被长按拖拽
*
* @param canDrag 是true否false
*/
public void setDragEnable(boolean canDrag) {
public void setLongPressDragEnable(boolean canDrag) {
isCanDrag = canDrag;
}
@ -133,6 +136,19 @@ public class ItemTouchCallback extends ItemTouchHelper.Callback {
@Override
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
//不为空闲状态,即为拖拽或侧滑状态
if (viewHolder instanceof IItemTouchHelperViewHolder) {
IItemTouchHelperViewHolder itemTouchHelperViewHolder =
(IItemTouchHelperViewHolder) viewHolder;
itemTouchHelperViewHolder.onItemSelected();
}
}
if (onItemTouchListener != null) {
if (viewHolder == null) {
onItemTouchListener.onEnd();
}
}
super.onSelectedChanged(viewHolder, actionState);
final boolean swiping = actionState == ItemTouchHelper.ACTION_STATE_DRAG;
if (viewPager != null) {
@ -140,6 +156,21 @@ public class ItemTouchCallback extends ItemTouchHelper.Callback {
}
}
@Override
public void clearView(@NonNull @NotNull RecyclerView recyclerView, @NonNull @NotNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if (viewHolder instanceof IItemTouchHelperViewHolder) {
IItemTouchHelperViewHolder itemTouchHelperViewHolder =
(IItemTouchHelperViewHolder) viewHolder;
itemTouchHelperViewHolder.onItemClear();
}
}
@Override
public void onMoved(@NonNull @NotNull RecyclerView recyclerView, @NonNull @NotNull RecyclerView.ViewHolder viewHolder, int fromPos, @NonNull @NotNull RecyclerView.ViewHolder target, int toPos, int x, int y) {
super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y);
}
public interface OnItemTouchListener {
/**
* 当某个Item被滑动删除的时候
@ -156,5 +187,10 @@ public class ItemTouchCallback extends ItemTouchHelper.Callback {
* @return 开发者处理了操作应该返回true开发者没有处理就返回false
*/
boolean onMove(int srcPosition, int targetPosition);
/**
* 当滑动删除或拖拽结束时调用
*/
void onEnd();
}
}

@ -0,0 +1,16 @@
package xyz.fycz.myreader.ui.adapter.helper;
import androidx.recyclerview.widget.RecyclerView;
/**
* @author fengyue
* @date 2021/6/3 18:39
*/
public interface OnStartDragListener {
/**
* 当View需要拖拽时回调
*
* @param viewHolder The holder of view to drag
*/
void onStartDrag(RecyclerView.ViewHolder viewHolder);
}

@ -1,6 +1,8 @@
package xyz.fycz.myreader.ui.adapter.holder;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
@ -9,6 +11,7 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.RecyclerView;
import java.util.HashMap;
@ -26,6 +29,8 @@ import xyz.fycz.myreader.greendao.entity.rule.BookSource;
import xyz.fycz.myreader.model.sourceAnalyzer.BookSourceManager;
import xyz.fycz.myreader.ui.activity.SourceEditActivity;
import xyz.fycz.myreader.ui.adapter.BookSourceAdapter;
import xyz.fycz.myreader.ui.adapter.helper.IItemTouchHelperViewHolder;
import xyz.fycz.myreader.ui.adapter.helper.OnStartDragListener;
import xyz.fycz.myreader.util.ShareUtils;
import xyz.fycz.myreader.util.help.StringHelper;
import xyz.fycz.myreader.util.ToastUtils;
@ -36,7 +41,7 @@ import xyz.fycz.myreader.widget.swipemenu.SwipeMenuLayout;
* @author fengyue
* @date 2021/2/10 16:52
*/
public class BookSourceHolder extends ViewHolderImpl<BookSource> {
public class BookSourceHolder extends ViewHolderImpl<BookSource> implements IItemTouchHelperViewHolder {
private FragmentActivity activity;
private BookSourceAdapter adapter;
private HashMap<BookSource, Boolean> mCheckMap;
@ -46,16 +51,22 @@ public class BookSourceHolder extends ViewHolderImpl<BookSource> {
private CheckBox cbSourceSelect;
private TextView tvSourceName;
private ImageView ivSwipeLeft;
private ImageView ivMove;
private Button btTop;
private Button btBan;
private Button btShare;
private Button btDelete;
public BookSourceHolder(FragmentActivity activity, BookSourceAdapter adapter, BookSourceAdapter.OnSwipeListener onSwipeListener) {
private OnStartDragListener onStartDragListener;
public BookSourceHolder(FragmentActivity activity, BookSourceAdapter adapter,
BookSourceAdapter.OnSwipeListener onSwipeListener,
OnStartDragListener onStartDragListener) {
this.activity = activity;
this.adapter = adapter;
this.onSwipeListener = onSwipeListener;
mCheckMap = adapter.getCheckMap();
this.onStartDragListener = onStartDragListener;
}
@Override
@ -70,25 +81,39 @@ public class BookSourceHolder extends ViewHolderImpl<BookSource> {
cbSourceSelect = findById(R.id.cb_source_select);
tvSourceName = findById(R.id.tv_source_name);
ivSwipeLeft = findById(R.id.iv_swipe_left);
ivMove = findById(R.id.iv_move);
btTop = findById(R.id.bt_top);
btBan = findById(R.id.bt_ban);
btShare = findById(R.id.bt_share);
btDelete = findById(R.id.btnDelete);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public void onBind(BookSource data, int pos) {
public void onBind(RecyclerView.ViewHolder holder, BookSource data, int pos) {
banOrUse(data);
initClick(data, pos);
layout.setSwipeEnable(!adapter.ismEditState());
if (adapter.ismEditState()) {
cbSourceSelect.setVisibility(View.VISIBLE);
ivSwipeLeft.setVisibility(View.GONE);
ivMove.setVisibility(View.VISIBLE);
} else {
cbSourceSelect.setVisibility(View.GONE);
ivSwipeLeft.setVisibility(View.VISIBLE);
ivMove.setVisibility(View.GONE);
}
cbSourceSelect.setChecked(mCheckMap.get(data));
ivMove.setOnTouchListener((view, motionEvent) -> {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
//通知ItemTouchHelper开始拖拽
if (onStartDragListener != null) {
onStartDragListener.onStartDrag(holder);
}
}
return false;
}
);
}
private void initClick(BookSource data, int pos) {
@ -175,4 +200,14 @@ public class BookSourceHolder extends ViewHolderImpl<BookSource> {
btBan.setText(R.string.enable_use);
}
}
@Override
public void onItemSelected() {
getItemView().setTranslationZ(10);
}
@Override
public void onItemClear() {
getItemView().setTranslationZ(0);
}
}

@ -4,12 +4,15 @@ import android.app.Activity;
import android.view.View;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.App;
import xyz.fycz.myreader.base.adapter.ViewHolderImpl;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.rule.BookSource;
import xyz.fycz.myreader.model.sourceAnalyzer.BookSourceManager;
import xyz.fycz.myreader.ui.adapter.helper.IItemTouchHelperViewHolder;
import xyz.fycz.myreader.util.help.StringHelper;
import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
@ -20,7 +23,7 @@ import xyz.fycz.myreader.widget.CoverImageView;
* @author fengyue
* @date 2020/9/7 7:35
*/
public class BookStoreBookHolder extends ViewHolderImpl<Book> {
public class BookStoreBookHolder extends ViewHolderImpl<Book> implements IItemTouchHelperViewHolder {
private CoverImageView tvBookImg;
private TextView tvBookName;
@ -54,7 +57,7 @@ public class BookStoreBookHolder extends ViewHolderImpl<Book> {
}
@Override
public void onBind(Book data, int pos) {
public void onBind(RecyclerView.ViewHolder holder, Book data, int pos) {
tvBookName.setText(data.getName());
tvBookAuthor.setText(data.getAuthor());
tvBookNewestChapter.setText(StringHelper.isEmpty(data.getNewestChapterTitle()) ?
@ -75,4 +78,13 @@ public class BookStoreBookHolder extends ViewHolderImpl<Book> {
}
}
@Override
public void onItemSelected() {
getItemView().setTranslationZ(10);
}
@Override
public void onItemClear() {
getItemView().setTranslationZ(0);
}
}

@ -3,6 +3,7 @@ package xyz.fycz.myreader.ui.adapter.holder;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.adapter.ViewHolderImpl;
@ -26,7 +27,7 @@ public class CatalogHolder extends ViewHolderImpl<Chapter> {
}
@Override
public void onBind(Chapter data, int pos) {
public void onBind(RecyclerView.ViewHolder holder, Chapter data, int pos) {
if (ChapterService.isChapterCached(data.getBookId(), data.getTitle()) || data.getEnd() > 0) {
tvTitle.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(getContext(),R.drawable.selector_category_load), null, null, null);
} else {

@ -5,6 +5,9 @@ import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.adapter.ViewHolderImpl;
import xyz.fycz.myreader.common.APPCONST;
@ -49,7 +52,7 @@ public class FileHolder extends ViewHolderImpl<File> {
}
@Override
public void onBind(File data, int pos) {
public void onBind(RecyclerView.ViewHolder holder, File data, int pos) {
//判断是文件还是文件夹
if (data.isDirectory()){
setFolder(data);

@ -3,6 +3,8 @@ package xyz.fycz.myreader.ui.adapter.holder;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.HashMap;
import xyz.fycz.myreader.R;
@ -40,7 +42,7 @@ public class LocalSourceHolder extends ViewHolderImpl<BookSource> {
}
@Override
public void onBind(BookSource data, int pos) {
public void onBind(RecyclerView.ViewHolder holder, BookSource data, int pos) {
banOrUse(data);
cbSource.setChecked(mCheckMap.get(data));
tvEnable.setOnClickListener(v -> {

@ -6,6 +6,8 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.kongzue.dialogx.dialogs.BottomMenu;
import com.kongzue.dialogx.interfaces.OnMenuItemClickListener;
@ -49,7 +51,7 @@ public class ReadRecordHolder extends ViewHolderImpl<ReadRecord> {
}
@Override
public void onBind(ReadRecord data, int pos) {
public void onBind(RecyclerView.ViewHolder holder, ReadRecord data, int pos) {
if (!App.isDestroy((Activity) getContext())) {
ivBookImg.load(data.getBookImg(), data.getBookName(), data.getBookAuthor());
}

@ -6,6 +6,7 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
@ -67,7 +68,7 @@ public class ReplaceRuleHolder extends ViewHolderImpl<ReplaceRuleBean> {
}
@Override
public void onBind(ReplaceRuleBean data, int pos) {
public void onBind(RecyclerView.ViewHolder holder, ReplaceRuleBean data, int pos) {
banOrUse(data);
rlContent.setOnClickListener(v -> {

@ -5,6 +5,8 @@ import android.app.Activity;
import android.util.Log;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.zhy.view.flowlayout.TagFlowLayout;
import java.util.ArrayList;
@ -75,7 +77,7 @@ public class SearchBookHolder extends ViewHolderImpl<SearchBookBean> {
@SuppressLint("SetTextI18n")
@Override
public void onBind(SearchBookBean data, int pos) {
public void onBind(RecyclerView.ViewHolder holder, SearchBookBean data, int pos) {
List<Book> aBooks = mBooks.getValues(data);
if (aBooks == null || aBooks.size() == 0){
aBooks = new ArrayList<>();

@ -5,6 +5,7 @@ import android.text.TextUtils
import android.text.TextWatcher
import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import xyz.fycz.myreader.R
@ -30,7 +31,7 @@ class SourceEditHolder : ViewHolderImpl<EditEntity>() {
tvTip = findById(R.id.tv_tip)
}
override fun onBind(data: EditEntity, pos: Int) {
override fun onBind(holder: RecyclerView.ViewHolder, data: EditEntity, pos: Int) {
if (editText?.getTag(R.id.tag1) == null) {
val listener = object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {

@ -3,6 +3,9 @@ package xyz.fycz.myreader.ui.adapter.holder;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.adapter.ViewHolderImpl;
import xyz.fycz.myreader.greendao.entity.Book;
@ -36,7 +39,7 @@ public class SourceExchangeHolder extends ViewHolderImpl<Book> {
}
@Override
public void onBind(Book data, int pos) {
public void onBind(RecyclerView.ViewHolder holder, Book data, int pos) {
sourceTvTitle.setText(BookSourceManager.getSourceNameByStr(data.getSource()));
sourceTvChapter.setText(data.getNewestChapterTitle());
if (data.getSource() != null && data.getSource().equals(dialog.getmShelfBook().getSource()))

@ -15,8 +15,6 @@ import androidx.appcompat.app.AlertDialog;
import com.google.android.material.textfield.TextInputLayout;
import com.kongzue.dialogx.dialogs.BottomMenu;
import com.kongzue.dialogx.interfaces.BaseDialog;
import com.kongzue.dialogx.interfaces.OnDialogButtonClickListener;
import java.util.ArrayList;
import java.util.List;
@ -70,7 +68,7 @@ public class BookGroupDialog {
mGroupNames[i] = groupName;
}
if (isAdd) {
mGroupNames[mBookGroups.size()] = "添加分组";
mGroupNames[mBookGroups.size()] = "+ 新建分组";
}
}
@ -118,40 +116,18 @@ public class BookGroupDialog {
* 添加/重命名分组对话框
*/
public void showAddOrRenameGroupDia(boolean isRename, boolean isAddGroup, int groupNum, OnGroup onGroup) {
View view = LayoutInflater.from(mContext).inflate(R.layout.edit_dialog, null);
TextInputLayout textInputLayout = view.findViewById(R.id.text_input_lay);
int maxLen = 20;
textInputLayout.setCounterMaxLength(maxLen);
EditText editText = textInputLayout.getEditText();
editText.setHint("请输入分组名");
BookGroup bookGroup = !isRename ? new BookGroup() : mBookGroups.get(groupNum);
String oldName = bookGroup.getName();
if (isRename) {
editText.setText(oldName);
}
editText.requestFocus();
InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
mHandler.postDelayed(() -> {
imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
}, 220);
AlertDialog newGroupDia = MyAlertDialog.build(mContext)
.setTitle(!isRename ? "新建分组" : "重命名分组")
.setView(view)
.setCancelable(false)
.setPositiveButton("确认", null)
.setNegativeButton("取消", null)
.show();
Button posBtn = newGroupDia.getButton(AlertDialog.BUTTON_POSITIVE);
posBtn.setEnabled(false);
posBtn.setOnClickListener(v1 -> {
CharSequence newGroupName = editText.getText().toString();
int maxLen = 20;
MyAlertDialog.showFullScreenInputDia(mContext, !isRename ? "新建分组" : "重命名分组", "请输入分组名",
isRename ? oldName : "", null, true, maxLen, newGroupName -> {
for (CharSequence oldGroupName : mGroupNames) {
if (oldGroupName.equals(newGroupName)) {
ToastUtils.showWarring("分组[" + newGroupName + "]已存在,无法" + (!isRename ? "添加!" : "重命名!"));
return;
}
}
bookGroup.setName(newGroupName.toString());
bookGroup.setName(newGroupName);
if (!isRename) {
mBookGroupService.addBookGroup(bookGroup);
} else {
@ -165,33 +141,10 @@ public class BookGroupDialog {
ToastUtils.showSuccess("成功" +
(!isRename ? "添加分组[" : "成功将[" + oldName + "]重命名为[")
+ bookGroup.getName() + "]");
imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
newGroupDia.dismiss();
if (isAddGroup) {
if (onGroup != null) onGroup.addGroup();
}
});
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
String text = editText.getText().toString();
if (editText.getText().length() > 0 && editText.getText().length() <= maxLen && !text.equals(oldName)) {
posBtn.setEnabled(true);
} else {
posBtn.setEnabled(false);
}
}
});
}
/**

@ -1,5 +1,6 @@
package xyz.fycz.myreader.ui.dialog;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.text.Editable;
@ -18,6 +19,7 @@ import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.textfield.TextInputLayout;
import com.kongzue.dialogx.dialogs.BottomDialog;
import com.kongzue.dialogx.dialogs.FullScreenDialog;
import com.kongzue.dialogx.interfaces.OnBindView;
import xyz.fycz.myreader.R;
@ -26,6 +28,7 @@ import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.greendao.service.BookGroupService;
import xyz.fycz.myreader.util.CyptoUtils;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.StatusBarUtil;
import xyz.fycz.myreader.util.help.StringHelper;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.FingerprintUtils;
@ -45,12 +48,18 @@ public class MyAlertDialog {
return createInputDia(context, title, hint, initText, inputType, cancelable, maxLen, oic, posListener, null, null, null);
}
public static AlertDialog createInputDia(Context context, String title, String hint, String initText,
boolean cancelable, int maxLen, onInputChangeListener oic,
DialogInterface.OnClickListener posListener) {
return createInputDia(context, title, hint, initText, InputType.TYPE_CLASS_TEXT, cancelable, maxLen, oic, posListener);
}
public static AlertDialog createInputDia(Context context, String title, String hint, String initText,
Integer inputType, boolean cancelable, int maxLen, onInputChangeListener oic,
DialogInterface.OnClickListener posListener,
DialogInterface.OnClickListener negListener, String neutralBtn,
DialogInterface.OnClickListener neutralListener) {
View view = LayoutInflater.from(context).inflate(R.layout.edit_dialog, null);
View view = LayoutInflater.from(context).inflate(R.layout.edit_text, null);
TextInputLayout textInputLayout = view.findViewById(R.id.text_input_lay);
textInputLayout.setCounterMaxLength(maxLen);
@ -126,12 +135,62 @@ public class MyAlertDialog {
return inputDia;
}
public static AlertDialog createInputDia(Context context, String title, String hint, String initText,
boolean cancelable, int maxLen, onInputChangeListener oic,
DialogInterface.OnClickListener posListener) {
return createInputDia(context, title, hint, initText, InputType.TYPE_CLASS_TEXT, cancelable, maxLen, oic, posListener);
public static void showFullScreenInputDia(Context context, String title, String hint, String initText,
Integer inputType, boolean cancelable, int maxLen,
OnInputFinishListener posListener) {
FullScreenDialog.show(new OnBindView<FullScreenDialog>(R.layout.dialog_input) {
@Override
public void onBind(FullScreenDialog dialog, View view) {
TextView cancelBtn = view.findViewById(R.id.btn_cancel);
TextView finishBtn = view.findViewById(R.id.btn_finish);
TextView tvTitle = view.findViewById(R.id.tv_title);
TextInputLayout textInputLayout = view.findViewById(R.id.text_input_lay);
EditText editText = textInputLayout.getEditText();
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
tvTitle.setText(title);
cancelBtn.setOnClickListener(v -> {
imm.hideSoftInputFromWindow(editText.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
dialog.dismiss();
});
finishBtn.setOnClickListener(v -> {
imm.hideSoftInputFromWindow(editText.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
posListener.finish(editText.getText().toString());
dialog.dismiss();
});
textInputLayout.setCounterMaxLength(maxLen);
editText.setHint(hint);
if (inputType != null) editText.setInputType(inputType);
if (!StringHelper.isEmpty(initText)) editText.setText(initText);
editText.requestFocus();
App.getHandler().postDelayed(() -> imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED), 220);
finishBtn.setEnabled(false);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
String text = editText.getText().toString();
if (editText.getText().length() > 0 && editText.getText().length() <= maxLen && !text.equals(initText)) {
finishBtn.setEnabled(true);
} else {
finishBtn.setEnabled(false);
}
}
});
}
}).setCancelable(cancelable);
}
/**
* 验证隐私密码对话框
@ -142,6 +201,7 @@ public class MyAlertDialog {
public static void showPrivateVerifyDia(AppCompatActivity activity, OnVerify onVerify) {
showPrivateVerifyDia(activity, onVerify, null);
}
public static void showPrivateVerifyDia(AppCompatActivity activity, OnVerify onVerify, OnCancel onCancel) {
boolean openPrivate = SharedPreUtils.getInstance().getBoolean("openPrivate");
boolean openFingerprint = SharedPreUtils.getInstance().getBoolean("openFingerprint");
@ -168,12 +228,14 @@ public class MyAlertDialog {
/**
* 输入隐私密码对话框
*
* @param activity
* @param onVerify
*/
public static void showPrivatePwdInputDia(AppCompatActivity activity, OnVerify onVerify) {
showPrivatePwdInputDia(activity, onVerify, null);
}
public static void showPrivatePwdInputDia(AppCompatActivity activity, OnVerify onVerify, OnCancel onCancel) {
final String[] pwd = new String[1];
String pwds = SharedPreUtils.getInstance().getString("privatePwd");
@ -216,6 +278,7 @@ public class MyAlertDialog {
public static void showTipDialogWithLink(Context context, int msgId) {
showTipDialogWithLink(context, "提示", msgId);
}
public static void showTipDialogWithLink(Context context, String title, int msgId) {
/*TextView view = (TextView) LayoutInflater.from(context).inflate(R.layout.dialog_textview, null);
view.setText(msgId);
@ -231,6 +294,7 @@ public class MyAlertDialog {
}).setCancelButton("知道了");
}
public interface OnVerify {
void success(boolean needGoTo);
}
@ -242,4 +306,8 @@ public class MyAlertDialog {
public interface onInputChangeListener {
void onChange(String text);
}
public interface OnInputFinishListener{
void finish(String text);
}
}

@ -15,6 +15,7 @@ import android.widget.PopupMenu;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.kongzue.dialogx.dialogs.BottomMenu;
@ -41,6 +42,7 @@ import xyz.fycz.myreader.ui.activity.BookSourceActivity;
import xyz.fycz.myreader.ui.activity.SourceEditActivity;
import xyz.fycz.myreader.ui.adapter.BookSourceAdapter;
import xyz.fycz.myreader.ui.adapter.helper.ItemTouchCallback;
import xyz.fycz.myreader.ui.adapter.helper.OnStartDragListener;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.LoadingDialog;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
@ -70,6 +72,7 @@ public class DIYSourceFragment extends BaseFragment {
private boolean isSearch;
private PopupMenu featuresMenu;
private ItemTouchCallback itemTouchCallback;
private ItemTouchHelper itemTouchHelper;
private Disposable importSourceDis;
public DIYSourceFragment() {
@ -104,7 +107,7 @@ public class DIYSourceFragment extends BaseFragment {
mAdapter.toTop(which, bean);
}
}
});
}, viewHolder -> itemTouchHelper.startDrag(viewHolder));
refreshSources();
}
@ -118,9 +121,9 @@ public class DIYSourceFragment extends BaseFragment {
//设置拖拽
itemTouchCallback = new ItemTouchCallback();
itemTouchCallback.setOnItemTouchListener(mAdapter.getItemTouchListener());
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemTouchCallback);
itemTouchHelper = new ItemTouchHelper(itemTouchCallback);
itemTouchHelper.attachToRecyclerView(binding.recyclerView);
itemTouchCallback.setDragEnable(false);
itemTouchCallback.setLongPressDragEnable(false);
}
@SuppressLint("ClickableViewAccessibility")
@ -341,12 +344,10 @@ public class DIYSourceFragment extends BaseFragment {
featuresMenu.getMenu().setGroupVisible(R.id.edit_mode, true);
featuresMenu.getMenu().setGroupVisible(R.id.non_edit_mode, false);
mAdapter.setmEditState(true);
itemTouchCallback.setDragEnable(true);
} else if (itemId == R.id.action_finish) {
featuresMenu.getMenu().setGroupVisible(R.id.edit_mode, false);
featuresMenu.getMenu().setGroupVisible(R.id.non_edit_mode, true);
mAdapter.setmEditState(false);
itemTouchCallback.setDragEnable(false);
} else if (itemId == R.id.action_export) {
exportSources(mBookSources);
} else if (itemId == R.id.action_share) {

@ -28,6 +28,7 @@ import org.jetbrains.annotations.NotNull;
import java.text.Collator;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@ -127,19 +128,6 @@ public class BookcasePresenter implements BasePresenter {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
init();
break;
@ -298,7 +286,7 @@ public class BookcasePresenter implements BasePresenter {
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemTouchCallback);
itemTouchHelper.attachToRecyclerView(mBookcaseFragment.getRvBook());
itemTouchCallback.setOnItemTouchListener(mBookcaseAdapter.getItemTouchCallbackListener());
itemTouchCallback.setDragEnable(false);
itemTouchCallback.setLongPressDragEnable(false);
isBookcaseStyleChange = false;
} else {
mBookcaseAdapter.notifyDataSetChanged();
@ -368,6 +356,43 @@ public class BookcasePresenter implements BasePresenter {
}
public void refreshBook(Book book, boolean isChangeSource){
mBookcaseAdapter.getIsLoading().put(book.getId(), true);
mBookcaseAdapter.refreshBook(book.getChapterUrl());
final ArrayList<Chapter> mChapters = (ArrayList<Chapter>) mChapterService.findBookAllChapterByBookId(book.getId());
final ReadCrawler mReadCrawler = ReadCrawlerUtil.getReadCrawler(book.getSource());
BookApi.getBookChapters(book, mReadCrawler).flatMap(chapters -> Observable.create(emitter -> {
int noReadNum = chapters.size() - book.getChapterTotalNum();
book.setNoReadNum(Math.max(noReadNum, 0));
book.setNewestChapterTitle(chapters.get(chapters.size() - 1).getTitle());
mChapterService.updateAllOldChapterData(mChapters, chapters, book.getId());
mBookService.updateEntity(book);
if (isChangeSource){
if (mBookService.matchHistoryChapterPos(book, chapters)) {
ToastUtils.showSuccess("历史阅读章节匹配成功!");
} else {
ToastUtils.showError("历史阅读章节匹配失败!");
}
}
emitter.onNext(book);
emitter.onComplete();
})).compose(RxUtils::toSimpleSingle).subscribe(new MyObserver<Object>() {
@Override
public void onNext(@NotNull Object o) {
mBookcaseAdapter.getIsLoading().put(book.getId(), false);
mBookcaseAdapter.refreshBook(book.getChapterUrl());
}
@Override
public void onError(Throwable e) {
mBookcaseAdapter.getIsLoading().put(book.getId(), false);
mBookcaseAdapter.refreshBook(book.getChapterUrl());
ToastUtils.showError(String.format("《%s》目录刷新失败", book.getName()));
if (App.isDebug()) e.printStackTrace();
}
});
}
private synchronized void refreshBookshelf() {
refreshIndex++;
if (refreshIndex < mBooks.size()) {
@ -529,7 +554,7 @@ public class BookcasePresenter implements BasePresenter {
if (canEditBookcase()) {
mMainActivity.getViewPagerMain().setEnableScroll(false);
mBookcaseFragment.getSrlContent().setEnableRefresh(false);
itemTouchCallback.setDragEnable(mSetting.getSortStyle() == 0);
itemTouchCallback.setLongPressDragEnable(mSetting.getSortStyle() == 0);
mBookcaseAdapter.setmEditState(true);
mBookcaseFragment.getRlBookEdit().setVisibility(View.VISIBLE);
mMainActivity.initMenuAnim();
@ -543,7 +568,7 @@ public class BookcasePresenter implements BasePresenter {
} else {
mMainActivity.getViewPagerMain().setEnableScroll(true);
mBookcaseFragment.getSrlContent().setEnableRefresh(true);
itemTouchCallback.setDragEnable(false);
itemTouchCallback.setLongPressDragEnable(false);
mBookcaseAdapter.setmEditState(false);
mBookcaseFragment.getRlBookEdit().setVisibility(View.GONE);
mMainActivity.initMenuAnim();
@ -661,6 +686,21 @@ public class BookcasePresenter implements BasePresenter {
return this.ogcl != null;
}
//加入分组
public void addGroup(Book book){
mBookGroupDia.addGroup(book, new BookGroupDialog.OnGroup() {
@Override
public void change() {
init();
}
@Override
public void addGroup() {
mBookcaseFragment.getmBtnAddGroup().performClick();
}
});
}
/**********************************************缓存书籍***************************************************************/
/**
* 缓存所有书籍

@ -33,6 +33,7 @@ import xyz.fycz.myreader.util.utils.OkHttpUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
@Keep
@SuppressWarnings({"unused"})
public interface JsExtensions {
String TAG = JsExtensions.class.getSimpleName();

@ -0,0 +1,225 @@
package xyz.fycz.myreader.util.utils;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Log;
import android.widget.ImageView;
import com.google.zxing.EncodeHintType;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import cn.bingoogolapple.qrcode.zxing.QRCodeEncoder;
import io.reactivex.Single;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.annotations.NonNull;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.observer.MySingleObserver;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.entity.SharedBook;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.model.sourceAnalyzer.BookSourceManager;
import xyz.fycz.myreader.util.IOUtils;
import xyz.fycz.myreader.util.ShareUtils;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.ToastUtils;
/**
* @author fengyue
* @date 2021/6/3 21:46
*/
public class ShareBookUtil {
/**
* 分享书籍
*/
public static void shareBook(Context context, Book mBook, ImageView cover) {
if ("本地书籍".equals(mBook.getType())) {
File file = new File(mBook.getChapterUrl());
if (!file.exists()) {
ToastUtils.showWarring("书籍源文件不存在,无法分享!");
return;
}
try {
ShareUtils.share(context, file, mBook.getName() + ".txt", "text/plain");
} catch (Exception e) {
String dest = APPCONST.SHARE_FILE_DIR + File.separator + mBook.getName() + ".txt";
FileUtils.copy(mBook.getChapterUrl(), dest);
ShareUtils.share(context, new File(dest), mBook.getName() + ".txt", "text/plain");
}
return;
}
ToastUtils.showInfo("正在生成分享图片");
Single.create((SingleOnSubscribe<File>) emitter -> {
// 使用url
String url = SharedPreUtils.getInstance().getString(context.getString(R.string.downloadLink), URLCONST.LAN_ZOUS_URL);
if (url == null)
url = "";
int maxLength = 1273 - 1 - url.length();
SharedBook sharedBook = SharedBook.bookToSharedBook(mBook);
url = url + "#" + GsonExtensionsKt.getGSON().toJson(sharedBook);
Log.d("QRcode", "Length=" + url.length() + "\n" + url);
Bitmap bitmap;
QRCodeEncoder.HINTS.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
bitmap = QRCodeEncoder.syncEncodeQRCode(url, 360);
QRCodeEncoder.HINTS.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
File share = makeShareFile(context, mBook, cover, bitmap);
if (share == null) {
ToastUtils.showError("分享图片生成失败");
return;
}
emitter.onSuccess(share);
}).compose(RxUtils::toSimpleSingle)
.subscribe(new MySingleObserver<File>() {
@Override
public void onSuccess(@NonNull File File) {
share(context, File);
}
});
}
/**
* 生成分享图片
*
* @param QRCode
* @return
*/
private static File makeShareFile(Context context, Book mBook, ImageView cover, Bitmap QRCode) {
FileOutputStream fos = null;
try {
Bitmap back = BitmapFactory.decodeStream(context.getResources().getAssets().open("share.png")).copy(Bitmap.Config.ARGB_8888, true);
int backWidth = back.getWidth();
int backHeight = back.getHeight();
int margin = 60;
int marginTop = 24;
cover.setDrawingCacheEnabled(true);
Bitmap img = Bitmap.createBitmap(cover.getDrawingCache()).copy(Bitmap.Config.ARGB_8888, true);
cover.setDrawingCacheEnabled(false);
img = BitmapUtil.getBitmap(img, 152, 209);
Canvas cv = new Canvas(back);
cv.drawBitmap(img, margin, margin + marginTop * 2, null);
TextPaint textPaint = new TextPaint();
textPaint.setAntiAlias(true);
textPaint.setFilterBitmap(true);
textPaint.setColor(Color.BLACK);
textPaint.setTextSize(40);
String name = TextUtils.ellipsize(mBook.getName(), textPaint, backWidth - margin + marginTop * 3 - img.getWidth(), TextUtils.TruncateAt.END).toString();
cv.drawText(name, margin + marginTop + img.getWidth(), margin + marginTop * 4, textPaint);
textPaint.setColor(context.getResources().getColor(R.color.origin));
textPaint.setTextSize(32);
cv.drawText(mBook.getAuthor(), margin + marginTop + img.getWidth(), margin + marginTop * 6, textPaint);
textPaint.setColor(Color.BLACK);
cv.drawText(mBook.getType() == null ? "" : mBook.getType(), margin + marginTop + img.getWidth(), margin + marginTop * 8, textPaint);
cv.drawText("书源:" + BookSourceManager.getSourceNameByStr(mBook.getSource()), margin + marginTop + img.getWidth(), margin + marginTop * 10, textPaint);
int textSize = 35;
int textInterval = textSize / 2;
textPaint.setTextSize(textSize);
drawDesc(getDescLines(mBook, backWidth - margin * 2, textPaint), textPaint, cv, margin + marginTop * 4 + img.getHeight(), margin, textInterval);
cv.drawBitmap(QRCode, backWidth - QRCode.getWidth(), backHeight - QRCode.getHeight(), null);
cv.save();// 保存
cv.restore();// 存储
File share = FileUtils.getFile(APPCONST.SHARE_FILE_DIR + mBook.getName() + "_share.png");
fos = new FileOutputStream(share);
back.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
Log.i("tag", "saveBitmap success: " + share.getAbsolutePath());
back.recycle();
img.recycle();
QRCode.recycle();
return share;
} catch (Exception e) {
e.printStackTrace();
ToastUtils.showError(e.getLocalizedMessage() + "");
return null;
} finally {
IOUtils.close(fos);
}
}
/**
* 分享生成的图片
*
* @param share
*/
private static void share(Context context, File share) {
ShareUtils.share(context, share, "分享书籍", "image/png");
}
/**
* 绘制简介
*
* @param lines
* @param textPaint
* @param canvas
* @param top
* @param left
* @param textInterval
*/
private static void drawDesc(List<String> lines, TextPaint textPaint, Canvas canvas, int top, int left, int textInterval) {
float interval = textInterval + textPaint.getTextSize();
for (String line : lines) {
canvas.drawText(line, left, top, textPaint);
top += interval;
}
}
/**
* 生成简介lines
*
* @param width
* @param textPaint
* @return
*/
private static List<String> getDescLines(Book mBook, int width, TextPaint textPaint) {
List<String> lines = new ArrayList<>();
String desc = StringUtils.halfToFull(" ") + mBook.getDesc();
int i = 0;
int wordCount = 0;
String subStr = null;
while (desc.length() > 0) {
if (i == 9) {
lines.add(TextUtils.ellipsize(desc, textPaint, width / 1.8f, TextUtils.TruncateAt.END).toString());
break;
}
wordCount = textPaint.breakText(desc, true, width, null);
subStr = desc.substring(0, wordCount);
lines.add(subStr);
desc = desc.substring(wordCount);
i++;
}
return lines;
}
}

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M348.16,153.6m40.96,0l245.76,0q40.96,0 40.96,40.96l0,286.72q0,40.96 -40.96,40.96l-245.76,0q-40.96,0 -40.96,-40.96l0,-286.72q0,-40.96 40.96,-40.96Z"
android:fillColor="#689CD5"/>
<path
android:pathData="M174.08,409.6m40.96,0l593.92,0q40.96,0 40.96,40.96l0,378.88q0,40.96 -40.96,40.96l-593.92,0q-40.96,0 -40.96,-40.96l0,-378.88q0,-40.96 40.96,-40.96Z"
android:fillColor="#81B7EF"/>
<path
android:pathData="M122.88,588.8m40.96,0l696.32,0q40.96,0 40.96,40.96l0,199.68q0,40.96 -40.96,40.96l-696.32,0q-40.96,0 -40.96,-40.96l0,-199.68q0,-40.96 40.96,-40.96Z"
android:fillColor="#689CD5"/>
<path
android:pathData="M511.99,481.51C479.65,507.55 458.66,516.92 449.02,509.59c-9.64,-7.31 -7.6,-31.05 6.12,-71.22 -33.6,-24.41 -48.61,-42.36 -45.03,-53.86 3.58,-11.51 25.83,-16.82 66.74,-15.93C488.57,327.66 500.28,307.2 511.99,307.2s23.42,20.46 35.13,61.38c40.83,-1.17 63.08,4.15 66.74,15.93 3.67,11.79 -11.35,29.74 -45.03,53.86 13.34,40.45 15.38,64.19 6.12,71.22 -9.27,7.03 -30.25,-2.33 -62.96,-28.09z"
android:fillColor="#E2EEFD"/>
</vector>

@ -4,15 +4,12 @@
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M348.16,153.6m40.96,0l245.76,0q40.96,0 40.96,40.96l0,286.72q0,40.96 -40.96,40.96l-245.76,0q-40.96,0 -40.96,-40.96l0,-286.72q0,-40.96 40.96,-40.96Z"
android:fillColor="#689CD5"/>
android:fillColor="#FF000000"
android:pathData="M192,106.67m32,0l576,0q32,0 32,32l0,0q0,32 -32,32l-576,0q-32,0 -32,-32l0,0q0,-32 32,-32Z"/>
<path
android:pathData="M174.08,409.6m40.96,0l593.92,0q40.96,0 40.96,40.96l0,378.88q0,40.96 -40.96,40.96l-593.92,0q-40.96,0 -40.96,-40.96l0,-378.88q0,-40.96 40.96,-40.96Z"
android:fillColor="#81B7EF"/>
android:fillColor="#FF000000"
android:pathData="M544,248.36m0,32l0,576q0,32 -32,32l0,0q-32,0 -32,-32l0,-576q0,-32 32,-32l0,0q32,0 32,32Z"/>
<path
android:pathData="M122.88,588.8m40.96,0l696.32,0q40.96,0 40.96,40.96l0,199.68q0,40.96 -40.96,40.96l-696.32,0q-40.96,0 -40.96,-40.96l0,-199.68q0,-40.96 40.96,-40.96Z"
android:fillColor="#689CD5"/>
<path
android:pathData="M511.99,481.51C479.65,507.55 458.66,516.92 449.02,509.59c-9.64,-7.31 -7.6,-31.05 6.12,-71.22 -33.6,-24.41 -48.61,-42.36 -45.03,-53.86 3.58,-11.51 25.83,-16.82 66.74,-15.93C488.57,327.66 500.28,307.2 511.99,307.2s23.42,20.46 35.13,61.38c40.83,-1.17 63.08,4.15 66.74,15.93 3.67,11.79 -11.35,29.74 -45.03,53.86 13.34,40.45 15.38,64.19 6.12,71.22 -9.27,7.03 -30.25,-2.33 -62.96,-28.09z"
android:fillColor="#E2EEFD"/>
android:fillColor="#FF000000"
android:pathData="M489.39,251.56a32,32 0,0 1,41.64 -3.07l3.58,3.07 232.32,232.32a32,32 0,0 1,-41.69 48.34l-3.58,-3.11L512,319.45l-209.66,209.66a32,32 0,0 1,-41.69 3.11l-3.58,-3.11a32,32 0,0 1,-3.07 -41.64l3.07,-3.58 232.32,-232.32z"/>
</vector>

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/nothing" />
<stroke
android:width="1dp"
android:color="@color/textAssist" />
<corners
android:topLeftRadius="20dp"
android:topRightRadius="20dp"
android:bottomRightRadius="20dp"
android:bottomLeftRadius="20dp"/>
</shape>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:color="@color/md_blue_400"/>
<item android:state_enabled="false" android:color="@color/textSecondary"/>
</selector>

@ -0,0 +1,45 @@
<?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="600dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/btn_cancel"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="@string/cancel"
android:textColor="@color/textPrimary"
android:textSize="@dimen/text_normal_size" />
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/app_name"
android:textColor="@color/textPrimary"
android:textSize="@dimen/text_medium_size"
android:layout_weight="1" />
<TextView
android:id="@+id/btn_finish"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:text="@string/finish"
android:textColor="@drawable/selector_btn_input_finish"
android:textSize="@dimen/text_normal_size" />
</LinearLayout>
<include layout="@layout/edit_text"/>
</LinearLayout>

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.textfield.TextInputLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/text_input_lay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
app:counterEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.textfield.TextInputLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/text_input_lay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
android:paddingVertical="5dp"
app:counterEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text" />
</com.google.android.material.textfield.TextInputLayout>

@ -31,6 +31,7 @@
<com.scwang.smartrefresh.layout.SmartRefreshLayout
android:id="@+id/srl_book_list"
android:layout_above="@+id/rl_book_edit"
android:layout_width="match_parent"
android:layout_height="match_parent">

@ -26,7 +26,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@drawable/ic_top"
android:src="@drawable/ic_leaderboard"
/>
<TextView
android:layout_width="wrap_content"

@ -47,6 +47,17 @@
app:srcCompat="@drawable/ic_swipe_left"
app:tint="@color/textAssist" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_move"
android:visibility="gone"
android:layout_width="30dp"
android:layout_height="30dp"
android:padding="7dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
app:srcCompat="@drawable/ic_line_spacing3"
app:tint="@color/textAssist" />
</RelativeLayout>

@ -15,18 +15,6 @@
android:orientation="horizontal"
android:padding="5dp">
<CheckBox
android:id="@+id/cb_book_select"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:clickable="false"
android:enabled="true"
android:focusable="true"
android:paddingEnd="2dp"
android:theme="@style/MyCheckBox"
android:visibility="gone" />
<xyz.fycz.myreader.widget.CoverImageView
android:id="@+id/iv_book_img"
android:layout_width="48dp"

@ -0,0 +1,233 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="10dp"
android:paddingHorizontal="10dp">
<RelativeLayout
android:id="@+id/rl_book_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingVertical="5dp">
<LinearLayout
android:id="@+id/ll_book_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<xyz.fycz.myreader.widget.CoverImageView
android:id="@+id/iv_book_img"
android:layout_width="40dp"
android:layout_height="55dp"
android:scaleType="fitXY"
app:srcCompat="@mipmap/default_cover" />
<LinearLayout
android:id="@+id/ll_book_read"
android:paddingHorizontal="5dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_book_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:padding="4dp"
android:text="@string/app_name"
android:textColor="@color/textPrimary"
android:textSize="@dimen/text_normal_size" />
<TextView
android:id="@+id/tv_book_author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:padding="4dp"
android:text="@string/app_name"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_default_size" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/tv_theme_mode_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="5dp"
android:background="@drawable/menu_book_detail"
android:paddingHorizontal="10dp"
android:paddingVertical="5dp"
android:text="详情"
android:textColor="@color/textPrimary"
android:textSize="@dimen/text_small_size" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@color/sys_window_back" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_marginVertical="5dp"
android:paddingVertical="5dp">
<TextView
android:id="@+id/tv_top"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_book_Top"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_top" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/sc_is_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textColor="@color/textPrimary"
android:textSize="@dimen/text_small_size"
android:text="@string/menu_is_update"/>
</LinearLayout>
<TextView
android:id="@+id/tv_download"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_book_download"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_download" />
<TextView
android:id="@+id/tv_export_cathe"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_book_cache"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_export" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@color/sys_window_back" />
<LinearLayout
android:layout_width="match_parent"
android:layout_marginVertical="5dp"
android:layout_height="55dp"
android:paddingVertical="5dp">
<TextView
android:id="@+id/tv_change_source"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_change_source"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_change" />
<TextView
android:id="@+id/tv_set_group"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_group_setting"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_group" />
<TextView
android:id="@+id/tv_share"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_share"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_share" />
<TextView
android:id="@+id/tv_remove"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_book_delete"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_delete" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@color/sys_window_back" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_marginVertical="5dp"
android:paddingVertical="5dp">
<TextView
android:id="@+id/tv_edit"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_edit"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_edit" />
<TextView
android:id="@+id/tv_refresh"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/refresh_books"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_refresh" />
<TextView
android:id="@+id/tv_link"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_open_link"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_link" />
<TextView
android:id="@+id/tv_edit_source"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/book_source"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_source" />
</LinearLayout>
</LinearLayout>

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="10dp"
android:paddingHorizontal="10dp">
<RelativeLayout
android:id="@+id/rl_book_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingVertical="5dp">
<LinearLayout
android:id="@+id/ll_book_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<xyz.fycz.myreader.widget.CoverImageView
android:id="@+id/iv_book_img"
android:layout_width="40dp"
android:layout_height="55dp"
android:scaleType="fitXY"
app:srcCompat="@mipmap/default_cover" />
<LinearLayout
android:id="@+id/ll_book_read"
android:paddingHorizontal="5dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_book_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:padding="4dp"
android:text="@string/app_name"
android:textColor="@color/textPrimary"
android:textSize="@dimen/text_normal_size" />
<TextView
android:id="@+id/tv_book_author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:padding="4dp"
android:text="@string/app_name"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_default_size" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/tv_theme_mode_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="5dp"
android:background="@drawable/menu_book_detail"
android:paddingHorizontal="10dp"
android:paddingVertical="5dp"
android:text="详情"
android:textColor="@color/textPrimary"
android:textSize="@dimen/text_small_size" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@color/sys_window_back" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_marginVertical="5dp"
android:paddingVertical="5dp">
<TextView
android:id="@+id/tv_edit"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_edit"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_edit" />
<TextView
android:id="@+id/tv_top"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_book_Top"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_top" />
<TextView
android:id="@+id/tv_set_group"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_group_setting"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_group" />
<TextView
android:id="@+id/tv_remove"
style="@style/MAppTheme.TextAppearance.BookMenu"
android:clickable="true"
android:focusable="true"
android:text="@string/menu_book_delete"
app:drawableTint="@color/textPrimary"
app:drawableTopCompat="@drawable/ic_delete" />
</LinearLayout>
</LinearLayout>

@ -61,7 +61,7 @@
<string name="menu_book_detail">书籍详情</string>
<string name="menu_book_download">缓存书籍</string>
<string name="menu_book_cache">导出缓存</string>
<string name="menu_book_delete">删除/移除书籍</string>
<string name="menu_book_delete">删除书籍</string>
<string name="menu_bookcase_edit">编辑书架</string>
<string name="menu_bookcase_style">切换布局</string>

@ -192,6 +192,7 @@
<item name="android:textSize">@dimen/text_small_size</item>
</style>
<style name="dialogWindowAnim" parent="android:Animation" mce_bogus="1">
<item name="android:windowEnterAnimation">@anim/slide_bottom_in</item>
<item name="android:windowExitAnimation">@anim/slide_bottom_out</item>
@ -226,4 +227,15 @@
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
<!--bookMenu-->
<style name="MAppTheme.TextAppearance.BookMenu">
<item name="android:background">?android:attr/selectableItemBackground</item>
<item name="android:gravity">center_horizontal|bottom</item>
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:layout_weight">1</item>
<item name="android:textColor">@color/textPrimary</item>
<item name="android:textSize">@dimen/text_small_size</item>
</style>
</resources>

@ -1,2 +1,2 @@
#Wed Jun 02 20:55:52 CST 2021
VERSION_CODE=206
#Thu Jun 03 14:18:11 CST 2021
VERSION_CODE=207

Loading…
Cancel
Save