From 46d1a0f0ba7b529e6e06f541f15d63d02ff58335 Mon Sep 17 00:00:00 2001 From: Allen Date: Mon, 18 Mar 2019 18:50:38 +0800 Subject: [PATCH 01/17] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BB=BF=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E6=82=AC=E6=B5=AE=E6=8B=96=E6=8B=BDview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/misc.xml | 17 +- app/build.gradle | 16 +- .../activity/MainActivity.java | 112 ----------- .../activity/MainActivity.kt | 83 ++++++++ .../helper/DragViewHelper.kt | 69 +++++++ .../listener/OnDragTouchListener.kt | 184 ++++++++++++++++++ app/src/main/res/layout/activity_main.xml | 5 +- .../main/res/mipmap-xxhdpi/ic_camera_3x.png | Bin 0 -> 3885 bytes build.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 10 files changed, 367 insertions(+), 125 deletions(-) delete mode 100644 app/src/main/java/com/allen/androidcustomview/activity/MainActivity.java create mode 100644 app/src/main/java/com/allen/androidcustomview/activity/MainActivity.kt create mode 100644 app/src/main/java/com/allen/androidcustomview/helper/DragViewHelper.kt create mode 100644 app/src/main/java/com/allen/androidcustomview/listener/OnDragTouchListener.kt create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_camera_3x.png diff --git a/.idea/misc.xml b/.idea/misc.xml index ba7052b..4dd9348 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,26 +5,37 @@ - + diff --git a/app/build.gradle b/app/build.gradle index ff97b77..d9383c8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,7 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 27 @@ -21,13 +24,14 @@ android { } dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + implementation fileTree(include: ['*.jar'], dir: 'libs') + androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:27.1.1' + implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support:design:27.1.1' - compile 'com.android.support:recyclerview-v7:27.1.1' - testCompile 'junit:junit:4.12' - compile 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.40' + implementation 'com.android.support:recyclerview-v7:27.1.1' + testImplementation 'junit:junit:4.12' + implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.40' } diff --git a/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.java b/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.java deleted file mode 100644 index 9da4d34..0000000 --- a/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.allen.androidcustomview.activity; - -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.View; - -import com.allen.androidcustomview.R; -import com.allen.androidcustomview.adapter.MainAdapter; -import com.allen.androidcustomview.bean.TypeBean; -import com.allen.androidcustomview.tagview.TagActivity; -import com.allen.androidcustomview.widget.SuperDividerItemDecoration; -import com.chad.library.adapter.base.BaseQuickAdapter; - -import java.util.ArrayList; -import java.util.List; - - -public class MainActivity extends AppCompatActivity implements BaseQuickAdapter.OnItemClickListener { - - - private RecyclerView recyclerView; - - private MainAdapter adapter; - - private List typeBeans = new ArrayList<>(); - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - recyclerView = findViewById(R.id.recycler_view); - - adapter = new MainAdapter(getData()); - adapter.setOnItemClickListener(this); - recyclerView.setLayoutManager(new LinearLayoutManager(this)); - recyclerView.addItemDecoration(new SuperDividerItemDecoration.Builder(this) - .build()); - recyclerView.setAdapter(adapter); - } - - private List getData() { - typeBeans.add(new TypeBean("气泡漂浮动画", 0)); - typeBeans.add(new TypeBean("波浪动画--贝塞尔曲线实现", 1)); - typeBeans.add(new TypeBean("波浪动画--正余弦函数实现", 2)); - typeBeans.add(new TypeBean("水波(雷达)扩散效果", 3)); - typeBeans.add(new TypeBean("RecyclerView实现另类的Tag标签", 4)); - typeBeans.add(new TypeBean("按钮自定义动画", 5)); - typeBeans.add(new TypeBean("自定义支付密码输入框", 6)); - typeBeans.add(new TypeBean("自定义进度条", 7)); - typeBeans.add(new TypeBean("使用的带动画的view", 8)); - typeBeans.add(new TypeBean("粘性小球", 9)); - typeBeans.add(new TypeBean("banner", 10)); - typeBeans.add(new TypeBean("吸顶效果--一行代码实现", 11)); - typeBeans.add(new TypeBean("揭露动画", 12)); - typeBeans.add(new TypeBean("支付宝首页效果", 13)); - return typeBeans; - } - - @Override - public void onItemClick(BaseQuickAdapter adapter, View view, int position) { - switch (typeBeans.get(position).getType()) { - case 0: - startActivity(new Intent(MainActivity.this, BubbleViewActivity.class)); - break; - case 1: - startActivity(new Intent(MainActivity.this, WaveByBezierActivity.class)); - break; - case 2: - startActivity(new Intent(MainActivity.this, WaveBySinCosActivity.class)); - break; - case 3: - startActivity(new Intent(MainActivity.this, RadarActivity.class)); - break; - case 4: - startActivity(new Intent(MainActivity.this, TagActivity.class)); - break; - case 5: - startActivity(new Intent(MainActivity.this, AnimationBtnActivity.class)); - break; - case 6: - startActivity(new Intent(MainActivity.this, PayPsdViewActivity.class)); - break; - case 7: - startActivity(new Intent(MainActivity.this, ProgressBarActivity.class)); - break; - case 8: - startActivity(new Intent(MainActivity.this, AnimationViewActivity.class)); - break; - case 9: - startActivity(new Intent(MainActivity.this, DragBallActivity.class)); - break; - case 10: - startActivity(new Intent(MainActivity.this, BannerActivity.class)); - break; - case 11: - startActivity(new Intent(MainActivity.this, HoverItemActivity.class)); - break; - case 12: - startActivity(new Intent(MainActivity.this, RevealAnimationActivity.class)); - break; - case 13: - startActivity(new Intent(MainActivity.this, AliPayHomeActivity.class)); - break; - default: - - } - } -} diff --git a/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.kt b/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.kt new file mode 100644 index 0000000..5099d37 --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.kt @@ -0,0 +1,83 @@ +package com.allen.androidcustomview.activity + +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.LinearLayoutManager +import android.view.View +import android.widget.Toast +import com.allen.androidcustomview.R +import com.allen.androidcustomview.adapter.MainAdapter +import com.allen.androidcustomview.bean.TypeBean +import com.allen.androidcustomview.helper.DragViewHelper +import com.allen.androidcustomview.tagview.TagActivity +import com.allen.androidcustomview.widget.SuperDividerItemDecoration +import com.chad.library.adapter.base.BaseQuickAdapter +import kotlinx.android.synthetic.main.activity_main.* +import java.util.* + + +class MainActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickListener { + + private var adapter: MainAdapter? = null + + private val typeBeans = ArrayList() + + private val data: List + get() { + typeBeans.add(TypeBean("气泡漂浮动画", 0)) + typeBeans.add(TypeBean("波浪动画--贝塞尔曲线实现", 1)) + typeBeans.add(TypeBean("波浪动画--正余弦函数实现", 2)) + typeBeans.add(TypeBean("水波(雷达)扩散效果", 3)) + typeBeans.add(TypeBean("RecyclerView实现另类的Tag标签", 4)) + typeBeans.add(TypeBean("按钮自定义动画", 5)) + typeBeans.add(TypeBean("自定义支付密码输入框", 6)) + typeBeans.add(TypeBean("自定义进度条", 7)) + typeBeans.add(TypeBean("使用的带动画的view", 8)) + typeBeans.add(TypeBean("粘性小球", 9)) + typeBeans.add(TypeBean("banner", 10)) + typeBeans.add(TypeBean("吸顶效果--一行代码实现", 11)) + typeBeans.add(TypeBean("揭露动画", 12)) + typeBeans.add(TypeBean("支付宝首页效果", 13)) + return typeBeans + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + adapter = MainAdapter(data) + adapter!!.onItemClickListener = this + recycler_view.layoutManager = LinearLayoutManager(this) + recycler_view.addItemDecoration(SuperDividerItemDecoration.Builder(this) + .build()) + recycler_view.adapter = adapter + + DragViewHelper.addDragView(this, + root_view, + "网络图片地址", + defaultImgResId = R.mipmap.ic_camera_3x, + onClick = { + Toast.makeText(this, "点击事件", Toast.LENGTH_SHORT).show() + }) + } + + override fun onItemClick(adapter: BaseQuickAdapter<*, *>, view: View, position: Int) { + when (typeBeans[position].type) { + 0 -> startActivity(Intent(this@MainActivity, BubbleViewActivity::class.java)) + 1 -> startActivity(Intent(this@MainActivity, WaveByBezierActivity::class.java)) + 2 -> startActivity(Intent(this@MainActivity, WaveBySinCosActivity::class.java)) + 3 -> startActivity(Intent(this@MainActivity, RadarActivity::class.java)) + 4 -> startActivity(Intent(this@MainActivity, TagActivity::class.java)) + 5 -> startActivity(Intent(this@MainActivity, AnimationBtnActivity::class.java)) + 6 -> startActivity(Intent(this@MainActivity, PayPsdViewActivity::class.java)) + 7 -> startActivity(Intent(this@MainActivity, ProgressBarActivity::class.java)) + 8 -> startActivity(Intent(this@MainActivity, AnimationViewActivity::class.java)) + 9 -> startActivity(Intent(this@MainActivity, DragBallActivity::class.java)) + 10 -> startActivity(Intent(this@MainActivity, BannerActivity::class.java)) + 11 -> startActivity(Intent(this@MainActivity, HoverItemActivity::class.java)) + 12 -> startActivity(Intent(this@MainActivity, RevealAnimationActivity::class.java)) + 13 -> startActivity(Intent(this@MainActivity, AliPayHomeActivity::class.java)) + } + } +} diff --git a/app/src/main/java/com/allen/androidcustomview/helper/DragViewHelper.kt b/app/src/main/java/com/allen/androidcustomview/helper/DragViewHelper.kt new file mode 100644 index 0000000..6022b9d --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/helper/DragViewHelper.kt @@ -0,0 +1,69 @@ +package com.allen.androidcustomview.helper + +import android.annotation.SuppressLint +import android.content.Context +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.RelativeLayout +import com.allen.androidcustomview.R +import com.allen.androidcustomview.listener.OnDragTouchListener +import com.allen.androidcustomview.utils.DisplayUtils + +/** + *
+ *      @author : Allen
+ *      e-mail  : lygttpod@163.com
+ *      date    : 2019/03/14
+ *      desc    : 拖拽view辅助类
+ * 
+ */ +object DragViewHelper { + + @SuppressLint("ClickableViewAccessibility") + fun addDragView(context: Context, + parent: ViewGroup, + imgUrl: String, + defaultImgResId: Int = R.mipmap.ic_launcher, + dragViewSize: Float = 60f, + dragViewOriginalMarginRight: Float = 20f, + dragViewOriginalMarginBottom: Float = 20f, + autoPullToBorder: Boolean = true, + onClick: (() -> Unit)? = null): ImageView { + val dragView = ImageView(context) + parent.post { + val onDragTouchListener = OnDragTouchListener() + onDragTouchListener.clickListener = { + onClick?.invoke() + } + onDragTouchListener.mMaxWidth = parent.width + onDragTouchListener.mMaxHeight = parent.height + onDragTouchListener.mBorderMargin = DisplayUtils.dip2px(context, 15f).toFloat() + onDragTouchListener.mIsAutoToBorder = autoPullToBorder + dragView.scaleType = ImageView.ScaleType.CENTER_CROP + dragView.setOnTouchListener(onDragTouchListener) + val layoutParams = RelativeLayout.LayoutParams(DisplayUtils.dip2px(context, dragViewSize), DisplayUtils.dip2px(context, dragViewSize)) + layoutParams.leftMargin = parent.width - DisplayUtils.dip2px(context, dragViewSize + dragViewOriginalMarginRight) + layoutParams.topMargin = parent.height - DisplayUtils.dip2px(context, dragViewSize + dragViewOriginalMarginBottom) +// GlideApp.with(context).load(imgUrl).into(dragView) + dragView.setBackgroundResource(defaultImgResId) + parent.addView(dragView, layoutParams) + } + + return dragView + } + + fun removeDragView(parent: ViewGroup, view: View?) { + if (view != null) { + parent.removeView(view) + } + } + + fun updateDragView(context: Context, dragView: ImageView?, imgUrl: String) { + if (dragView != null) { +// GlideApp.with(context).load(imgUrl).into(dragView) + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/listener/OnDragTouchListener.kt b/app/src/main/java/com/allen/androidcustomview/listener/OnDragTouchListener.kt new file mode 100644 index 0000000..246553f --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/listener/OnDragTouchListener.kt @@ -0,0 +1,184 @@ +package com.allen.androidcustomview.listener + +import android.animation.Animator +import android.animation.AnimatorSet +import android.animation.ValueAnimator +import android.annotation.SuppressLint +import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import android.view.animation.DecelerateInterpolator + +/** + *
+ *      @author : Allen
+ *      e-mail  : lygttpod@163.com
+ *      date    : 2019/03/14
+ *      desc    :
+ * 
+ */ +class OnDragTouchListener : View.OnTouchListener { + + //手指按下时的初始位置 + private var mOriginalX: Float = 0f + private var mOriginalY: Float = 0f + //记录手指与view的左上角的距离 + private var mDistanceX: Float = 0f + private var mDistanceY: Float = 0f + //拖拽view的上下左右距离 + private var left: Int = 0 + private var top: Int = 0 + private var right: Int = 0 + private var bottom: Int = 0 + //最小的拖拽距离,小于这个值认为不是拖拽,区分是点击还是拖拽 + private var minDragDistance = 10f + private var mLayoutParams: ViewGroup.MarginLayoutParams? = null + + //可拖拽屏幕区域宽高 + var mMaxWidth: Int = 0 + var mMaxHeight: Int = 0 + + //点击事件 + var clickListener: (() -> Unit)? = null + + //标记是否自动吸附到边缘 + var mIsAutoToBorder = true + //吸附在边缘时候距离边界的距离 + var mBorderMargin = 0f + + @SuppressLint("ClickableViewAccessibility") + override fun onTouch(view: View, event: MotionEvent): Boolean { + when (event.action) { + MotionEvent.ACTION_DOWN -> { + view.parent.requestDisallowInterceptTouchEvent(true) + + mLayoutParams = mLayoutParams ?: view.layoutParams as ViewGroup.MarginLayoutParams + + mOriginalX = event.rawX + mOriginalY = event.rawY + + mDistanceX = event.rawX - view.left + mDistanceY = event.rawY - view.top + } + + MotionEvent.ACTION_MOVE -> { + left = (event.rawX - mDistanceX).toInt() + top = (event.rawY - mDistanceY).toInt() + right = left + view.width + bottom = top + view.height + if (left < 0) { + left = 0 + right = left + view.width + } + if (top < 0) { + top = 0 + bottom = top + view.height + } + if (right > mMaxWidth) { + right = mMaxWidth + left = right - view.width + } + if (bottom > mMaxHeight) { + bottom = mMaxHeight + top = bottom - view.height + } + + //如果其他view刷新导致重绘会调用layout方法,导致位置一闪一闪的,所有要用layoutParams设置位置 + //view.layout(left, top, right, bottom) + mLayoutParams?.setMargins(left, top, 0, 0) + view.layoutParams = mLayoutParams + } + MotionEvent.ACTION_UP -> { + //如果移动距离过小,则判定为点击 + if (Math.abs(event.rawX - mOriginalX) < minDragDistance && Math.abs(event.rawY - mOriginalY) < minDragDistance) { + clickListener?.invoke() + } else { + setAutoToBorder(view) + } + view.parent.requestDisallowInterceptTouchEvent(false) + } + } + return true + } + + + /** + * 开启自动拖拽 + * + * @param v 拉动控件 + */ + private fun setAutoToBorder(v: View) { + if (!mIsAutoToBorder) return + setAnimation(v) + } + + private fun setAnimation(v: View) { + val animatorSet = AnimatorSet() + if (getTopOrBottomAnimation(v) == null) { + animatorSet.play(getLeftOrRightAnimation(v)) + } else { + animatorSet.play(getLeftOrRightAnimation(v)).with(getTopOrBottomAnimation(v)) + } + animatorSet.duration = 300 + animatorSet.start() + } + + + /** + * 获取吸附在左右边界的动画 + */ + private fun getLeftOrRightAnimation(v: View): Animator { + //当用户拖拽完后,让控件回到最近的边缘 + var leftAndRightEnd = mBorderMargin + //吸附在右边边界处 + if (left + v.width / 2 >= mMaxWidth / 2) { + leftAndRightEnd = (mMaxWidth - v.width - mBorderMargin) + } + val animator = ValueAnimator.ofFloat(left.toFloat(), leftAndRightEnd) + animator.interpolator = DecelerateInterpolator() + animator.addUpdateListener { animation -> + val leftMargin = (animation.animatedValue as Float).toInt() + mLayoutParams?.leftMargin = leftMargin + v.layoutParams = mLayoutParams + } + return animator + } + + /** + * 获取吸附在顶部或者底部的动画 + */ + private fun getTopOrBottomAnimation(v: View): Animator? { + //吸附在上下边界处 + var topOrBottomEnd: Float + //吸附在下边界处 + return when { + //吸附到距离底部mBorderMargin的距离的位置 + top + v.height >= mMaxHeight - mBorderMargin -> { + topOrBottomEnd = mMaxHeight - v.height - mBorderMargin + createTopOrBottomAnimation(v, topOrBottomEnd) + } + //吸附到距离顶部mBorderMargin的距离的位置 + top <= mBorderMargin -> { + topOrBottomEnd = mBorderMargin + createTopOrBottomAnimation(v, topOrBottomEnd) + } + else -> null + } + } + + /** + * 创建底吸附到底部或顶部的动画 + */ + private fun createTopOrBottomAnimation(view: View, end: Float): Animator? { + val animator = ValueAnimator.ofFloat(top.toFloat(), end) + animator.interpolator = DecelerateInterpolator() + animator.addUpdateListener { animation -> + val topMargin = (animation.animatedValue as Float).toInt() + mLayoutParams?.topMargin = topMargin + view.layoutParams = mLayoutParams + } + return animator + } + + +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index b4d12f4..0df5fab 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,6 @@ - @@ -8,5 +9,5 @@ android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /> - + diff --git a/app/src/main/res/mipmap-xxhdpi/ic_camera_3x.png b/app/src/main/res/mipmap-xxhdpi/ic_camera_3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e5d04a117e20fd20965154031dcb439f99a29628 GIT binary patch literal 3885 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 Date: Thu, 4 Apr 2019 17:44:14 +0800 Subject: [PATCH 02/17] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8F=AF=E6=8B=96?= =?UTF-8?q?=E6=8B=BDview=E8=87=AA=E5=8A=A8=E5=90=B8=E8=BE=B9=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../listener/OnDragTouchListener.kt | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/allen/androidcustomview/listener/OnDragTouchListener.kt b/app/src/main/java/com/allen/androidcustomview/listener/OnDragTouchListener.kt index 246553f..587a745 100644 --- a/app/src/main/java/com/allen/androidcustomview/listener/OnDragTouchListener.kt +++ b/app/src/main/java/com/allen/androidcustomview/listener/OnDragTouchListener.kt @@ -3,7 +3,6 @@ package com.allen.androidcustomview.listener import android.animation.Animator import android.animation.AnimatorSet import android.animation.ValueAnimator -import android.annotation.SuppressLint import android.view.MotionEvent import android.view.View import android.view.ViewGroup @@ -46,7 +45,11 @@ class OnDragTouchListener : View.OnTouchListener { //吸附在边缘时候距离边界的距离 var mBorderMargin = 0f - @SuppressLint("ClickableViewAccessibility") + var mBorderMarginLeft = -1f + var mBorderMarginRight = -1f + var mBorderMarginTop = -1f + var mBorderMarginBottom = -1f + override fun onTouch(view: View, event: MotionEvent): Boolean { when (event.action) { MotionEvent.ACTION_DOWN -> { @@ -96,6 +99,8 @@ class OnDragTouchListener : View.OnTouchListener { setAutoToBorder(view) } view.parent.requestDisallowInterceptTouchEvent(false) + //调取performClick()方法消除警告OnDragTouchListener#onTouch should call View#performClick when a click is detected more... + view.performClick() } } return true @@ -129,12 +134,12 @@ class OnDragTouchListener : View.OnTouchListener { */ private fun getLeftOrRightAnimation(v: View): Animator { //当用户拖拽完后,让控件回到最近的边缘 - var leftAndRightEnd = mBorderMargin + var leftOrRightEnd = getBorderMargin(mBorderMarginLeft) //吸附在右边边界处 if (left + v.width / 2 >= mMaxWidth / 2) { - leftAndRightEnd = (mMaxWidth - v.width - mBorderMargin) + leftOrRightEnd = (mMaxWidth - v.width - getBorderMargin(mBorderMarginRight)) } - val animator = ValueAnimator.ofFloat(left.toFloat(), leftAndRightEnd) + val animator = ValueAnimator.ofFloat(left.toFloat(), leftOrRightEnd) animator.interpolator = DecelerateInterpolator() animator.addUpdateListener { animation -> val leftMargin = (animation.animatedValue as Float).toInt() @@ -154,18 +159,19 @@ class OnDragTouchListener : View.OnTouchListener { return when { //吸附到距离底部mBorderMargin的距离的位置 top + v.height >= mMaxHeight - mBorderMargin -> { - topOrBottomEnd = mMaxHeight - v.height - mBorderMargin + topOrBottomEnd = mMaxHeight - v.height - getBorderMargin(mBorderMarginBottom) createTopOrBottomAnimation(v, topOrBottomEnd) } //吸附到距离顶部mBorderMargin的距离的位置 top <= mBorderMargin -> { - topOrBottomEnd = mBorderMargin + topOrBottomEnd = getBorderMargin(mBorderMarginTop) createTopOrBottomAnimation(v, topOrBottomEnd) } else -> null } } + /** * 创建底吸附到底部或顶部的动画 */ @@ -180,5 +186,11 @@ class OnDragTouchListener : View.OnTouchListener { return animator } + /** + * 获取吸附的边界值 + */ + private fun getBorderMargin(margin: Float): Float { + return if (margin != -1f) margin else mBorderMargin + } } \ No newline at end of file From 20a510194044be46125a215752249dde76a917f1 Mon Sep 17 00:00:00 2001 From: Allen Date: Wed, 15 May 2019 20:36:17 +0800 Subject: [PATCH 03/17] =?UTF-8?q?=E6=96=B0=E5=A2=9ERecyclerView=E7=9A=84it?= =?UTF-8?q?em=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/misc.xml | 2 +- app/src/main/AndroidManifest.xml | 1 + .../activity/MainActivity.kt | 2 + .../activity/RecyclerViewItemAnimActivity.kt | 98 +++ .../adapter/ItemAnimAdapter.kt | 19 + .../anim/BaseItemAnimation.java | 730 ++++++++++++++++++ .../anim/RotateXItemAnimation.kt | 41 + .../anim/RotateYItemAnimation.kt | 36 + .../anim/ScaleItemAnimation.kt | 43 ++ .../anim/SlideItemAnimation.kt | 38 + .../anim/SuperItemAnimation.kt | 69 ++ .../widget/status/StatusBuilder.kt | 50 ++ .../widget/status/StatusLayout.kt | 27 + .../widget/status/StatusManager.kt | 29 + app/src/main/res/drawable/shape_item_bg.xml | 8 + .../main/res/layout/activity_pay_psd_view.xml | 7 +- .../activity_recycler_view_item_anim.xml | 102 +++ app/src/main/res/layout/item_danmu_layout.xml | 28 + 18 files changed, 1326 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/allen/androidcustomview/activity/RecyclerViewItemAnimActivity.kt create mode 100644 app/src/main/java/com/allen/androidcustomview/adapter/ItemAnimAdapter.kt create mode 100644 app/src/main/java/com/allen/androidcustomview/anim/BaseItemAnimation.java create mode 100644 app/src/main/java/com/allen/androidcustomview/anim/RotateXItemAnimation.kt create mode 100644 app/src/main/java/com/allen/androidcustomview/anim/RotateYItemAnimation.kt create mode 100644 app/src/main/java/com/allen/androidcustomview/anim/ScaleItemAnimation.kt create mode 100644 app/src/main/java/com/allen/androidcustomview/anim/SlideItemAnimation.kt create mode 100644 app/src/main/java/com/allen/androidcustomview/anim/SuperItemAnimation.kt create mode 100644 app/src/main/java/com/allen/androidcustomview/widget/status/StatusBuilder.kt create mode 100644 app/src/main/java/com/allen/androidcustomview/widget/status/StatusLayout.kt create mode 100644 app/src/main/java/com/allen/androidcustomview/widget/status/StatusManager.kt create mode 100644 app/src/main/res/drawable/shape_item_bg.xml create mode 100644 app/src/main/res/layout/activity_recycler_view_item_anim.xml create mode 100644 app/src/main/res/layout/item_danmu_layout.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 4dd9348..ba4f0e0 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -35,7 +35,7 @@
- + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1b5ec68..4ea93db 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -36,6 +36,7 @@ + \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.kt b/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.kt index 5099d37..ec7f83c 100644 --- a/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.kt +++ b/app/src/main/java/com/allen/androidcustomview/activity/MainActivity.kt @@ -39,6 +39,7 @@ class MainActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickListener { typeBeans.add(TypeBean("吸顶效果--一行代码实现", 11)) typeBeans.add(TypeBean("揭露动画", 12)) typeBeans.add(TypeBean("支付宝首页效果", 13)) + typeBeans.add(TypeBean("RecyclerView的item动画", 14)) return typeBeans } @@ -78,6 +79,7 @@ class MainActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickListener { 11 -> startActivity(Intent(this@MainActivity, HoverItemActivity::class.java)) 12 -> startActivity(Intent(this@MainActivity, RevealAnimationActivity::class.java)) 13 -> startActivity(Intent(this@MainActivity, AliPayHomeActivity::class.java)) + 14 -> startActivity(Intent(this@MainActivity, RecyclerViewItemAnimActivity::class.java)) } } } diff --git a/app/src/main/java/com/allen/androidcustomview/activity/RecyclerViewItemAnimActivity.kt b/app/src/main/java/com/allen/androidcustomview/activity/RecyclerViewItemAnimActivity.kt new file mode 100644 index 0000000..bda6db8 --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/activity/RecyclerViewItemAnimActivity.kt @@ -0,0 +1,98 @@ +package com.allen.androidcustomview.activity + +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.DefaultItemAnimator +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import com.allen.androidcustomview.R +import com.allen.androidcustomview.adapter.ItemAnimAdapter +import com.allen.androidcustomview.anim.RotateXItemAnimation +import com.allen.androidcustomview.anim.RotateYItemAnimation +import com.allen.androidcustomview.anim.ScaleItemAnimation +import com.allen.androidcustomview.anim.SlideItemAnimation +import kotlinx.android.synthetic.main.activity_recycler_view_item_anim.* +import kotlin.random.Random + +/** + *
+ *      @author : Allen
+ *      e-mail  : lygttpod@163.com
+ *      date    : 2019/05/11
+ *      desc    :
+ * 
+ */ +class RecyclerViewItemAnimActivity : AppCompatActivity() { + + var adapter: ItemAnimAdapter? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_recycler_view_item_anim) + initListener() + initView() + initData() + } + + private fun initView() { + adapter = ItemAnimAdapter(arrayListOf()) + recycler_view.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false) + recycler_view.adapter = adapter + recycler_view.itemAnimator = DefaultItemAnimator() + } + + private fun initListener() { + normal_btn.setOnClickListener { setReverseLayout(false) } + reverse_btn.setOnClickListener { setReverseLayout(true) } + scale_btn.setOnClickListener { setItemAnimation(ScaleItemAnimation()) } + slide_btn.setOnClickListener { setItemAnimation(SlideItemAnimation()) } + rotate_x_btn.setOnClickListener { setItemAnimation(RotateXItemAnimation()) } + rotate_y_btn.setOnClickListener { setItemAnimation(RotateYItemAnimation()) } + + add_btn.setOnClickListener { + adapter?.addData(0, getItemData()) + (recycler_view.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(0, 0) + } + + remove_btn.setOnClickListener { + if (adapter?.data?.size ?: 0 > 0) { + adapter?.remove(0) + (recycler_view.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(0, 0) + } + } + } + + private fun setReverseLayout(reverseLayout: Boolean) { + recycler_view.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, reverseLayout) + adapter?.data?.clear() + adapter?.notifyDataSetChanged() + } + + private fun setItemAnimation(itemAnimation: RecyclerView.ItemAnimator) { + adapter?.data?.clear() + adapter?.notifyDataSetChanged() + recycler_view.itemAnimator = itemAnimation + } + + private fun initData(): ArrayList { + val list: ArrayList = arrayListOf() + list.add("人生如戏,全靠演技") + list.add("年轻就是资本") + list.add("我的一颗眼泪掉进了海洋,当我找到它的那一天就是我停止爱你的那一天") + list.add("你若一直在,我便一直爱") + list.add("路,跪着也要走完") + list.add("美丽的彩虹就像一座七彩的桥一样高挂在雨后的天空") + list.add("留情不留命,留命伤感情") + list.add("宽容就是在别人和自己意见不一致时也不要勉强") + list.add("那些曾经以为念念不忘的事情,就在我们念念不忘的过程里,被我们遗忘了") + list.add("朝花夕拾捡的是枯萎") + list.add("黄绢幼妇,其土老人") + list.add("要有最朴素的生活,与最遥远的梦想,即使明日天寒地冻,路远马亡") + list.add("不是路不平,而是你不行") + return list + } + + private fun getItemData(): String { + return initData()[Random.nextInt(100) / 10 + 1] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/adapter/ItemAnimAdapter.kt b/app/src/main/java/com/allen/androidcustomview/adapter/ItemAnimAdapter.kt new file mode 100644 index 0000000..b1a28ab --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/adapter/ItemAnimAdapter.kt @@ -0,0 +1,19 @@ +package com.allen.androidcustomview.adapter + +import com.allen.androidcustomview.R +import com.chad.library.adapter.base.BaseQuickAdapter +import com.chad.library.adapter.base.BaseViewHolder + +/** + *
+ *      @author : Allen
+ *      e-mail  :lygttpod@163.com
+ *      date    : 2019/05/11
+ *      desc    :
+ * 
+ */ +class ItemAnimAdapter(list: ArrayList) : BaseQuickAdapter(R.layout.item_danmu_layout, list) { + override fun convert(helper: BaseViewHolder, item: String) { + helper.setText(R.id.content_tv, item) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/anim/BaseItemAnimation.java b/app/src/main/java/com/allen/androidcustomview/anim/BaseItemAnimation.java new file mode 100644 index 0000000..2564076 --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/anim/BaseItemAnimation.java @@ -0,0 +1,730 @@ +package com.allen.androidcustomview.anim; + + +/** + *
+ *      @author : Allen
+ *      e-mail  : lygttpod@163.com
+ *      date    : 2019/05/11
+ *      desc    : 基于DefaultItemAnimator扩展的BaseItemAnimation,暴露常用方法
+ * 
+ */ + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.support.annotation.NonNull; +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.support.v7.widget.SimpleItemAnimator; +import android.view.View; +import android.view.ViewPropertyAnimator; + +import java.util.ArrayList; +import java.util.List; + +public abstract class BaseItemAnimation extends SimpleItemAnimator { + private static final boolean DEBUG = false; + + private static TimeInterpolator sDefaultInterpolator; + + private ArrayList mPendingRemovals = new ArrayList<>(); + private ArrayList mPendingAdditions = new ArrayList<>(); + private ArrayList mPendingMoves = new ArrayList<>(); + private ArrayList mPendingChanges = new ArrayList<>(); + + ArrayList> mAdditionsList = new ArrayList<>(); + ArrayList> mMovesList = new ArrayList<>(); + ArrayList> mChangesList = new ArrayList<>(); + + ArrayList mAddAnimations = new ArrayList<>(); + ArrayList mMoveAnimations = new ArrayList<>(); + ArrayList mRemoveAnimations = new ArrayList<>(); + ArrayList mChangeAnimations = new ArrayList<>(); + + private static class MoveInfo { + public ViewHolder holder; + public int fromX, fromY, toX, toY; + + MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) { + this.holder = holder; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + } + + private static class ChangeInfo { + public ViewHolder oldHolder, newHolder; + public int fromX, fromY, toX, toY; + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) { + this.oldHolder = oldHolder; + this.newHolder = newHolder; + } + + ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + this(oldHolder, newHolder); + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + + @Override + public String toString() { + return "ChangeInfo{" + + "oldHolder=" + oldHolder + + ", newHolder=" + newHolder + + ", fromX=" + fromX + + ", fromY=" + fromY + + ", toX=" + toX + + ", toY=" + toY + + '}'; + } + } + + @Override + public void runPendingAnimations() { + boolean removalsPending = !mPendingRemovals.isEmpty(); + boolean movesPending = !mPendingMoves.isEmpty(); + boolean changesPending = !mPendingChanges.isEmpty(); + boolean additionsPending = !mPendingAdditions.isEmpty(); + if (!removalsPending && !movesPending && !additionsPending && !changesPending) { + // nothing to animate + return; + } + // First, remove stuff + for (ViewHolder holder : mPendingRemovals) { + animateRemoveImpl(holder); + } + mPendingRemovals.clear(); + // Next, move stuff + if (movesPending) { + final ArrayList moves = new ArrayList<>(); + moves.addAll(mPendingMoves); + mMovesList.add(moves); + mPendingMoves.clear(); + Runnable mover = new Runnable() { + @Override + public void run() { + for (MoveInfo moveInfo : moves) { + animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, + moveInfo.toX, moveInfo.toY); + } + moves.clear(); + mMovesList.remove(moves); + } + }; + if (removalsPending) { + View view = moves.get(0).holder.itemView; + ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration()); + } else { + mover.run(); + } + } + // Next, change stuff, to run in parallel with move animations + if (changesPending) { + final ArrayList changes = new ArrayList<>(); + changes.addAll(mPendingChanges); + mChangesList.add(changes); + mPendingChanges.clear(); + Runnable changer = new Runnable() { + @Override + public void run() { + for (ChangeInfo change : changes) { + animateChangeImpl(change); + } + changes.clear(); + mChangesList.remove(changes); + } + }; + if (removalsPending) { + ViewHolder holder = changes.get(0).oldHolder; + ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration()); + } else { + changer.run(); + } + } + // Next, add stuff + if (additionsPending) { + final ArrayList additions = new ArrayList<>(); + additions.addAll(mPendingAdditions); + mAdditionsList.add(additions); + mPendingAdditions.clear(); + Runnable adder = new Runnable() { + @Override + public void run() { + for (ViewHolder holder : additions) { + animateAddImpl(holder); + } + additions.clear(); + mAdditionsList.remove(additions); + } + }; + if (removalsPending || movesPending || changesPending) { + long removeDuration = removalsPending ? getRemoveDuration() : 0; + long moveDuration = movesPending ? getMoveDuration() : 0; + long changeDuration = changesPending ? getChangeDuration() : 0; + long totalDelay = removeDuration + Math.max(moveDuration, changeDuration); + View view = additions.get(0).itemView; + ViewCompat.postOnAnimationDelayed(view, adder, totalDelay); + } else { + adder.run(); + } + } + } + + @Override + public boolean animateRemove(final ViewHolder holder) { + resetAnimation(holder); + mPendingRemovals.add(holder); + return true; + } + + private void animateRemoveImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimator animation = view.animate(); + mRemoveAnimations.add(holder); + setRemoveAnimation(holder, animation); + // TODO: 2019-05-15 animation.setDuration(getRemoveDuration()).alpha(0).setListener( + animation.setDuration(getRemoveDuration()).setListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchRemoveStarting(holder); + } + + @Override + public void onAnimationEnd(Animator animator) { + animation.setListener(null); + // TODO: 2019-05-15 view.setAlpha(1); + setRemoveAnimationEnd(holder); + dispatchRemoveFinished(holder); + mRemoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateAdd(final ViewHolder holder) { + resetAnimation(holder); + // TODO: 2019-05-15 holder.itemView.setAlpha(0); + setAddItemAnimationInit(holder); + mPendingAdditions.add(holder); + return true; + } + + void animateAddImpl(final ViewHolder holder) { + final View view = holder.itemView; + final ViewPropertyAnimator animation = view.animate(); + mAddAnimations.add(holder); + setAddItemAnimation(holder, animation); + // TODO: 2019-05-15 animation.alpha(1).setDuration(getAddDuration()).setListener(new AnimatorListenerAdapter() { + animation.setDuration(getAddDuration()).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchAddStarting(holder); + } + + @Override + public void onAnimationEnd(Animator animator) { + animation.setListener(null); + dispatchAddFinished(holder); + mAddAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + + @Override + public void onAnimationCancel(Animator animation) { + // TODO: 2019-05-15 view.setAlpha(1); + setAddItemAnimationCancel(holder); + } + }).start(); + } + + @Override + public boolean animateMove(final ViewHolder holder, int fromX, int fromY, + int toX, int toY) { + final View view = holder.itemView; + fromX += (int) holder.itemView.getTranslationX(); + fromY += (int) holder.itemView.getTranslationY(); + resetAnimation(holder); + int deltaX = toX - fromX; + int deltaY = toY - fromY; + if (deltaX == 0 && deltaY == 0) { + dispatchMoveFinished(holder); + return false; + } + if (deltaX != 0) { + view.setTranslationX(-deltaX); + } + if (deltaY != 0) { + view.setTranslationY(-deltaY); + } + mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY)); + return true; + } + + void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) { + final View view = holder.itemView; + final int deltaX = toX - fromX; + final int deltaY = toY - fromY; + if (deltaX != 0) { + view.animate().translationX(0); + } + if (deltaY != 0) { + view.animate().translationY(0); + } + // TODO: make EndActions end listeners instead, since end actions aren't called when + // vpas are canceled (and can't end them. why?) + // need listener functionality in VPACompat for this. Ick. + final ViewPropertyAnimator animation = view.animate(); + mMoveAnimations.add(holder); + animation.setDuration(getMoveDuration()).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchMoveStarting(holder); + } + + @Override + public void onAnimationCancel(Animator animator) { + if (deltaX != 0) { + view.setTranslationX(0); + } + if (deltaY != 0) { + view.setTranslationY(0); + } + } + + @Override + public void onAnimationEnd(Animator animator) { + animation.setListener(null); + dispatchMoveFinished(holder); + mMoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + if (oldHolder == newHolder) { + // Don't know how to run change animations when the same view holder is re-used. + // run a move animation to handle position changes. + return animateMove(oldHolder, fromX, fromY, toX, toY); + } + final float prevTranslationX = oldHolder.itemView.getTranslationX(); + final float prevTranslationY = oldHolder.itemView.getTranslationY(); + final float prevAlpha = oldHolder.itemView.getAlpha(); + resetAnimation(oldHolder); + int deltaX = (int) (toX - fromX - prevTranslationX); + int deltaY = (int) (toY - fromY - prevTranslationY); + // recover prev translation state after ending animation + oldHolder.itemView.setTranslationX(prevTranslationX); + oldHolder.itemView.setTranslationY(prevTranslationY); + oldHolder.itemView.setAlpha(prevAlpha); + if (newHolder != null) { + // carry over translation values + resetAnimation(newHolder); + newHolder.itemView.setTranslationX(-deltaX); + newHolder.itemView.setTranslationY(-deltaY); + // TODO: 2019-05-15 newHolder.itemView.setAlpha(0); + setNewChangeAnimationInit(newHolder); + } + mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); + return true; + } + + void animateChangeImpl(final ChangeInfo changeInfo) { + final ViewHolder holder = changeInfo.oldHolder; + final View view = holder == null ? null : holder.itemView; + final ViewHolder newHolder = changeInfo.newHolder; + final View newView = newHolder != null ? newHolder.itemView : null; + if (view != null) { + final ViewPropertyAnimator oldViewAnim = view.animate().setDuration( + getChangeDuration()); + mChangeAnimations.add(changeInfo.oldHolder); + oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX); + oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY); + setOldChangeAnimation(holder, oldViewAnim); + // TODO: 2019-05-15 oldViewAnim.alpha(0).setListener(new AnimatorListenerAdapter() { + oldViewAnim.setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchChangeStarting(changeInfo.oldHolder, true); + } + + @Override + public void onAnimationEnd(Animator animator) { + oldViewAnim.setListener(null); + // TODO: 2019-05-15 view.setAlpha(1); + setOldChangeAnimationEnd(holder); + view.setTranslationX(0); + view.setTranslationY(0); + dispatchChangeFinished(changeInfo.oldHolder, true); + mChangeAnimations.remove(changeInfo.oldHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + if (newView != null) { + final ViewPropertyAnimator newViewAnimation = newView.animate(); + mChangeAnimations.add(changeInfo.newHolder); + setNewChangeAnimation(newHolder, newViewAnimation); + // TODO: 2019-05-15 newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).alpha(1) + newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchChangeStarting(changeInfo.newHolder, false); + } + + @Override + public void onAnimationEnd(Animator animator) { + newViewAnimation.setListener(null); + // TODO: 2019-05-15 newView.setAlpha(1); + setNewChangeAnimationEnd(newHolder); + newView.setTranslationX(0); + newView.setTranslationY(0); + dispatchChangeFinished(changeInfo.newHolder, false); + mChangeAnimations.remove(changeInfo.newHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + } + + private void endChangeAnimation(List infoList, ViewHolder item) { + for (int i = infoList.size() - 1; i >= 0; i--) { + ChangeInfo changeInfo = infoList.get(i); + if (endChangeAnimationIfNecessary(changeInfo, item)) { + if (changeInfo.oldHolder == null && changeInfo.newHolder == null) { + infoList.remove(changeInfo); + } + } + } + } + + private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) { + if (changeInfo.oldHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder); + } + if (changeInfo.newHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder); + } + } + + private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) { + boolean oldItem = false; + if (changeInfo.newHolder == item) { + changeInfo.newHolder = null; + } else if (changeInfo.oldHolder == item) { + changeInfo.oldHolder = null; + oldItem = true; + } else { + return false; + } + // TODO: 2019-05-15 item.itemView.setAlpha(1); + setNewChangeAnimationEnd(item); + item.itemView.setTranslationX(0); + item.itemView.setTranslationY(0); + dispatchChangeFinished(item, oldItem); + return true; + } + + @Override + public void endAnimation(ViewHolder item) { + final View view = item.itemView; + // this will trigger end callback which should set properties to their target values. + view.animate().cancel(); + // TODO if some other animations are chained to end, how do we cancel them as well? + for (int i = mPendingMoves.size() - 1; i >= 0; i--) { + MoveInfo moveInfo = mPendingMoves.get(i); + if (moveInfo.holder == item) { + view.setTranslationY(0); + view.setTranslationX(0); + dispatchMoveFinished(item); + mPendingMoves.remove(i); + } + } + endChangeAnimation(mPendingChanges, item); + if (mPendingRemovals.remove(item)) { + // TODO: 2019-05-15 view.setAlpha(1); + setRemoveAnimationEnd(item); + dispatchRemoveFinished(item); + } + if (mPendingAdditions.remove(item)) { + // TODO: 2019-05-15 view.setAlpha(1); + setAddItemAnimationCancel(item); + dispatchAddFinished(item); + } + + for (int i = mChangesList.size() - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + endChangeAnimation(changes, item); + if (changes.isEmpty()) { + mChangesList.remove(i); + } + } + for (int i = mMovesList.size() - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + for (int j = moves.size() - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + if (moveInfo.holder == item) { + view.setTranslationY(0); + view.setTranslationX(0); + dispatchMoveFinished(item); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(i); + } + break; + } + } + } + for (int i = mAdditionsList.size() - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + if (additions.remove(item)) { + // TODO: 2019-05-15 view.setAlpha(1); + setAddItemAnimationCancel(item); + dispatchAddFinished(item); + if (additions.isEmpty()) { + mAdditionsList.remove(i); + } + } + } + + // animations should be ended by the cancel above. + //noinspection PointlessBooleanExpression,ConstantConditions + if (mRemoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mRemoveAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mAddAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mAddAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mChangeAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mChangeAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mMoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mMoveAnimations list"); + } + dispatchFinishedWhenDone(); + } + + private void resetAnimation(ViewHolder holder) { + if (sDefaultInterpolator == null) { + sDefaultInterpolator = new ValueAnimator().getInterpolator(); + } + holder.itemView.animate().setInterpolator(sDefaultInterpolator); + endAnimation(holder); + } + + @Override + public boolean isRunning() { + return (!mPendingAdditions.isEmpty() + || !mPendingChanges.isEmpty() + || !mPendingMoves.isEmpty() + || !mPendingRemovals.isEmpty() + || !mMoveAnimations.isEmpty() + || !mRemoveAnimations.isEmpty() + || !mAddAnimations.isEmpty() + || !mChangeAnimations.isEmpty() + || !mMovesList.isEmpty() + || !mAdditionsList.isEmpty() + || !mChangesList.isEmpty()); + } + + /** + * Check the state of currently pending and running animations. If there are none + * pending/running, call {@link #dispatchAnimationsFinished()} to notify any + * listeners. + */ + void dispatchFinishedWhenDone() { + if (!isRunning()) { + dispatchAnimationsFinished(); + } + } + + @Override + public void endAnimations() { + int count = mPendingMoves.size(); + for (int i = count - 1; i >= 0; i--) { + MoveInfo item = mPendingMoves.get(i); + View view = item.holder.itemView; + view.setTranslationY(0); + view.setTranslationX(0); + dispatchMoveFinished(item.holder); + mPendingMoves.remove(i); + } + count = mPendingRemovals.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingRemovals.get(i); + dispatchRemoveFinished(item); + mPendingRemovals.remove(i); + } + count = mPendingAdditions.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingAdditions.get(i); + // TODO: 2019-05-15 view.setAlpha(1); + setAddItemAnimationCancel(item); + dispatchAddFinished(item); + mPendingAdditions.remove(i); + } + count = mPendingChanges.size(); + for (int i = count - 1; i >= 0; i--) { + endChangeAnimationIfNecessary(mPendingChanges.get(i)); + } + mPendingChanges.clear(); + if (!isRunning()) { + return; + } + + int listCount = mMovesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + count = moves.size(); + for (int j = count - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + ViewHolder item = moveInfo.holder; + View view = item.itemView; + view.setTranslationY(0); + view.setTranslationX(0); + dispatchMoveFinished(moveInfo.holder); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(moves); + } + } + } + listCount = mAdditionsList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + count = additions.size(); + for (int j = count - 1; j >= 0; j--) { + ViewHolder item = additions.get(j); + // TODO: 2019-05-15 item.itemView.setAlpha(1); + setAddItemAnimationCancel(item); + dispatchAddFinished(item); + additions.remove(j); + if (additions.isEmpty()) { + mAdditionsList.remove(additions); + } + } + } + listCount = mChangesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + count = changes.size(); + for (int j = count - 1; j >= 0; j--) { + endChangeAnimationIfNecessary(changes.get(j)); + if (changes.isEmpty()) { + mChangesList.remove(changes); + } + } + } + + cancelAll(mRemoveAnimations); + cancelAll(mMoveAnimations); + cancelAll(mAddAnimations); + cancelAll(mChangeAnimations); + + dispatchAnimationsFinished(); + } + + void cancelAll(List viewHolders) { + for (int i = viewHolders.size() - 1; i >= 0; i--) { + viewHolders.get(i).itemView.animate().cancel(); + } + } + + /** + * {@inheritDoc} + *

+ * If the payload list is not empty, DefaultItemAnimator returns true. + * When this is the case: + *

    + *
  • If you override {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, both + * ViewHolder arguments will be the same instance. + *
  • + *
  • + * If you are not overriding {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, + * then DefaultItemAnimator will call {@link #animateMove(ViewHolder, int, int, int, int)} and + * run a move animation instead. + *
  • + *
+ */ + @Override + public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder, + @NonNull List payloads) { + return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads); + } + + + /** + * 设置item添加动画初始化状态(比如默认透明度为0) + * + * @param holder 添加的ViewHolder + */ + public abstract void setAddItemAnimationInit(ViewHolder holder); + + /** + * 设置item添加动画 + * + * @param holder 添加的ViewHolder + * @param animator 添加的ViewHolder对应动画对象 + */ + public abstract void setAddItemAnimation(ViewHolder holder, ViewPropertyAnimator animator); + + /** + * 设置取消添加item动画,还原状态以复用 + * + * @param holder 添加的ViewHolder + */ + public abstract void setAddItemAnimationCancel(ViewHolder holder); + + + /** + * 设置item移除动画 + * + * @param holder 添加的ViewHolder + * @param animator 添加的ViewHolder对应动画对象 + */ + public abstract void setRemoveAnimation(ViewHolder holder, ViewPropertyAnimator animator); + + + /** + * 设置结束移除item动画,还原状态以复用 + * + * @param holder 添加的ViewHolder + */ + public abstract void setRemoveAnimationEnd(ViewHolder holder); + + + public abstract void setOldChangeAnimation(ViewHolder holder, ViewPropertyAnimator animator); + + public abstract void setOldChangeAnimationEnd(ViewHolder holder); + + public abstract void setNewChangeAnimationInit(ViewHolder holder); + + public abstract void setNewChangeAnimation(ViewHolder holder, ViewPropertyAnimator animator); + + public abstract void setNewChangeAnimationEnd(ViewHolder holder); + + +} diff --git a/app/src/main/java/com/allen/androidcustomview/anim/RotateXItemAnimation.kt b/app/src/main/java/com/allen/androidcustomview/anim/RotateXItemAnimation.kt new file mode 100644 index 0000000..91a1d75 --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/anim/RotateXItemAnimation.kt @@ -0,0 +1,41 @@ +package com.allen.androidcustomview.anim + +import android.support.v7.widget.RecyclerView +import android.view.ViewPropertyAnimator + +/** + *
+ *      @author : Allen
+ *      e-mail  : lygttpod@163.com
+ *      date    : 2019/05/15
+ *      desc    :
+ * 
+ */ +class RotateXItemAnimation(animDuration: Long = 500) : SuperItemAnimation(animDuration) { + + init { + addDuration = animDuration + removeDuration = animDuration + } + + override fun setAddItemAnimInit(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.rotationX = -90f + } + + override fun setAddItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) { + animator?.rotationX(0f) + } + + override fun setAddItemAnimCancel(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.rotationX = 0f + } + + override fun setRemoveItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) { + animator?.rotationX(-90f) + } + + override fun setRemoveItemAnimEnd(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.rotationX = 0f + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/anim/RotateYItemAnimation.kt b/app/src/main/java/com/allen/androidcustomview/anim/RotateYItemAnimation.kt new file mode 100644 index 0000000..429400a --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/anim/RotateYItemAnimation.kt @@ -0,0 +1,36 @@ +package com.allen.androidcustomview.anim + +import android.support.v7.widget.RecyclerView +import android.view.ViewPropertyAnimator + +/** + *
+ *      @author : Allen
+ *      e-mail  : lygttpod@163.com
+ *      date    : 2019/05/15
+ *      desc    :
+ * 
+ */ +class RotateYItemAnimation(animDuration: Long = 500) : SuperItemAnimation(animDuration) { + + override fun setAddItemAnimInit(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.rotationY = -90f + } + + override fun setAddItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) { + animator?.rotationY(0f) + } + + override fun setAddItemAnimCancel(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.rotationY = 0f + } + + override fun setRemoveItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) { + animator?.rotationY(-90f) + } + + override fun setRemoveItemAnimEnd(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.rotationY = 0f + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/anim/ScaleItemAnimation.kt b/app/src/main/java/com/allen/androidcustomview/anim/ScaleItemAnimation.kt new file mode 100644 index 0000000..dcf47ef --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/anim/ScaleItemAnimation.kt @@ -0,0 +1,43 @@ +package com.allen.androidcustomview.anim + +import android.support.v7.widget.RecyclerView +import android.view.ViewPropertyAnimator + +/** + *
+ *      @author : Allen
+ *      e-mail  : lygttpod@163.com
+ *      date    : 2019/05/15
+ *      desc    :
+ * 
+ */ +class ScaleItemAnimation(animDuration: Long = 500) : SuperItemAnimation(animDuration) { + + override fun setAddItemAnimInit(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.scaleX = 0f + holder?.itemView?.scaleY = 0f + } + + override fun setAddItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) { + holder?.itemView?.pivotX = 0f + holder?.itemView?.pivotY = 0f + animator?.scaleX(1f)?.scaleY(1f) + } + + override fun setAddItemAnimCancel(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.scaleX = 1f + holder?.itemView?.scaleY = 1f + } + + override fun setRemoveItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) { + holder?.itemView?.pivotX = 1f + holder?.itemView?.pivotY = 1f + animator?.scaleX(0f)?.scaleY(0f) + } + + override fun setRemoveItemAnimEnd(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.scaleX = 1f + holder?.itemView?.scaleY = 1f + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/anim/SlideItemAnimation.kt b/app/src/main/java/com/allen/androidcustomview/anim/SlideItemAnimation.kt new file mode 100644 index 0000000..ee63641 --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/anim/SlideItemAnimation.kt @@ -0,0 +1,38 @@ +package com.allen.androidcustomview.anim + +import android.support.v7.widget.RecyclerView +import android.view.ViewPropertyAnimator + +/** + *
+ *      @author : Allen
+ *      e-mail  : lygttpod@163.com
+ *      date    : 2019/05/15
+ *      desc    :
+ * 
+ */ +class SlideItemAnimation(animDuration: Long = 500) : SuperItemAnimation(animDuration) { + + override fun setAddItemAnimInit(holder: RecyclerView.ViewHolder?) { + val with = holder?.itemView?.width ?: 0 + holder?.itemView?.translationX = -with.toFloat() + } + + override fun setAddItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) { + animator?.translationX(0f) + } + + override fun setAddItemAnimCancel(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.translationX = 0f + } + + override fun setRemoveItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) { + val with = holder?.itemView?.width ?: 0 + animator?.translationX(-with.toFloat()) + } + + override fun setRemoveItemAnimEnd(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.translationX = 0f + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/anim/SuperItemAnimation.kt b/app/src/main/java/com/allen/androidcustomview/anim/SuperItemAnimation.kt new file mode 100644 index 0000000..a177c98 --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/anim/SuperItemAnimation.kt @@ -0,0 +1,69 @@ +package com.allen.androidcustomview.anim + +import android.support.v7.widget.RecyclerView +import android.view.ViewPropertyAnimator + +/** + *
+ *      @author : Allen
+ *      e-mail  : lygttpod@163.com
+ *      date    : 2019/05/15
+ *      desc    :
+ * 
+ */ +abstract class SuperItemAnimation(animDuration: Long = 200) : BaseItemAnimation() { + + init { + addDuration = animDuration + removeDuration = animDuration + } + + abstract fun setAddItemAnimInit(holder: RecyclerView.ViewHolder?) + abstract fun setAddItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) + abstract fun setAddItemAnimCancel(holder: RecyclerView.ViewHolder?) + + abstract fun setRemoveItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) + abstract fun setRemoveItemAnimEnd(holder: RecyclerView.ViewHolder?) + + + override fun setAddItemAnimationInit(holder: RecyclerView.ViewHolder?) { + setAddItemAnimInit(holder) + } + + override fun setAddItemAnimation(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) { + setAddItemAnim(holder, animator) + } + + override fun setAddItemAnimationCancel(holder: RecyclerView.ViewHolder?) { + setAddItemAnimCancel(holder) + } + + override fun setRemoveAnimation(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) { + setRemoveItemAnim(holder, animator) + } + + override fun setRemoveAnimationEnd(holder: RecyclerView.ViewHolder?) { + setRemoveItemAnimEnd(holder) + } + + + override fun setOldChangeAnimation(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) { + animator?.alpha(0f) + } + + override fun setOldChangeAnimationEnd(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.alpha = 1f + } + + override fun setNewChangeAnimationInit(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.alpha = 0f + } + + override fun setNewChangeAnimation(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) { + animator?.alpha(1f) + } + + override fun setNewChangeAnimationEnd(holder: RecyclerView.ViewHolder?) { + holder?.itemView?.alpha = 1f + } +} \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/widget/status/StatusBuilder.kt b/app/src/main/java/com/allen/androidcustomview/widget/status/StatusBuilder.kt new file mode 100644 index 0000000..a036c92 --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/widget/status/StatusBuilder.kt @@ -0,0 +1,50 @@ +package com.allen.androidcustomview.widget.status + +import android.view.View + +/** + *
+ *      @author : Allen
+ *      e-mail  : lygttpod@163.com
+ *      date    : 2019/03/19
+ *      desc    :
+ * 
+ */ +class StatusBuilder { + + class Builder{ + + var contentView: View? = null + var errorView: View? = null + var emptyView: View? = null + var loadingView: View? = null + + fun setContentView(contentView: View):Builder { + this.contentView = contentView + return this + } + fun setContentView(layoutResId: Int):Builder { + this.contentView = contentView + return this + } + + fun setErrorView(errorView: View):Builder { + this.errorView = errorView + return this + } + + fun setEmptyView(emptyView: View):Builder { + this.emptyView = emptyView + return this + } + + fun setLoadingView(loadingView: View):Builder { + this.loadingView = loadingView + return this + } + + fun build(){ + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/widget/status/StatusLayout.kt b/app/src/main/java/com/allen/androidcustomview/widget/status/StatusLayout.kt new file mode 100644 index 0000000..470b9bc --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/widget/status/StatusLayout.kt @@ -0,0 +1,27 @@ +package com.allen.androidcustomview.widget.status + +import android.content.Context +import android.util.AttributeSet +import android.widget.FrameLayout + +/** + *
+ *      @author : Allen
+ *      e-mail  : lygttpod@163.com
+ *      date    : 2019/03/19
+ *      desc    : 状态布局
+ * 
+ */ + +class StatusLayout : FrameLayout { + + constructor(context: Context) : this(context, null) + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + initAttrs(attrs) + } + + private fun initAttrs(attrs: AttributeSet?) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/widget/status/StatusManager.kt b/app/src/main/java/com/allen/androidcustomview/widget/status/StatusManager.kt new file mode 100644 index 0000000..383baf7 --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/widget/status/StatusManager.kt @@ -0,0 +1,29 @@ +package com.allen.androidcustomview.widget.status + +/** + *
+ *      @author : Allen
+ *      e-mail  : lygttpod@163.com
+ *      date    : 2019/03/19
+ *      desc    :
+ * 
+ */ +object StatusManager { + + + fun setEmptyView(){ + + } + + fun setErrorView(){ + + } + + fun setContentView() { + + } + + fun setLoadingView() { + + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_item_bg.xml b/app/src/main/res/drawable/shape_item_bg.xml new file mode 100644 index 0000000..a15600f --- /dev/null +++ b/app/src/main/res/drawable/shape_item_bg.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_pay_psd_view.xml b/app/src/main/res/layout/activity_pay_psd_view.xml index 01a8df9..d3481b0 100644 --- a/app/src/main/res/layout/activity_pay_psd_view.xml +++ b/app/src/main/res/layout/activity_pay_psd_view.xml @@ -5,14 +5,15 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" + android:paddingTop="50dp" tools:context="com.allen.androidcustomview.activity.PayPsdViewActivity"> + + + + + + + + + +