一、自定义FlowLayout
open class FlowLayout : ViewGroup {
private val TAG = "FlowLayout"
//每个item横向间距
private val mHorizontalSpacing: Int = dp2px(16)
//每个item横向间距
private val mVerticalSpacing: Int = dp2px(8)
// 记录所有的行,一行一行的存储,用于layout
private val allLines: MutableList<List<View>> = ArrayList()
// 记录每一行的行高,用于layout
var lineHeights: MutableList<Int> = ArrayList()
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
//内存 抖动
clearMeasureParams()
val selfWidth = MeasureSpec.getSize(widthMeasureSpec)
val selfHeight = MeasureSpec.getSize(heightMeasureSpec)
//保存一行中的所有的view
var lineViews: MutableList<View> = ArrayList()
//记录这行已经使用了多宽的size
var lineWidthUsed = 0
// 一行的行高
var lineHeight = 0
// measure过程中,子View要求的父ViewGroup的宽
var parentNeededWidth = 0
// measure过程中,子View要求的父ViewGroup的高
var parentNeededHeight = 0
for (i in 0..childCount) {
val childView = getChildAt(i)
val lp = childView?.layoutParams
if (childView?.visibility != View.GONE && lp != null) {
val childWidthMeasure =
getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, lp.width)
val childHeightMeasure =
getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, lp.height)
childView.measure(childWidthMeasure, childHeightMeasure)
//获取子view的度量宽高
val measuredChildWidth = childView.measuredWidth
val measuredChildHeight = childView.measuredHeight
//如果需要换行
if (measuredChildWidth + lineWidthUsed + mHorizontalSpacing > selfWidth) {
//一旦换行,我们就可以判断当前行需要的宽和高了,所以此时要记录下来
allLines.add(lineViews)
lineHeights.add(lineHeight)
parentNeededHeight += lineHeight + mVerticalSpacing
parentNeededWidth =
parentNeededWidth.coerceAtLeast(lineWidthUsed + mHorizontalSpacing)
lineViews = ArrayList()
lineWidthUsed = 0
lineHeight = 0
}
// view 是分行layout的,所以要记录每一行有哪些view,这样可以方便layout布局
lineViews.add(childView)
//每行都会有自己的宽和高
lineWidthUsed += measuredChildWidth + mHorizontalSpacing
lineHeight = lineHeight.coerceAtLeast(measuredChildHeight)
if (i == childCount - 1) {
allLines.add(lineViews)
lineHeights.add(lineHeight)
parentNeededHeight += lineHeight + mVerticalSpacing
parentNeededWidth =
parentNeededWidth.coerceAtLeast(lineWidthUsed + mHorizontalSpacing)
}
}
}
//再度量自己,保存
//根据子View的度量结果,来重新度量自己ViewGroup
// 作为一个ViewGroup,它自己也是一个View,它的大小也需要根据它的父亲给它提供的宽高来度量
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val realWidth = if (widthMode == MeasureSpec.EXACTLY)
selfWidth else parentNeededWidth
val realHeight = if (heightMode == MeasureSpec.EXACTLY)
selfHeight else parentNeededHeight
setMeasuredDimension(realWidth, realHeight)
}
private fun clearMeasureParams() {
allLines.clear()
lineHeights.clear()
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
var curL = paddingLeft
var curT = paddingTop
for (i in allLines.indices) {
val childViews = allLines[i]
val childHeight = lineHeights[i]
for (j in childViews.indices) {
val view = childViews[j]
var left = curL
var top = curT
var right = curL + view.measuredWidth
var bottom = curT + view.measuredHeight
view.layout(left, top, right, bottom)
curL = right + mHorizontalSpacing
}
curT += childHeight + mVerticalSpacing
curL = paddingLeft
}
}
fun dp2px(dp: Int): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp.toFloat(),
Resources.getSystem().displayMetrics
).toInt()
}
fun addView(views: List<View>?) {
if (views == null){
throw NullPointerException("child view not null!")
}
views.forEach {
addView(it)
}
}
}
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.kotlindemo.layout.FlowLayout
android:id="@+id/flowLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
class MainActivity : AppCompatActivity() {
var views: MutableList<View> = arrayListOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
for (i in 0 until 10) {
val textView = TextView(this)
textView.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
textView.text = if (i % 2 == 0) "哈哈哈哈哈哈$i" else "嗷嗷嗷$i"
textView.setTextColor(Color.parseColor("#EF0909"))
textView.setBackgroundColor(Color.parseColor("#03A9F4"))
textView.textSize = 14f
views.add(textView)
}
flowLayout.addView(views)
}
}
二、自定义ViewPager解决大小不变问题
class MyViewPager : ViewPager {
private val TAG = "MyViewPager"
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var heightMeasure = heightMeasureSpec
var height = 0
Log.d(TAG, "onMeasure: getChildCount: $childCount")
for (i in 0 until childCount) {
val childView = getChildAt(i)
val lp = childView.layoutParams
val childWidth = getChildMeasureSpec(widthMeasureSpec, 0, lp.width)
var childHeight = getChildMeasureSpec(heightMeasureSpec, 0, lp.height)
childView.measure(childWidth, childHeight)
val h = childView.measuredHeight
if (h > height){
height = h
}
Log.d(TAG, "onMeasure: $h height: $height")
heightMeasure = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
}
super.onMeasure(widthMeasureSpec, heightMeasure)
}
}
class MyPagerAdapter : PagerAdapter {
private val TAG = "MyPagerAdapter"
lateinit var mImages: List<String>
lateinit var mContent: Context
constructor(images: List<String>, context: Context) : super() {
this.mImages = images
this.mContent = context
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
var pos = position
pos %= mImages.size
val view = LayoutInflater.from(mContent)
.inflate(R.layout.linear_item, container, false)
val textView = view.findViewById<TextView>(R.id.tv)
textView.text = mImages[pos]
container.addView(view)
return view
}
override fun isViewFromObject(view: View, o: Any): Boolean {
return view == o
}
override fun destroyItem(container: ViewGroup, position: Int, o: Any) {
container.removeView(o as View?)
}
override fun getCount(): Int {
return Int.MAX_VALUE
}
}
class PageTransform : ViewPager.PageTransformer {
private val DEFAULT_MIN_ALPHA = 0.3f
private val mMinAlpha = DEFAULT_MIN_ALPHA
private val DEFAULT_MAX_ROTATE = 15.0f
private val mMaxRotate = DEFAULT_MAX_ROTATE
override fun transformPage(view: View, position: Float) {
if (position < -1) {
//透明度
view.alpha = mMinAlpha;
//旋转
view.rotation = mMaxRotate * -1
view.pivotX = view.width.toFloat()
view.pivotY = view.height.toFloat()
} else if (position <= 1) {
if (position < 0) {
//position是0到-1的变化,p1+position就是从1到0的变化
//(p1 - mMinAlpha) * (p1 + position)就是(p1 - mMinAlpha)到0的变化
//再加上一个mMinAlpha,就变为1到mMinAlpha的变化。
val factor = mMinAlpha + (1 - mMinAlpha) * (1 + position)
view.alpha = factor
view.rotation = mMaxRotate * position
//position为width/2到width的变化
view.pivotX = view.width * 0.5f * (1 - position)
view.pivotY = view.height.toFloat()
} else {
//minAlpha到1的变化
val factor = mMinAlpha + (1 - mMinAlpha) * (1 - position)
view.alpha = factor
view.rotation = mMaxRotate * position
view.pivotX = view.width * 0.5f * (1- position)
view.pivotY = view.height.toFloat()
}
} else {
view.alpha = mMinAlpha
view.rotation = mMaxRotate
view.pivotX = 0f
view.pivotY = view.height.toFloat()
}
}
}
class MainActivity : AppCompatActivity() {
private var viewPager: ViewPager? = null
private var radioGroup: RadioGroup? = null
private var images: MutableList<String>? = null
private var index = 0
private var preIndex = 0
private val timer: Timer = Timer()
private val isContinue = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
images = ArrayList()
images?.let {
it.add("哈哈哈")
it.add("哈哈哈")
it.add("哈哈哈")
it.add("哈哈哈")
val pagerAdapter = MyPagerAdapter(it, this)
viewPager?.pageMargin = 30
viewPager?.offscreenPageLimit = 3
viewPager?.adapter = pagerAdapter
viewPager?.addOnPageChangeListener(onPageChangeListener)
viewPager?.setPageTransformer(true, PageTransform())
initRadioButton(it.size)
}
timer.schedule(object : TimerTask() {
override fun run() {
if (isContinue) {
handler.sendEmptyMessage(1)
}
}
}, 2000, 2000)
}
private fun initRadioButton(length: Int) {
for (i in 0 until length) {
val imageView = ImageView(this)
imageView.setImageResource(R.drawable.rg_selector)
imageView.setPadding(20, 0, 0, 0)
radioGroup!!.addView(
imageView, ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
radioGroup!!.getChildAt(0).isEnabled = false
}
}
var onPageChangeListener: OnPageChangeListener = object : OnPageChangeListener {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
}
override fun onPageSelected(position: Int) {
index = position
setCurrentDot(index % images!!.size)
}
override fun onPageScrollStateChanged(state: Int) {}
}
private fun setCurrentDot(i: Int) {
if (radioGroup!!.getChildAt(i) != null) {
//当前按钮不可改变
radioGroup!!.getChildAt(i).isEnabled = false
}
if (radioGroup!!.getChildAt(preIndex) != null) {
//上个按钮可以改变
radioGroup!!.getChildAt(preIndex).isEnabled = true
//当前位置变为上一个,继续下次轮播
preIndex = i
}
}
var handler: Handler = object : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
1 -> {
index++
viewPager!!.currentItem = index
}
}
}
}
}
网友评论