如上所示,横向均分显示3个item,默认中间项目显示放大和播放动画,左右无限滑动,但是实际上只有5项,下面的几个小圆点显示目前是在第几项,每过5秒自动滑到下一个item。
要求:中间项放大且方播放动画,那么它看起来则要占据比较大的空间来给它进行动画播放,如果是动态改变View的属性的话,会无比复杂,因为必须是均分才能刚好显示3个item。
而且修改还需要无闪烁,你修改后还得修改回来,很容易就显示错乱。所以最后解决并没有真实去修改View的属性。
这里主要介绍recyclerView的处理。
1、横向均分处理。
基本上就是获取屏幕的宽减去两边padding再除以3,至于多出来的2dp是为了当: 向左或者向右滑动一个item,这个item刚好看不见,但是由于回收的基准线没过,导致它,看不见,但是还没被回收,在这里我们需要它马上进入回收复用池。
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.recommend_female_users_rv_item, parent,false)
itemView.layoutParams.width = (width - Utils.dip2px(context, 56f)) / 3
return MyHolder(itemView)
}
//做出改变,拿到第一个可见View var position = layoutManager?.findFirstVisibleItemPosition() 貌似拿到第一个,然后再+1就OK了。 但是在某些手机上,非常无奈地发现,它的可见item居然是5个,我非常郁闷。 那么只能再拿最后一个可见item: var lastPosition = layoutManager?.findLastVisibleItemPosition() 然后判断是3个还是5个,如果是5个可见的+2,否则+1处理。
2、左右无限滑。
在adapter里: 返回Integer的最大数,基本上就可以满足无限滑了 override fun getItemCount(): Int = Integer.MAX_VALUE onBindViewHolder的时候: var realPosition = position%data.size,拿到它在数据集里面的位置 rv设置完adapter之后: layoutManager?.scrollToPositionWithOffset(Integer.MAX_VALUE / 2 + 1, 0)
3、如何确保刚好滑动item的中间
LinearSnapHelper().attachToRecyclerView(rv) 默认提供了工具类,以前不知道的时候自己去计算判断然后再帮忙二次滑动,有点淡淡的忧伤。
监听rv的滑动当挺下来的时候由于我们让它刚好滑动中间,所以会有二次滑动,在我的手机上第一次滑动停下来的时候再次触发滑动是没有时间间隔的,所以我延迟20毫秒去做事。
rv?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
when (newState) {
SCROLL_STATE_IDLE -> {
delayExecute()
isNeedUpdateView = true
isScroll = false
}
else -> {
if (isNeedUpdateView) {
isNeedUpdateView = false
isScroll = true
}
}
}
}
})
fun delayExecute() {
timer?.schedule(object : TimerTask() {
override fun run() {
if (isNeedUpdateView) {
runOnUiThread {
updateSecondItem()
}
}
}
}, 20)
}
同时把上次的itemView的动画复原
private fun restoreItemView() {
try {
val viewhodler = rv?.getChildViewHolder(layoutManager?.findViewByPosition(location)!!) as RecommendFemaleUsersAdapter.MyHolder
viewhodler.rootView.clearAnimation()
viewhodler.rippleLayout.stopRippleAnimation()
} catch (e : Exception) {
e.printStackTrace()
}
}
如果从中间往左或者右滑动一格,当前item还在屏幕中,所以需要我们找到这个Viewholder,并把它的动画停止,view还原。 如果是滑动了两格,到了屏幕之外,我们是无法再拿到这个Viewholder的,会FC,所以加错误捕捉。 同时设置rv的缓存: rv?.setItemViewCacheSize(0) 让所有的回收ViewHolder都进入pool池,这样复用的时候都会经过onBindViewHolder,我们在这个方法里初始化的时候停止所有的动画即可。
4、更新ViewHolder
直接通过findViewByPosition找到View,再通过getChildViewHolder找到ViewHolder
fun updateView(position: Int) {
try {
val viewhodler = rv?.getChildViewHolder(layoutManager?.findViewByPosition(position)!!) as RecommendFemaleUsersAdapter.MyHolder
viewhodler.rootView.startAnimation(animator)
viewhodler.rippleLayout.startRippleAnimation()
val viewhodlerLeft = rv?.getChildViewHolder(layoutManager?.findViewByPosition(position-1)!!) as RecommendFemaleUsersAdapter.MyHolder
viewhodlerLeft.rootView.clearAnimation()
viewhodlerLeft.rippleLayout.stopRippleAnimation()
val viewhodlerRight = rv?.getChildViewHolder(layoutManager?.findViewByPosition(position+1)!!) as RecommendFemaleUsersAdapter.MyHolder
viewhodlerRight.rootView.clearAnimation()
viewhodlerRight.rippleLayout.stopRippleAnimation()
} catch (e : Exception) {
e.printStackTrace()
}
}
或者 mAdapter?.notifyItemChanged(position + 1, 2) 第一个是position,第二个是类型,自己定义。
override fun onBindViewHolder(holder: MyHolder, position: Int, payloads: MutableList<Any>) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads)
} else if (payloads.size > 0 && payloads[0] is Integer){
when(payloads[0] as Int) {
2 -> {
val animator = AnimationUtils.loadAnimation(context, R.anim.scale_textview)
holder.rootView.startAnimation(animator)
holder.rippleLayout.startRippleAnimation()
}
}
}
}
这个更新的方式好处是默认拿到了holder: MyHolder, position: Int
5、动画突破布局边界
android:clipChildren=“false” 无论是属性突破还是补间动画的放大都可以。 要知道我们屏幕上看到的是很多层幕布通过计算裁剪clip之后得到的。 而设置这个属性是允许裁剪的时候保留对应的画面。 如果你设置了这个属性没有得到自己想要的结果绝对是可以调试的,具体要看对应的业务,之后通过网上去找对应的资料,这里我就不详细说了。
6、adapter
class RecommendFemaleUsersAdapter(val context: Context, var data: ArrayList<RecommendUserInfoBean>, val width: Int) : RecyclerView.Adapter<RecommendFemaleUsersAdapter.MyHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.recommend_female_users_rv_item, parent,false)
itemView.layoutParams.width = (width - Utils.dip2px(context, 56f)) / 3
return MyHolder(itemView)
}
override fun getItemCount(): Int = Integer.MAX_VALUE
override fun onBindViewHolder(holder: MyHolder, position: Int) {
var realPosition = position%data.size
holder.avatar.loadUrl(data[realPosition].avatar)
holder.rootView.cameraDistance
holder.rippleLayout.stopRippleAnimation()
}
inner class MyHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var rootView = itemView
var rippleLayout: RippleLayout = itemView.findViewById(R.id.cwv_wave)
var avatar: ImageView = itemView.findViewById<ImageView>(R.id.iv_avatar)
var realAvatar: ImageView = itemView.findViewById<ImageView>(R.id.real_avatar)
}
override fun onBindViewHolder(holder: MyHolder, position: Int, payloads: MutableList<Any>) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads)
} else if (payloads.size > 0 && payloads[0] is Integer){
when(payloads[0] as Int) {
2 -> {
val animator = AnimationUtils.loadAnimation(context, R.anim.scale_textview)
holder.rootView.startAnimation(animator)
holder.rippleLayout.startRippleAnimation()
}
}
}
}
在onCreateViewHolder方法里,设置View的属性,对item是没有问题的,如果你原来设置了居中,那么还是会居中。 如果你是在onBindViewHolder方法里,设置View的属性。 那么问题来了,你会发现你的itemView里面的控件整体的位置属性设置失效了。 由于我这里不需要,暂时没有跟进解决方案。 是因为我开始不是按照补间动画的方案去处理,而是实际上改变View的属性,最后发现是个神坑,引出了N多问题,最后无奈回归这种处理方式。
7、下面的小圆点
添加小圆点
fun addIndicationView() {
for (index in 0 until mData.size) {
val textView = TextView(context)
val layoutParams =
LinearLayout.LayoutParams(Utils.dip2px(context, 5f), Utils.dip2px(context, 5f))
layoutParams.setMargins(10, 0, 0, 0)
textView.layoutParams = layoutParams
textView.setBackgroundResource(R.drawable.custom_indication)
textView.isEnabled = false
circleLayout?.addView(textView)
}
}
更新小圆点
fun updateIndicator(newPosition: Int) {
circleLayout?.getChildAt(currentPosition)?.isEnabled = false
currentPosition = newPosition%mData.size
circleLayout?.getChildAt(currentPosition)?.isEnabled = true
}
小圆点选择器效果
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true">
<shape android:shape="oval">
<solid android:color="@color/indicator_check" />
<corners android:radius="15dp"/>
</shape>
</item>
<item>
<shape android:shape="oval">
<solid android:color="@color/indicator_uncheck" />
<corners android:radius="15dp"/>
</shape>
</item>
</selector>
8、dialog的一些设置
style
<style name="NormalDialog" parent="android:style/Theme.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">true</item>
</style>
设置全屏
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.recommend_remale_users_dialog)
window?.setGravity(Gravity.CENTER)
window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
this.setCanceledOnTouchOutside(false)
val dm = mContext.resources.displayMetrics
width = dm.widthPixels
initView()
initData()
initListener()
}
回调:private val clickAction: (Boolean) -> Unit 我这里定义的是Boolean,根据情况自己定义。 clickAction.invoke(isNotMoreShowDialog) 接收参数叫clickAction的函数,传参是Boolean,Unit:没有返回值。 相比java去定义接口等操作,kotlin用起来简直不要太爽。
|