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 |
||||
* 作用:翻页动画的模式 |
||||
*/ |
||||
public enum PageMode { |
||||
enum class PageMode { |
||||
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