作者: Jooyer, 時間: 2018.12.08
Github地址,歡迎點贊,fork
我偶爾一次發現掌閱的開啟書本動畫不錯,然後度娘了一下,發現一個連結:download.csdn.net/download/we… , 下載下來學習膜拜了一下,發現有些動畫過度和掌閱有點不一樣,所以我就拷貝原始碼並略微修改了下,如果想看原著,上面已經貼了,這個效果是GIF,真機更流暢一些.
一共就是2個類+2個跳轉動畫
首先,看看 BookOpenView
public class BookOpenView extends FrameLayout implements
Animator.AnimatorListener {
private BookOpenViewValue mStartValue;
private AnimatorSet mAnimatorSet1, mAnimatorSet2;
private TextView tv_book_name;
private ImageView iv_book_content;
private View iv_book_cover;
private ConstraintLayout cl_book_container;
// 預設開啟書本動畫是關閉的
private AtomicBoolean isOpened = new AtomicBoolean(false);
public BookOpenView(@NonNull Context context) {
super(context);
initView(context);
}
public BookOpenView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public BookOpenView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
public void initView(Context context) {
LayoutInflater.from(context).inflate(R.layout.book_open_close, this);
setBackgroundColor(Color.TRANSPARENT);
tv_book_name = (TextView) findViewById(R.id.tv_book_name);
iv_book_content = (ImageView) findViewById(R.id.iv_book_content);
iv_book_cover = (View) findViewById(R.id.iv_book_cover);
cl_book_container = (ConstraintLayout) findViewById(R.id.cl_book_container);
}
/**
* 開啟動畫
*/
public synchronized void startAnim(final BookOpenViewValue startValue, BookOpenViewValue endValue) {
if (!isOpened.get()) {
if (!TextUtils.isEmpty(startValue.getBookName())){
tv_book_name.setText(startValue.getBookName());
}
// 如果有圖片,可以在此給 iv_book_cover 設定,圖解儘量不要太大,10K以內為佳
mStartValue = startValue;
System.out.println("startAnim===========mStartValue: " + mStartValue.toString() + " ========startValue: " + startValue.toString());
LayoutParams layoutParams = (LayoutParams) cl_book_container.getLayoutParams();
layoutParams.width = startValue.getRight() - startValue.getLeft();
layoutParams.height = startValue.getBottom() - startValue.getTop();
cl_book_container.setLayoutParams(layoutParams);
iv_book_content.setLayoutParams(layoutParams);
cl_book_container.setTranslationX(startValue.getLeft());
cl_book_container.setTranslationY(startValue.getTop());
// 計算縮放的起始位置和縮放中心點
final int x1 = ((layoutParams.width * (endValue.getRight() - layoutParams.width))
- layoutParams.width * (endValue.getRight() - startValue.getRight()))
/ (endValue.getRight() - layoutParams.width);
final float sX1 = (layoutParams.width - x1 + endValue.getRight() - startValue.getRight()) * 1.0f / (layoutParams.width - x1);
final float sX2 = (x1 + startValue.getLeft()) * 1.0f / x1;
final float sX = Math.max(sX1, sX2);
final int y1 = ((layoutParams.height * (endValue.getBottom() - layoutParams.height))
- layoutParams.height * (endValue.getBottom() - startValue.getBottom()))
/ (endValue.getBottom() - layoutParams.height);
final float sY1 = (layoutParams.height - y1 + endValue.getBottom() - startValue.getBottom()) * 1.0f / (layoutParams.height - y1);
final float sY2 = (y1 + startValue.getTop()) * 1.0f / y1;
final float sY = Math.max(sY1, sY2);
mStartValue.setX(x1);
mStartValue.setsX(sX);
mStartValue.setY(y1);
mStartValue.setsY(sY);
cl_book_container.setPivotX(0);
cl_book_container.setPivotY(y1);
// 對 封面進行縮放
ObjectAnimator scaleX = ObjectAnimator.ofFloat(cl_book_container,
"scaleX", 1.0f, sX * 0.8f);
scaleX.setDuration(1000);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(cl_book_container,
"scaleY", 1.0f, sY);
scaleY.setDuration(1000);
// 對 封面進行平移
ObjectAnimator translationLine = ObjectAnimator.ofFloat(cl_book_container,
"translationX", startValue.getLeft(), 0);
translationLine.setDuration(1000);
// 對封面進行旋轉
ObjectAnimator rotationY = ObjectAnimator.ofFloat(cl_book_container,
"rotationY", 0, -150);
rotationY.setDuration(600);
mAnimatorSet1 = new AnimatorSet();
mAnimatorSet1.playTogether(scaleX, scaleY, translationLine, rotationY);
mAnimatorSet1.start();
/* ------------------------------------------------ */
// 對 內部內容進行縮放,必須和封面保持一致的動作
iv_book_content.setPivotX(x1);
iv_book_content.setPivotY(y1);
iv_book_content.setTranslationX(startValue.getLeft());
iv_book_content.setTranslationY(startValue.getTop());
ObjectAnimator scaleX2 = ObjectAnimator.ofFloat(iv_book_content,
"scaleX", 1.0f, sX);
scaleX2.setDuration(1000);
ObjectAnimator scaleY2 = ObjectAnimator.ofFloat(iv_book_content,
"scaleY", 1.0f, sY);
scaleY2.setDuration(1000);
mAnimatorSet2 = new AnimatorSet();
mAnimatorSet2.playTogether(scaleX2, scaleY2);
mAnimatorSet2.addListener(this);
mAnimatorSet2.start();
}
}
/**
* 關閉動畫 , 邏輯和開始動畫相反
*/
public synchronized void closeAnim() {
if (isOpened.get()) {
setVisibility(VISIBLE);
setAlpha(1.0f);
cl_book_container.setScaleX(mStartValue.getsX() * 0.8f);
cl_book_container.setScaleY(mStartValue.getsY());
cl_book_container.setRotationY(-150);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(cl_book_container,
"scaleX", mStartValue.getsX() * 0.8f, 1.0f);
scaleX.setDuration(1000);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(cl_book_container,
"scaleY", mStartValue.getsY(), 1.0f);
scaleY.setDuration(1000);
ObjectAnimator translationLine = ObjectAnimator.ofFloat(cl_book_container,
"translationX", 0, mStartValue.getLeft());
translationLine.setDuration(1000);
ObjectAnimator rotationY = ObjectAnimator.ofFloat(cl_book_container,
"rotationY", -150, 0);
rotationY.setDuration(600);
rotationY.setStartDelay(400);
mAnimatorSet1 = new AnimatorSet();
mAnimatorSet1.playTogether(scaleX, scaleY, translationLine, rotationY);
mAnimatorSet1.start();
/* ------------------------------------------------ */
iv_book_content.setScaleX(mStartValue.getsX());
iv_book_content.setScaleY(mStartValue.getsY());
ObjectAnimator scaleX2 = ObjectAnimator.ofFloat(iv_book_content,
"scaleX", mStartValue.getsX(), 1.0f);
scaleX2.setDuration(1000);
ObjectAnimator scaleY2 = ObjectAnimator.ofFloat(iv_book_content,
"scaleY", mStartValue.getsY(), 1.0f);
scaleY2.setDuration(1000);
mAnimatorSet2 = new AnimatorSet();
mAnimatorSet2.playTogether(scaleX2, scaleY2);
mAnimatorSet2.start();
mAnimatorSet2.addListener(this);
}
}
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
System.out.println("onAnimationEnd========== " + isOpened.get());
if (isOpened.get()) { // 關閉書本動畫執行了
isOpened.set(false);
if (null != mEndListener) {
mEndListener.onRemove();
}
} else { // 開啟書本動畫
isOpened.set(true);
if (null != mEndListener) {
mEndListener.onAnimationEnd();
}
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
public void cancel() {
mAnimatorSet1.cancel();
mAnimatorSet2.cancel();
}
public interface OnAnimationEndListener {
void onAnimationEnd();
void onRemove();
}
private OnAnimationEndListener mEndListener;
public void setEndListener(OnAnimationEndListener endListener) {
mEndListener = endListener;
}
}
複製程式碼
內容略多,主要涉及計算,這個細節調整,大家可根據自己喜歡的效果調整試試
再來看一個類: BookOpenViewValue ,一目瞭然啊
public class BookOpenViewValue {
private int left;
private int top;
private int right;
private int bottom;
private float x;
private float y;
private float sX;
private float sY;
private String bookName;
private String bookCover;
public BookOpenViewValue(){}
public BookOpenViewValue(int left, int top, int right, int bottom){
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public int getRight() {
return right;
}
public void setRight(int right) {
this.right = right;
}
public int getBottom() {
return bottom;
}
public void setBottom(int bottom) {
this.bottom = bottom;
}
public int getLeft() {
return left;
}
public void setLeft(int left) {
this.left = left;
}
public int getTop() {
return top;
}
public void setTop(int top) {
this.top = top;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public float getsX() {
return sX;
}
public void setsX(float sX) {
this.sX = sX;
}
public float getsY() {
return sY;
}
public void setsY(float sY) {
this.sY = sY;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getBookCover() {
return bookCover;
}
public void setBookCover(String bookCover) {
this.bookCover = bookCover;
}
}
複製程式碼
最後來看看在 Activity 中用法
class MainActivity : AppCompatActivity() {
private var mBookOpenView: BookOpenView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
ll_contain.setOnClickListener {
val window = window.decorView as ViewGroup
val location = IntArray(2)
ll_contain.getLocationInWindow(location)
val startValue = BookOpenViewValue(location[0], location[1],
location[0] + (ll_contain.right - ll_contain.left),
location[1] + (ll_contain.bottom - ll_contain.top))
val endValue = BookOpenViewValue(window.left,
window.top,
window.right,
window.bottom)
mBookOpenView = BookOpenView(this@MainActivity)
window.addView(mBookOpenView)
mBookOpenView?.startAnim(startValue, endValue)
mBookOpenView?.setEndListener(object : BookOpenView.OnAnimationEndListener {
override fun onAnimationEnd() {
println("onAnimationEnd========== onAnimationEnd")
startActivity(Intent(this@MainActivity, ReadActivity::class.java))
overridePendingTransition(R.anim.read_fade_in, R.anim.read_fade_out)
}
override fun onRemove() {
println("onAnimationEnd========== onRemove")
window.removeView(mBookOpenView)
}
})
}
}
// 這個就是執行從 ReadActivity 返回後的動畫
override fun onResume() {
super.onResume()
println("onAnimationEnd========== onResume")
mBookOpenView?.closeAnim()
}
}
複製程式碼
跳轉的 Activity 更簡單了:
class ReadActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_read)
}
// 重寫主要是去掉系統預設跳轉動畫,方便那邊執行closeAnim
override fun finish() {
super.finish()
overridePendingTransition(0, 0)
}
}
複製程式碼
最後貼上2個跳轉動畫: res/anim/
read_fade_in.xml
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromAlpha="1.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="1.0" >
</alpha>
複製程式碼
read_fade_out.xml
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromAlpha="1.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="0.0" >
</alpha>
複製程式碼