parent
9a8a88944a
commit
9be6d925eb
@ -1,166 +0,0 @@ |
|||||||
package com.novel.read.utlis; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import android.os.Environment; |
|
||||||
|
|
||||||
import com.novel.read.model.db.BookChapterBean; |
|
||||||
import com.novel.read.model.db.CollBookBean; |
|
||||||
import com.novel.read.model.db.SearchListTable; |
|
||||||
|
|
||||||
import org.litepal.LitePal; |
|
||||||
|
|
||||||
import java.io.File; |
|
||||||
import java.math.BigDecimal; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author: LiJun 390057892@qq.com |
|
||||||
* @date: 2018/4/11 16:00 |
|
||||||
*/ |
|
||||||
|
|
||||||
public class CleanCacheUtils { |
|
||||||
|
|
||||||
private static CleanCacheUtils manager; |
|
||||||
|
|
||||||
public static CleanCacheUtils getInstance() { |
|
||||||
return manager == null ? (manager = new CleanCacheUtils()) : manager; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param context |
|
||||||
* @return |
|
||||||
* @throws Exception |
|
||||||
* 获取当前缓存 |
|
||||||
*/ |
|
||||||
public String getTotalCacheSize(Context context) throws Exception { |
|
||||||
long cacheSize = getFolderSize(context.getCacheDir()); |
|
||||||
if (Environment.getExternalStorageState().equals( |
|
||||||
Environment.MEDIA_MOUNTED)) { |
|
||||||
cacheSize += getFolderSize(context.getExternalCacheDir()); |
|
||||||
} |
|
||||||
return getFormatSize(cacheSize); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param context |
|
||||||
* 删除缓存 |
|
||||||
*/ |
|
||||||
public static void clearAllCache(Context context) { |
|
||||||
deleteDir(context.getCacheDir()); |
|
||||||
if (Environment.getExternalStorageState().equals( |
|
||||||
Environment.MEDIA_MOUNTED)) { |
|
||||||
deleteDir(context.getExternalCacheDir()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static boolean deleteDir(File dir) { |
|
||||||
if (dir != null && dir.isDirectory()) { |
|
||||||
String[] children = dir.list(); |
|
||||||
int size = 0; |
|
||||||
if (children != null) { |
|
||||||
size = children.length; |
|
||||||
for (int i = 0; i < size; i++) { |
|
||||||
boolean success = deleteDir(new File(dir, children[i])); |
|
||||||
if (!success) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
if (dir == null) { |
|
||||||
return true; |
|
||||||
} else { |
|
||||||
|
|
||||||
return dir.delete(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// 获取文件
|
|
||||||
// Context.getExternalFilesDir() --> SDCard/Android/data/你的应用的包名/files/
|
|
||||||
// 目录,一般放一些长时间保存的数据
|
|
||||||
// Context.getExternalCacheDir() -->
|
|
||||||
// SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据
|
|
||||||
public static long getFolderSize(File file) throws Exception { |
|
||||||
long size = 0; |
|
||||||
try { |
|
||||||
File[] fileList = file.listFiles(); |
|
||||||
int size2 = 0; |
|
||||||
if (fileList != null) { |
|
||||||
size2 = fileList.length; |
|
||||||
for (int i = 0; i < size2; i++) { |
|
||||||
// 如果下面还有文件
|
|
||||||
if (fileList[i].isDirectory()) { |
|
||||||
size = size + getFolderSize(fileList[i]); |
|
||||||
} else { |
|
||||||
size = size + fileList[i].length(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} catch (Exception e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
return size; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 格式化单位 |
|
||||||
* 计算缓存的大小 |
|
||||||
* @param size |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static String getFormatSize(double size) { |
|
||||||
double kiloByte = size / 1024; |
|
||||||
if (kiloByte < 1) { |
|
||||||
// return size + "Byte";
|
|
||||||
return "0K"; |
|
||||||
} |
|
||||||
|
|
||||||
double megaByte = kiloByte / 1024; |
|
||||||
if (megaByte < 1) { |
|
||||||
BigDecimal result1 = new BigDecimal(Double.toString(kiloByte)); |
|
||||||
return result1.setScale(2, BigDecimal.ROUND_HALF_UP) |
|
||||||
.toPlainString() + "KB"; |
|
||||||
} |
|
||||||
|
|
||||||
double gigaByte = megaByte / 1024; |
|
||||||
if (gigaByte < 1) { |
|
||||||
BigDecimal result2 = new BigDecimal(Double.toString(megaByte)); |
|
||||||
return result2.setScale(2, BigDecimal.ROUND_HALF_UP) |
|
||||||
.toPlainString() + "MB"; |
|
||||||
} |
|
||||||
|
|
||||||
double teraBytes = gigaByte / 1024; |
|
||||||
if (teraBytes < 1) { |
|
||||||
BigDecimal result3 = new BigDecimal(Double.toString(gigaByte)); |
|
||||||
return result3.setScale(2, BigDecimal.ROUND_HALF_UP) |
|
||||||
.toPlainString() + "GB"; |
|
||||||
} |
|
||||||
BigDecimal result4 = new BigDecimal(teraBytes); |
|
||||||
return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() |
|
||||||
+ "TB"; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* 清除缓存 |
|
||||||
* |
|
||||||
* @param clearReadPos 是否删除阅读记录 |
|
||||||
*/ |
|
||||||
public synchronized void clearCache(boolean clearReadPos, boolean clearCollect,Context context) { |
|
||||||
try { |
|
||||||
clearAllCache(context); |
|
||||||
// 删除搜索记录(SharePreference)
|
|
||||||
if (clearReadPos) { |
|
||||||
LitePal.deleteAll(SearchListTable.class); |
|
||||||
} |
|
||||||
// 清空书架
|
|
||||||
if (clearCollect) { |
|
||||||
LitePal.deleteAll(CollBookBean.class); |
|
||||||
LitePal.deleteAll(BookChapterBean.class); |
|
||||||
} |
|
||||||
} catch (Exception e) { |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,170 @@ |
|||||||
|
package com.novel.read.utlis |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.os.Environment |
||||||
|
|
||||||
|
import com.novel.read.model.db.BookChapterBean |
||||||
|
import com.novel.read.model.db.CollBookBean |
||||||
|
import com.novel.read.model.db.SearchListTable |
||||||
|
|
||||||
|
import org.litepal.LitePal |
||||||
|
|
||||||
|
import java.io.File |
||||||
|
import java.math.BigDecimal |
||||||
|
|
||||||
|
/** |
||||||
|
* @author: LiJun 390057892@qq.com |
||||||
|
* @date: 2018/4/11 16:00 |
||||||
|
*/ |
||||||
|
|
||||||
|
class CleanCacheUtils { |
||||||
|
|
||||||
|
/** |
||||||
|
* @param context |
||||||
|
* @return |
||||||
|
* @throws Exception |
||||||
|
* 获取当前缓存 |
||||||
|
*/ |
||||||
|
@Throws(Exception::class) |
||||||
|
fun getTotalCacheSize(context: Context): String { |
||||||
|
var cacheSize = getFolderSize(context.cacheDir) |
||||||
|
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) { |
||||||
|
cacheSize += getFolderSize(context.externalCacheDir) |
||||||
|
} |
||||||
|
return getFormatSize(cacheSize.toDouble()) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 清除缓存 |
||||||
|
* |
||||||
|
* @param clearReadPos 是否删除阅读记录 |
||||||
|
*/ |
||||||
|
@Synchronized |
||||||
|
fun clearCache(clearReadPos: Boolean, clearCollect: Boolean, context: Context) { |
||||||
|
try { |
||||||
|
clearAllCache(context) |
||||||
|
// 删除搜索记录(SharePreference) |
||||||
|
if (clearReadPos) { |
||||||
|
LitePal.deleteAll(SearchListTable::class.java) |
||||||
|
} |
||||||
|
// 清空书架 |
||||||
|
if (clearCollect) { |
||||||
|
LitePal.deleteAll(CollBookBean::class.java) |
||||||
|
LitePal.deleteAll(BookChapterBean::class.java) |
||||||
|
} |
||||||
|
} catch (e: Exception) { |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
|
||||||
|
private var instance: CleanCacheUtils? = null |
||||||
|
|
||||||
|
@Synchronized |
||||||
|
fun getInstance(): CleanCacheUtils { |
||||||
|
if (instance == null) { |
||||||
|
instance = CleanCacheUtils() |
||||||
|
} |
||||||
|
return instance as CleanCacheUtils |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param context |
||||||
|
* 删除缓存 |
||||||
|
*/ |
||||||
|
fun clearAllCache(context: Context) { |
||||||
|
deleteDir(context.cacheDir) |
||||||
|
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) { |
||||||
|
deleteDir(context.externalCacheDir) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun deleteDir(dir: File?): Boolean { |
||||||
|
if (dir != null && dir.isDirectory) { |
||||||
|
val children = dir.list() |
||||||
|
var size = 0 |
||||||
|
if (children != null) { |
||||||
|
size = children.size |
||||||
|
for (i in 0 until size) { |
||||||
|
val success = deleteDir(File(dir, children[i])) |
||||||
|
if (!success) { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
return dir?.delete() ?: true |
||||||
|
} |
||||||
|
|
||||||
|
// 获取文件 |
||||||
|
// Context.getExternalFilesDir() --> SDCard/Android/data/你的应用的包名/files/ |
||||||
|
// 目录,一般放一些长时间保存的数据 |
||||||
|
// Context.getExternalCacheDir() --> |
||||||
|
// SDCard/Android/data/你的应用包名/cache/目录,一般存放临时缓存数据 |
||||||
|
@Throws(Exception::class) |
||||||
|
fun getFolderSize(file: File?): Long { |
||||||
|
var size: Long = 0 |
||||||
|
try { |
||||||
|
val fileList = file!!.listFiles() |
||||||
|
var size2 = 0 |
||||||
|
if (fileList != null) { |
||||||
|
size2 = fileList.size |
||||||
|
for (i in 0 until size2) { |
||||||
|
// 如果下面还有文件 |
||||||
|
if (fileList[i].isDirectory) { |
||||||
|
size = size + getFolderSize(fileList[i]) |
||||||
|
} else { |
||||||
|
size = size + fileList[i].length() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} catch (e: Exception) { |
||||||
|
e.printStackTrace() |
||||||
|
} |
||||||
|
|
||||||
|
return size |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 格式化单位 |
||||||
|
* 计算缓存的大小 |
||||||
|
* @param size |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
fun getFormatSize(size: Double): String { |
||||||
|
val kiloByte = size / 1024 |
||||||
|
if (kiloByte < 1) { |
||||||
|
// return size + "Byte"; |
||||||
|
return "0K" |
||||||
|
} |
||||||
|
|
||||||
|
val megaByte = kiloByte / 1024 |
||||||
|
if (megaByte < 1) { |
||||||
|
val result1 = BigDecimal(java.lang.Double.toString(kiloByte)) |
||||||
|
return result1.setScale(2, BigDecimal.ROUND_HALF_UP) |
||||||
|
.toPlainString() + "KB" |
||||||
|
} |
||||||
|
|
||||||
|
val gigaByte = megaByte / 1024 |
||||||
|
if (gigaByte < 1) { |
||||||
|
val result2 = BigDecimal(java.lang.Double.toString(megaByte)) |
||||||
|
return result2.setScale(2, BigDecimal.ROUND_HALF_UP) |
||||||
|
.toPlainString() + "MB" |
||||||
|
} |
||||||
|
|
||||||
|
val teraBytes = gigaByte / 1024 |
||||||
|
if (teraBytes < 1) { |
||||||
|
val result3 = BigDecimal(java.lang.Double.toString(gigaByte)) |
||||||
|
return result3.setScale(2, BigDecimal.ROUND_HALF_UP) |
||||||
|
.toPlainString() + "GB" |
||||||
|
} |
||||||
|
val result4 = BigDecimal(teraBytes) |
||||||
|
return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "TB" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,37 +0,0 @@ |
|||||||
package com.novel.read.utlis; |
|
||||||
|
|
||||||
import android.app.AlertDialog; |
|
||||||
import android.content.Context; |
|
||||||
import android.content.DialogInterface; |
|
||||||
|
|
||||||
/** |
|
||||||
* create by zlj on 2019/6/19 |
|
||||||
* describe: |
|
||||||
*/ |
|
||||||
public class DialogUtils { |
|
||||||
|
|
||||||
private static DialogUtils dialogUtils; |
|
||||||
|
|
||||||
public static DialogUtils getInstance() { |
|
||||||
if (dialogUtils == null) { |
|
||||||
dialogUtils = new DialogUtils(); |
|
||||||
} |
|
||||||
return dialogUtils; |
|
||||||
} |
|
||||||
|
|
||||||
private DialogUtils() { } |
|
||||||
|
|
||||||
public void showAlertDialog(Context context, String msg, DialogInterface.OnClickListener dialogListener) { |
|
||||||
// context = context.getApplicationContext();
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(context); |
|
||||||
builder.setTitle("操作提示").setMessage(msg).setCancelable(false).setPositiveButton("确定", dialogListener).setNegativeButton("取消", new DialogInterface.OnClickListener() { |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onClick(DialogInterface dialog, int id) { |
|
||||||
dialog.cancel(); |
|
||||||
} |
|
||||||
}); |
|
||||||
AlertDialog alert = builder.create(); |
|
||||||
alert.show(); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,39 @@ |
|||||||
|
package com.novel.read.utlis |
||||||
|
|
||||||
|
import android.app.AlertDialog |
||||||
|
import android.content.Context |
||||||
|
import android.content.DialogInterface |
||||||
|
|
||||||
|
/** |
||||||
|
* create by zlj on 2019/6/19 |
||||||
|
* describe: |
||||||
|
*/ |
||||||
|
class DialogUtils private constructor() { |
||||||
|
|
||||||
|
fun showAlertDialog( |
||||||
|
context: Context, |
||||||
|
msg: String, |
||||||
|
dialogListener: DialogInterface.OnClickListener |
||||||
|
) { |
||||||
|
// context = context.getApplicationContext(); |
||||||
|
val builder = AlertDialog.Builder(context) |
||||||
|
builder.setTitle("操作提示").setMessage(msg).setCancelable(false) |
||||||
|
.setPositiveButton("确定", dialogListener) |
||||||
|
.setNegativeButton("取消") { dialog, id -> dialog.cancel() } |
||||||
|
val alert = builder.create() |
||||||
|
alert.show() |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
|
||||||
|
private var instance: DialogUtils? = null |
||||||
|
|
||||||
|
@Synchronized |
||||||
|
fun getInstance(): DialogUtils { |
||||||
|
if (instance == null) { |
||||||
|
instance = DialogUtils() |
||||||
|
} |
||||||
|
return instance as DialogUtils |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,9 +1,9 @@ |
|||||||
package com.novel.read.widget.page; |
package com.novel.read.widget.page |
||||||
|
|
||||||
/** |
/** |
||||||
* Created by zlj |
* Created by zlj |
||||||
* 作用:翻页动画的模式 |
* 作用:翻页动画的模式 |
||||||
*/ |
*/ |
||||||
public enum PageMode { |
enum class PageMode { |
||||||
SIMULATION, COVER, SLIDE, NONE, SCROLL |
SIMULATION, COVER, SLIDE, NONE, SCROLL |
||||||
} |
} |
@ -1,35 +0,0 @@ |
|||||||
package com.novel.read.widget.page; |
|
||||||
|
|
||||||
|
|
||||||
import androidx.annotation.ColorRes; |
|
||||||
|
|
||||||
import com.novel.read.R; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by zlj |
|
||||||
* 页面的展示风格。 |
|
||||||
*/ |
|
||||||
public enum PageStyle { |
|
||||||
BG_0(R.color.read_font_one, R.color.read_bg_one), |
|
||||||
BG_1(R.color.read_font_two, R.color.read_bg_two), |
|
||||||
// BG_2(R.color.nb_read_font_3, R.color.nb_read_bg_3),
|
|
||||||
BG_3(R.color.read_font_four, R.color.read_bg_four), |
|
||||||
BG_4(R.color.read_font_five, R.color.read_bg_five), |
|
||||||
NIGHT(R.color.read_font_night, R.color.read_bg_night),; |
|
||||||
|
|
||||||
private int fontColor; |
|
||||||
private int bgColor; |
|
||||||
|
|
||||||
PageStyle(@ColorRes int fontColor, @ColorRes int bgColor) { |
|
||||||
this.fontColor = fontColor; |
|
||||||
this.bgColor = bgColor; |
|
||||||
} |
|
||||||
|
|
||||||
public int getFontColor() { |
|
||||||
return fontColor; |
|
||||||
} |
|
||||||
|
|
||||||
public int getBgColor() { |
|
||||||
return bgColor; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,19 @@ |
|||||||
|
package com.novel.read.widget.page |
||||||
|
|
||||||
|
|
||||||
|
import androidx.annotation.ColorRes |
||||||
|
|
||||||
|
import com.novel.read.R |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by zlj |
||||||
|
* 页面的展示风格。 |
||||||
|
*/ |
||||||
|
enum class PageStyle private constructor(@param:ColorRes val fontColor: Int, @param:ColorRes val bgColor: Int) { |
||||||
|
BG_0(R.color.read_font_one, R.color.read_bg_one), |
||||||
|
BG_1(R.color.read_font_two, R.color.read_bg_two), |
||||||
|
// BG_2(R.color.nb_read_font_3, R.color.nb_read_bg_3), |
||||||
|
BG_3(R.color.read_font_four, R.color.read_bg_four), |
||||||
|
BG_4(R.color.read_font_five, R.color.read_bg_five), |
||||||
|
NIGHT(R.color.read_font_night, R.color.read_bg_night) |
||||||
|
} |
@ -1,4 +0,0 @@ |
|||||||
package com.novel.read.widget.page; |
|
||||||
|
|
||||||
public final class Void { |
|
||||||
} |
|
@ -0,0 +1,3 @@ |
|||||||
|
package com.novel.read.widget.page |
||||||
|
|
||||||
|
class Void |
@ -1,101 +0,0 @@ |
|||||||
package com.novel.read.widget.page.anim; |
|
||||||
|
|
||||||
import android.graphics.Bitmap; |
|
||||||
import android.graphics.Canvas; |
|
||||||
import android.graphics.Rect; |
|
||||||
import android.graphics.drawable.GradientDrawable; |
|
||||||
import android.view.View; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by zlj |
|
||||||
* 覆盖动画 |
|
||||||
*/ |
|
||||||
|
|
||||||
public class CoverPageAnim extends HorizonPageAnim { |
|
||||||
|
|
||||||
private Rect mSrcRect, mDestRect; |
|
||||||
private GradientDrawable mBackShadowDrawableLR; |
|
||||||
|
|
||||||
public CoverPageAnim(int w, int h, View view, OnPageChangeListener listener) { |
|
||||||
super(w, h, view, listener); |
|
||||||
mSrcRect = new Rect(0, 0, mViewWidth, mViewHeight); |
|
||||||
mDestRect = new Rect(0, 0, mViewWidth, mViewHeight); |
|
||||||
int[] mBackShadowColors = new int[] { 0x66000000,0x00000000}; |
|
||||||
mBackShadowDrawableLR = new GradientDrawable( |
|
||||||
GradientDrawable.Orientation.LEFT_RIGHT, mBackShadowColors); |
|
||||||
mBackShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void drawStatic(Canvas canvas) { |
|
||||||
if (isCancel){ |
|
||||||
mNextBitmap = mCurBitmap.copy(Bitmap.Config.RGB_565, true); |
|
||||||
canvas.drawBitmap(mCurBitmap, 0, 0, null); |
|
||||||
}else { |
|
||||||
canvas.drawBitmap(mNextBitmap, 0, 0, null); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
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); |
|
||||||
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; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//添加阴影
|
|
||||||
public void addShadow(int left, Canvas canvas) { |
|
||||||
mBackShadowDrawableLR.setBounds(left, 0, left + 30 , mScreenHeight); |
|
||||||
mBackShadowDrawableLR.draw(canvas); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void startAnim() { |
|
||||||
super.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 = (400 * Math.abs(dx)) / mViewWidth; |
|
||||||
mScroller.startScroll((int) mTouchX, 0, dx, 0, duration); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,97 @@ |
|||||||
|
package com.novel.read.widget.page.anim |
||||||
|
|
||||||
|
import android.graphics.Bitmap |
||||||
|
import android.graphics.Canvas |
||||||
|
import android.graphics.Rect |
||||||
|
import android.graphics.drawable.GradientDrawable |
||||||
|
import android.view.View |
||||||
|
import com.novel.read.widget.page.PageAnimation |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by zlj |
||||||
|
* 覆盖动画 |
||||||
|
*/ |
||||||
|
|
||||||
|
class CoverPageAnim(w: Int, h: Int, view: View, listener: PageAnimation.OnPageChangeListener) : |
||||||
|
HorizonPageAnim(w, h, view, listener) { |
||||||
|
|
||||||
|
private val mSrcRect: Rect |
||||||
|
private val mDestRect: Rect |
||||||
|
private val mBackShadowDrawableLR: GradientDrawable |
||||||
|
|
||||||
|
init { |
||||||
|
mSrcRect = Rect(0, 0, mViewWidth, mViewHeight) |
||||||
|
mDestRect = Rect(0, 0, mViewWidth, mViewHeight) |
||||||
|
val mBackShadowColors = intArrayOf(0x66000000, 0x00000000) |
||||||
|
mBackShadowDrawableLR = GradientDrawable( |
||||||
|
GradientDrawable.Orientation.LEFT_RIGHT, mBackShadowColors |
||||||
|
) |
||||||
|
mBackShadowDrawableLR.gradientType = GradientDrawable.LINEAR_GRADIENT |
||||||
|
} |
||||||
|
|
||||||
|
override fun drawStatic(canvas: Canvas) { |
||||||
|
if (isCancel) { |
||||||
|
mNextBitmap = mCurBitmap.copy(Bitmap.Config.RGB_565, true) |
||||||
|
canvas.drawBitmap(mCurBitmap, 0f, 0f, null) |
||||||
|
} else { |
||||||
|
canvas.drawBitmap(mNextBitmap, 0f, 0f, null) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun drawMove(canvas: Canvas) { |
||||||
|
|
||||||
|
when (mDirection) { |
||||||
|
PageAnimation.Direction.NEXT -> { |
||||||
|
var dis = (mViewWidth - mStartX + mTouchX).toInt() |
||||||
|
if (dis > mViewWidth) { |
||||||
|
dis = mViewWidth |
||||||
|
} |
||||||
|
//计算bitmap截取的区域 |
||||||
|
mSrcRect.left = mViewWidth - dis |
||||||
|
//计算bitmap在canvas显示的区域 |
||||||
|
mDestRect.right = dis |
||||||
|
canvas.drawBitmap(mNextBitmap, 0f, 0f, null) |
||||||
|
canvas.drawBitmap(mCurBitmap, mSrcRect, mDestRect, null) |
||||||
|
addShadow(dis, canvas) |
||||||
|
} |
||||||
|
else -> { |
||||||
|
mSrcRect.left = (mViewWidth - mTouchX).toInt() |
||||||
|
mDestRect.right = mTouchX.toInt() |
||||||
|
canvas.drawBitmap(mCurBitmap, 0f, 0f, null) |
||||||
|
canvas.drawBitmap(mNextBitmap, mSrcRect, mDestRect, null) |
||||||
|
addShadow(mTouchX.toInt(), canvas) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//添加阴影 |
||||||
|
fun addShadow(left: Int, canvas: Canvas) { |
||||||
|
mBackShadowDrawableLR.setBounds(left, 0, left + 30, mScreenHeight) |
||||||
|
mBackShadowDrawableLR.draw(canvas) |
||||||
|
} |
||||||
|
|
||||||
|
override fun startAnim() { |
||||||
|
super.startAnim() |
||||||
|
var dx = 0 |
||||||
|
when (mDirection) { |
||||||
|
PageAnimation.Direction.NEXT -> if (isCancel) { |
||||||
|
var dis = (mViewWidth - mStartX + mTouchX).toInt() |
||||||
|
if (dis > mViewWidth) { |
||||||
|
dis = mViewWidth |
||||||
|
} |
||||||
|
dx = mViewWidth - dis |
||||||
|
} else { |
||||||
|
dx = (-(mTouchX + (mViewWidth - mStartX))).toInt() |
||||||
|
} |
||||||
|
else -> if (isCancel) { |
||||||
|
dx = (-mTouchX).toInt() |
||||||
|
} else { |
||||||
|
dx = (mViewWidth - mTouchX).toInt() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//滑动速度保持一致 |
||||||
|
val duration = 400 * Math.abs(dx) / mViewWidth |
||||||
|
mScroller.startScroll(mTouchX.toInt(), 0, dx, 0, duration) |
||||||
|
} |
||||||
|
} |
@ -1,38 +0,0 @@ |
|||||||
package com.novel.read.widget.page.anim; |
|
||||||
|
|
||||||
import android.graphics.Canvas; |
|
||||||
import android.view.View; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by zlj |
|
||||||
* 无 |
|
||||||
*/ |
|
||||||
|
|
||||||
public class NonePageAnim extends HorizonPageAnim{ |
|
||||||
|
|
||||||
public NonePageAnim(int w, int h, View view, OnPageChangeListener listener) { |
|
||||||
super(w, h, view, listener); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void drawStatic(Canvas canvas) { |
|
||||||
if (isCancel){ |
|
||||||
canvas.drawBitmap(mCurBitmap, 0, 0, null); |
|
||||||
}else { |
|
||||||
canvas.drawBitmap(mNextBitmap, 0, 0, null); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void drawMove(Canvas canvas) { |
|
||||||
if (isCancel){ |
|
||||||
canvas.drawBitmap(mCurBitmap, 0, 0, null); |
|
||||||
}else { |
|
||||||
canvas.drawBitmap(mNextBitmap, 0, 0, null); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void startAnim() { |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,32 @@ |
|||||||
|
package com.novel.read.widget.page.anim |
||||||
|
|
||||||
|
import android.graphics.Canvas |
||||||
|
import android.view.View |
||||||
|
import com.novel.read.widget.page.PageAnimation |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by zlj |
||||||
|
* 无 |
||||||
|
*/ |
||||||
|
|
||||||
|
class NonePageAnim(w: Int, h: Int, view: View, listener: PageAnimation.OnPageChangeListener) : |
||||||
|
HorizonPageAnim(w, h, view, listener) { |
||||||
|
|
||||||
|
override fun drawStatic(canvas: Canvas) { |
||||||
|
if (isCancel) { |
||||||
|
canvas.drawBitmap(mCurBitmap, 0f, 0f, null) |
||||||
|
} else { |
||||||
|
canvas.drawBitmap(mNextBitmap, 0f, 0f, null) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun drawMove(canvas: Canvas) { |
||||||
|
if (isCancel) { |
||||||
|
canvas.drawBitmap(mCurBitmap, 0f, 0f, null) |
||||||
|
} else { |
||||||
|
canvas.drawBitmap(mNextBitmap, 0f, 0f, null) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun startAnim() {} |
||||||
|
} |
@ -1,408 +0,0 @@ |
|||||||
package com.novel.read.widget.page.anim; |
|
||||||
|
|
||||||
import android.graphics.Bitmap; |
|
||||||
import android.graphics.Canvas; |
|
||||||
import android.graphics.Rect; |
|
||||||
import android.view.MotionEvent; |
|
||||||
import android.view.VelocityTracker; |
|
||||||
import android.view.View; |
|
||||||
|
|
||||||
import com.novel.read.widget.page.PageAnimation; |
|
||||||
|
|
||||||
import java.util.ArrayDeque; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.Iterator; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by zlj |
|
||||||
* 原理:仿照ListView源码实现的上下滑动效果 |
|
||||||
* 问题: |
|
||||||
* 1. 向上翻页,重复的问题 (完成) |
|
||||||
* 2. 滑动卡顿的问题。原因:由于绘制的数据过多造成的卡顿问题。 (主要是文字绘制需要的时长比较多) 解决办法:做文字缓冲 |
|
||||||
* 3. 弱网环境下,显示的问题 |
|
||||||
*/ |
|
||||||
public class ScrollPageAnim extends PageAnimation { |
|
||||||
private static final String TAG = "ScrollAnimation"; |
|
||||||
// 滑动追踪的时间
|
|
||||||
private static final int VELOCITY_DURATION = 1000; |
|
||||||
private VelocityTracker mVelocity; |
|
||||||
|
|
||||||
// 整个Bitmap的背景显示
|
|
||||||
private Bitmap mBgBitmap; |
|
||||||
|
|
||||||
// 下一个展示的图片
|
|
||||||
private Bitmap mNextBitmap; |
|
||||||
|
|
||||||
// 被废弃的图片列表
|
|
||||||
private ArrayDeque<BitmapView> mScrapViews; |
|
||||||
// 正在被利用的图片列表
|
|
||||||
private ArrayList<BitmapView> mActiveViews = new ArrayList<>(2); |
|
||||||
|
|
||||||
// 是否处于刷新阶段
|
|
||||||
private boolean isRefresh = true; |
|
||||||
|
|
||||||
public ScrollPageAnim(int w, int h, int marginWidth, int marginHeight, |
|
||||||
View view, OnPageChangeListener listener) { |
|
||||||
super(w, h, marginWidth, marginHeight, view, listener); |
|
||||||
// 创建两个BitmapView
|
|
||||||
initWidget(); |
|
||||||
} |
|
||||||
|
|
||||||
private void initWidget() { |
|
||||||
mBgBitmap = Bitmap.createBitmap(mScreenWidth, mScreenHeight, Bitmap.Config.RGB_565); |
|
||||||
|
|
||||||
mScrapViews = new ArrayDeque<>(2); |
|
||||||
for (int i = 0; i < 2; ++i) { |
|
||||||
BitmapView view = new BitmapView(); |
|
||||||
view.bitmap = Bitmap.createBitmap(mViewWidth, mViewHeight, Bitmap.Config.RGB_565); |
|
||||||
view.srcRect = new Rect(0, 0, mViewWidth, mViewHeight); |
|
||||||
view.destRect = new Rect(0, 0, mViewWidth, mViewHeight); |
|
||||||
view.top = 0; |
|
||||||
view.bottom = view.bitmap.getHeight(); |
|
||||||
|
|
||||||
mScrapViews.push(view); |
|
||||||
} |
|
||||||
onLayout(); |
|
||||||
isRefresh = false; |
|
||||||
} |
|
||||||
|
|
||||||
// 修改布局,填充内容
|
|
||||||
private void onLayout() { |
|
||||||
// 如果还没有开始加载,则从上到下进行绘制
|
|
||||||
if (mActiveViews.size() == 0) { |
|
||||||
fillDown(0, 0); |
|
||||||
mDirection = Direction.NONE; |
|
||||||
} else { |
|
||||||
int offset = (int) (mTouchY - mLastY); |
|
||||||
// 判断是下滑还是上拉 (下滑)
|
|
||||||
if (offset > 0) { |
|
||||||
int topEdge = mActiveViews.get(0).top; |
|
||||||
fillUp(topEdge, offset); |
|
||||||
} |
|
||||||
// 上拉
|
|
||||||
else { |
|
||||||
// 底部的距离 = 当前底部的距离 + 滑动的距离 (因为上滑,得到的值肯定是负的)
|
|
||||||
int bottomEdge = mActiveViews.get(mActiveViews.size() - 1).bottom; |
|
||||||
fillDown(bottomEdge, offset); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// 底部填充
|
|
||||||
private Iterator<BitmapView> downIt; |
|
||||||
|
|
||||||
/** |
|
||||||
* 创建View填充底部空白部分 |
|
||||||
* |
|
||||||
* @param bottomEdge :当前最后一个View的底部,在整个屏幕上的位置,即相对于屏幕顶部的距离 |
|
||||||
* @param offset :滑动的偏移量 |
|
||||||
*/ |
|
||||||
private void fillDown(int bottomEdge, int offset) { |
|
||||||
|
|
||||||
downIt = mActiveViews.iterator(); |
|
||||||
BitmapView view; |
|
||||||
|
|
||||||
// 进行删除
|
|
||||||
while (downIt.hasNext()) { |
|
||||||
view = downIt.next(); |
|
||||||
view.top = view.top + offset; |
|
||||||
view.bottom = view.bottom + offset; |
|
||||||
// 设置允许显示的范围
|
|
||||||
view.destRect.top = view.top; |
|
||||||
view.destRect.bottom = view.bottom; |
|
||||||
|
|
||||||
// 判断是否越界了
|
|
||||||
if (view.bottom <= 0) { |
|
||||||
// 添加到废弃的View中
|
|
||||||
mScrapViews.add(view); |
|
||||||
// 从Active中移除
|
|
||||||
downIt.remove(); |
|
||||||
// 如果原先是从上加载,现在变成从下加载,则表示取消
|
|
||||||
if (mDirection == Direction.UP) { |
|
||||||
mListener.pageCancel(); |
|
||||||
mDirection = Direction.NONE; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// 滑动之后的最后一个 View 的距离屏幕顶部上的实际位置
|
|
||||||
int realEdge = bottomEdge + offset; |
|
||||||
|
|
||||||
// 进行填充
|
|
||||||
while (realEdge < mViewHeight && mActiveViews.size() < 2) { |
|
||||||
// 从废弃的Views中获取一个
|
|
||||||
view = mScrapViews.getFirst(); |
|
||||||
/* //擦除其Bitmap(重新创建会不会更好一点)
|
|
||||||
eraseBitmap(view.bitmap,view.bitmap.getWidth(),view.bitmap.getHeight(),0,0);*/ |
|
||||||
if (view == null) return; |
|
||||||
|
|
||||||
Bitmap cancelBitmap = mNextBitmap; |
|
||||||
mNextBitmap = view.bitmap; |
|
||||||
|
|
||||||
if (!isRefresh) { |
|
||||||
boolean hasNext = mListener.hasNext(); //如果不成功则无法滑动
|
|
||||||
|
|
||||||
// 如果不存在next,则进行还原
|
|
||||||
if (!hasNext) { |
|
||||||
mNextBitmap = cancelBitmap; |
|
||||||
for (BitmapView activeView : mActiveViews) { |
|
||||||
activeView.top = 0; |
|
||||||
activeView.bottom = mViewHeight; |
|
||||||
// 设置允许显示的范围
|
|
||||||
activeView.destRect.top = activeView.top; |
|
||||||
activeView.destRect.bottom = activeView.bottom; |
|
||||||
} |
|
||||||
abortAnim(); |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// 如果加载成功,那么就将View从ScrapViews中移除
|
|
||||||
mScrapViews.removeFirst(); |
|
||||||
// 添加到存活的Bitmap中
|
|
||||||
mActiveViews.add(view); |
|
||||||
mDirection = Direction.DOWN; |
|
||||||
|
|
||||||
// 设置Bitmap的范围
|
|
||||||
view.top = realEdge; |
|
||||||
view.bottom = realEdge + view.bitmap.getHeight(); |
|
||||||
// 设置允许显示的范围
|
|
||||||
view.destRect.top = view.top; |
|
||||||
view.destRect.bottom = view.bottom; |
|
||||||
|
|
||||||
realEdge += view.bitmap.getHeight(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private Iterator<BitmapView> upIt; |
|
||||||
|
|
||||||
/** |
|
||||||
* 创建View填充顶部空白部分 |
|
||||||
* |
|
||||||
* @param topEdge : 当前第一个View的顶部,到屏幕顶部的距离 |
|
||||||
* @param offset : 滑动的偏移量 |
|
||||||
*/ |
|
||||||
private void fillUp(int topEdge, int offset) { |
|
||||||
// 首先进行布局的调整
|
|
||||||
upIt = mActiveViews.iterator(); |
|
||||||
BitmapView view; |
|
||||||
while (upIt.hasNext()) { |
|
||||||
view = upIt.next(); |
|
||||||
view.top = view.top + offset; |
|
||||||
view.bottom = view.bottom + offset; |
|
||||||
//设置允许显示的范围
|
|
||||||
view.destRect.top = view.top; |
|
||||||
view.destRect.bottom = view.bottom; |
|
||||||
|
|
||||||
// 判断是否越界了
|
|
||||||
if (view.top >= mViewHeight) { |
|
||||||
// 添加到废弃的View中
|
|
||||||
mScrapViews.add(view); |
|
||||||
// 从Active中移除
|
|
||||||
upIt.remove(); |
|
||||||
|
|
||||||
// 如果原先是下,现在变成从上加载了,则表示取消加载
|
|
||||||
|
|
||||||
if (mDirection == Direction.DOWN) { |
|
||||||
mListener.pageCancel(); |
|
||||||
mDirection = Direction.NONE; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// 滑动之后,第一个 View 的顶部距离屏幕顶部的实际位置。
|
|
||||||
int realEdge = topEdge + offset; |
|
||||||
|
|
||||||
// 对布局进行View填充
|
|
||||||
while (realEdge > 0 && mActiveViews.size() < 2) { |
|
||||||
// 从废弃的Views中获取一个
|
|
||||||
view = mScrapViews.getFirst(); |
|
||||||
if (view == null) return; |
|
||||||
|
|
||||||
// 判断是否存在上一章节
|
|
||||||
Bitmap cancelBitmap = mNextBitmap; |
|
||||||
mNextBitmap = view.bitmap; |
|
||||||
if (!isRefresh) { |
|
||||||
boolean hasPrev = mListener.hasPrev(); // 如果不成功则无法滑动
|
|
||||||
// 如果不存在next,则进行还原
|
|
||||||
if (!hasPrev) { |
|
||||||
mNextBitmap = cancelBitmap; |
|
||||||
for (BitmapView activeView : mActiveViews) { |
|
||||||
activeView.top = 0; |
|
||||||
activeView.bottom = mViewHeight; |
|
||||||
// 设置允许显示的范围
|
|
||||||
activeView.destRect.top = activeView.top; |
|
||||||
activeView.destRect.bottom = activeView.bottom; |
|
||||||
} |
|
||||||
abortAnim(); |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
||||||
// 如果加载成功,那么就将View从ScrapViews中移除
|
|
||||||
mScrapViews.removeFirst(); |
|
||||||
// 加入到存活的对象中
|
|
||||||
mActiveViews.add(0, view); |
|
||||||
mDirection = Direction.UP; |
|
||||||
// 设置Bitmap的范围
|
|
||||||
view.top = realEdge - view.bitmap.getHeight(); |
|
||||||
view.bottom = realEdge; |
|
||||||
|
|
||||||
// 设置允许显示的范围
|
|
||||||
view.destRect.top = view.top; |
|
||||||
view.destRect.bottom = view.bottom; |
|
||||||
realEdge -= view.bitmap.getHeight(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 对Bitmap进行擦除 |
|
||||||
* |
|
||||||
* @param b |
|
||||||
* @param width |
|
||||||
* @param height |
|
||||||
* @param paddingLeft |
|
||||||
* @param paddingTop |
|
||||||
*/ |
|
||||||
private void eraseBitmap(Bitmap b, int width, int height, |
|
||||||
int paddingLeft, int paddingTop) { |
|
||||||
/* if (mInitBitmapPix == null) return; |
|
||||||
b.setPixels(mInitBitmapPix, 0, width, paddingLeft, paddingTop, width, height);*/ |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 重置位移 |
|
||||||
*/ |
|
||||||
public void resetBitmap() { |
|
||||||
isRefresh = true; |
|
||||||
// 将所有的Active加入到Scrap中
|
|
||||||
for (BitmapView view : mActiveViews) { |
|
||||||
mScrapViews.add(view); |
|
||||||
} |
|
||||||
// 清除所有的Active
|
|
||||||
mActiveViews.clear(); |
|
||||||
// 重新进行布局
|
|
||||||
onLayout(); |
|
||||||
isRefresh = false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean onTouchEvent(MotionEvent event) { |
|
||||||
int x = (int) event.getX(); |
|
||||||
int y = (int) event.getY(); |
|
||||||
|
|
||||||
// 初始化速度追踪器
|
|
||||||
if (mVelocity == null) { |
|
||||||
mVelocity = VelocityTracker.obtain(); |
|
||||||
} |
|
||||||
|
|
||||||
mVelocity.addMovement(event); |
|
||||||
// 设置触碰点
|
|
||||||
setTouchPoint(x, y); |
|
||||||
|
|
||||||
switch (event.getAction()) { |
|
||||||
case MotionEvent.ACTION_DOWN: |
|
||||||
isRunning = false; |
|
||||||
// 设置起始点
|
|
||||||
setStartPoint(x, y); |
|
||||||
// 停止动画
|
|
||||||
abortAnim(); |
|
||||||
break; |
|
||||||
case MotionEvent.ACTION_MOVE: |
|
||||||
mVelocity.computeCurrentVelocity(VELOCITY_DURATION); |
|
||||||
isRunning = true; |
|
||||||
// 进行刷新
|
|
||||||
mView.postInvalidate(); |
|
||||||
break; |
|
||||||
case MotionEvent.ACTION_UP: |
|
||||||
isRunning = false; |
|
||||||
// 开启动画
|
|
||||||
startAnim(); |
|
||||||
// 删除检测器
|
|
||||||
mVelocity.recycle(); |
|
||||||
mVelocity = null; |
|
||||||
break; |
|
||||||
|
|
||||||
case MotionEvent.ACTION_CANCEL: |
|
||||||
try { |
|
||||||
mVelocity.recycle(); // if velocityTracker won't be used should be recycled
|
|
||||||
mVelocity = null; |
|
||||||
} catch (Exception e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
break; |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
BitmapView tmpView; |
|
||||||
|
|
||||||
@Override |
|
||||||
public void draw(Canvas canvas) { |
|
||||||
//进行布局
|
|
||||||
onLayout(); |
|
||||||
|
|
||||||
//绘制背景
|
|
||||||
canvas.drawBitmap(mBgBitmap, 0, 0, null); |
|
||||||
//绘制内容
|
|
||||||
canvas.save(); |
|
||||||
//移动位置
|
|
||||||
canvas.translate(0, mMarginHeight); |
|
||||||
//裁剪显示区域
|
|
||||||
canvas.clipRect(0, 0, mViewWidth, mViewHeight); |
|
||||||
/* //设置背景透明
|
|
||||||
canvas.drawColor(0x40);*/ |
|
||||||
//绘制Bitmap
|
|
||||||
for (int i = 0; i < mActiveViews.size(); ++i) { |
|
||||||
tmpView = mActiveViews.get(i); |
|
||||||
canvas.drawBitmap(tmpView.bitmap, tmpView.srcRect, tmpView.destRect, null); |
|
||||||
} |
|
||||||
canvas.restore(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public synchronized void startAnim() { |
|
||||||
isRunning = true; |
|
||||||
mScroller.fling(0, (int) mTouchY, 0, (int) mVelocity.getYVelocity() |
|
||||||
, 0, 0, Integer.MAX_VALUE * -1, Integer.MAX_VALUE); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void scrollAnim() { |
|
||||||
if (mScroller.computeScrollOffset()) { |
|
||||||
int x = mScroller.getCurrX(); |
|
||||||
int y = mScroller.getCurrY(); |
|
||||||
setTouchPoint(x, y); |
|
||||||
if (mScroller.getFinalX() == x && mScroller.getFinalY() == y) { |
|
||||||
isRunning = false; |
|
||||||
} |
|
||||||
mView.postInvalidate(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void abortAnim() { |
|
||||||
if (!mScroller.isFinished()) { |
|
||||||
mScroller.abortAnimation(); |
|
||||||
isRunning = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Bitmap getBgBitmap() { |
|
||||||
return mBgBitmap; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Bitmap getNextBitmap() { |
|
||||||
return mNextBitmap; |
|
||||||
} |
|
||||||
|
|
||||||
private static class BitmapView { |
|
||||||
Bitmap bitmap; |
|
||||||
Rect srcRect; |
|
||||||
Rect destRect; |
|
||||||
int top; |
|
||||||
int bottom; |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,412 @@ |
|||||||
|
package com.novel.read.widget.page.anim |
||||||
|
|
||||||
|
import android.graphics.Bitmap |
||||||
|
import android.graphics.Canvas |
||||||
|
import android.graphics.Rect |
||||||
|
import android.view.MotionEvent |
||||||
|
import android.view.VelocityTracker |
||||||
|
import android.view.View |
||||||
|
|
||||||
|
import com.novel.read.widget.page.PageAnimation |
||||||
|
|
||||||
|
import java.util.ArrayDeque |
||||||
|
import java.util.ArrayList |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by zlj |
||||||
|
* 原理:仿照ListView源码实现的上下滑动效果 |
||||||
|
* 问题: |
||||||
|
* 1. 向上翻页,重复的问题 (完成) |
||||||
|
* 2. 滑动卡顿的问题。原因:由于绘制的数据过多造成的卡顿问题。 (主要是文字绘制需要的时长比较多) 解决办法:做文字缓冲 |
||||||
|
* 3. 弱网环境下,显示的问题 |
||||||
|
*/ |
||||||
|
class ScrollPageAnim( |
||||||
|
w: Int, h: Int, marginWidth: Int, marginHeight: Int, |
||||||
|
view: View, listener: PageAnimation.OnPageChangeListener |
||||||
|
) : PageAnimation(w, h, marginWidth, marginHeight, view, listener) { |
||||||
|
private var mVelocity: VelocityTracker? = null |
||||||
|
|
||||||
|
// 整个Bitmap的背景显示 |
||||||
|
private var mBgBitmap: Bitmap? = null |
||||||
|
|
||||||
|
// 下一个展示的图片 |
||||||
|
private var mNextBitmap: Bitmap? = null |
||||||
|
|
||||||
|
// 被废弃的图片列表 |
||||||
|
private var mScrapViews: ArrayDeque<BitmapView>? = null |
||||||
|
// 正在被利用的图片列表 |
||||||
|
private val mActiveViews = ArrayList<BitmapView>(2) |
||||||
|
|
||||||
|
// 是否处于刷新阶段 |
||||||
|
private var isRefresh = true |
||||||
|
|
||||||
|
// 底部填充 |
||||||
|
private var downIt: MutableIterator<BitmapView>? = null |
||||||
|
|
||||||
|
private var upIt: MutableIterator<BitmapView>? = null |
||||||
|
|
||||||
|
|
||||||
|
private var tmpView: BitmapView? = null |
||||||
|
|
||||||
|
init { |
||||||
|
// 创建两个BitmapView |
||||||
|
initWidget() |
||||||
|
} |
||||||
|
|
||||||
|
private fun initWidget() { |
||||||
|
mBgBitmap = Bitmap.createBitmap(mScreenWidth, mScreenHeight, Bitmap.Config.RGB_565) |
||||||
|
|
||||||
|
mScrapViews = ArrayDeque(2) |
||||||
|
for (i in 0..1) { |
||||||
|
val view = BitmapView() |
||||||
|
view.bitmap = Bitmap.createBitmap(mViewWidth, mViewHeight, Bitmap.Config.RGB_565) |
||||||
|
view.srcRect = Rect(0, 0, mViewWidth, mViewHeight) |
||||||
|
view.destRect = Rect(0, 0, mViewWidth, mViewHeight) |
||||||
|
view.top = 0 |
||||||
|
view.bottom = view.bitmap!!.height |
||||||
|
|
||||||
|
mScrapViews!!.push(view) |
||||||
|
} |
||||||
|
onLayout() |
||||||
|
isRefresh = false |
||||||
|
} |
||||||
|
|
||||||
|
// 修改布局,填充内容 |
||||||
|
private fun onLayout() { |
||||||
|
// 如果还没有开始加载,则从上到下进行绘制 |
||||||
|
if (mActiveViews.size == 0) { |
||||||
|
fillDown(0, 0) |
||||||
|
mDirection = PageAnimation.Direction.NONE |
||||||
|
} else { |
||||||
|
val offset = (mTouchY - mLastY).toInt() |
||||||
|
// 判断是下滑还是上拉 (下滑) |
||||||
|
if (offset > 0) { |
||||||
|
val topEdge = mActiveViews[0].top |
||||||
|
fillUp(topEdge, offset) |
||||||
|
} else { |
||||||
|
// 底部的距离 = 当前底部的距离 + 滑动的距离 (因为上滑,得到的值肯定是负的) |
||||||
|
val bottomEdge = mActiveViews[mActiveViews.size - 1].bottom |
||||||
|
fillDown(bottomEdge, offset) |
||||||
|
}// 上拉 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建View填充底部空白部分 |
||||||
|
* |
||||||
|
* @param bottomEdge :当前最后一个View的底部,在整个屏幕上的位置,即相对于屏幕顶部的距离 |
||||||
|
* @param offset :滑动的偏移量 |
||||||
|
*/ |
||||||
|
private fun fillDown(bottomEdge: Int, offset: Int) { |
||||||
|
|
||||||
|
downIt = mActiveViews.iterator() |
||||||
|
var view: BitmapView? |
||||||
|
|
||||||
|
// 进行删除 |
||||||
|
while (downIt!!.hasNext()) { |
||||||
|
view = downIt!!.next() |
||||||
|
view.top = view.top + offset |
||||||
|
view.bottom = view.bottom + offset |
||||||
|
// 设置允许显示的范围 |
||||||
|
view.destRect!!.top = view.top |
||||||
|
view.destRect!!.bottom = view.bottom |
||||||
|
|
||||||
|
// 判断是否越界了 |
||||||
|
if (view.bottom <= 0) { |
||||||
|
// 添加到废弃的View中 |
||||||
|
mScrapViews!!.add(view) |
||||||
|
// 从Active中移除 |
||||||
|
downIt!!.remove() |
||||||
|
// 如果原先是从上加载,现在变成从下加载,则表示取消 |
||||||
|
if (mDirection == PageAnimation.Direction.UP) { |
||||||
|
mListener.pageCancel() |
||||||
|
mDirection = PageAnimation.Direction.NONE |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 滑动之后的最后一个 View 的距离屏幕顶部上的实际位置 |
||||||
|
var realEdge = bottomEdge + offset |
||||||
|
|
||||||
|
// 进行填充 |
||||||
|
while (realEdge < mViewHeight && mActiveViews.size < 2) { |
||||||
|
// 从废弃的Views中获取一个 |
||||||
|
view = mScrapViews!!.first |
||||||
|
/* //擦除其Bitmap(重新创建会不会更好一点) |
||||||
|
eraseBitmap(view.bitmap,view.bitmap.getWidth(),view.bitmap.getHeight(),0,0);*/ |
||||||
|
if (view == null) return |
||||||
|
|
||||||
|
val cancelBitmap = mNextBitmap |
||||||
|
mNextBitmap = view.bitmap |
||||||
|
|
||||||
|
if (!isRefresh) { |
||||||
|
val hasNext = mListener.hasNext() //如果不成功则无法滑动 |
||||||
|
|
||||||
|
// 如果不存在next,则进行还原 |
||||||
|
if (!hasNext) { |
||||||
|
mNextBitmap = cancelBitmap |
||||||
|
for (activeView in mActiveViews) { |
||||||
|
activeView.top = 0 |
||||||
|
activeView.bottom = mViewHeight |
||||||
|
// 设置允许显示的范围 |
||||||
|
activeView.destRect!!.top = activeView.top |
||||||
|
activeView.destRect!!.bottom = activeView.bottom |
||||||
|
} |
||||||
|
abortAnim() |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 如果加载成功,那么就将View从ScrapViews中移除 |
||||||
|
mScrapViews!!.removeFirst() |
||||||
|
// 添加到存活的Bitmap中 |
||||||
|
mActiveViews.add(view) |
||||||
|
mDirection = PageAnimation.Direction.DOWN |
||||||
|
|
||||||
|
// 设置Bitmap的范围 |
||||||
|
view.top = realEdge |
||||||
|
view.bottom = realEdge + view.bitmap!!.height |
||||||
|
// 设置允许显示的范围 |
||||||
|
view.destRect!!.top = view.top |
||||||
|
view.destRect!!.bottom = view.bottom |
||||||
|
|
||||||
|
realEdge += view.bitmap!!.height |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建View填充顶部空白部分 |
||||||
|
* |
||||||
|
* @param topEdge : 当前第一个View的顶部,到屏幕顶部的距离 |
||||||
|
* @param offset : 滑动的偏移量 |
||||||
|
*/ |
||||||
|
private fun fillUp(topEdge: Int, offset: Int) { |
||||||
|
// 首先进行布局的调整 |
||||||
|
upIt = mActiveViews.iterator() |
||||||
|
var view: BitmapView? |
||||||
|
while (upIt!!.hasNext()) { |
||||||
|
view = upIt!!.next() |
||||||
|
view.top = view.top + offset |
||||||
|
view.bottom = view.bottom + offset |
||||||
|
//设置允许显示的范围 |
||||||
|
view.destRect!!.top = view.top |
||||||
|
view.destRect!!.bottom = view.bottom |
||||||
|
|
||||||
|
// 判断是否越界了 |
||||||
|
if (view.top >= mViewHeight) { |
||||||
|
// 添加到废弃的View中 |
||||||
|
mScrapViews!!.add(view) |
||||||
|
// 从Active中移除 |
||||||
|
upIt!!.remove() |
||||||
|
|
||||||
|
// 如果原先是下,现在变成从上加载了,则表示取消加载 |
||||||
|
|
||||||
|
if (mDirection == PageAnimation.Direction.DOWN) { |
||||||
|
mListener.pageCancel() |
||||||
|
mDirection = PageAnimation.Direction.NONE |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 滑动之后,第一个 View 的顶部距离屏幕顶部的实际位置。 |
||||||
|
var realEdge = topEdge + offset |
||||||
|
|
||||||
|
// 对布局进行View填充 |
||||||
|
while (realEdge > 0 && mActiveViews.size < 2) { |
||||||
|
// 从废弃的Views中获取一个 |
||||||
|
view = mScrapViews!!.first |
||||||
|
if (view == null) return |
||||||
|
|
||||||
|
// 判断是否存在上一章节 |
||||||
|
val cancelBitmap = mNextBitmap |
||||||
|
mNextBitmap = view.bitmap |
||||||
|
if (!isRefresh) { |
||||||
|
val hasPrev = mListener.hasPrev() // 如果不成功则无法滑动 |
||||||
|
// 如果不存在next,则进行还原 |
||||||
|
if (!hasPrev) { |
||||||
|
mNextBitmap = cancelBitmap |
||||||
|
for (activeView in mActiveViews) { |
||||||
|
activeView.top = 0 |
||||||
|
activeView.bottom = mViewHeight |
||||||
|
// 设置允许显示的范围 |
||||||
|
activeView.destRect!!.top = activeView.top |
||||||
|
activeView.destRect!!.bottom = activeView.bottom |
||||||
|
} |
||||||
|
abortAnim() |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
// 如果加载成功,那么就将View从ScrapViews中移除 |
||||||
|
mScrapViews!!.removeFirst() |
||||||
|
// 加入到存活的对象中 |
||||||
|
mActiveViews.add(0, view) |
||||||
|
mDirection = PageAnimation.Direction.UP |
||||||
|
// 设置Bitmap的范围 |
||||||
|
view.top = realEdge - view.bitmap!!.height |
||||||
|
view.bottom = realEdge |
||||||
|
|
||||||
|
// 设置允许显示的范围 |
||||||
|
view.destRect!!.top = view.top |
||||||
|
view.destRect!!.bottom = view.bottom |
||||||
|
realEdge -= view.bitmap!!.height |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 对Bitmap进行擦除 |
||||||
|
* |
||||||
|
* @param b |
||||||
|
* @param width |
||||||
|
* @param height |
||||||
|
* @param paddingLeft |
||||||
|
* @param paddingTop |
||||||
|
*/ |
||||||
|
private fun eraseBitmap( |
||||||
|
b: Bitmap, width: Int, height: Int, |
||||||
|
paddingLeft: Int, paddingTop: Int |
||||||
|
) { |
||||||
|
/* if (mInitBitmapPix == null) return; |
||||||
|
b.setPixels(mInitBitmapPix, 0, width, paddingLeft, paddingTop, width, height);*/ |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 重置位移 |
||||||
|
*/ |
||||||
|
fun resetBitmap() { |
||||||
|
isRefresh = true |
||||||
|
// 将所有的Active加入到Scrap中 |
||||||
|
for (view in mActiveViews) { |
||||||
|
mScrapViews!!.add(view) |
||||||
|
} |
||||||
|
// 清除所有的Active |
||||||
|
mActiveViews.clear() |
||||||
|
// 重新进行布局 |
||||||
|
onLayout() |
||||||
|
isRefresh = false |
||||||
|
} |
||||||
|
|
||||||
|
override fun onTouchEvent(event: MotionEvent): Boolean { |
||||||
|
val x = event.x.toInt() |
||||||
|
val y = event.y.toInt() |
||||||
|
|
||||||
|
// 初始化速度追踪器 |
||||||
|
if (mVelocity == null) { |
||||||
|
mVelocity = VelocityTracker.obtain() |
||||||
|
} |
||||||
|
|
||||||
|
mVelocity!!.addMovement(event) |
||||||
|
// 设置触碰点 |
||||||
|
setTouchPoint(x.toFloat(), y.toFloat()) |
||||||
|
|
||||||
|
when (event.action) { |
||||||
|
MotionEvent.ACTION_DOWN -> { |
||||||
|
isRunning = false |
||||||
|
// 设置起始点 |
||||||
|
setStartPoint(x.toFloat(), y.toFloat()) |
||||||
|
// 停止动画 |
||||||
|
abortAnim() |
||||||
|
} |
||||||
|
MotionEvent.ACTION_MOVE -> { |
||||||
|
mVelocity!!.computeCurrentVelocity(VELOCITY_DURATION) |
||||||
|
isRunning = true |
||||||
|
// 进行刷新 |
||||||
|
mView.postInvalidate() |
||||||
|
} |
||||||
|
MotionEvent.ACTION_UP -> { |
||||||
|
isRunning = false |
||||||
|
// 开启动画 |
||||||
|
startAnim() |
||||||
|
// 删除检测器 |
||||||
|
mVelocity!!.recycle() |
||||||
|
mVelocity = null |
||||||
|
} |
||||||
|
|
||||||
|
MotionEvent.ACTION_CANCEL -> try { |
||||||
|
mVelocity!!.recycle() // if velocityTracker won't be used should be recycled |
||||||
|
mVelocity = null |
||||||
|
} catch (e: Exception) { |
||||||
|
e.printStackTrace() |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
override fun draw(canvas: Canvas) { |
||||||
|
//进行布局 |
||||||
|
onLayout() |
||||||
|
|
||||||
|
//绘制背景 |
||||||
|
canvas.drawBitmap(mBgBitmap!!, 0f, 0f, null) |
||||||
|
//绘制内容 |
||||||
|
canvas.save() |
||||||
|
//移动位置 |
||||||
|
canvas.translate(0f, mMarginHeight.toFloat()) |
||||||
|
//裁剪显示区域 |
||||||
|
canvas.clipRect(0, 0, mViewWidth, mViewHeight) |
||||||
|
/* //设置背景透明 |
||||||
|
canvas.drawColor(0x40);*/ |
||||||
|
//绘制Bitmap |
||||||
|
for (i in mActiveViews.indices) { |
||||||
|
tmpView = mActiveViews[i] |
||||||
|
canvas.drawBitmap(tmpView!!.bitmap!!, tmpView!!.srcRect, tmpView!!.destRect!!, null) |
||||||
|
} |
||||||
|
canvas.restore() |
||||||
|
} |
||||||
|
|
||||||
|
@Synchronized |
||||||
|
override fun startAnim() { |
||||||
|
isRunning = true |
||||||
|
mScroller.fling( |
||||||
|
0, |
||||||
|
mTouchY.toInt(), |
||||||
|
0, |
||||||
|
mVelocity!!.yVelocity.toInt(), |
||||||
|
0, |
||||||
|
0, |
||||||
|
Integer.MAX_VALUE * -1, |
||||||
|
Integer.MAX_VALUE |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
override fun scrollAnim() { |
||||||
|
if (mScroller.computeScrollOffset()) { |
||||||
|
val x = mScroller.currX |
||||||
|
val y = mScroller.currY |
||||||
|
setTouchPoint(x.toFloat(), y.toFloat()) |
||||||
|
if (mScroller.finalX == x && mScroller.finalY == y) { |
||||||
|
isRunning = false |
||||||
|
} |
||||||
|
mView.postInvalidate() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun abortAnim() { |
||||||
|
if (!mScroller.isFinished) { |
||||||
|
mScroller.abortAnimation() |
||||||
|
isRunning = false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun getBgBitmap(): Bitmap? { |
||||||
|
return mBgBitmap |
||||||
|
} |
||||||
|
|
||||||
|
override fun getNextBitmap(): Bitmap? { |
||||||
|
return mNextBitmap |
||||||
|
} |
||||||
|
|
||||||
|
private class BitmapView { |
||||||
|
internal var bitmap: Bitmap? = null |
||||||
|
internal var srcRect: Rect? = null |
||||||
|
internal var destRect: Rect? = null |
||||||
|
internal var top: Int = 0 |
||||||
|
internal var bottom: Int = 0 |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
private val TAG = "ScrollAnimation" |
||||||
|
// 滑动追踪的时间 |
||||||
|
private val VELOCITY_DURATION = 1000 |
||||||
|
} |
||||||
|
} |
@ -1,101 +0,0 @@ |
|||||||
package com.novel.read.widget.page.anim; |
|
||||||
|
|
||||||
import android.graphics.Canvas; |
|
||||||
import android.graphics.Rect; |
|
||||||
import android.view.View; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by zlj |
|
||||||
*/ |
|
||||||
|
|
||||||
public class SlidePageAnim extends HorizonPageAnim { |
|
||||||
private Rect mSrcRect, mDestRect,mNextSrcRect,mNextDestRect; |
|
||||||
|
|
||||||
public SlidePageAnim(int w, int h, View view, OnPageChangeListener listener) { |
|
||||||
super(w, h, view, listener); |
|
||||||
mSrcRect = new Rect(0, 0, mViewWidth, mViewHeight); |
|
||||||
mDestRect = new Rect(0, 0, mViewWidth, mViewHeight); |
|
||||||
mNextSrcRect = new Rect(0, 0, mViewWidth, mViewHeight); |
|
||||||
mNextDestRect = new Rect(0, 0, mViewWidth, mViewHeight); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void drawStatic(Canvas canvas) { |
|
||||||
if (isCancel){ |
|
||||||
canvas.drawBitmap(mCurBitmap, 0, 0, null); |
|
||||||
}else { |
|
||||||
canvas.drawBitmap(mNextBitmap, 0, 0, null); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void drawMove(Canvas canvas) { |
|
||||||
int dis = 0; |
|
||||||
switch (mDirection){ |
|
||||||
case NEXT: |
|
||||||
//左半边的剩余区域
|
|
||||||
dis = (int) (mScreenWidth - mStartX + mTouchX); |
|
||||||
if (dis > mScreenWidth){ |
|
||||||
dis = mScreenWidth; |
|
||||||
} |
|
||||||
//计算bitmap截取的区域
|
|
||||||
mSrcRect.left = mScreenWidth - dis; |
|
||||||
//计算bitmap在canvas显示的区域
|
|
||||||
mDestRect.right = dis; |
|
||||||
//计算下一页截取的区域
|
|
||||||
mNextSrcRect.right = mScreenWidth - dis; |
|
||||||
//计算下一页在canvas显示的区域
|
|
||||||
mNextDestRect.left = dis; |
|
||||||
|
|
||||||
canvas.drawBitmap(mNextBitmap,mNextSrcRect,mNextDestRect,null); |
|
||||||
canvas.drawBitmap(mCurBitmap,mSrcRect,mDestRect,null); |
|
||||||
break; |
|
||||||
default: |
|
||||||
dis = (int) (mTouchX - mStartX); |
|
||||||
if (dis < 0){ |
|
||||||
dis = 0; |
|
||||||
mStartX = mTouchX; |
|
||||||
} |
|
||||||
mSrcRect.left = mScreenWidth - dis; |
|
||||||
mDestRect.right = dis; |
|
||||||
|
|
||||||
//计算下一页截取的区域
|
|
||||||
mNextSrcRect.right = mScreenWidth - dis; |
|
||||||
//计算下一页在canvas显示的区域
|
|
||||||
mNextDestRect.left = dis; |
|
||||||
|
|
||||||
canvas.drawBitmap(mCurBitmap,mNextSrcRect,mNextDestRect,null); |
|
||||||
canvas.drawBitmap(mNextBitmap,mSrcRect,mDestRect,null); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void startAnim() { |
|
||||||
super.startAnim(); |
|
||||||
int dx = 0; |
|
||||||
switch (mDirection){ |
|
||||||
case NEXT: |
|
||||||
if (isCancel){ |
|
||||||
int dis = (int)((mScreenWidth - mStartX) + mTouchX); |
|
||||||
if (dis > mScreenWidth){ |
|
||||||
dis = mScreenWidth; |
|
||||||
} |
|
||||||
dx = mScreenWidth - dis; |
|
||||||
}else{ |
|
||||||
dx = (int) -(mTouchX + (mScreenWidth - mStartX)); |
|
||||||
} |
|
||||||
break; |
|
||||||
default: |
|
||||||
if (isCancel){ |
|
||||||
dx = (int)-Math.abs(mTouchX - mStartX); |
|
||||||
}else{ |
|
||||||
dx = (int) (mScreenWidth - (mTouchX - mStartX)); |
|
||||||
} |
|
||||||
break; |
|
||||||
} |
|
||||||
//滑动速度保持一致
|
|
||||||
int duration = (400 * Math.abs(dx)) / mScreenWidth; |
|
||||||
mScroller.startScroll((int) mTouchX, 0, dx, 0, duration); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,92 @@ |
|||||||
|
package com.novel.read.widget.page.anim |
||||||
|
|
||||||
|
import android.graphics.Canvas |
||||||
|
import android.graphics.Rect |
||||||
|
import android.view.View |
||||||
|
import com.novel.read.widget.page.PageAnimation |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by zlj |
||||||
|
*/ |
||||||
|
|
||||||
|
class SlidePageAnim(w: Int, h: Int, view: View, listener: PageAnimation.OnPageChangeListener) : |
||||||
|
HorizonPageAnim(w, h, view, listener) { |
||||||
|
|
||||||
|
private val mSrcRect: Rect = Rect(0, 0, mViewWidth, mViewHeight) |
||||||
|
private val mDestRect: Rect = Rect(0, 0, mViewWidth, mViewHeight) |
||||||
|
private val mNextSrcRect: Rect = Rect(0, 0, mViewWidth, mViewHeight) |
||||||
|
private val mNextDestRect: Rect = Rect(0, 0, mViewWidth, mViewHeight) |
||||||
|
|
||||||
|
override fun drawStatic(canvas: Canvas) { |
||||||
|
if (isCancel) { |
||||||
|
canvas.drawBitmap(mCurBitmap, 0f, 0f, null) |
||||||
|
} else { |
||||||
|
canvas.drawBitmap(mNextBitmap, 0f, 0f, null) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun drawMove(canvas: Canvas) { |
||||||
|
var dis = 0 |
||||||
|
when (mDirection) { |
||||||
|
PageAnimation.Direction.NEXT -> { |
||||||
|
//左半边的剩余区域 |
||||||
|
dis = (mScreenWidth - mStartX + mTouchX).toInt() |
||||||
|
if (dis > mScreenWidth) { |
||||||
|
dis = mScreenWidth |
||||||
|
} |
||||||
|
//计算bitmap截取的区域 |
||||||
|
mSrcRect.left = mScreenWidth - dis |
||||||
|
//计算bitmap在canvas显示的区域 |
||||||
|
mDestRect.right = dis |
||||||
|
//计算下一页截取的区域 |
||||||
|
mNextSrcRect.right = mScreenWidth - dis |
||||||
|
//计算下一页在canvas显示的区域 |
||||||
|
mNextDestRect.left = dis |
||||||
|
|
||||||
|
canvas.drawBitmap(mNextBitmap, mNextSrcRect, mNextDestRect, null) |
||||||
|
canvas.drawBitmap(mCurBitmap, mSrcRect, mDestRect, null) |
||||||
|
} |
||||||
|
else -> { |
||||||
|
dis = (mTouchX - mStartX).toInt() |
||||||
|
if (dis < 0) { |
||||||
|
dis = 0 |
||||||
|
mStartX = mTouchX |
||||||
|
} |
||||||
|
mSrcRect.left = mScreenWidth - dis |
||||||
|
mDestRect.right = dis |
||||||
|
|
||||||
|
//计算下一页截取的区域 |
||||||
|
mNextSrcRect.right = mScreenWidth - dis |
||||||
|
//计算下一页在canvas显示的区域 |
||||||
|
mNextDestRect.left = dis |
||||||
|
|
||||||
|
canvas.drawBitmap(mCurBitmap, mNextSrcRect, mNextDestRect, null) |
||||||
|
canvas.drawBitmap(mNextBitmap, mSrcRect, mDestRect, null) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun startAnim() { |
||||||
|
super.startAnim() |
||||||
|
var dx = 0 |
||||||
|
when (mDirection) { |
||||||
|
PageAnimation.Direction.NEXT -> if (isCancel) { |
||||||
|
var dis = (mScreenWidth - mStartX + mTouchX).toInt() |
||||||
|
if (dis > mScreenWidth) { |
||||||
|
dis = mScreenWidth |
||||||
|
} |
||||||
|
dx = mScreenWidth - dis |
||||||
|
} else { |
||||||
|
dx = (-(mTouchX + (mScreenWidth - mStartX))).toInt() |
||||||
|
} |
||||||
|
else -> if (isCancel) { |
||||||
|
dx = (-Math.abs(mTouchX - mStartX)).toInt() |
||||||
|
} else { |
||||||
|
dx = (mScreenWidth - (mTouchX - mStartX)).toInt() |
||||||
|
} |
||||||
|
} |
||||||
|
//滑动速度保持一致 |
||||||
|
val duration = 400 * Math.abs(dx) / mScreenWidth |
||||||
|
mScroller.startScroll(mTouchX.toInt(), 0, dx, 0, duration) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue