Commit b6ce8584 authored by jyx's avatar jyx

添加分页

parent 31277fae
...@@ -178,8 +178,10 @@ dependencies { ...@@ -178,8 +178,10 @@ dependencies {
// 网络请求 // 网络请求
implementation 'com.squareup.retrofit2:retrofit:2.7.1' implementation 'com.squareup.retrofit2:retrofit:2.7.1'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0' implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
// 异步 // 异步
implementation "io.reactivex.rxjava2:rxjava:2.2.6" implementation "io.reactivex.rxjava2:rxjava:2.2.10"
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex:rxandroid:1.2.1' implementation 'io.reactivex:rxandroid:1.2.1'
// 状态栏适配 // 状态栏适配
implementation 'com.readystatesoftware.systembartint:systembartint:1.0.3' implementation 'com.readystatesoftware.systembartint:systembartint:1.0.3'
...@@ -249,6 +251,9 @@ dependencies { ...@@ -249,6 +251,9 @@ dependencies {
implementation 'com.squareup:javawriter:2.1.1' implementation 'com.squareup:javawriter:2.1.1'
// umeng // umeng
// 数据库
implementation 'org.litepal.guolindev:core:3.1.1'
// 穿山甲聚合 接入【穿山甲、快手】 // 穿山甲聚合 接入【穿山甲、快手】
implementation "com.pangle.cn:mediation-sdk:6.1.2.5" implementation "com.pangle.cn:mediation-sdk:6.1.2.5"
// 穿山甲Applog 上报组件(必须) // 穿山甲Applog 上报组件(必须)
......
...@@ -117,6 +117,10 @@ ...@@ -117,6 +117,10 @@
android:name=".ui.activitys.BookReadActivity" android:name=".ui.activitys.BookReadActivity"
android:exported="false" android:exported="false"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<activity
android:name=".ui.activitys.LongBookReadActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity <activity
android:name=".ui.activitys.WxLoginActivity" android:name=".ui.activitys.WxLoginActivity"
android:configChanges="orientation|screenSize|keyboardHidden" android:configChanges="orientation|screenSize|keyboardHidden"
......
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<!--
Define the database name of your application.
By default each database name should be end with .db.
If you didn't name your database end with .db,
LitePal would plus the suffix automatically for you.
For example:
<dbname value="demo" />
-->
<dbname value="bookRepository" />
<!--
Define the version of your database. Each time you want
to upgrade your database, the version tag would helps.
Modify the models you defined in the mapping tag, and just
make the version value plus one, the upgrade of database
will be processed automatically without concern.
For example:
<version value="1" />
-->
<version value="1" />
<list>
<mapping class="com.mints.enjoyboxjia.read.models.BookModel" />
<mapping class="com.mints.enjoyboxjia.read.models.TxtChapterModel" />
<mapping class="com.mints.enjoyboxjia.read.models.BookRecordModel" />
</list>
</litepal>
\ No newline at end of file
package com.mints.enjoyboxjia; package com.mints.enjoyboxjia;
import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
...@@ -26,7 +27,6 @@ import com.orhanobut.logger.FormatStrategy; ...@@ -26,7 +27,6 @@ import com.orhanobut.logger.FormatStrategy;
import com.orhanobut.logger.Logger; import com.orhanobut.logger.Logger;
import com.orhanobut.logger.PrettyFormatStrategy; import com.orhanobut.logger.PrettyFormatStrategy;
import com.tencent.bugly.crashreport.CrashReport; import com.tencent.bugly.crashreport.CrashReport;
import com.umeng.commonsdk.UMConfigure;
import rx.Scheduler; import rx.Scheduler;
import rx.schedulers.Schedulers; import rx.schedulers.Schedulers;
...@@ -39,6 +39,7 @@ import rx.schedulers.Schedulers; ...@@ -39,6 +39,7 @@ import rx.schedulers.Schedulers;
public class MintsApplication extends MultiDexApplication { public class MintsApplication extends MultiDexApplication {
private static Context mContext; private static Context mContext;
private static MintsApplication mApplication;
public static String OAID = ""; public static String OAID = "";
private static LocalBroadcastManager mLocalBroadcatManager; private static LocalBroadcastManager mLocalBroadcatManager;
private Scheduler defaultSubscribeScheduler; private Scheduler defaultSubscribeScheduler;
...@@ -52,9 +53,14 @@ public class MintsApplication extends MultiDexApplication { ...@@ -52,9 +53,14 @@ public class MintsApplication extends MultiDexApplication {
return mContext; return mContext;
} }
public static MintsApplication getApplication() {
return mApplication;
}
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
mApplication = this;
mContext = this.getApplicationContext(); mContext = this.getApplicationContext();
// 友盟SDK预初始化函数 // 友盟SDK预初始化函数
......
...@@ -18,8 +18,12 @@ object Constant { ...@@ -18,8 +18,12 @@ object Constant {
const val PAGE_SIZE = 10 const val PAGE_SIZE = 10
const val ROUTE_BOOK_ID = "BOOK_ID"
// 协议地址 // 协议地址
var REGISTER_URL = BuildConfig.MainIp+"/agreements/${VersionUtils.getEndPkg()}/yhxy.html"//注册协议 var REGISTER_URL =
var PRIVACY_URL = BuildConfig.MainIp+"/agreements/${VersionUtils.getEndPkg()}/syzc.html"//隐私协议 BuildConfig.MainIp + "/agreements/${VersionUtils.getEndPkg()}/yhxy.html"//注册协议
var MEMBERS_URL = BuildConfig.MainIp+"/agreements/${VersionUtils.getEndPkg()}/gmxy.html"//会员付费服务协议 var PRIVACY_URL = BuildConfig.MainIp + "/agreements/${VersionUtils.getEndPkg()}/syzc.html"//隐私协议
var MEMBERS_URL =
BuildConfig.MainIp + "/agreements/${VersionUtils.getEndPkg()}/gmxy.html"//会员付费服务协议
} }
\ No newline at end of file
package com.mints.enjoyboxjia.mvp.model;
import java.io.Serializable;
/**
* @author Assen
* @date 2024/9/25
* @desc
*/
public class BookChapterBean implements Serializable {
/**
* id : 408212
* chapterId : 407740
* content :
* 女儿来到这个世界上的第五年。
* 出门上班前,我照常给了她一个告别吻。
* 女儿神色呆滞,没有任何回应。
* 我有些失望地把头放回冰箱,或许,下次放在烤箱里,这个吻会更有温度。
* contentMd :
* 女儿来到这个世界上的第五年。
* 出门上班前,我照常给了她一个告别吻。
* 女儿神色呆滞,没有任何回应。
* 我有些失望地把头放回冰箱,或许,下次放在烤箱里,这个吻会更有温度。
*/
private String id;
private String chapterId;
private String content;
private String contentMd;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getChapterId() {
return chapterId;
}
public void setChapterId(String chapterId) {
this.chapterId = chapterId;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getContentMd() {
return contentMd;
}
public void setContentMd(String contentMd) {
this.contentMd = contentMd;
}
}
package com.mints.enjoyboxjia.mvp.model; package com.mints.enjoyboxjia.mvp.model;
import com.mints.enjoyboxjia.read.models.TxtChapterModel;
import java.io.Serializable; import java.io.Serializable;
import java.util.List;
public class BookContentBean implements Serializable { public class BookContentBean implements Serializable {
/** /**
...@@ -44,13 +47,13 @@ public class BookContentBean implements Serializable { ...@@ -44,13 +47,13 @@ public class BookContentBean implements Serializable {
private int id; private int id;
private String userId; private String userId;
private Object username; private String username;
private String title; private String title;
private String avatar; private String avatar;
private String author; private String author;
private String summary; private String summary;
private Object content; private String content;
private Object contentMd; private String contentMd;
private String free; private String free;
private String freeMd; private String freeMd;
private String charge; private String charge;
...@@ -76,7 +79,7 @@ public class BookContentBean implements Serializable { ...@@ -76,7 +79,7 @@ public class BookContentBean implements Serializable {
private int collectCount; private int collectCount;
private boolean activeReadType; private boolean activeReadType;
private int freeNum; private int freeNum;
private Object articleChapterList; private List<ArticleChapterList> articleChapterList;
public int getId() { public int getId() {
return id; return id;
...@@ -98,7 +101,7 @@ public class BookContentBean implements Serializable { ...@@ -98,7 +101,7 @@ public class BookContentBean implements Serializable {
return username; return username;
} }
public void setUsername(Object username) { public void setUsername(String username) {
this.username = username; this.username = username;
} }
...@@ -134,19 +137,19 @@ public class BookContentBean implements Serializable { ...@@ -134,19 +137,19 @@ public class BookContentBean implements Serializable {
this.summary = summary; this.summary = summary;
} }
public Object getContent() { public String getContent() {
return content; return content;
} }
public void setContent(Object content) { public void setContent(String content) {
this.content = content; this.content = content;
} }
public Object getContentMd() { public String getContentMd() {
return contentMd; return contentMd;
} }
public void setContentMd(Object contentMd) { public void setContentMd(String contentMd) {
this.contentMd = contentMd; this.contentMd = contentMd;
} }
...@@ -350,11 +353,100 @@ public class BookContentBean implements Serializable { ...@@ -350,11 +353,100 @@ public class BookContentBean implements Serializable {
this.freeNum = freeNum; this.freeNum = freeNum;
} }
public Object getArticleChapterList() { public List<ArticleChapterList> getArticleChapterList() {
return articleChapterList; return articleChapterList;
} }
public void setArticleChapterList(Object articleChapterList) { public void setArticleChapterList(List<ArticleChapterList> articleChapterList) {
this.articleChapterList = articleChapterList; this.articleChapterList = articleChapterList;
} }
public static class ArticleChapterList implements Serializable {
/**
* id : 407740
* bookId : 80670
* chapterNum : 1
* chapterName : 导语
* wordCount : null
* isVip : 0
* createTime : null
* updateTime : null
*/
private int id;
private int bookId;
private int chapterNum;
private String chapterName;
private Object wordCount;
private int isVip;
private Object createTime;
private Object updateTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public int getChapterNum() {
return chapterNum;
}
public void setChapterNum(int chapterNum) {
this.chapterNum = chapterNum;
}
public String getChapterName() {
return chapterName;
}
public void setChapterName(String chapterName) {
this.chapterName = chapterName;
}
public Object getWordCount() {
return wordCount;
}
public void setWordCount(Object wordCount) {
this.wordCount = wordCount;
}
public int getIsVip() {
return isVip;
}
public void setIsVip(int isVip) {
this.isVip = isVip;
}
public Object getCreateTime() {
return createTime;
}
public void setCreateTime(Object createTime) {
this.createTime = createTime;
}
public Object getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Object updateTime) {
this.updateTime = updateTime;
}
}
} }
...@@ -6,6 +6,7 @@ import com.google.gson.JsonObject; ...@@ -6,6 +6,7 @@ import com.google.gson.JsonObject;
import com.mints.enjoyboxjia.BuildConfig; import com.mints.enjoyboxjia.BuildConfig;
import com.mints.enjoyboxjia.mvp.model.BannerList; import com.mints.enjoyboxjia.mvp.model.BannerList;
import com.mints.enjoyboxjia.mvp.model.BaseResponse; import com.mints.enjoyboxjia.mvp.model.BaseResponse;
import com.mints.enjoyboxjia.mvp.model.BookChapterBean;
import com.mints.enjoyboxjia.mvp.model.BookList2Bean; import com.mints.enjoyboxjia.mvp.model.BookList2Bean;
import com.mints.enjoyboxjia.mvp.model.HotBookTypeList; import com.mints.enjoyboxjia.mvp.model.HotBookTypeList;
import com.mints.enjoyboxjia.mvp.model.BookContentBean; import com.mints.enjoyboxjia.mvp.model.BookContentBean;
...@@ -21,10 +22,12 @@ import com.orhanobut.logger.Logger; ...@@ -21,10 +22,12 @@ import com.orhanobut.logger.Logger;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import io.reactivex.Single;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor; import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit; import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.http.Body; import retrofit2.http.Body;
import retrofit2.http.POST; import retrofit2.http.POST;
import rx.Observable; import rx.Observable;
...@@ -210,6 +213,14 @@ public interface LoanService { ...@@ -210,6 +213,14 @@ public interface LoanService {
@POST("api/book/info") @POST("api/book/info")
Observable<BaseResponse<BookContentBean>> getBookDetail(@Body Map<String, Object> vo); Observable<BaseResponse<BookContentBean>> getBookDetail(@Body Map<String, Object> vo);
/**
* 获取章节数据
*
* @return
*/
@POST("api/book/chapterinfo")
Single<BaseResponse<BookChapterBean>> getChapterInfo(@Body Map<String, Object> vo);
/** /**
* 添加阅读记录 * 添加阅读记录
* *
...@@ -276,6 +287,7 @@ public interface LoanService { ...@@ -276,6 +287,7 @@ public interface LoanService {
.baseUrl(BuildConfig.MainIp) .baseUrl(BuildConfig.MainIp)
.addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build(); .build();
......
package com.mints.enjoyboxjia.read;
public enum AnimType {
NONE(0), SLIDE(1), COVER(2), ALIKE(3);
final int type;
AnimType(int type) {
this.type = type;
}
public int getType() {
return type;
}
}
package com.mints.enjoyboxjia.read;
import com.mints.enjoyboxjia.read.models.BookModel;
import com.mints.enjoyboxjia.read.models.TxtChapterModel;
import org.litepal.LitePal;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.SingleOnSubscribe;
public class BookManager {
private static final String TAG = "BookManager";
private String chapterName;
private String bookId;
private long chapterLen;
private long position;
private Map<String, Cache> cacheMap = new HashMap<>();
private static volatile BookManager sInstance;
public static BookManager getInstance(){
if (sInstance == null){
synchronized (BookManager.class){
if (sInstance == null){
sInstance = new BookManager();
}
}
}
return sInstance;
}
public boolean openChapter(String bookId,String chapterName){
return openChapter(bookId,chapterName,0);
}
public boolean openChapter(String bookId,String chapterName,long position){
//如果文件不存在,则打开失败
File file = new File(Constants.BOOK_CACHE_PATH + bookId
+ File.separator + chapterName + FileUtils.SUFFIX_NB);
if (!file.exists()){
return false;
}
this.bookId = bookId;
this.chapterName = chapterName;
this.position = position;
createCache();
return true;
}
private void createCache(){
//创建Cache
if (!cacheMap.containsKey(chapterName)){
Cache cache = new Cache();
File file = getBookFile(bookId, chapterName);
//TODO:数据加载默认utf-8(以后会增加判断),FileUtils采用Reader获取数据的,可能用byte会更好一点
char[] array = FileUtils.getFileContent(file).toCharArray();
WeakReference<char[]> charReference = new WeakReference<char[]>(array);
cache.size = array.length;
cache.data = charReference;
cacheMap.put(chapterName, cache);
chapterLen = cache.size;
}
else {
chapterLen = cacheMap.get(chapterName).getSize();
}
}
public void setPosition(long position){
this.position = position;
}
public long getPosition(){
return position;
}
//获取上一段
public String getPrevPara(){
//首先判断是否Position已经达到起始位置,已经越界
if (position < 0){
return null;
}
//初始化从后向前获取的起始点,终止点,文本
int end = (int)position;
int begin = end;
char[] array = getContent();
while (begin >= 0) { //判断指针是否达到章节的起始位置
char character = array[begin]; //获取当前指针下的字符
//判断当前字符是否为换行,如果为换行,就代表获取到了一个段落,并退出。
//有可能发生初始指针指的就是换行符的情况。
if ((character+"").equals("\n") && begin != end) {
position = begin;
//当当前指针指向换行符的时候向后退一步
begin++;
break;
}
//向前进一步
begin--;
}
//最后end获取到段落的起始点,begin是段落的终止点。
//当越界的时候,保证begin在章节内
if (begin < 0){
begin = 0;//在章节内
position = -1; //越界
}
int size = end+1 - begin;
return new String(array,begin,size);
}
//获取下一段
public String getNextPara(){
//首先判断是否Position已经达到终点位置
if (position >= chapterLen){
return null;
}
//初始化起始点,终止点。
int begin = (int)position;
int end = begin;
char[] array = getContent();
while (end < chapterLen) { //判断指针是否在章节的末尾位置
char character = array[end]; //获取当前指针下的字符
//判断当前字符是否为换行,如果为换行,就代表获取到了一个段落,并退出。
//有可能发生初始指针指的就是换行符的情况。
//这里当遇到\n的时候,不需要回退
if ((character+"").equals("\n") && begin != end){
++end;//指向下一字段
position = end;
break;
}
//指向下一字段
end++;
}
//所要获取的字段的长度
int size = end - begin;
return new String(array,begin,size);
}
//获取章节的内容
public char[] getContent() {
if (cacheMap.size() == 0){
return new char[1];
}
char[] block = cacheMap.get(chapterName).getData().get();
if (block == null) {
File file = getBookFile(bookId, chapterName);
block = FileUtils.getFileContent(file).toCharArray();
Cache cache = cacheMap.get(chapterName);
cache.data = new WeakReference<char[]>(block);
}
return block;
}
public long getChapterLen(){
return chapterLen;
}
public void clear(){
cacheMap.clear();
position = 0;
chapterLen = 0;
}
/**
* 创建或获取存储文件
* @param folderName
* @param fileName
* @return
*/
public static File getBookFile(String folderName, String fileName){
return FileUtils.getFile(Constants.BOOK_CACHE_PATH + folderName
+ File.separator + fileName + FileUtils.SUFFIX_NB);
}
public static long getBookSize(String folderName){
return FileUtils.getDirSize(FileUtils
.getFolder(Constants.BOOK_CACHE_PATH + folderName));
}
/**
* 根据文件名判断是否被缓存过 (因为可能数据库显示被缓存过,但是文件中却没有的情况,所以需要根据文件判断是否被缓存
* 过)
* @param folderName : bookId
* @param fileName: chapterName
* @return
*/
public static boolean isChapterCached(String folderName, String fileName){
File file = new File(Constants.BOOK_CACHE_PATH + folderName
+ File.separator + fileName + FileUtils.SUFFIX_NB);
return file.exists();
}
public class Cache {
private long size;
private WeakReference<char[]> data;
public WeakReference<char[]> getData() {
return data;
}
public void setData(WeakReference<char[]> data) {
this.data = data;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
}
public Single<Void> deleteCollBookInRx(BookModel bean) {
return Single.create(new SingleOnSubscribe<Void>() {
@Override
public void subscribe(SingleEmitter<Void> e) throws Exception {
//查看文本中是否存在删除的数据
LitePal.delete(BookModel.class,bean.getId());
//删除任务
// deleteDownloadTask(bean.get_id());
//删除目录
LitePal.delete(TxtChapterModel.class,bean.getId());
//删除CollBook
}
});
}
public BookModel getCollectedBook(String bookId){
return LitePal.where("id=?",bookId).findFirst(BookModel.class);
}
//存储已收藏书籍
public void saveCollBookWithAsync(BookModel bean){
//启动异步存储
boolean resuly = bean.save();
// mSession.startAsyncSession()
// .runInTx(
// () -> {
// if (bean.getBookChapters() != null){
// // 存储BookChapterBean
// mSession.getBookChapterBeanDao()
// .insertOrReplaceInTx(bean.getBookChapters());
// }
// //存储CollBook (确保先后顺序,否则出错)
// mCollBookDao.insertOrReplace(bean);
// }
// );
}
}
package com.mints.enjoyboxjia.read;
import java.io.File;
public class Constants {
public static String IP_ADDRESS = "10.129.82.19";
//阅读界面 默认字体大小
public static int DEFAULT_TEXT_SIZE = 18;
//时间格式
public static final String FORMAT_TIME = "HH:mm";
public static final String FORMAT_DATE = "YYYY-MM-DD";
public static final String FORMAT_BOOK_DATE = "yyyy-MM-dd'T'HH:mm:ss";
//回调代码
public static final int MAINACTIVITY_REQUEST_CODE = 1;
public static final int FILESELECTOR_RESULT_CODE = 2;
public static final String READBOOK_KEY = "readBookKey";
public static final String FIELSELECTOR_RESULT_KEY = "fileSelectorResultKey";
//SharedPreference
public static final String IS_FIRST = "isFirst";
public static final String FIRST_KEY = "firstKey";
public static final String COOKIE_DATA = "cookieData";
public static final String COOKIE_KEY = "Authorization";
public static final String CACHE_PATH_KEY = "cachePath";
//BookCachePath (因为getCachePath引用了Context,所以必须是静态变量,不能够是静态常量)
public static String BOOK_CACHE_PATH = FileUtils.getCachePath()+ File.separator
+ "book_cache"+ File.separator;
//URL_BASE
// public static final String API_BASE_URL = "http://api.zhuishushenqi.com";
public static final String API_BASE_URL = "http://" + IP_ADDRESS + ":8080";
public static final String IMG_BASE_URL = "http://statics.zhuishushenqi.com";
}
package com.mints.enjoyboxjia.read;
public enum Direction {
NONE,NEXT,PRE
}
package com.mints.enjoyboxjia.read;
import android.os.Environment;
import com.mints.enjoyboxjia.MintsApplication;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.SingleOnSubscribe;
public class FileUtils {
//采用自己的格式去设置文件,防止文件被系统文件查询到
public static final String SUFFIX_NB = ".nb";
public static final String SUFFIX_TXT = ".txt";
public static final String SUFFIX_EPUB = ".epub";
public static final String SUFFIX_PDF = ".pdf";
//获取文件夹
public static File getFolder(String filePath){
File file = new File(filePath);
//如果文件夹不存在,就创建它
if (!file.exists()){
file.mkdirs();
}
return file;
}
//获取文件
public static synchronized File getFile(String filePath){
File file = new File(filePath);
try {
if (!file.exists()){
//创建父类文件夹
getFolder(file.getParent());
//创建文件
file.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
//获取Cache文件夹
public static String getCachePath(){
if (isSdCardExist()){
return MintsApplication.getContext()
.getExternalCacheDir()
.getAbsolutePath();
}
else{
return MintsApplication.getContext()
.getCacheDir()
.getAbsolutePath();
}
}
public static long getDirSize(File file){
//判断文件是否存在
if (file.exists()) {
//如果是目录则递归计算其内容的总大小
if (file.isDirectory()) {
File[] children = file.listFiles();
long size = 0;
for (File f : children)
size += getDirSize(f);
return size;
} else {
return file.length();
}
} else {
return 0;
}
}
public static String getFileSize(long size) {
if (size <= 0) return "0";
final String[] units = new String[]{"b", "kb", "M", "G", "T"};
//计算单位的,原理是利用lg,公式是 lg(1024^n) = nlg(1024),最后 nlg(1024)/lg(1024) = n。
int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
//计算原理是,size/单位值。单位值指的是:比如说b = 1024,KB = 1024^2
return new DecimalFormat("#,##0.##").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
}
/**
* 本来是获取File的内容的。但是为了解决文本缩进、换行的问题
* 这个方法就是专门用来获取书籍的...
*
* 应该放在BookRepository中。。。
* @param file
* @return
*/
public static String getFileContent(File file){
Reader reader = null;
String str = null;
StringBuilder sb = new StringBuilder();
try {
reader = new FileReader(file);
BufferedReader br = new BufferedReader(reader);
while ((str = br.readLine()) != null){
//过滤空语句
if (!str.equals("")){
//由于sb会自动过滤\n,所以需要加上去
sb.append(" "+str+"\n");
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
close(reader);
}
return sb.toString();
}
//判断是否挂载了SD卡
public static boolean isSdCardExist(){
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
return true;
}
return false;
}
//递归删除文件夹下的数据
public static synchronized void deleteFile(String filePath){
File file = new File(filePath);
if (!file.exists()) return;
if (file.isDirectory()){
File[] files = file.listFiles();
for (File subFile : files){
String path = subFile.getPath();
deleteFile(path);
}
}
//删除文件
file.delete();
}
//由于递归的耗时问题,取巧只遍历内部三层
//获取txt文件
public static List<File> getTxtFiles(String filePath, int layer){
List txtFiles = new ArrayList();
File file = new File(filePath);
//如果层级为 3,则直接返回
if (layer == 3){
return txtFiles;
}
//获取文件夹
File[] dirs = file.listFiles(
pathname -> {
if (pathname.isDirectory() && !pathname.getName().startsWith(".")) {
return true;
}
//获取txt文件
else if(pathname.getName().endsWith(".txt")){
txtFiles.add(pathname);
return false;
}
else{
return false;
}
}
);
//遍历文件夹
for (File dir : dirs){
//递归遍历txt文件
txtFiles.addAll(getTxtFiles(dir.getPath(),layer + 1));
}
return txtFiles;
}
//由于遍历比较耗时
public static Single<List<File>> getSDTxtFile(){
//外部存储卡路径
String rootPath = Environment.getExternalStorageDirectory().getPath();
return Single.create(new SingleOnSubscribe<List<File>>() {
@Override
public void subscribe(SingleEmitter<List<File>> e) throws Exception {
List<File> files = getTxtFiles(rootPath,0);
e.onSuccess(files);
}
});
}
//获取文件的编码格式
public static Charset getCharset(String fileName) {
BufferedInputStream bis = null;
Charset charset = Charset.forName("GBK ");;
byte[] first3Bytes = new byte[3];
try {
boolean checked = false;
bis = new BufferedInputStream(new FileInputStream(fileName));
bis.mark(0);
int read = bis.read(first3Bytes, 0, 3);
if (read == -1)
return charset;
if (first3Bytes[0] == (byte) 0xEF
&& first3Bytes[1] == (byte) 0xBB
&& first3Bytes[2] == (byte) 0xBF) {
charset = Charset.forName("utf8");
checked = true;
}
/*
* 不支持 UTF16LE 和 UTF16BE
else if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
charset = Charset.UTF16LE;
checked = true;
} else if (first3Bytes[0] == (byte) 0xFE
&& first3Bytes[1] == (byte) 0xFF) {
charset = Charset.UTF16BE;
checked = true;
} else */
bis.mark(0);
if (!checked) {
while ((read = bis.read()) != -1) {
if (read >= 0xF0)
break;
if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的,也算是GBK
break;
if (0xC0 <= read && read <= 0xDF) {
read = bis.read();
if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF)
// (0x80 - 0xBF),也可能在GB编码内
continue;
else
break;
} else if (0xE0 <= read && read <= 0xEF) {// 也有可能出错,但是几率较小
read = bis.read();
if (0x80 <= read && read <= 0xBF) {
read = bis.read();
if (0x80 <= read && read <= 0xBF) {
charset = Charset.forName("utf8");
break;
} else
break;
} else
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
close(bis);
}
return charset;
}
static void close(Closeable closeable){
if (closeable == null) return;
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
//close error
}
}
}
package com.mints.enjoyboxjia.read;
import android.util.Log;
import com.mints.enjoyboxjia.MintsApplication;
import com.mints.enjoyboxjia.manager.AppHttpManager;
import com.mints.enjoyboxjia.mvp.model.BaseResponse;
import com.mints.enjoyboxjia.mvp.model.BookChapterBean;
import com.mints.enjoyboxjia.mvp.model.BookContentBean;
import com.mints.enjoyboxjia.read.models.BookModel;
import com.mints.enjoyboxjia.read.models.TxtChapterModel;
import com.mints.library.net.neterror.BaseSubscriber;
import com.mints.library.net.neterror.Throwable;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class NetworkPageLoader extends PageLoader {
private Subscription mChapterSub;
private boolean isSkip = false;
public NetworkPageLoader(YPageView pageView, BookModel bookModel) {
super(pageView, bookModel);
}
@Override
public void initData() {
loadCurrentChapter(curChapterIndex);
}
@Override
public BufferedReader getChapterReader(TxtChapterModel chapterModel) throws IOException {
File file = new File(Constants.BOOK_CACHE_PATH + bookModel.getBookId()
+ File.separator + chapterModel.title + FileUtils.SUFFIX_NB);
if (!file.exists()) return null;
Reader reader = new FileReader(file);
return new BufferedReader(reader);
}
//加载章节列表
@Override
public void loadChapters() {
//已缓存
if (mChapterList != null && !mChapterList.isEmpty()) return;
Map<String, Object> params = new HashMap<>();
params.put("id", bookModel.getBookId());
MintsApplication application = MintsApplication.getApplication();
AppHttpManager.getInstance(application).call(
application.getLoanService().getBookDetail(params),
new BaseSubscriber<BaseResponse<BookContentBean>>() {
@Override
public void onNext(BaseResponse<BookContentBean> baseResponse) {
List<BookContentBean.ArticleChapterList> articleChapterList =
baseResponse.getData().getArticleChapterList();
for (BookContentBean.ArticleChapterList chapterList : articleChapterList) {
TxtChapterModel txtChapterModel = new TxtChapterModel();
txtChapterModel.setChapterId("" + chapterList.getId());
txtChapterModel.setBook_id(bookModel.getId());
txtChapterModel.setTitle("第" + chapterList.getChapterNum() + "章节");
mChapterList.add(txtChapterModel);
}
chapterChangeListener.finishedLoadChapters(mChapterList);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
}
);
}
@Override
public void loadNextChapters() {
loadNextChapter();
}
@Override
public void loadPreChapters() {
loadPrevChapter();
}
@Override
public void preChapter() {
super.preChapter();
loadPrevChapter();
}
@Override
public void nextChapter() {
super.nextChapter();
loadNextChapter();
}
@Override
public void skipToChapter(int pos) {
if (Math.abs(pos - curChapterIndex) > 1) {
curChapterIndex = pos;
isSkip = true;
loadCurrentChapter(pos);
} else {
skipToChapterReal(pos);
}
}
private void skipToChapterReal(int pos) {
super.skipToChapter(pos);
loadCurrentChapter(pos);
}
/**
* 加载当前页的前面两个章节
*/
private void loadPrevChapter() {
if (chapterChangeListener != null) {
int end = curChapterIndex;
int begin = end - 2;
if (begin < 0) {
begin = 0;
}
requestChapters(begin, end);
}
}
/**
* 加载前一页,当前页,后一页。
*/
private void loadCurrentChapter(int cur) {
if (chapterChangeListener != null) {
int begin = cur;
int end = cur;
// 是否当前不是最后一章
if (end < mChapterList.size()) {
end = end + 1;
if (end >= mChapterList.size()) {
end = mChapterList.size() - 1;
}
}
// 如果当前不是第一章
if (begin != 0) {
begin = begin - 1;
if (begin < 0) {
begin = 0;
}
}
requestChapters(begin, end);
}
}
/**
* 加载当前页的后两个章节
*/
private void loadNextChapter() {
if (chapterChangeListener != null) {
// 提示加载后两章
int begin = curChapterIndex + 1;
int end = begin + 1;
// 判断是否大于最后一章
if (begin >= mChapterList.size()) {
// 如果下一章超出目录了,就没有必要加载了
return;
}
if (end > mChapterList.size()) {
end = mChapterList.size() - 1;
}
requestChapters(begin, end);
}
}
private void requestChapters(int start, int end) {
// 检验输入值
if (start < 0) {
start = 0;
}
if (end >= mChapterList.size()) {
end = mChapterList.size() - 1;
}
List<TxtChapterModel> chapters = new ArrayList<>();
// 过滤,哪些数据已经加载了
for (int i = start; i <= end; ++i) {
TxtChapterModel txtChapter = mChapterList.get(i);
if (!hasChapterData(txtChapter)) {
chapters.add(txtChapter);
}
}
if (!chapters.isEmpty()) {
chapterChangeListener.requestChapters(chapters);
loadChapter(String.valueOf(bookModel.getBookId()), chapters);
} else {
if (isSkip) isSkip = !isSkip;
pageView.showContent();
}
}
private boolean hasChapterData(TxtChapterModel chapter) {
return BookManager.isChapterCached(String.valueOf(bookModel.getBookId()), chapter.title);
}
//加载章节具体内容
private void loadChapter(String bookId, List<TxtChapterModel> bookChapters) {
int size = bookChapters.size();
//取消上次的任务,防止多次加载
if (mChapterSub != null) {
mChapterSub.cancel();
}
List<Single<BaseResponse<BookChapterBean>>> chapterInfos = new ArrayList<>(size);
ArrayDeque<String> titles = new ArrayDeque<>(size);
for (int i = 0; i < size; ++i) {
TxtChapterModel bookChapter = bookChapters.get(i);
Log.v("yzll", "缓存章节:" + bookChapter.getTitle());
Map<String, Object> params = new HashMap<>();
params.put("articleId", bookModel.getBookId());
params.put("chapterId", bookChapter.getChapterId());
Single<BaseResponse<BookChapterBean>> chapterInfoSingle = MintsApplication.getApplication().getLoanService().getChapterInfo(params).map(bean -> bean);
chapterInfos.add(chapterInfoSingle);
titles.add(bookChapter.getTitle());
}
new Thread(() -> {
Flowable<BaseResponse<BookChapterBean>> concat = Single.concat(chapterInfos);
concat.subscribeOn(Schedulers.io());
concat.observeOn(AndroidSchedulers.mainThread());
concat.subscribe(
new Subscriber<BaseResponse<BookChapterBean>>() {
String title = titles.poll();
@Override
public void onSubscribe(Subscription s) {
s.request(Integer.MAX_VALUE);
mChapterSub = s;
}
@Override
public void onNext(BaseResponse<BookChapterBean> bookChapterBean) {
//存储数据
saveChapterInfo(bookId, title, bookChapterBean.getData().getContent());
//将获取到的数据进行存储
title = titles.poll();
}
@Override
public void onError(java.lang.Throwable t) {
}
@Override
public void onComplete() {
if (isSkip) {
isSkip = !isSkip;
skipToChapterReal(curChapterIndex);
} else {
pageView.showContent();
}
}
});
}).start();
}
/**
* 存储章节
*
* @param folderName
* @param fileName
* @param content
*/
public void saveChapterInfo(String folderName, String fileName, String content) {
File file = BookManager.getBookFile(folderName, fileName);
//获取流并存储
Writer writer = null;
try {
writer = new BufferedWriter(new FileWriter(file));
writer.write(content);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
close(writer);
}
}
//关闭数据流
private void close(Closeable closeable) {
if (closeable == null) return;
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.mints.enjoyboxjia.read;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.text.TextPaint;
import com.mints.enjoyboxjia.read.models.BookModel;
import com.mints.enjoyboxjia.read.models.TxtChapterModel;
import com.mints.enjoyboxjia.read.models.TxtPageModel;
import com.mints.enjoyboxjia.utils.StringUtils;
import com.mints.library.utils.Utils;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public abstract class PageLoader {
//显示容器
public YPageView pageView;
//加载的书籍
BookModel bookModel;
// 当前书籍章节列表
public List<TxtChapterModel> mChapterList;
// 当前显示的页
private TxtPageModel mCurPage;
// 被遮盖的页,或者认为被取消显示的页
private TxtPageModel mCancelPage;
// 上一章的页面列表缓存
private List<TxtPageModel> mPrePageList;
// 当前章节的页面列表
private List<TxtPageModel> mCurPageList;
// 下一章的页面列表缓存
private List<TxtPageModel> mNextPageList;
//当前章节位置
int curChapterIndex = 0;
//上一章节位置
int lastChapterIndex = 0;
//当前页面位置
int curPageIndex = 0;
//行间距
private int mTextInterval;
//标题的行间距
private int mTitleInterval;
//书籍绘制区域的宽高
private int mVisibleWidth;
private int mVisibleHeight;
//应用的宽高
private int mDisplayWidth;
private int mDisplayHeight;
//电池的百分比
private int mBatteryLevel = 80;
//间距
private int mMarginWidth;
private int mMarginHeight;
//标题画笔
Paint mTitleTextPaint;
//内容画笔
Paint mContentTextPaint;
// 绘制电池的画笔
private Paint mBatteryPaint;
// 绘制提示的画笔
private Paint mTipPaint;
//字体大小 单位dp
int mTextSize = 18;
//背景颜色
int bgColor = 0xFFCEC29C;
//字体颜色
int textColor = Color.BLACK;
//记录上一次颜色 用于恢复
int lastBgColor;
int lastTextColor;
//页面变换监听者
OnChapterChangeListener chapterChangeListener;
public PageLoader(YPageView pageView, BookModel bookModel) {
this.pageView = pageView;
this.bookModel = bookModel;
initPaint();
// initDimens();
//initData();
}
//初始化数据
public void initData() {
if (curChapterIndex > 0) mPrePageList = loadPageList(curChapterIndex - 1);
mCurPageList = loadPageList(curChapterIndex);
if (curChapterIndex < mChapterList.size() - 1)
mNextPageList = loadPageList(curChapterIndex + 1);
mCurPage = mCurPageList.get(curPageIndex);
updateCurPage();
}
//初始化尺寸
public void initDimens() {
mMarginWidth = 40;
mMarginHeight = 50;
mDisplayHeight = pageView.getHeight();
mDisplayWidth = pageView.getWidth();
mVisibleWidth = pageView.getWidth() - mMarginWidth * 2;
mVisibleHeight = pageView.getHeight() - mMarginHeight;
mTitleInterval = (int) (mTitleTextPaint.getTextSize() / 2);
mTextInterval = (int) (mContentTextPaint.getTextSize() / 2);
}
//初始化画笔
private void initPaint() {
// 绘制页面内容的画笔
mContentTextPaint = new TextPaint();
mContentTextPaint.setColor(Color.BLACK);
mContentTextPaint.setTextSize(dp2px(mTextSize));
mContentTextPaint.setAntiAlias(true);
mContentTextPaint.setSubpixelText(true);
// 绘制标题的画笔
mTitleTextPaint = new TextPaint();
mTitleTextPaint.setColor(Color.BLACK);
mTitleTextPaint.setTextSize(dp2px(mTextSize + 5));
mTitleTextPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mTitleTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mTitleTextPaint.setAntiAlias(true);
// 绘制提示的画笔
mTipPaint = new Paint();
mTipPaint.setColor(textColor);
mTipPaint.setTextAlign(Paint.Align.LEFT); // 绘制的起始点
mTipPaint.setTextSize(dp2px(12)); // Tip默认的字体大小
mTipPaint.setAntiAlias(true);
mTipPaint.setSubpixelText(true);
// 绘制电池的画笔
mBatteryPaint = new Paint();
mBatteryPaint.setAntiAlias(true);
mBatteryPaint.setDither(true);
}
public void reloadPageList() {
if (curChapterIndex > 0) mPrePageList = loadPageList(curChapterIndex - 1);
mCurPageList = loadPageList(curChapterIndex);
if (curChapterIndex < mChapterList.size() - 1)
mNextPageList = loadPageList(curChapterIndex + 1);
mCurPage = mCurPageList.get(0);
updateCurPage();
}
//加载某章内容
public List<TxtPageModel> loadPageList(int chapterIndex) {
if (mChapterList.isEmpty()) return null;
TxtChapterModel chapterModel = mChapterList.get(chapterIndex);
List<TxtPageModel> pageList = null;
try {
BufferedReader br = getChapterReader(chapterModel);
if (br != null) {
pageList = loadPages(chapterModel, br);
} else {
// reloadPageList();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return pageList;
}
//根据章节模型将数据封装成页
private List<TxtPageModel> loadPages(TxtChapterModel chapter, BufferedReader br) {
//章节的所有页面
List<TxtPageModel> pageLists = new ArrayList<>();
//每页的行
List<String> lines = new ArrayList<>();
int readHeight = mVisibleHeight;
boolean showTitle = true;
String paragraph = chapter.getTitle();//默认展示标题
int titleLinesCount = 0;
//流式读取书籍数据
try {
while (showTitle || (paragraph = br.readLine()) != null) {
//一行的字数
int wordCount = 0;
//一行的内容
paragraph = paragraph.replace("&nbsp", " ")
.replace("<br/>", " ")
.replace("<br />", " ")
.replace(";", " ");
String lineContent = null;
while (paragraph.length() > 0) {
//检查是否能一行的高度
if (showTitle) {
readHeight -= mTitleTextPaint.getTextSize();
} else {
readHeight -= mContentTextPaint.getTextSize();
}
// 一页已经填充满了,创建 TextPage
if (readHeight <= 0) {
// 创建Page
TxtPageModel page = new TxtPageModel();
page.position = pageLists.size();
page.title = chapter.getTitle();
page.lines = new ArrayList<>(lines);
page.titleLines = titleLinesCount;
pageLists.add(page);
// 重置Lines
lines.clear();
readHeight = mVisibleHeight;
titleLinesCount = 0;
continue;
}
//一页未填满 测量每行的字数
if (showTitle) {
wordCount = mTitleTextPaint.breakText(paragraph,
true, mVisibleWidth, null);
} else {
wordCount = mContentTextPaint.breakText(paragraph,
true, mVisibleWidth, null);
}
//截取一行的内容
lineContent = paragraph.substring(0, wordCount);
if (!lineContent.equals("\n")) {
//一行不为空则加入数组
lines.add(lineContent);
//设置段落间距
if (showTitle) {
titleLinesCount += 1;
readHeight -= mTitleInterval;
showTitle = false;
} else {
readHeight -= mTextInterval;
}
}
//裁剪
paragraph = paragraph.substring(wordCount);
}
//增加段落的间距
// if (!showTitle && lines.size() != 0) {
// readHeight = readHeight - mTextInterval;
// }
if (showTitle) {
readHeight = readHeight - mTitleInterval;
showTitle = false;
}
}
//处理尾页
if (lines.size() != 0) {
//创建Page
TxtPageModel page = new TxtPageModel();
page.position = pageLists.size();
page.title = chapter.getTitle();
page.lines = new ArrayList<>(lines);
page.titleLines = titleLinesCount;
pageLists.add(page);
//重置Lines
lines.clear();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return pageLists;
}
//绘制页面
public void drawPage(Bitmap bitmap) {
Canvas canvas = new Canvas(bitmap);
drawBG(canvas);
drawContent(canvas);
pageView.invalidate();
}
//绘制背景
private void drawBG(Canvas canvas) {
int tipMarginHeight = Utils.dpToPx(pageView.getContext(), 3);
canvas.drawColor(bgColor);
/******绘制电池********/
int visibleRight = mDisplayWidth - mMarginWidth;
int visibleBottom = mDisplayHeight - tipMarginHeight;
int outFrameWidth = (int) mTipPaint.measureText("xxx");
int outFrameHeight = (int) mTipPaint.getTextSize();
int polarHeight = Utils.dpToPx(pageView.getContext(), 6);
int polarWidth = Utils.dpToPx(pageView.getContext(), 2);
int border = 1;
int innerMargin = 1;
//电极的制作
int polarLeft = visibleRight - polarWidth;
int polarTop = visibleBottom - (outFrameHeight + polarHeight) / 2;
Rect polar = new Rect(polarLeft, polarTop, visibleRight,
polarTop + polarHeight - Utils.dpToPx(pageView.getContext(), 2));
mBatteryPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(polar, mBatteryPaint);
//外框的制作
int outFrameLeft = polarLeft - outFrameWidth;
int outFrameTop = visibleBottom - outFrameHeight;
int outFrameBottom = visibleBottom - Utils.dpToPx(pageView.getContext(), 2);
Rect outFrame = new Rect(outFrameLeft, outFrameTop, polarLeft, outFrameBottom);
mBatteryPaint.setStyle(Paint.Style.STROKE);
mBatteryPaint.setStrokeWidth(border);
canvas.drawRect(outFrame, mBatteryPaint);
//内框的制作
float innerWidth = (outFrame.width() - innerMargin * 2 - border) * (mBatteryLevel / 100.0f);
RectF innerFrame = new RectF(outFrameLeft + border + innerMargin, outFrameTop + border + innerMargin,
outFrameLeft + border + innerMargin + innerWidth, outFrameBottom - border - innerMargin);
mBatteryPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(innerFrame, mBatteryPaint);
/******绘制当前时间********/
//底部的字显示的位置Y
float y = mDisplayHeight - mTipPaint.getFontMetrics().bottom - tipMarginHeight;
String time = StringUtils.dateConvert(System.currentTimeMillis(), Constants.FORMAT_TIME);
float x = outFrameLeft - mTipPaint.measureText(time) - Utils.dpToPx(pageView.getContext(), 4);
canvas.drawText(time, x, y, mTipPaint);
canvas.drawText(String.format("%d/%d页", curPageIndex + 1, mCurPageList == null ? 0 : mCurPageList.size()), dp2px(10), y, mTipPaint);
}
//绘制内容
private void drawContent(Canvas canvas) {
if (mCurPage == null) return;
float top = mMarginHeight;
String str = null;
//确定距离
int contentInterval = (int) mContentTextPaint.getTextSize() + mTextInterval;
int titleInterval = (int) mTitleTextPaint.getTextSize() + mTitleInterval;
//对标题进行绘制
for (int i = 0; i < mCurPage.titleLines; ++i) {
str = mCurPage.lines.get(i);
//设置顶部间距
if (i == 0) {
top += 10;
}
//计算文字显示的起始点
int start = (int) (mVisibleWidth - mTitleTextPaint.measureText(str)) / 2;
//进行绘制
canvas.drawText(str, start, top, mTitleTextPaint);
// canvas.drawText(str, 50, 100, mTitleTextPaint);
//设置尾部间距
if (i == mCurPage.titleLines - 1) {
top += titleInterval;
} else {
top += titleInterval;
}
}
//对内容进行绘制
for (int i = mCurPage.titleLines; i < mCurPage.lines.size(); ++i) {
str = mCurPage.lines.get(i);
canvas.drawText(str, mMarginWidth, top, mContentTextPaint);
if (str.endsWith("\n")) {
top += contentInterval + 10;
} else {
//行间距
top += contentInterval;
}
}
}
//上一页
public boolean prePage() {
if (curPageIndex > 0) {
curPageIndex--;
} else {
List<TxtPageModel> tempPageList = loadPageList(curChapterIndex - 1);
if (tempPageList == null) {
loadPreChapters();
return false;
}
if (curChapterIndex == 0) return false;
lastChapterIndex = curChapterIndex--;
mNextPageList = mCurPageList;
mCurPageList = mPrePageList;
if (curChapterIndex > 0)
mPrePageList = tempPageList;
curPageIndex = mCurPageList.size() - 1;
if (chapterChangeListener != null) {
chapterChangeListener.onChapterChange(curChapterIndex);
}
}
mCancelPage = mCurPage;
mCurPage = mCurPageList.get(curPageIndex);
pageView.drawNextPage();
return true;
}
//下一页
public boolean nextPage() {
if (curPageIndex >= 0 && curPageIndex < mCurPageList.size() - 1) {
curPageIndex++;
} else {
List<TxtPageModel> tempPageList = loadPageList(curChapterIndex + 1);
if (tempPageList == null) {
loadNextChapters();
return false;
}
if (curChapterIndex >= mChapterList.size() - 1) return false;
lastChapterIndex = curChapterIndex++;
mPrePageList = mCurPageList;
mCurPageList = mNextPageList;
if (curChapterIndex < mChapterList.size() - 1)
mNextPageList = loadPageList(curChapterIndex + 1);
curPageIndex = 0;
if (chapterChangeListener != null) {
chapterChangeListener.onChapterChange(curChapterIndex);
}
}
mCancelPage = mCurPage;
mCurPage = mCurPageList.get(curPageIndex);
pageView.drawNextPage();
return true;
}
//取消翻页
public void cancelChangePage() {
// 加载到下一章取消了
if (mCurPage.position == 0 && curChapterIndex > lastChapterIndex) {
if (mPrePageList != null) {
cancelNextChapter();
} else {
mCurPage = new TxtPageModel();
}
}
// 加载上一章取消了
else if (mCurPageList == null
|| (mCurPage.position == mCurPageList.size() - 1
&& curChapterIndex < lastChapterIndex)) {
if (mNextPageList != null) {
cancelPreChapter();
} else {
mCurPage = new TxtPageModel();
}
} else {
// 假设加载到下一页,又取消了。那么需要重新装载。
mCurPage = mCancelPage;
curPageIndex = mCurPage.position;
}
}
//翻到下一章 取消加载
private void cancelNextChapter() {
int temp = lastChapterIndex;
lastChapterIndex = curChapterIndex;
curChapterIndex = temp;
mNextPageList = mCurPageList;
mCurPageList = mPrePageList;
mPrePageList = null;
if (curChapterIndex > 0) mPrePageList = loadPageList(curChapterIndex - 1);
curPageIndex = mCurPageList.size() - 1;
mCurPage = mCurPageList.get(curPageIndex);
mCancelPage = null;
}
//翻到上一章 取消加载
private void cancelPreChapter() {
// 重置位置点
int temp = lastChapterIndex;
lastChapterIndex = curChapterIndex;
curChapterIndex = temp;
// 重置页面列表
mPrePageList = mCurPageList;
mCurPageList = mNextPageList;
mNextPageList = null;
if (curChapterIndex < mChapterList.size() - 1)
mNextPageList = loadPageList(curChapterIndex + 1);
curPageIndex = 0;
mCurPage = mCurPageList.get(curPageIndex);
mCancelPage = null;
}
//下一章
public void nextChapter() {
if (curChapterIndex < mChapterList.size() - 1) skipToChapter(++curChapterIndex);
}
//上一章
public void preChapter() {
if (curChapterIndex > 0) skipToChapter(--curChapterIndex);
}
//跳转到某一章
public void skipToChapter(int pos) {
curChapterIndex = pos;
if (chapterChangeListener != null) {
chapterChangeListener.onChapterChange(curChapterIndex);
}
curPageIndex = 0;
reloadPageList();
}
//设置翻页模式
public void setPageMode(AnimType animType) {
pageView.setAnimType(animType);
}
//设置背景颜色
public void setPageBgColor(int color) {
lastBgColor = this.bgColor;
this.bgColor = color;
//updateCurPage();
}
//设置字体颜色
public void setTextColor(int color) {
lastTextColor = this.textColor;
this.textColor = color;
}
//设置阅读样式
public void setPageStyle(int bgColor, int textColor) {
lastBgColor = this.bgColor;
lastTextColor = this.textColor;
this.bgColor = bgColor;
this.textColor = textColor;
this.mTitleTextPaint.setColor(textColor);
this.mContentTextPaint.setColor(textColor);
updateCurPage();
}
//恢复上次颜色
public void cancelColorChange() {
this.bgColor = lastBgColor;
this.textColor = lastTextColor;
this.mTitleTextPaint.setColor(textColor);
this.mContentTextPaint.setColor(textColor);
updateCurPage();
}
//设置字体大小
public void setTextSize(int textSize) {
mTextSize = textSize;
}
//更新字体大小
public void updateTextSize(int size) {
mTextSize = size;
initPaint();
initDimens();
reloadPageList();
}
private void updateCurPage() {
mCurPage = mCurPageList.get(curPageIndex);
pageView.drawNextPage();
pageView.invalidate();
}
public int getCurChapterIndex() {
return curChapterIndex;
}
public int getCurPageIndex() {
return curPageIndex;
}
public void setCurChapterIndex(int curChapterIndex) {
this.curChapterIndex = curChapterIndex;
}
public void setCurPageIndex(int curPageIndex) {
this.curPageIndex = curPageIndex;
}
public int getBgColor() {
return bgColor;
}
public void setChapterChangeListener(OnChapterChangeListener listener) {
this.chapterChangeListener = listener;
}
public int getmTextSize() {
return mTextSize;
}
public int getTextColor() {
return textColor;
}
/********************************abstract***********************************/
public abstract BufferedReader getChapterReader(TxtChapterModel chapterModel) throws IOException;
public abstract void loadChapters() throws IOException;
public abstract void loadNextChapters();
public abstract void loadPreChapters();
private float dp2px(float dp) {
return pageView.getContext().getResources().getDisplayMetrics().density * dp;
}
/**************************interface*******************************/
public interface OnChapterChangeListener {
void onChapterChange(int pos);
void requestChapters(List<TxtChapterModel> requestChapters);
void finishedLoadChapters(List<TxtChapterModel> bookChapters);
}
}
package com.mints.enjoyboxjia.read;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import com.mints.enjoyboxjia.read.anim.AlikeAnim;
import com.mints.enjoyboxjia.read.anim.CoverAnim;
import com.mints.enjoyboxjia.read.anim.NoneAnim;
import com.mints.enjoyboxjia.read.anim.PageAnim;
import com.mints.enjoyboxjia.read.anim.SlideAnim;
import com.mints.enjoyboxjia.read.models.BookModel;
import java.io.IOException;
public class YPageView extends View implements SlideAnim.OnPageChangeListener {
/**
* 属性
*/
//背景颜色
//private int bgColor = 0xFFCEC29C;
//内容加载器
PageLoader mPageLoader;
//触摸事件位移记录
int mStartX = 0;
int mStartY = 0;
//是否是在移动
private boolean isMove = false;
// 唤醒菜单的区域
private RectF mCenterRect = null;
//中心点击事件回调
private OnCenterClickListener listener;
//是否能触摸
boolean canTouch = true;
//翻页方向
boolean isNext = true;
//翻页动画
AnimType animType = AnimType.SLIDE;
PageAnim pageAnim;
public YPageView(Context context) {
this(context, null);
}
public YPageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public YPageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/*******************************初始化方法***************************************/
private void initData() throws IOException {
setAnimType(animType);
mPageLoader.initDimens();
mPageLoader.loadChapters();
}
public void showCategory() {
mPageLoader.initData();
}
public void showContent() {
mPageLoader.reloadPageList();
}
/*******************************自定义绘制方法***************************************/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
try {
initData();
} catch (IOException e) {
e.printStackTrace();
}
}
//自定义绘制
@Override
protected void onDraw(Canvas canvas) {
//canvas.drawColor(bgColor);
// mPageLoader.drawPage(coverAnimation.getmNextBitmap());
if (pageAnim == null) return;
pageAnim.drawViewPages(canvas);
}
/*******************************Setter and Getter***************************************/
public PageLoader getPageLoader(BookModel bookModel) {
if (mPageLoader != null) return mPageLoader;
mPageLoader = new NetworkPageLoader(this, bookModel);
return mPageLoader;
}
public void setOnCenterClickListener(OnCenterClickListener listener) {
this.listener = listener;
}
public void setAnimType(AnimType animType) {
this.animType = animType;
if (this.getWidth() == 0 || getHeight() == 0) return;
Bitmap tempBitmap = null;
if (pageAnim != null)
tempBitmap = pageAnim.getmNextBitmap().copy(Bitmap.Config.RGB_565, true);
//显示出来后再创建
switch (animType) {
case COVER:
pageAnim = new CoverAnim(this, this);
break;
case ALIKE:
pageAnim = new AlikeAnim(this, this);
break;
case SLIDE:
pageAnim = new SlideAnim(this, this);
break;
default:
pageAnim = new NoneAnim(this, this);
break;
}
if (tempBitmap != null) {
pageAnim.setmCurBitmap(tempBitmap);
}
pageAnim.changePage();
}
public AnimType getAnimType() {
return animType;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (!canTouch && event.getAction() != MotionEvent.ACTION_DOWN) return true;
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mStartX = x;
mStartY = y;
isMove = false;
//canTouch = mTouchListener.onTouch();
pageAnim.onTouchEvent(event);
break;
case MotionEvent.ACTION_MOVE:
// 判断是否大于最小滑动值。
int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
if (!isMove) {
isMove = Math.abs(mStartX - event.getX()) > 100 || Math.abs(mStartY - event.getY()) > slop;
isNext = event.getX() - mStartX < 0;
}
if (isMove) {
pageAnim.onTouchEvent(event);
}
break;
case MotionEvent.ACTION_UP:
if (!isMove) {
//设置中间区域范围
if (mCenterRect == null) {
mCenterRect = new RectF(getWidth() / 5, getHeight() / 3,
getWidth() * 4 / 5, getHeight() * 2 / 3);
}
//是否点击了中间
if (mCenterRect.contains(x, y)) {
// if (mTouchListener != null) {
// mTouchListener.center();
// }
if (listener != null) listener.centerClicked();
return true;
}
}
//如果滑动了,则进行翻页
if (isMove) {
// if (isNext) mPageLoader.nextPage();
// else mPageLoader.prePage();
isNext = true;
isMove = !isMove;
}
pageAnim.onTouchEvent(event);
break;
}
return true;
}
@Override
public void computeScroll() {
// 判断Scroller是否执行完毕
//进行滑动
Log.v("yzll", "computScroll");
if (pageAnim == null) return;
pageAnim.scrollAnim();
super.computeScroll();
}
//判断是否存在上一页
private boolean hasPrevPage() {
return mPageLoader.prePage();
}
//判断是否下一页存在
private boolean hasNextPage() {
return mPageLoader.nextPage();
}
//绘制下一页
public void drawNextPage() {
pageAnim.changePage();
drawCurPage();
}
//绘制当前页
public void drawCurPage() {
mPageLoader.drawPage(pageAnim.getmNextBitmap());
}
//取消翻页
public void cancelChangePage() {
mPageLoader.cancelChangePage();
}
/*********************interface implements*************************/
public interface OnCenterClickListener {
void centerClicked();
}
@Override
public boolean hasPrev() {
return this.hasPrevPage();
}
@Override
public boolean hasNext() {
return this.hasNextPage();
}
@Override
public void pageCancel() {
cancelChangePage();
}
}
package com.mints.enjoyboxjia.read.anim;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Region;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.view.MotionEvent;
import android.view.View;
import com.mints.enjoyboxjia.read.Direction;
public class AlikeAnim extends PageAnim {
private static final String TAG = "SimulationPageAnim";
private int mCornerX = 1; // 拖拽点对应的页脚
private int mCornerY = 1;
private Path mPath0;
private Path mPath1;
private PointF mBezierStart1 = new PointF(); // 贝塞尔曲线起始点
private PointF mBezierControl1 = new PointF(); // 贝塞尔曲线控制点
private PointF mBeziervertex1 = new PointF(); // 贝塞尔曲线顶点
private PointF mBezierEnd1 = new PointF(); // 贝塞尔曲线结束点
private PointF mBezierStart2 = new PointF(); // 另一条贝塞尔曲线
private PointF mBezierControl2 = new PointF();
private PointF mBeziervertex2 = new PointF();
private PointF mBezierEnd2 = new PointF();
private float mMiddleX;
private float mMiddleY;
private float mDegrees;
private float mTouchToCornerDis;
private ColorMatrixColorFilter mColorMatrixFilter;
private Matrix mMatrix;
private float[] mMatrixArray = {0, 0, 0, 0, 0, 0, 0, 0, 1.0f};
private boolean mIsRTandLB; // 是否属于右上左下
private float mMaxLength;
private GradientDrawable mBackShadowDrawableLR; // 有阴影的GradientDrawable
private GradientDrawable mBackShadowDrawableRL;
private GradientDrawable mFolderShadowDrawableLR;
private GradientDrawable mFolderShadowDrawableRL;
private GradientDrawable mFrontShadowDrawableHBT;
private GradientDrawable mFrontShadowDrawableHTB;
private GradientDrawable mFrontShadowDrawableVLR;
private GradientDrawable mFrontShadowDrawableVRL;
private Paint mPaint;
// 适配 android 高版本无法使用 XOR 的问题
private Path mXORPath;
public AlikeAnim(View view, OnPageChangeListener listener) {
super(view, listener);
mPath0 = new Path();
mPath1 = new Path();
mXORPath = new Path();
mMaxLength = (float) Math.hypot(mViewWidth, mViewHeight);
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
createDrawable();
ColorMatrix cm = new ColorMatrix();//设置颜色数组
float array[] = {1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0};
cm.set(array);
mColorMatrixFilter = new ColorMatrixColorFilter(cm);
mMatrix = new Matrix();
mTouchX = 0.01f; // 不让x,y为0,否则在点计算时会有问题
mTouchY = 0.01f;
}
@Override
public void drawMove(Canvas canvas) {
switch (mDirection) {
case NEXT:
calcPoints();
drawCurrentPageArea(canvas, mCurBitmap, mPath0);
drawNextPageAreaAndShadow(canvas, mNextBitmap);
drawCurrentPageShadow(canvas);
drawCurrentBackArea(canvas, mCurBitmap);
break;
default:
calcPoints();
drawCurrentPageArea(canvas, mNextBitmap, mPath0);
drawNextPageAreaAndShadow(canvas, mCurBitmap);
drawCurrentPageShadow(canvas);
drawCurrentBackArea(canvas, mNextBitmap);
break;
}
}
@Override
public void startAnim() {
int dx, dy;
// dx 水平方向滑动的距离,负值会使滚动向左滚动
// dy 垂直方向滑动的距离,负值会使滚动向上滚动
if (isCancel) {
if (mCornerX > 0 && mDirection.equals(Direction.NEXT)) {
dx = (int) (mViewWidth - mTouchX);
} else {
dx = -(int) mTouchX;
}
if (!mDirection.equals(Direction.NEXT)) {
dx = (int) -(mViewWidth + mTouchX);
}
if (mCornerY > 0) {
dy = (int) (mViewHeight - mTouchY);
} else {
dy = -(int) mTouchY; // 防止mTouchY最终变为0
}
} else {
if (mCornerX > 0 && mDirection.equals(Direction.NEXT)) {
dx = -(int) (mViewWidth + mTouchX);
} else {
dx = (int) (mViewWidth - mTouchX + mViewWidth);
}
if (mCornerY > 0) {
dy = (int) (mViewHeight - mTouchY);
} else {
dy = (int) (1 - mTouchY); // 防止mTouchY最终变为0
}
}
mScroller.startScroll((int) mTouchX, (int) mTouchY, dx, dy, 400);
}
@Override
public boolean onTouchEvent(MotionEvent event){
//点击位置
int x = (int) event.getX();
int y = (int) event.getY();
//更新滑动坐标
mLastX = mTouchX;
mLastY = mTouchY;
mTouchX = x;
mTouchY = y;
//触摸y中间位置吧y变成屏幕高度
if ((mStartY > mViewHeight / 3 && mStartY < mViewHeight * 2 / 3) || mDirection.equals(Direction.PRE)) {
mTouchY = mViewHeight;
}
if (mStartY > mViewHeight / 3 && mStartY < mViewHeight / 2 && mDirection.equals(Direction.NEXT)) {
mTouchY = 1;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mStartX = x;
mStartY = y;
//移动的点击位置
mLastX = 0;
mLastY = 0;
//未开始动画
isAnim = false;
//取消
isCancel = false;
//默认有下一页
noNext = false;
//默认未移动
isMove = false;
calcCornerXY(x, y);
break;
case MotionEvent.ACTION_MOVE:
//判断是否移动了
if (!isMove) {
isMove = Math.abs(mStartX - x) > slop || Math.abs(mStartY - y) > slop;
}
//处理滑动
if (isMove) {
//还未开始滑动
if (mLastX == mStartX) {
//判断方向
if (x - mStartX > 0) {
mDirection = Direction.PRE;
configDirection(mDirection);
//如果一页不存在
if (!mListener.hasPrev()) {
noNext = true;
return true;
}
} else {
mDirection = Direction.NEXT;
configDirection(mDirection);
//如果一页不存在
if (!mListener.hasNext()) {
noNext = true;
return true;
}
}
}
//已经开始滑动
else {
//判断是否需要取消翻页
//即滑动位置到了与初始滑动方向相反时
if (mDirection == Direction.NEXT) {
if (x - mLastX > 0) {
isCancel = true;
} else {
isCancel = false;
}
} else {
if (x - mLastX < 0) {
isCancel = true;
} else {
isCancel = false;
}
}
}
mLastX = x;
mLastY = y;
if (!noNext){
isAnim = true;
mView.invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
if (!isMove) {
isAnim = true;
if (x < mViewWidth / 2) {
mDirection = Direction.PRE;
} else {
mDirection = Direction.NEXT;
}
configDirection(mDirection);
if (mDirection == Direction.NEXT) {
//判断是否下一页存在
boolean hasNext = mListener.hasNext();
if (!hasNext) {
return true;
}
} else {
boolean hasPrev = mListener.hasPrev();
if (!hasPrev) {
return true;
}
}
}
// 是否取消翻页
if (isCancel){
mListener.pageCancel();
}
// 开启翻页效果
if (!noNext) {
startAnim();
mView.invalidate();
}
break;
}
return true;
}
public void configDirection(Direction direction) {
switch (direction) {
case PRE:
//上一页滑动不出现对角
if (mStartX > mViewWidth / 2) {
calcCornerXY(mStartX, mViewHeight);
} else {
calcCornerXY(mViewWidth - mStartX, mViewHeight);
}
break;
case NEXT:
if (mViewWidth / 2 > mStartX) {
calcCornerXY(mViewWidth - mStartX, mStartY);
}
break;
}
}
/**
* 创建阴影的GradientDrawable
*/
private void createDrawable() {
int[] color = {0x333333, 0xb0333333};
mFolderShadowDrawableRL = new GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, color);
mFolderShadowDrawableRL
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFolderShadowDrawableLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, color);
mFolderShadowDrawableLR
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
// 背面颜色组
int[] mBackShadowColors = new int[]{0xff111111, 0x111111};
mBackShadowDrawableRL = new GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, mBackShadowColors);
mBackShadowDrawableRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mBackShadowDrawableLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, mBackShadowColors);
mBackShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);
// 前面颜色组
int[] mFrontShadowColors = new int[]{0x80111111, 0x111111};
mFrontShadowDrawableVLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, mFrontShadowColors);
mFrontShadowDrawableVLR
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFrontShadowDrawableVRL = new GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, mFrontShadowColors);
mFrontShadowDrawableVRL
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFrontShadowDrawableHTB = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, mFrontShadowColors);
mFrontShadowDrawableHTB
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
mFrontShadowDrawableHBT = new GradientDrawable(
GradientDrawable.Orientation.BOTTOM_TOP, mFrontShadowColors);
mFrontShadowDrawableHBT
.setGradientType(GradientDrawable.LINEAR_GRADIENT);
}
/**
* 绘制翻起页背面
*
* @param canvas
* @param bitmap
*/
private void drawCurrentBackArea(Canvas canvas, Bitmap bitmap) {
int i = (int) (mBezierStart1.x + mBezierControl1.x) / 2;
float f1 = Math.abs(i - mBezierControl1.x);
int i1 = (int) (mBezierStart2.y + mBezierControl2.y) / 2;
float f2 = Math.abs(i1 - mBezierControl2.y);
float f3 = Math.min(f1, f2);
mPath1.reset();
mPath1.moveTo(mBeziervertex2.x, mBeziervertex2.y);
mPath1.lineTo(mBeziervertex1.x, mBeziervertex1.y);
mPath1.lineTo(mBezierEnd1.x, mBezierEnd1.y);
mPath1.lineTo(mTouchX, mTouchY);
mPath1.lineTo(mBezierEnd2.x, mBezierEnd2.y);
mPath1.close();
GradientDrawable mFolderShadowDrawable;
int left;
int right;
if (mIsRTandLB) {
left = (int) (mBezierStart1.x - 1);
right = (int) (mBezierStart1.x + f3 + 1);
mFolderShadowDrawable = mFolderShadowDrawableLR;
} else {
left = (int) (mBezierStart1.x - f3 - 1);
right = (int) (mBezierStart1.x + 1);
mFolderShadowDrawable = mFolderShadowDrawableRL;
}
canvas.save();
try {
canvas.clipPath(mPath0);
canvas.clipPath(mPath1, Region.Op.INTERSECT);
} catch (Exception e) {
}
mPaint.setColorFilter(mColorMatrixFilter);
//对Bitmap进行取色
int color = bitmap.getPixel(1, 1);
//获取对应的三色
int red = (color & 0xff0000) >> 16;
int green = (color & 0x00ff00) >> 8;
int blue = (color & 0x0000ff);
//转换成含有透明度的颜色
int tempColor = Color.argb(200, red, green, blue);
float dis = (float) Math.hypot(mCornerX - mBezierControl1.x,
mBezierControl2.y - mCornerY);
float f8 = (mCornerX - mBezierControl1.x) / dis;
float f9 = (mBezierControl2.y - mCornerY) / dis;
mMatrixArray[0] = 1 - 2 * f9 * f9;
mMatrixArray[1] = 2 * f8 * f9;
mMatrixArray[3] = mMatrixArray[1];
mMatrixArray[4] = 1 - 2 * f8 * f8;
mMatrix.reset();
mMatrix.setValues(mMatrixArray);
mMatrix.preTranslate(-mBezierControl1.x, -mBezierControl1.y);
mMatrix.postTranslate(mBezierControl1.x, mBezierControl1.y);
canvas.drawBitmap(bitmap, mMatrix, mPaint);
//背景叠加
canvas.drawColor(tempColor);
mPaint.setColorFilter(null);
canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
mFolderShadowDrawable.setBounds(left, (int) mBezierStart1.y, right,
(int) (mBezierStart1.y + mMaxLength));
mFolderShadowDrawable.draw(canvas);
canvas.restore();
}
public void scrollAnim() {
if (mScroller.computeScrollOffset()) {
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
mLastX = mTouchX;
mLastY = mTouchY;
mTouchX = x;
mTouchY = y;
//触摸y中间位置吧y变成屏幕高度
if ((mStartY > mViewHeight / 3 && mStartY < mViewHeight * 2 / 3) || mDirection.equals(Direction.PRE)) {
mTouchY = mViewHeight;
}
if (mStartY > mViewHeight / 3 && mStartY < mViewHeight / 2 && mDirection.equals(Direction.NEXT)) {
mTouchY = 1;
}
if (mScroller.getFinalX() == x && mScroller.getFinalY() == y){
isAnim = false;
}
mView.postInvalidate();
}
}
/**
* 绘制翻起页的阴影
*
* @param canvas
*/
private void drawCurrentPageShadow(Canvas canvas) {
double degree;
if (mIsRTandLB) {
degree = Math.PI
/ 4
- Math.atan2(mBezierControl1.y - mTouchY, mTouchX
- mBezierControl1.x);
} else {
degree = Math.PI
/ 4
- Math.atan2(mTouchY - mBezierControl1.y, mTouchX
- mBezierControl1.x);
}
// 翻起页阴影顶点与touch点的距离
double d1 = (float) 25 * 1.414 * Math.cos(degree);
double d2 = (float) 25 * 1.414 * Math.sin(degree);
float x = (float) (mTouchX + d1);
float y;
if (mIsRTandLB) {
y = (float) (mTouchY + d2);
} else {
y = (float) (mTouchY - d2);
}
mPath1.reset();
mPath1.moveTo(x, y);
mPath1.lineTo(mTouchX, mTouchY);
mPath1.lineTo(mBezierControl1.x, mBezierControl1.y);
mPath1.lineTo(mBezierStart1.x, mBezierStart1.y);
mPath1.close();
float rotateDegrees;
canvas.save();
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
mXORPath.reset();
mXORPath.moveTo(0f, 0f);
mXORPath.lineTo(canvas.getWidth(), 0f);
mXORPath.lineTo(canvas.getWidth(), canvas.getHeight());
mXORPath.lineTo(0f, canvas.getHeight());
mXORPath.close();
// 取 path 的补集,作为 canvas 的交集
mXORPath.op(mPath0, Path.Op.XOR);
canvas.clipPath(mXORPath);
} else {
canvas.clipPath(mPath0, Region.Op.XOR);
}
canvas.clipPath(mPath1, Region.Op.INTERSECT);
} catch (Exception e) {
// TODO: handle exception
}
int leftx;
int rightx;
GradientDrawable mCurrentPageShadow;
if (mIsRTandLB) {
leftx = (int) (mBezierControl1.x);
rightx = (int) mBezierControl1.x + 25;
mCurrentPageShadow = mFrontShadowDrawableVLR;
} else {
leftx = (int) (mBezierControl1.x - 25);
rightx = (int) mBezierControl1.x + 1;
mCurrentPageShadow = mFrontShadowDrawableVRL;
}
rotateDegrees = (float) Math.toDegrees(Math.atan2(mTouchX
- mBezierControl1.x, mBezierControl1.y - mTouchY));
canvas.rotate(rotateDegrees, mBezierControl1.x, mBezierControl1.y);
mCurrentPageShadow.setBounds(leftx,
(int) (mBezierControl1.y - mMaxLength), rightx,
(int) (mBezierControl1.y));
mCurrentPageShadow.draw(canvas);
canvas.restore();
mPath1.reset();
mPath1.moveTo(x, y);
mPath1.lineTo(mTouchX, mTouchY);
mPath1.lineTo(mBezierControl2.x, mBezierControl2.y);
mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);
mPath1.close();
canvas.save();
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
mXORPath.reset();
mXORPath.moveTo(0f, 0f);
mXORPath.lineTo(canvas.getWidth(), 0f);
mXORPath.lineTo(canvas.getWidth(), canvas.getHeight());
mXORPath.lineTo(0f, canvas.getHeight());
mXORPath.close();
// 取 path 的补给,作为 canvas 的交集
mXORPath.op(mPath0, Path.Op.XOR);
canvas.clipPath(mXORPath);
} else {
canvas.clipPath(mPath0, Region.Op.XOR);
}
canvas.clipPath(mPath1, Region.Op.INTERSECT);
} catch (Exception e) {
}
if (mIsRTandLB) {
leftx = (int) (mBezierControl2.y);
rightx = (int) (mBezierControl2.y + 25);
mCurrentPageShadow = mFrontShadowDrawableHTB;
} else {
leftx = (int) (mBezierControl2.y - 25);
rightx = (int) (mBezierControl2.y + 1);
mCurrentPageShadow = mFrontShadowDrawableHBT;
}
rotateDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl2.y
- mTouchY, mBezierControl2.x - mTouchX));
canvas.rotate(rotateDegrees, mBezierControl2.x, mBezierControl2.y);
float temp;
if (mBezierControl2.y < 0)
temp = mBezierControl2.y - mViewHeight;
else
temp = mBezierControl2.y;
int hmg = (int) Math.hypot(mBezierControl2.x, temp);
if (hmg > mMaxLength)
mCurrentPageShadow
.setBounds((int) (mBezierControl2.x - 25) - hmg, leftx,
(int) (mBezierControl2.x + mMaxLength) - hmg,
rightx);
else
mCurrentPageShadow.setBounds(
(int) (mBezierControl2.x - mMaxLength), leftx,
(int) (mBezierControl2.x), rightx);
mCurrentPageShadow.draw(canvas);
canvas.restore();
}
private void drawNextPageAreaAndShadow(Canvas canvas, Bitmap bitmap) {
mPath1.reset();
mPath1.moveTo(mBezierStart1.x, mBezierStart1.y);
mPath1.lineTo(mBeziervertex1.x, mBeziervertex1.y);
mPath1.lineTo(mBeziervertex2.x, mBeziervertex2.y);
mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);
mPath1.lineTo(mCornerX, mCornerY);
mPath1.close();
mDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl1.x
- mCornerX, mBezierControl2.y - mCornerY));
int leftx;
int rightx;
GradientDrawable mBackShadowDrawable;
if (mIsRTandLB) { //左下及右上
leftx = (int) (mBezierStart1.x);
rightx = (int) (mBezierStart1.x + mTouchToCornerDis / 4);
mBackShadowDrawable = mBackShadowDrawableLR;
} else {
leftx = (int) (mBezierStart1.x - mTouchToCornerDis / 4);
rightx = (int) mBezierStart1.x;
mBackShadowDrawable = mBackShadowDrawableRL;
}
canvas.save();
try {
canvas.clipPath(mPath0);
canvas.clipPath(mPath1, Region.Op.INTERSECT);
} catch (Exception e) {
}
canvas.drawBitmap(bitmap, 0, 0, null);
canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
mBackShadowDrawable.setBounds(leftx, (int) mBezierStart1.y, rightx,
(int) (mMaxLength + mBezierStart1.y));//左上及右下角的xy坐标值,构成一个矩形
mBackShadowDrawable.draw(canvas);
canvas.restore();
}
private void drawCurrentPageArea(Canvas canvas, Bitmap bitmap, Path path) {
mPath0.reset();
mPath0.moveTo(mBezierStart1.x, mBezierStart1.y);
mPath0.quadTo(mBezierControl1.x, mBezierControl1.y, mBezierEnd1.x,
mBezierEnd1.y);
mPath0.lineTo(mTouchX, mTouchY);
mPath0.lineTo(mBezierEnd2.x, mBezierEnd2.y);
mPath0.quadTo(mBezierControl2.x, mBezierControl2.y, mBezierStart2.x,
mBezierStart2.y);
mPath0.lineTo(mCornerX, mCornerY);
mPath0.close();
canvas.save();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
mXORPath.reset();
mXORPath.moveTo(0f, 0f);
mXORPath.lineTo(canvas.getWidth(), 0f);
mXORPath.lineTo(canvas.getWidth(), canvas.getHeight());
mXORPath.lineTo(0f, canvas.getHeight());
mXORPath.close();
// 取 path 的补给,作为 canvas 的交集
mXORPath.op(path, Path.Op.XOR);
canvas.clipPath(mXORPath);
} else {
canvas.clipPath(path, Region.Op.XOR);
}
canvas.drawBitmap(bitmap, 0, 0, null);
try {
canvas.restore();
} catch (Exception e) {
}
}
/**
* 计算拖拽点对应的拖拽脚
*
* @param x
* @param y
*/
public void calcCornerXY(float x, float y) {
if (x <= mViewWidth / 2) {
mCornerX = 0;
} else {
mCornerX = mViewWidth;
}
if (y <= mViewHeight / 2) {
mCornerY = 0;
} else {
mCornerY = mViewHeight;
}
if ((mCornerX == 0 && mCornerY == mViewHeight)
|| (mCornerX == mViewWidth && mCornerY == 0)) {
mIsRTandLB = true;
} else {
mIsRTandLB = false;
}
}
private void calcPoints() {
mMiddleX = (mTouchX + mCornerX) / 2;
mMiddleY = (mTouchY + mCornerY) / 2;
mBezierControl1.x = mMiddleX - (mCornerY - mMiddleY)
* (mCornerY - mMiddleY) / (mCornerX - mMiddleX);
mBezierControl1.y = mCornerY;
mBezierControl2.x = mCornerX;
float f4 = mCornerY - mMiddleY;
if (f4 == 0) {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
* (mCornerX - mMiddleX) / 0.1f;
} else {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
* (mCornerX - mMiddleX) / (mCornerY - mMiddleY);
}
mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x)
/ 2;
mBezierStart1.y = mCornerY;
// 当mBezierStart1.x < 0或者mBezierStart1.x > 480时
// 如果继续翻页,会出现BUG故在此限制
if (mTouchX > 0 && mTouchX < mViewWidth) {
if (mBezierStart1.x < 0 || mBezierStart1.x > mViewWidth) {
if (mBezierStart1.x < 0)
mBezierStart1.x = mViewWidth - mBezierStart1.x;
float f1 = Math.abs(mCornerX - mTouchX);
float f2 = mViewWidth * f1 / mBezierStart1.x;
mTouchX = Math.abs(mCornerX - f2);
float f3 = Math.abs(mCornerX - mTouchX)
* Math.abs(mCornerY - mTouchY) / f1;
mTouchY = Math.abs(mCornerY - f3);
mMiddleX = (mTouchX + mCornerX) / 2;
mMiddleY = (mTouchY + mCornerY) / 2;
mBezierControl1.x = mMiddleX - (mCornerY - mMiddleY)
* (mCornerY - mMiddleY) / (mCornerX - mMiddleX);
mBezierControl1.y = mCornerY;
mBezierControl2.x = mCornerX;
float f5 = mCornerY - mMiddleY;
if (f5 == 0) {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
* (mCornerX - mMiddleX) / 0.1f;
} else {
mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
* (mCornerX - mMiddleX) / (mCornerY - mMiddleY);
}
mBezierStart1.x = mBezierControl1.x
- (mCornerX - mBezierControl1.x) / 2;
}
}
mBezierStart2.x = mCornerX;
mBezierStart2.y = mBezierControl2.y - (mCornerY - mBezierControl2.y)
/ 2;
mTouchToCornerDis = (float) Math.hypot((mTouchX - mCornerX),
(mTouchY - mCornerY));
mBezierEnd1 = getCross(new PointF(mTouchX, mTouchY), mBezierControl1, mBezierStart1,
mBezierStart2);
mBezierEnd2 = getCross(new PointF(mTouchX, mTouchY), mBezierControl2, mBezierStart1,
mBezierStart2);
mBeziervertex1.x = (mBezierStart1.x + 2 * mBezierControl1.x + mBezierEnd1.x) / 4;
mBeziervertex1.y = (2 * mBezierControl1.y + mBezierStart1.y + mBezierEnd1.y) / 4;
mBeziervertex2.x = (mBezierStart2.x + 2 * mBezierControl2.x + mBezierEnd2.x) / 4;
mBeziervertex2.y = (2 * mBezierControl2.y + mBezierStart2.y + mBezierEnd2.y) / 4;
}
/**
* 求解直线P1P2和直线P3P4的交点坐标
*
* @param P1
* @param P2
* @param P3
* @param P4
* @return
*/
private PointF getCross(PointF P1, PointF P2, PointF P3, PointF P4) {
PointF CrossP = new PointF();
// 二元函数通式: y=ax+b
float a1 = (P2.y - P1.y) / (P2.x - P1.x);
float b1 = ((P1.x * P2.y) - (P2.x * P1.y)) / (P1.x - P2.x);
float a2 = (P4.y - P3.y) / (P4.x - P3.x);
float b2 = ((P3.x * P4.y) - (P4.x * P3.y)) / (P3.x - P4.x);
CrossP.x = (b2 - b1) / (a1 - a2);
CrossP.y = a1 * CrossP.x + b1;
return CrossP;
}
}
package com.mints.enjoyboxjia.read.anim;
import android.graphics.Matrix;
import android.view.animation.Animation;
import android.view.animation.Transformation;
public class ContentScaleAnimation extends Animation {
private float mPivotX;
private float mPivotY;
// 控件左上角坐标
private float mStartX;
private float mStartY;
private final float scaleTimes;
private boolean mReverse;
public ContentScaleAnimation(float mPivotXValue, float mPivotYValue, float scaleTimes, boolean mReverse) {
this.mStartX = mPivotXValue;
this.mStartY = mPivotYValue;
this.scaleTimes = scaleTimes;
this.mReverse = mReverse;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
//缩放方法
Matrix matrix=t.getMatrix();
if (mReverse) {
matrix.postScale(1 + (scaleTimes - 1) * (1.0f - interpolatedTime), 1 + (scaleTimes - 1) * (1.0f - interpolatedTime), mPivotX - mStartX, mPivotY - mStartY);
} else {
matrix.postScale(1 + (scaleTimes - 1) * interpolatedTime, 1 + (scaleTimes - 1) * interpolatedTime, mPivotX - mStartX, mPivotY - mStartY);
//matrix.postScale(1 + (scaleTimes - 1) * interpolatedTime, 1 + (scaleTimes - 1) * interpolatedTime, mPivotX - mPivotXValue , 171 );
}
}
//缩放点坐标值
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mPivotX = resolvePivotX(mStartX, parentWidth, width);
mPivotY = resolvePivoY(mStartY, parentHeight, height);
}
//缩放点坐标值 缩放点到自身左边距离/缩放点到父控件左边的距离=缩放点自身右侧距离/缩放点到父控件右边的距离
private float resolvePivotX(float margingLeft, int parentWidth, int width) {
//换算得出的结果
return (margingLeft * parentWidth) / (parentWidth - width);
}
private float resolvePivoY(float marginTop, int parentHeight, int height) {
return (marginTop * parentHeight) / (parentHeight - height);
}
public void reverse() {
mReverse = !mReverse;
}
public boolean getMReverse() {
return mReverse;
}
}
\ No newline at end of file
package com.mints.enjoyboxjia.read.anim;
import android.graphics.Canvas;
import android.graphics.drawable.GradientDrawable;
import android.view.View;
public class CoverAnim extends SlideAnim {
private GradientDrawable mBackShadowDrawableLR;
public CoverAnim(View view, OnPageChangeListener mListener) {
super(view, mListener);
int[] mBackShadowColors = new int[] { 0x66000000,0x00000000};
mBackShadowDrawableLR = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, mBackShadowColors);
mBackShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);
}
//添加阴影
public void addShadow(int left,Canvas canvas) {
mBackShadowDrawableLR.setBounds(left, 0, left + 30 , mViewHeight);
mBackShadowDrawableLR.draw(canvas);
}
@Override
public void drawMove(Canvas canvas) {
if (!isAnim) return;
switch (mDirection){
case NEXT:
int dis = (int) (mViewWidth - mStartX + mTouchX);
if (dis > mViewWidth){
dis = mViewWidth;
}
//计算bitmap截取的区域
mSrcRect.left = mViewWidth - dis;
//计算bitmap在canvas显示的区域
mDestRect.right = dis;
canvas.drawBitmap(mNextBitmap,0,0,null);
canvas.drawBitmap(mCurBitmap,mSrcRect,mDestRect,null);
addShadow(dis,canvas);
break;
default:
mSrcRect.left = (int) (mViewWidth - mTouchX);
mDestRect.right = (int) mTouchX;
canvas.drawBitmap(mCurBitmap,0,0,null);
canvas.drawBitmap(mNextBitmap,mSrcRect,mDestRect,null);
addShadow((int) mTouchX,canvas);
break;
}
}
}
package com.mints.enjoyboxjia.read.anim;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;
import com.mints.enjoyboxjia.read.Direction;
public class NoneAnim extends PageAnim {
public NoneAnim(View view, OnPageChangeListener mListener) {
super(view, mListener);
}
@Override
public void startAnim() {
}
@Override
public boolean onTouchEvent(MotionEvent event){
//点击位置
int x = (int) event.getX();
int y = (int) event.getY();
//更新滑动坐标
mLastX = mTouchX;
mLastY = mTouchY;
mTouchX = x;
mTouchY = y;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mStartX = x;
mStartY = y;
//移动的点击位置
mLastX = 0;
mLastY = 0;
//未开始动画
isAnim = false;
//取消
isCancel = false;
break;
case MotionEvent.ACTION_MOVE:
//判断是否移动了
if (!isMove) {
isMove = Math.abs(mStartX - x) > slop || Math.abs(mStartY - y) > slop;
}
//处理滑动
if (isMove) {
//还未开始滑动
if (mLastX == mStartX) {
//判断方向
if (x - mStartX > 0) {
mDirection = Direction.PRE;
//如果一页不存在
if (!mListener.hasPrev()) {
noNext = true;
return true;
}
} else {
mDirection = Direction.NEXT;
//如果一页不存在
if (!mListener.hasNext()) {
noNext = true;
return true;
}
}
}
//已经开始滑动
else {
//判断是否需要取消翻页
//即滑动位置到了与初始滑动方向相反时
if (mDirection == Direction.NEXT) {
if (x - mLastX > 0) {
isCancel = true;
} else {
isCancel = false;
}
} else {
if (x - mLastX < 0) {
isCancel = true;
} else {
isCancel = false;
}
}
}
mLastX = x;
mLastY = y;
isAnim = true;
mView.invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (!isMove) {
if (x < mViewWidth / 2) {
mDirection = Direction.PRE;
} else {
mDirection = Direction.NEXT;
}
if (mDirection == Direction.NEXT) {
//判断是否下一页存在
boolean hasNext = mListener.hasNext();
if (!hasNext) {
return true;
}
} else {
boolean hasPrev = mListener.hasPrev();
if (!hasPrev) {
return true;
}
}
}
// 是否取消翻页
if (isCancel){
mListener.pageCancel();
}
// 开启翻页效果
if (!noNext) {
startAnim();
mView.invalidate();
}
break;
}
return true;
}
@Override
public void scrollAnim() {
}
@Override
public void drawMove(Canvas canvas) {
if (isCancel){
canvas.drawBitmap(mCurBitmap, 0, 0, null);
}else {
canvas.drawBitmap(mNextBitmap, 0, 0, null);
}
}
}
package com.mints.enjoyboxjia.read.anim;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Scroller;
import com.mints.enjoyboxjia.read.Direction;
public abstract class PageAnim {
//绘制的页面
Bitmap mCurBitmap;
Bitmap mNextBitmap;
//滑动器
public Scroller mScroller;
//需要动画的视图
View mView;
//视图的尺寸
protected int mViewWidth;
protected int mViewHeight;
//起始点
protected float mStartX;
protected float mStartY;
//触碰点
protected float mTouchX;
protected float mTouchY;
//上一个触碰点
protected float mLastX;
protected float mLastY;
//是否在移动
protected boolean isMove;
//最短滑动距离
int slop;
//滑动方向
Direction mDirection = Direction.NONE;
//是否取消翻页
boolean isCancel;
//页面位移矩阵
protected Rect mSrcRect, mDestRect;
//是否开始动画
boolean isAnim = false;
//监听者
protected OnPageChangeListener mListener;
//是否没下一页或者上一页
protected boolean noNext = false;
public PageAnim(View view, OnPageChangeListener mListener){
mView = view;
mScroller = new Scroller(mView.getContext());
this.mListener = mListener;
initData();
}
/***********************************public***********************************/
//初始化数据
protected void initData(){
mViewWidth = mView.getWidth();
mViewHeight = mView.getHeight();
mCurBitmap = Bitmap.createBitmap(mView.getWidth(),mView.getHeight(),Bitmap.Config.RGB_565);
mNextBitmap = Bitmap.createBitmap(mView.getWidth(),mView.getHeight(),Bitmap.Config.RGB_565);
mSrcRect = new Rect(0, 0, mViewWidth, mViewHeight);
mDestRect = new Rect(0, 0, mViewWidth, mViewHeight);
slop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
}
//画页面
public void drawViewPages(Canvas canvas){
if (isAnim){
drawMove(canvas);
}else {
if (isCancel){
mNextBitmap = mCurBitmap.copy(Bitmap.Config.RGB_565, true);
canvas.drawBitmap(mCurBitmap, 0, 0, null);
}else {
canvas.drawBitmap(mNextBitmap, 0, 0, null);
}
}
}
public void changePage(){
Bitmap bitmap = mCurBitmap;
mCurBitmap = mNextBitmap;
mNextBitmap = bitmap;
}
/************************************abstract****************************************/
//开启翻页动画
public abstract void startAnim();
public Bitmap getmCurBitmap() {
return mCurBitmap;
}
//获取下页的bitmap
public Bitmap getmNextBitmap() {
return mNextBitmap;
}
public void setmCurBitmap(Bitmap mCurBitmap) {
this.mCurBitmap = mCurBitmap;
}
public void setmNextBitmap(Bitmap mNextBitmap) {
this.mNextBitmap = mNextBitmap;
}
//处理触摸事件
public abstract boolean onTouchEvent(MotionEvent event);
//计算滚动
public abstract void scrollAnim();
//绘制翻页动画
public abstract void drawMove(Canvas canvas);
/************************************interface****************************************/
//监听界面变化 PageView实现
public interface OnPageChangeListener {
boolean hasPrev();
boolean hasNext();
void pageCancel();
}
}
package com.mints.enjoyboxjia.read.anim;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.Log;
import android.view.animation.Animation;
import android.view.animation.Transformation;
public class Rotate3DAnimation extends Animation {
private static final String TAG = "Rotate3DAnimation";
private final float mFromDegrees;
private final float mToDegrees;
private final float mMarginLeft;
private final float mMarginTop;
// private final float mDepthZ;
private final float mAnimationScale;
private boolean reverse;
private Camera mCamera;
// 旋转中心
private float mPivotX;
private float mPivotY;
private float scale = 1; // <------- 像素密度
public Rotate3DAnimation(Context context, float mFromDegrees, float mToDegrees, float mMarginLeft, float mMarginTop,
float animationScale, boolean reverse) {
this.mFromDegrees = mFromDegrees;
this.mToDegrees = mToDegrees;
this.mMarginLeft = mMarginLeft;
this.mMarginTop = mMarginTop;
this.mAnimationScale = animationScale;
this.reverse = reverse;
// 获取手机像素密度 (即dp与px的比例)
scale = context.getResources().getDisplayMetrics().density;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
mPivotX = calculatePivotX(mMarginLeft, parentWidth, width);
mPivotY = calculatePivotY(mMarginTop, parentHeight, height);
Log.i(TAG,"width:"+width+",height:"+height+",pw:"+parentWidth+",ph:"+parentHeight);
Log.i(TAG,"中心点x:"+mPivotX+",中心点y:"+mPivotY);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
float degrees = reverse ? mToDegrees + (mFromDegrees - mToDegrees) * interpolatedTime : mFromDegrees + (mToDegrees - mFromDegrees) * interpolatedTime;
Matrix matrix = t.getMatrix();
Camera camera = mCamera;
camera.save();
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
// 修正失真,主要修改 MPERSP_0 和 MPERSP_1
float[] mValues = new float[9];
matrix.getValues(mValues); //获取数值
mValues[6] = mValues[6] / scale; //数值修正
mValues[7] = mValues[7] / scale; //数值修正
matrix.setValues(mValues); //重新赋值
if (reverse) {
matrix.postScale(1 + (mAnimationScale - 1) * interpolatedTime, 1 + (mAnimationScale - 1) * interpolatedTime,
mPivotX - mMarginLeft, mPivotY - mMarginTop);
} else {
matrix.postScale(1 + (mAnimationScale - 1) * (1 - interpolatedTime), 1 + (mAnimationScale - 1) * (1 - interpolatedTime),
mPivotX - mMarginLeft, mPivotY - mMarginTop);
}
}
/**
* 计算缩放的中心点的横坐标
*
* @param marginLeft 该View距离父布局左边的距离
* @param parentWidth 父布局的宽度
* @param width View的宽度
* @return 缩放中心点的横坐标
*/
public float calculatePivotX(float marginLeft, float parentWidth, float width) {
return parentWidth * marginLeft / (parentWidth - width);
}
/**
* 计算缩放的中心点的纵坐标
*
* @param marginTop 该View顶部距离父布局顶部的距离
* @param parentHeight 父布局的高度
* @param height 子布局的高度
* @return 缩放的中心点的纵坐标
*/
public float calculatePivotY(float marginTop, float parentHeight, float height) {
return parentHeight * marginTop / (parentHeight - height);
}
public void reverse() {
reverse = !reverse;
}
}
package com.mints.enjoyboxjia.read.anim;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;
import com.mints.enjoyboxjia.read.Direction;
public class SlideAnim extends PageAnim {
public SlideAnim(View view, PageAnim.OnPageChangeListener mListener){
super(view,mListener);
}
public void startAnim(){
int dx = 0;
switch (mDirection){
case NEXT:
if (isCancel){
int dis = (int) ((mViewWidth - mStartX) + mTouchX);
if (dis > mViewWidth){
dis = mViewWidth;
}
dx = mViewWidth - dis;
}else{
dx = (int) -(mTouchX + (mViewWidth - mStartX));
}
break;
default:
if (isCancel){
dx = (int) -mTouchX;
}else{
dx = (int) (mViewWidth - mTouchX);
}
break;
}
//滑动速度保持一致
int duration = (800 * Math.abs(dx)) / mViewWidth;
mScroller.startScroll((int) mTouchX, 0, dx, 0, duration);
}
public Bitmap getmCurBitmap() {
return mCurBitmap;
}
public Bitmap getmNextBitmap() {
return mNextBitmap;
}
public boolean onTouchEvent(MotionEvent event){
//点击位置
int x = (int) event.getX();
int y = (int) event.getY();
//更新滑动坐标
mLastX = mTouchX;
mLastY = mTouchY;
mTouchX = x;
mTouchY = y;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mStartX = x;
mStartY = y;
//移动的点击位置
mLastX = 0;
mLastY = 0;
//未开始动画
isAnim = false;
//取消
isCancel = false;
break;
case MotionEvent.ACTION_MOVE:
//判断是否移动了
if (!isMove) {
isMove = Math.abs(mStartX - x) > slop || Math.abs(mStartY - y) > slop;
}
//处理滑动
if (isMove) {
//还未开始滑动
if (mLastX == mStartX) {
//判断方向
if (x - mStartX > 0) {
mDirection = Direction.PRE;
//如果一页不存在
if (!mListener.hasPrev()) {
noNext = true;
return true;
}
} else {
mDirection = Direction.NEXT;
//如果一页不存在
if (!mListener.hasNext()) {
noNext = true;
return true;
}
}
}
//已经开始滑动
else {
//判断是否需要取消翻页
//即滑动位置到了与初始滑动方向相反时
if (mDirection == Direction.NEXT) {
if (x - mLastX > 0) {
isCancel = true;
} else {
isCancel = false;
}
} else {
if (x - mLastX < 0) {
isCancel = true;
} else {
isCancel = false;
}
}
}
mLastX = x;
mLastY = y;
isAnim = true;
mView.invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (!isMove) {
if (x < mViewWidth / 2) {
mDirection = Direction.PRE;
} else {
mDirection = Direction.NEXT;
}
if (mDirection == Direction.NEXT) {
//判断是否下一页存在
boolean hasNext = mListener.hasNext();
if (!hasNext) {
return true;
}
} else {
boolean hasPrev = mListener.hasPrev();
if (!hasPrev) {
return true;
}
}
}
// 是否取消翻页
if (isCancel){
mListener.pageCancel();
}
// 开启翻页效果
if (!noNext) {
startAnim();
mView.invalidate();
}
break;
}
return true;
}
public void scrollAnim() {
if (mScroller.computeScrollOffset()) {
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
mLastX = mTouchX;
mLastY = mTouchY;
mTouchX = x;
mTouchY = y;
if (mScroller.getFinalX() == x && mScroller.getFinalY() == y){
isAnim = false;
}
mView.postInvalidate();
}
}
//绘制翻页动画
public void drawMove(Canvas canvas) {
switch (mDirection){
case NEXT:
int dis = (int) (mViewWidth - mStartX + mTouchX);
if (dis > mViewWidth){
dis = mViewWidth;
}
//计算bitmap截取的区域
mSrcRect.left = mViewWidth - dis;
//计算bitmap在canvas显示的区域
mDestRect.right = dis;
canvas.drawBitmap(mNextBitmap,0,0,null);
canvas.drawBitmap(mCurBitmap,mSrcRect,mDestRect,null);
break;
default:
mSrcRect.left = (int) (mViewWidth - mTouchX);
mDestRect.right = (int) mTouchX;
canvas.drawBitmap(mCurBitmap,0,0,null);
canvas.drawBitmap(mNextBitmap,mSrcRect,mDestRect,null);
break;
}
}
}
package com.mints.enjoyboxjia.read.models;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.RequiresApi;
import org.litepal.crud.LitePalSupport;
/**
* 书籍的模型
*/
public class BookModel extends LitePalSupport implements Parcelable {
//LitePal自动生成id
private Long id = 0l;
private String bookId;
private String bookTitle;
private String coverResource;
private String record;
private String filePath;
public BookModel(String bookTitle, String coverResource, String record, String filePath) {
this.bookTitle = bookTitle;
this.coverResource = coverResource;
this.record = record;
this.filePath = filePath;
}
public BookModel() {
}
@RequiresApi(api = Build.VERSION_CODES.Q)
protected BookModel(Parcel in) {
id = in.readLong();
bookId = in.readString();
bookTitle = in.readString();
coverResource = in.readString();
record = in.readString();
filePath = in.readString();
}
public static final Creator<BookModel> CREATOR = new Creator<BookModel>() {
@RequiresApi(api = Build.VERSION_CODES.Q)
@Override
public BookModel createFromParcel(Parcel in) {
BookModel model = new BookModel();
model.id = in.readLong();
model.bookId = in.readString();
model.bookTitle = in.readString();
model.coverResource = in.readString();
model.record = in.readString();
model.filePath = in.readString();
return model;
}
@Override
public BookModel[] newArray(int size) {
return new BookModel[size];
}
};
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getBookId() {
return bookId;
}
public void setBookId(String bookId) {
this.bookId = bookId;
}
public String getBookTitle() {
return bookTitle;
}
public void setBookTitle(String bookTitle) {
this.bookTitle = bookTitle;
}
public String getCoverResource() {
return coverResource;
}
public void setCoverResource(String coverResource) {
this.coverResource = coverResource;
}
public String getRecord() {
return record;
}
public void setRecord(String record) {
this.record = record;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(id);
dest.writeString(bookId);
dest.writeString(bookTitle);
dest.writeString(coverResource);
dest.writeString(record);
dest.writeString(filePath);
}
}
package com.mints.enjoyboxjia.read.models;
import org.litepal.crud.LitePalSupport;
public class BookRecordModel extends LitePalSupport {
private String book_id;
private int chapterPos;
private int pagePos;
private int text_color;
private int page_color;
private int text_size;
private int anim_type;
public String getBook_id() {
return book_id;
}
public void setBook_id(String book_id) {
this.book_id = book_id;
}
public int getChapterPos() {
return chapterPos;
}
public void setChapterPos(int chapterPos) {
this.chapterPos = chapterPos;
}
public int getPagePos() {
return pagePos;
}
public void setPagePos(int pagePos) {
this.pagePos = pagePos;
}
public int getText_color() {
return text_color;
}
public void setText_color(int text_color) {
this.text_color = text_color;
}
public int getPage_color() {
return page_color;
}
public void setPage_color(int page_color) {
this.page_color = page_color;
}
public int getText_size() {
return text_size;
}
public void setText_size(int text_size) {
this.text_size = text_size;
}
public int getAnim_type() {
return anim_type;
}
public void setAnim_type(int anim_type) {
this.anim_type = anim_type;
}
}
package com.mints.enjoyboxjia.read.models;
import com.google.gson.annotations.SerializedName;
import io.reactivex.SingleObserver;
public class ChapterInfoBean {
private String title;
@SerializedName("content")
private String body;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
package com.mints.enjoyboxjia.read.models;
import com.google.gson.annotations.SerializedName;
import org.litepal.crud.LitePalSupport;
import java.util.Date;
/**
* 小说的章节
* */
public class TxtChapterModel extends LitePalSupport {
@SerializedName("Local_id")
long id;
//chapterId
@SerializedName("id")
private String chapterId;
//章节名
@SerializedName("indexName")
public String title;
//章节链接
String link;
//章节的起始和结束位置
public long start;
public long end;
//书籍id
private long book_id;
private Integer indexNum;
private Integer wordCount;
private Byte isVip;
private Integer bookPrice;
private String storageType;
private Date createTime;
private Date updateTime;
public TxtChapterModel() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getChapterId() {
return chapterId;
}
public void setChapterId(String chapterId) {
this.chapterId = chapterId;
}
public TxtChapterModel(long book_id) {
this.book_id = book_id;
}
public TxtChapterModel(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public long getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public long getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
public long getBook_id() {
return book_id;
}
public void setBook_id(long book_id) {
this.book_id = book_id;
}
public Integer getIndexNum() {
return indexNum;
}
public void setIndexNum(Integer indexNum) {
this.indexNum = indexNum;
}
public Integer getWordCount() {
return wordCount;
}
public void setWordCount(Integer wordCount) {
this.wordCount = wordCount;
}
public Byte getIsVip() {
return isVip;
}
public void setIsVip(Byte isVip) {
this.isVip = isVip;
}
public Integer getBookPrice() {
return bookPrice;
}
public void setBookPrice(Integer bookPrice) {
this.bookPrice = bookPrice;
}
public String getStorageType() {
return storageType;
}
public void setStorageType(String storageType) {
this.storageType = storageType == null ? null : storageType.trim();
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
package com.mints.enjoyboxjia.read.models;
import java.util.List;
/**
* 小说显示的页面
* */
public class TxtPageModel {
public int position;
public String title;
public int titleLines;
public List<String> lines;
}
...@@ -6,6 +6,7 @@ import androidx.core.content.ContextCompat ...@@ -6,6 +6,7 @@ import androidx.core.content.ContextCompat
import com.mints.enjoyboxjia.R import com.mints.enjoyboxjia.R
import com.mints.enjoyboxjia.common.BOOK_CONTENT_COLOR import com.mints.enjoyboxjia.common.BOOK_CONTENT_COLOR
import com.mints.enjoyboxjia.common.BOOK_CONTENT_SIZE import com.mints.enjoyboxjia.common.BOOK_CONTENT_SIZE
import com.mints.enjoyboxjia.common.Constant
import com.mints.enjoyboxjia.manager.ReadTimeCount import com.mints.enjoyboxjia.manager.ReadTimeCount
import com.mints.enjoyboxjia.mvp.model.BookContentBean import com.mints.enjoyboxjia.mvp.model.BookContentBean
import com.mints.enjoyboxjia.mvp.presenters.BookReadPresenter import com.mints.enjoyboxjia.mvp.presenters.BookReadPresenter
...@@ -21,10 +22,6 @@ import net.nightwhistler.htmlspanner.HtmlSpanner ...@@ -21,10 +22,6 @@ import net.nightwhistler.htmlspanner.HtmlSpanner
class BookReadActivity : BaseActivity(), View.OnClickListener, BookReadView { class BookReadActivity : BaseActivity(), View.OnClickListener, BookReadView {
companion object {
const val BOOK_ID = "BOOK_ID"
}
private var bookId = "" private var bookId = ""
private var isCollect = false private var isCollect = false
private val readPresenter by lazy { BookReadPresenter() } private val readPresenter by lazy { BookReadPresenter() }
...@@ -38,7 +35,7 @@ class BookReadActivity : BaseActivity(), View.OnClickListener, BookReadView { ...@@ -38,7 +35,7 @@ class BookReadActivity : BaseActivity(), View.OnClickListener, BookReadView {
override fun getBundleExtras(extras: Bundle?) { override fun getBundleExtras(extras: Bundle?) {
super.getBundleExtras(extras) super.getBundleExtras(extras)
extras?.let { extras?.let {
bookId = extras.getString(BOOK_ID).toString() bookId = extras.getString(Constant.ROUTE_BOOK_ID).toString()
} }
} }
......
...@@ -80,7 +80,7 @@ class BookSearchActivity : BaseActivity(), View.OnClickListener, BookSearchView, ...@@ -80,7 +80,7 @@ class BookSearchActivity : BaseActivity(), View.OnClickListener, BookSearchView,
searchHotBookAdapter = SearchHotBookAdapter(this) searchHotBookAdapter = SearchHotBookAdapter(this)
searchHotBookAdapter?.setOnItemClickListener { _, _, position -> searchHotBookAdapter?.setOnItemClickListener { _, _, position ->
val bundle = Bundle() val bundle = Bundle()
bundle.putString(BookReadActivity.BOOK_ID, searchHotBookData[position].id) bundle.putString(Constant.ROUTE_BOOK_ID, searchHotBookData[position].id)
readyGo(BookReadActivity::class.java, bundle) readyGo(BookReadActivity::class.java, bundle)
} }
rv_hot.layoutManager = LinearLayoutManager(this) rv_hot.layoutManager = LinearLayoutManager(this)
...@@ -91,7 +91,7 @@ class BookSearchActivity : BaseActivity(), View.OnClickListener, BookSearchView, ...@@ -91,7 +91,7 @@ class BookSearchActivity : BaseActivity(), View.OnClickListener, BookSearchView,
searchBookAdapter = SearchBookAdapter(this) searchBookAdapter = SearchBookAdapter(this)
searchBookAdapter?.setOnItemClickListener { _, _, position -> searchBookAdapter?.setOnItemClickListener { _, _, position ->
val bundle = Bundle() val bundle = Bundle()
bundle.putString(BookReadActivity.BOOK_ID, searchBookData[position].id) bundle.putString(Constant.ROUTE_BOOK_ID, searchBookData[position].id)
readyGo(BookReadActivity::class.java, bundle) readyGo(BookReadActivity::class.java, bundle)
} }
rv_search_show.layoutManager = LinearLayoutManager(this) rv_search_show.layoutManager = LinearLayoutManager(this)
...@@ -240,4 +240,9 @@ class BookSearchActivity : BaseActivity(), View.OnClickListener, BookSearchView, ...@@ -240,4 +240,9 @@ class BookSearchActivity : BaseActivity(), View.OnClickListener, BookSearchView,
++searchPage ++searchPage
searchBook(et_search.text.toString()) searchBook(et_search.text.toString())
} }
override fun onDestroy() {
super.onDestroy()
searchPresenter.detachView()
}
} }
package com.mints.enjoyboxjia.ui.activitys
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import com.mints.enjoyboxjia.R
import com.mints.enjoyboxjia.common.BOOK_CONTENT_COLOR
import com.mints.enjoyboxjia.common.BOOK_CONTENT_SIZE
import com.mints.enjoyboxjia.common.Constant
import com.mints.enjoyboxjia.manager.ReadTimeCount
import com.mints.enjoyboxjia.mvp.model.BookContentBean
import com.mints.enjoyboxjia.mvp.presenters.BookReadPresenter
import com.mints.enjoyboxjia.mvp.views.BookReadView
import com.mints.enjoyboxjia.read.AnimType
import com.mints.enjoyboxjia.read.PageLoader
import com.mints.enjoyboxjia.read.models.BookModel
import com.mints.enjoyboxjia.read.models.BookRecordModel
import com.mints.enjoyboxjia.read.models.TxtChapterModel
import com.mints.enjoyboxjia.ui.activitys.base.BaseActivity
import com.mints.enjoyboxjia.ui.widgets.BookSettingDialog
import com.mints.enjoyboxjia.utils.AppPreferencesManager
import com.mints.enjoyboxjia.utils.LogUtil
import kotlinx.android.synthetic.main.activity_long_book_read.*
import kotlinx.android.synthetic.main.header_layout.*
import org.litepal.LitePal
class LongBookReadActivity : BaseActivity(), View.OnClickListener, BookReadView {
private val readPresenter by lazy { BookReadPresenter() }
private var mBookModel: BookModel? = null
private var mPageLoader: PageLoader? = null
private var bookSettingDialog: BookSettingDialog? = null
override fun getContentViewLayoutID() = R.layout.activity_long_book_read
override fun isApplyKitKatTranslucency() = false
override fun getBundleExtras(extras: Bundle?) {
super.getBundleExtras(extras)
extras?.let {
mBookModel = BookModel()
mBookModel?.bookId = extras.getString(Constant.ROUTE_BOOK_ID).toString()
}
}
override fun initViewsAndEvents() {
iv_left_icon.visibility = View.VISIBLE
iv_left_icon.setImageResource(R.mipmap.ic_arrow_back)
readPresenter.attachView(this)
initWidgets()
initListener()
popBookSettingDialog()
}
override fun onResume() {
super.onResume()
ReadTimeCount.instance.startCountReadTime()
}
override fun onPause() {
super.onPause()
ReadTimeCount.instance.endCountReadTime()
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.iv_left_icon -> finish()
R.id.tab_setting_chapter -> {
}
R.id.tab_setting_book -> {
bookSettingDialog?.show()
}
R.id.tab_setting_share -> {}
R.id.btn_submit -> {
val bundle = Bundle()
bundle.putBoolean(VipActivity.IS_RECOMMEND, true)
readyGo(VipActivity::class.java, bundle)
}
}
}
private fun popBookSettingDialog() {
bookSettingDialog = BookSettingDialog(this)
bookSettingDialog?.setOnBookSettingCallback(object :
BookSettingDialog.OnBookSettingCallback {
override fun onFontSize(size: Int) {
// tv_content.textSize = size.toFloat()
AppPreferencesManager.get().put(BOOK_CONTENT_SIZE, size)
}
override fun onBgColor(color: Int) {
// pageview.setBackgroundColor(color)
AppPreferencesManager.get().put(BOOK_CONTENT_COLOR, color)
}
})
bookSettingDialog?.loadSetting()
}
private fun initWidgets() {
//书籍
mPageLoader = pageview.getPageLoader(mBookModel)
pageview.animType = AnimType.ALIKE
//从数据库加载分章信息
mPageLoader?.mChapterList = LitePal.where("book_id=?", "" + mBookModel?.id)
.find(TxtChapterModel::class.java)
//获取上次的数据
val recordModel: BookRecordModel? = LitePal.where("book_id=?", "" + mBookModel?.id)
.findFirst(BookRecordModel::class.java)
if (recordModel != null) {
mPageLoader?.run {
curChapterIndex = recordModel.chapterPos
curPageIndex = recordModel.pagePos
setTextSize(recordModel.text_size)
textColor = recordModel.text_color
setPageBgColor(recordModel.page_color)
when (recordModel.anim_type) {
1 -> pageview.animType = AnimType.SLIDE
2 -> pageview.animType = AnimType.COVER
3 -> pageview.animType = AnimType.ALIKE
else -> pageview.animType = AnimType.NONE
}
}
}
//章节回调
mPageLoader?.setChapterChangeListener(object : PageLoader.OnChapterChangeListener {
override fun onChapterChange(pos: Int) {
LogUtil.d("XXXIX ----> $pos")
// if (pos >= mPageLoader.){
//
// }
// readChaptersAdapter.setChapter(pos)
}
override fun requestChapters(requestChapters: List<TxtChapterModel>) {}
override fun finishedLoadChapters(bookChapters: List<TxtChapterModel>) {
mPageLoader?.pageView?.showCategory()
// readChaptersAdapter.setChapterModels(bookChapters)
}
})
//初始化目录
// initChapterRecyclerView()
}
private fun initListener() {
iv_left_icon.setOnClickListener(this)
tab_setting_chapter.setOnClickListener(this)
tab_setting_share.setOnClickListener(this)
tab_setting_book.setOnClickListener(this)
btn_submit.setOnClickListener(this)
}
override fun getBookDetailSuc(data: BookContentBean) {
}
override fun getBookDetailFail() {
}
override fun onDestroy() {
super.onDestroy()
readPresenter.detachView()
}
}
...@@ -12,10 +12,11 @@ import com.mints.enjoyboxjia.common.AppConfig ...@@ -12,10 +12,11 @@ import com.mints.enjoyboxjia.common.AppConfig
import com.mints.enjoyboxjia.common.Constant import com.mints.enjoyboxjia.common.Constant
import com.mints.enjoyboxjia.ui.activitys.base.BaseActivity import com.mints.enjoyboxjia.ui.activitys.base.BaseActivity
import com.mints.enjoyboxjia.ui.fragment.BookCityFragment import com.mints.enjoyboxjia.ui.fragment.BookCityFragment
import com.mints.enjoyboxjia.ui.fragment.CountFragment
import com.mints.enjoyboxjia.ui.fragment.BookShelfFragment import com.mints.enjoyboxjia.ui.fragment.BookShelfFragment
import com.mints.enjoyboxjia.ui.fragment.CountFragment
import com.mints.enjoyboxjia.ui.fragment.MyFragment import com.mints.enjoyboxjia.ui.fragment.MyFragment
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import org.litepal.LitePal
/** /**
* 描述:首页 * 描述:首页
...@@ -40,6 +41,9 @@ class MainActivity : BaseActivity(), View.OnClickListener { ...@@ -40,6 +41,9 @@ class MainActivity : BaseActivity(), View.OnClickListener {
override fun initViewsAndEvents() { override fun initViewsAndEvents() {
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
//数据库 LitePal
LitePal.initialize(this)
AppConfig.fragmentClickFlag = Constant.FRAGMENT_CLICK_TWO AppConfig.fragmentClickFlag = Constant.FRAGMENT_CLICK_TWO
if (bookCityFragment == null) { if (bookCityFragment == null) {
bookCityFragment = BookCityFragment() bookCityFragment = BookCityFragment()
......
...@@ -5,6 +5,7 @@ import android.view.LayoutInflater ...@@ -5,6 +5,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.mints.enjoyboxjia.R import com.mints.enjoyboxjia.R
import com.mints.enjoyboxjia.common.Constant
import com.mints.enjoyboxjia.mvp.model.* import com.mints.enjoyboxjia.mvp.model.*
import com.mints.enjoyboxjia.mvp.presenters.WatchRecordPresenter import com.mints.enjoyboxjia.mvp.presenters.WatchRecordPresenter
import com.mints.enjoyboxjia.mvp.views.WatchRecordView import com.mints.enjoyboxjia.mvp.views.WatchRecordView
...@@ -38,7 +39,7 @@ class WatchRecordActivity : BaseActivity(), WatchRecordView { ...@@ -38,7 +39,7 @@ class WatchRecordActivity : BaseActivity(), WatchRecordView {
mWatchRecordAdapter?.setNewInstance(datas) mWatchRecordAdapter?.setNewInstance(datas)
mWatchRecordAdapter?.setOnItemClickListener { adapter, view, position -> mWatchRecordAdapter?.setOnItemClickListener { adapter, view, position ->
val bundle = Bundle() val bundle = Bundle()
bundle.putString(BookReadActivity.BOOK_ID, datas.get(position).id) bundle.putString(Constant.ROUTE_BOOK_ID, datas.get(position).id)
readyGo(BookReadActivity::class.java, bundle) readyGo(BookReadActivity::class.java, bundle)
} }
mWatchRecordAdapter?.setOnItemCollectClickListener(object : mWatchRecordAdapter?.setOnItemCollectClickListener(object :
......
...@@ -72,7 +72,7 @@ class BookCityFragment : LazyLoadBaseFragment(), BookCityView, View.OnClickListe ...@@ -72,7 +72,7 @@ class BookCityFragment : LazyLoadBaseFragment(), BookCityView, View.OnClickListe
hotBookAdapter = HotBookAdapter(requireActivity()) hotBookAdapter = HotBookAdapter(requireActivity())
hotBookAdapter?.setOnItemClickListener { _, _, position -> hotBookAdapter?.setOnItemClickListener { _, _, position ->
val bundle = Bundle() val bundle = Bundle()
bundle.putString(BookReadActivity.BOOK_ID, hotBookData[position].id) bundle.putString(Constant.ROUTE_BOOK_ID, hotBookData[position].id)
readyGo(BookReadActivity::class.java, bundle) readyGo(BookReadActivity::class.java, bundle)
} }
rv_hotbook.layoutManager = GridLayoutManager(requireContext(), 4) rv_hotbook.layoutManager = GridLayoutManager(requireContext(), 4)
...@@ -104,7 +104,7 @@ class BookCityFragment : LazyLoadBaseFragment(), BookCityView, View.OnClickListe ...@@ -104,7 +104,7 @@ class BookCityFragment : LazyLoadBaseFragment(), BookCityView, View.OnClickListe
} }
view.setOnClickListener { view.setOnClickListener {
val bundle = Bundle() val bundle = Bundle()
bundle.putString(BookReadActivity.BOOK_ID, headData.id) bundle.putString(Constant.ROUTE_BOOK_ID, headData.id)
readyGo(BookReadActivity::class.java, bundle) readyGo(BookReadActivity::class.java, bundle)
} }
return view return view
...@@ -114,7 +114,7 @@ class BookCityFragment : LazyLoadBaseFragment(), BookCityView, View.OnClickListe ...@@ -114,7 +114,7 @@ class BookCityFragment : LazyLoadBaseFragment(), BookCityView, View.OnClickListe
newBookAdapter = NewBookAdapter(requireActivity()) newBookAdapter = NewBookAdapter(requireActivity())
newBookAdapter?.setOnItemClickListener { _, _, position -> newBookAdapter?.setOnItemClickListener { _, _, position ->
val bundle = Bundle() val bundle = Bundle()
bundle.putString(BookReadActivity.BOOK_ID, newBookData[position].id) bundle.putString(Constant.ROUTE_BOOK_ID, newBookData[position].id)
readyGo(BookReadActivity::class.java, bundle) readyGo(BookReadActivity::class.java, bundle)
} }
rv_newbook.layoutManager = GridLayoutManager(requireContext(), 2) rv_newbook.layoutManager = GridLayoutManager(requireContext(), 2)
...@@ -172,7 +172,7 @@ class BookCityFragment : LazyLoadBaseFragment(), BookCityView, View.OnClickListe ...@@ -172,7 +172,7 @@ class BookCityFragment : LazyLoadBaseFragment(), BookCityView, View.OnClickListe
.setAdapter(ImageTitleAdapter(data.records)) .setAdapter(ImageTitleAdapter(data.records))
.setOnBannerListener { data2, _ -> .setOnBannerListener { data2, _ ->
val bundle = Bundle() val bundle = Bundle()
bundle.putString(BookReadActivity.BOOK_ID, (data2 as BookBean).id) bundle.putString(Constant.ROUTE_BOOK_ID, (data2 as BookBean).id)
readyGo(BookReadActivity::class.java, bundle) readyGo(BookReadActivity::class.java, bundle)
} }
} }
......
...@@ -116,11 +116,11 @@ class BookListFragment : LazyLoadBaseFragment(), PicContentView, OnRefreshListen ...@@ -116,11 +116,11 @@ class BookListFragment : LazyLoadBaseFragment(), PicContentView, OnRefreshListen
hotStyleAdapter = CountAdapter() hotStyleAdapter = CountAdapter()
hotStyleAdapter.animationEnable = false hotStyleAdapter.animationEnable = false
rv_pic_list.adapter = hotStyleAdapter rv_pic_list.adapter = hotStyleAdapter
hotStyleAdapter?.setOnCountItemClickListener(object : hotStyleAdapter.setOnCountItemClickListener(object :
CountAdapter.OnCountItemClickListener { CountAdapter.OnCountItemClickListener {
override fun onItemClick(bean: BookBean) { override fun onItemClick(bean: BookBean) {
val bundle = Bundle() val bundle = Bundle()
bundle.putString(BookReadActivity.BOOK_ID, bean.id) bundle.putString(Constant.ROUTE_BOOK_ID, bean.id)
readyGo(BookReadActivity::class.java, bundle) readyGo(BookReadActivity::class.java, bundle)
} }
}) })
......
...@@ -49,7 +49,7 @@ class BookShelfFragment : LazyLoadBaseFragment(), BookShelfView, View.OnClickLis ...@@ -49,7 +49,7 @@ class BookShelfFragment : LazyLoadBaseFragment(), BookShelfView, View.OnClickLis
} }
val bundle = Bundle() val bundle = Bundle()
bundle.putString(BookReadActivity.BOOK_ID, shelfBookData[position].id) bundle.putString(Constant.ROUTE_BOOK_ID, shelfBookData[position].id)
readyGo(BookReadActivity::class.java, bundle) readyGo(BookReadActivity::class.java, bundle)
} }
rv_bookshelf.layoutManager = GridLayoutManager(requireContext(), 3) rv_bookshelf.layoutManager = GridLayoutManager(requireContext(), 3)
...@@ -62,7 +62,7 @@ class BookShelfFragment : LazyLoadBaseFragment(), BookShelfView, View.OnClickLis ...@@ -62,7 +62,7 @@ class BookShelfFragment : LazyLoadBaseFragment(), BookShelfView, View.OnClickLis
likeBookAdapter = LikeBookAdapter(requireActivity()) likeBookAdapter = LikeBookAdapter(requireActivity())
likeBookAdapter?.setOnItemClickListener { _, _, position -> likeBookAdapter?.setOnItemClickListener { _, _, position ->
val bundle = Bundle() val bundle = Bundle()
bundle.putString(BookReadActivity.BOOK_ID, likeBookData[position].id) bundle.putString(Constant.ROUTE_BOOK_ID, likeBookData[position].id)
readyGo(BookReadActivity::class.java, bundle) readyGo(BookReadActivity::class.java, bundle)
} }
rv_booklike.layoutManager = GridLayoutManager(requireContext(), 4) rv_booklike.layoutManager = GridLayoutManager(requireContext(), 4)
...@@ -185,6 +185,7 @@ class BookShelfFragment : LazyLoadBaseFragment(), BookShelfView, View.OnClickLis ...@@ -185,6 +185,7 @@ class BookShelfFragment : LazyLoadBaseFragment(), BookShelfView, View.OnClickLis
} }
fun delete() { fun delete() {
if (shelfBookData.size > 0) {
var strArr = "" var strArr = ""
for (datum in shelfBookData) { for (datum in shelfBookData) {
if (datum.isChecked && datum.id != null) { if (datum.isChecked && datum.id != null) {
...@@ -193,6 +194,7 @@ class BookShelfFragment : LazyLoadBaseFragment(), BookShelfView, View.OnClickLis ...@@ -193,6 +194,7 @@ class BookShelfFragment : LazyLoadBaseFragment(), BookShelfView, View.OnClickLis
} }
bookShelfPresenter.cancelCollect(strArr) bookShelfPresenter.cancelCollect(strArr)
} }
}
override fun onClick(v: View?) { override fun onClick(v: View?) {
v?.let { v?.let {
......
...@@ -103,7 +103,10 @@ class MyFragment : LazyLoadBaseFragment(), MyView, View.OnClickListener { ...@@ -103,7 +103,10 @@ class MyFragment : LazyLoadBaseFragment(), MyView, View.OnClickListener {
backPhoneDialog() backPhoneDialog()
} }
R.id.ll_my_set -> { R.id.ll_my_set -> {
readyGo(SettingsActivity::class.java) val bundle = Bundle()
bundle.putString(Constant.ROUTE_BOOK_ID, "80670")
readyGo(LongBookReadActivity::class.java, bundle)
// readyGo(SettingsActivity::class.java)
} }
} }
......
package com.mints.enjoyboxjia.utils;
import android.content.Context;
import androidx.annotation.StringRes;
import com.mints.enjoyboxjia.MintsApplication;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class StringUtils {
private static final String TAG = "StringUtils";
private static final int HOUR_OF_DAY = 24;
private static final int DAY_OF_YESTERDAY = 2;
private static final int TIME_UNIT = 60;
//将时间转换成日期
public static String dateConvert(long time, String pattern) {
Date date = new Date(time);
SimpleDateFormat format = new SimpleDateFormat(pattern);
return format.format(date);
}
//将日期转换成昨天、今天、明天
public static String dateConvert(String source, String pattern) {
DateFormat format = new SimpleDateFormat(pattern);
Calendar calendar = Calendar.getInstance();
try {
Date date = format.parse(source);
long curTime = calendar.getTimeInMillis();
calendar.setTime(date);
//将MISC 转换成 sec
long difSec = Math.abs((curTime - date.getTime()) / 1000);
long difMin = difSec / 60;
long difHour = difMin / 60;
long difDate = difHour / 60;
int oldHour = calendar.get(Calendar.HOUR);
//如果没有时间
if (oldHour == 0) {
//比日期:昨天今天和明天
if (difDate == 0) {
return "今天";
} else if (difDate < DAY_OF_YESTERDAY) {
return "昨天";
} else {
DateFormat convertFormat = new SimpleDateFormat("yyyy-MM-dd");
String value = convertFormat.format(date);
return value;
}
}
if (difSec < TIME_UNIT) {
return difSec + "秒前";
} else if (difMin < TIME_UNIT) {
return difMin + "分钟前";
} else if (difHour < HOUR_OF_DAY) {
return difHour + "小时前";
} else if (difDate < DAY_OF_YESTERDAY) {
return "昨天";
} else {
DateFormat convertFormat = new SimpleDateFormat("yyyy-MM-dd");
String value = convertFormat.format(date);
return value;
}
} catch (ParseException e) {
e.printStackTrace();
}
return "";
}
public static String toFirstCapital(String str) {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
public static String getString(Context context, @StringRes int id) {
return context.getResources().getString(id);
}
public static String getString(Context context, @StringRes int id, Object... formatArgs) {
return MintsApplication.getContext().getResources().getString(id, formatArgs);
}
/**
* 将文本中的半角字符,转换成全角字符
*
* @param input
* @return
*/
public static String halfToFull(String input) {
char[] c = input.toCharArray();
for (int i = 0; i < c.length; i++) {
if (c[i] == 32) //半角空格
{
c[i] = (char) 12288;
continue;
}
//根据实际情况,过滤不需要转换的符号
//if (c[i] == 46) //半角点号,不转换
// continue;
if (c[i] > 32 && c[i] < 127) //其他符号都转换为全角
c[i] = (char) (c[i] + 65248);
}
return new String(c);
}
//功能:字符串全角转换为半角
public static String fullToHalf(String input) {
char[] c = input.toCharArray();
for (int i = 0; i < c.length; i++) {
if (c[i] == 12288) //全角空格
{
c[i] = (char) 32;
continue;
}
if (c[i] > 65280 && c[i] < 65375)
c[i] = (char) (c[i] - 65248);
}
return new String(c);
}
}
...@@ -43,7 +43,7 @@ public final class Utils { ...@@ -43,7 +43,7 @@ public final class Utils {
/** /**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素) * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/ */
public static int dip2px(Context context, float dpValue) { public static int dpToPx(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density; final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f); return (int) (dpValue * scale + 0.5f);
} }
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/view_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical">
<include layout="@layout/header_layout" />
<LinearLayout
android:id="@+id/view_book"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.mints.enjoyboxjia.read.YPageView
android:id="@+id/pageview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="@+id/view_vip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:text="后续内容更精彩,充值后继续阅读"
android:textColor="@color/color_goldenrod" />
<TextView
android:id="@+id/btn_submit"
android:layout_width="300dp"
android:layout_height="38dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="40dp"
android:background="@drawable/btn_bg_gold"
android:gravity="center"
android:text="开通会员,免费阅读本书"
android:textColor="@color/color_goldenrod"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="1.目前充值会员暂不支持退款,一经购买不可退换\n\n2.未满18岁的未成年人需要在监护人主导,同意下进行相关付费操作\n\n3.充值一般在5分钟内到账,如未到账请提供支付截图在'我的'页面联系客服\n\n4.之前充值账户请登录后继续阅读"
android:textColor="@color/color_956244" />
</LinearLayout>
</FrameLayout>
<LinearLayout
android:id="@+id/view_setting"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="5dp"
android:paddingBottom="5dp">
<LinearLayout
android:id="@+id/tab_setting_chapter"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/tab_iv_bookshelf"
android:layout_width="20dp"
android:layout_height="20dp"
android:contentDescription="@null"
android:paddingTop="2dp" />
<TextView
android:id="@+id/tab_tv_bookshelf"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="目录"
android:textColor="@color/black"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/tab_setting_book"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:contentDescription="@null"
android:src="@mipmap/ic_book_setting" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="设置"
android:textColor="@color/black"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/tab_setting_share"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.0"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:contentDescription="@null"
android:src="@mipmap/ic_book_share" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:text="分享"
android:textColor="@color/black"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="202dp" android:layout_height="206dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:orientation="vertical"> android:orientation="vertical">
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<ImageView <ImageView
android:id="@+id/image_iv" android:id="@+id/image_iv"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="150dp" android:layout_height="140dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:src="@mipmap/bg_book_more" android:src="@mipmap/bg_book_more"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
...@@ -21,20 +21,20 @@ ...@@ -21,20 +21,20 @@
android:id="@+id/title_tv" android:id="@+id/title_tv"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="4dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:lines="2"
android:maxLines="2"
android:textColor="@color/black" android:textColor="@color/black"
app:layout_constraintStart_toStartOf="@id/image_iv" app:layout_constraintStart_toStartOf="@id/image_iv"
app:layout_constraintTop_toBottomOf="@id/image_iv" /> app:layout_constraintTop_toBottomOf="@id/image_iv" />
<TextView <TextView
android:id="@+id/label_tv" android:id="@+id/label_tv"
android:layout_width="106dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:ellipsize="end" android:lines="1"
android:maxLines="1"
android:textColor="@color/gray" android:textColor="@color/gray"
android:textSize="12sp" android:textSize="12sp"
app:layout_constraintStart_toStartOf="@id/image_iv" app:layout_constraintStart_toStartOf="@id/image_iv"
......
...@@ -2,11 +2,10 @@ ...@@ -2,11 +2,10 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="10dp" android:layout_marginBottom="10dp"
android:paddingLeft="6dp" android:orientation="horizontal"
android:layout_marginTop="6dp" android:paddingStart="5dp"
android:paddingBottom="5dp" android:paddingEnd="5dp">
android:orientation="horizontal">
<ImageView <ImageView
android:id="@+id/image_iv" android:id="@+id/image_iv"
...@@ -27,8 +26,8 @@ ...@@ -27,8 +26,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:lines="1"
android:ellipsize="end" android:ellipsize="end"
android:lines="1"
android:text="我去平行时空" android:text="我去平行时空"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="14sp" /> android:textSize="14sp" />
......
...@@ -38,9 +38,9 @@ ...@@ -38,9 +38,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:lines="3" android:lines="2"
android:ellipsize="end" android:ellipsize="end"
android:minLines="3" android:minLines="2"
android:textColor="@color/gray" android:textColor="@color/gray"
android:textSize="16sp" /> android:textSize="16sp" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment