package com.xw.common.custom
|
|
import android.annotation.SuppressLint
|
import android.content.Context
|
import android.graphics.Color
|
import android.text.TextPaint
|
import android.text.TextUtils
|
import android.util.AttributeSet
|
import com.xw.common.utils.LogUtils
|
import android.util.TypedValue
|
import android.view.Gravity
|
import android.view.View
|
import android.widget.LinearLayout
|
import androidx.appcompat.widget.AppCompatTextView
|
import com.xw.common.R
|
import com.xw.common.utils.ToolUtil
|
import kotlin.math.max
|
import kotlin.math.min
|
|
/**
|
* Created by zmc on 2017/5/15.
|
* 自定义显示文本布局
|
*/
|
class ShowTextLinearLayout : LinearLayout {
|
val TAG = "zmc-ShowTextLinearLayout"
|
|
// 是否能点击
|
private var canClickOn = true
|
|
/**
|
* 控件整体宽度
|
*/
|
private var width = 0
|
|
/**
|
* 默认背景
|
*/
|
private var backGround = 0
|
|
/**
|
* 默认字体颜色
|
*/
|
private var textColor = 0
|
|
/**
|
* 左右控件magrin
|
*/
|
private var margin_width = 0
|
|
/**
|
* 最小的左右padding
|
*/
|
private var min_padding_width = 0
|
|
/**
|
* 最大的左右padding
|
*/
|
private var max_padding_width = 0
|
|
/**
|
* 上下padding
|
*/
|
private var padding_height = 0
|
|
/**
|
* 左padding
|
*/
|
private var padding_left = 0
|
|
/**
|
* 右padding
|
*/
|
private var padding_right = 0
|
|
/**
|
* textview 字大小
|
*/
|
private var text_size = 0
|
|
/**
|
* 屏幕宽度
|
*/
|
private var screenWith = 0
|
|
/**
|
* 当前显示记录的行数
|
*/
|
var index: Int = 1
|
|
/**
|
* 当前行数显示的个数
|
*/
|
var num: Int = 0
|
|
/**
|
* 显示内容集合
|
*/
|
val showList: MutableList<String?> = ArrayList<String?>()
|
private var context: Context? = null
|
|
/**
|
* 需要选中的内容下标
|
*/
|
val selectIndexList = mutableListOf<Int>()
|
private var selectTextColor = 0
|
private var selectTextBg = 0
|
|
/**
|
* 保存当前显示的textview
|
*/
|
private val tv_list: MutableList<AppCompatTextView?> = ArrayList<AppCompatTextView?>()
|
|
/**
|
* 保存显示textview的子布局集合
|
*/
|
private val layout_list: MutableList<LinearLayout?> = ArrayList<LinearLayout?>()
|
|
|
private var isNeedToLoadData = false
|
|
constructor(context: Context) : super(context) {
|
init(context)
|
}
|
|
fun setCanClickOn(canClickOn: Boolean) {
|
this.canClickOn = canClickOn
|
}
|
|
@SuppressLint("UseKtx")
|
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
|
this.context = context
|
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ShowTextLinearLayout)
|
// 此处获取的为px单位
|
margin_width =
|
typedArray.getDimensionPixelSize(R.styleable.ShowTextLinearLayout_margin_width, 24)
|
min_padding_width =
|
typedArray.getDimensionPixelSize(R.styleable.ShowTextLinearLayout_min_padding_width, 24)
|
max_padding_width =
|
typedArray.getDimensionPixelSize(R.styleable.ShowTextLinearLayout_max_padding_width, 72)
|
padding_height =
|
typedArray.getDimensionPixelSize(R.styleable.ShowTextLinearLayout_padding_height, 16)
|
padding_left =
|
typedArray.getDimensionPixelSize(R.styleable.ShowTextLinearLayout_padding_left, 30)
|
padding_right =
|
typedArray.getDimensionPixelSize(R.styleable.ShowTextLinearLayout_padding_right, 30)
|
//text_size = typedArray.getDimensionPixelSize(R.styleable.ShowTextLinearLayout_text_size, 13);
|
text_size =
|
typedArray.getDimension(R.styleable.ShowTextLinearLayout_text_size, 13.0.toFloat())
|
.toInt()
|
textColor = typedArray.getColor(R.styleable.ShowTextLinearLayout_text_color, Color.BLACK)
|
backGround = typedArray.getResourceId(
|
R.styleable.ShowTextLinearLayout_text_bg,
|
R.drawable.shape_60_back_bg
|
)
|
LogUtils.e(TAG, "margin_width:" + margin_width)
|
LogUtils.e(TAG, "min_padding_width:" + min_padding_width)
|
LogUtils.e(TAG, "max_padding_width:" + max_padding_width)
|
LogUtils.e(TAG, "padding_height:" + padding_height)
|
LogUtils.e(TAG, "padding_left:" + padding_left)
|
LogUtils.e(TAG, "padding_right:" + padding_right)
|
LogUtils.e(TAG, "text_size:" + text_size)
|
LogUtils.e(TAG, "=======================================================================")
|
typedArray.recycle()
|
init(context)
|
}
|
|
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
context,
|
attrs,
|
defStyleAttr
|
) {
|
this.context = context
|
init(context)
|
}
|
|
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
|
super.onLayout(changed, l, t, r, b)
|
if (width == 0) {
|
width = getMeasuredWidth()
|
}
|
if (isNeedToLoadData) {
|
Calculation()
|
isNeedToLoadData = false
|
}
|
}
|
|
/**
|
* 初始化
|
*/
|
private fun init(context: Context) {
|
this.context = context
|
}
|
|
/**
|
* 移除所有显示视图
|
*/
|
fun remove() {
|
this.removeAllViews()
|
showList.removeAll(showList)
|
layout_list.removeAll(layout_list)
|
tv_list.removeAll(tv_list)
|
index = 1
|
num = 0
|
selectIndexList.removeAll(selectIndexList)
|
selectTextBg = 0
|
selectTextColor = 0
|
}
|
|
/**
|
* 设置选中样式
|
*
|
* @param textColorSelect
|
* @param drawable
|
*/
|
fun setSelectStyle(textColorSelect: Int, drawable: String?) {
|
this.selectTextColor = textColorSelect
|
this.selectTextBg = resources.getIdentifier(drawable, "drawable", context!!.packageName)
|
}
|
|
/**
|
* tv的点击处理
|
*
|
* @param position
|
*/
|
fun onTvClick(position: Int) {
|
if (selectTextColor != 0 && selectTextBg != 0 && tv_list.size > position) {
|
val indexOf = selectIndexList.indexOf(position)
|
if (indexOf != -1) {
|
tv_list[position]!!.setTextColor(textColor)
|
tv_list[position]!!.setBackgroundResource(backGround)
|
selectIndexList.removeAt(indexOf)
|
} else {
|
tv_list[position]!!.setTextColor(selectTextColor)
|
tv_list[position]!!.setBackgroundResource(selectTextBg)
|
selectIndexList.add(position)
|
}
|
}
|
}
|
|
/**
|
* tv的点击处理
|
*
|
* @param indexList 需要批量处理的下标集合
|
*/
|
fun onTvClick(indexList: MutableList<Int?>) {
|
// 传入的 indexList 有可能为当前的 selectIndexList for循环内 操作 remove 或者 add 会有问题
|
val copyList: MutableList<Int?> = ArrayList<Int?>()
|
for (i in indexList.indices) {
|
copyList.add(indexList[i])
|
}
|
|
for (i in copyList.indices) {
|
val position: Int = copyList[i]!!
|
if (selectTextColor != 0 && selectTextBg != 0 && tv_list.size > position) {
|
val indexOf = selectIndexList.indexOf(position)
|
if (indexOf != -1) {
|
tv_list[position]!!.setTextColor(textColor)
|
tv_list[position]!!.setBackgroundResource(backGround)
|
selectIndexList.removeAt(indexOf)
|
} else {
|
tv_list[position]!!.setTextColor(selectTextColor)
|
tv_list[position]!!.setBackgroundResource(selectTextBg)
|
selectIndexList.add(position)
|
}
|
}
|
}
|
}
|
|
/**
|
* 设置数据源
|
*
|
* @param lists
|
* @param context
|
* @param lists
|
* @param color
|
* @param drawable
|
*/
|
fun setData(
|
lists: List<String>,
|
context: Context,
|
selectIndex: List<String>,
|
color: Int,
|
drawable: String?
|
) {
|
this.showList.addAll(lists)
|
this.context = context
|
|
for (i in selectIndex.indices) {
|
val index = selectIndex[i].toInt()
|
if (index == -1 || index >= lists.size) {
|
continue
|
}
|
this.selectIndexList.add(selectIndex[i].toInt())
|
}
|
this.selectTextColor = color
|
this.selectTextBg = resources.getIdentifier(drawable, "drawable", context.packageName)
|
|
if (showList.isEmpty()) {
|
return
|
}
|
if (width != 0) {
|
Calculation()
|
} else {
|
isNeedToLoadData = true
|
}
|
}
|
|
fun setData(lists: List<String>, context: Context) {
|
this.showList.addAll(lists)
|
|
//该写法有版本限制 api 24 以上
|
//showList = lists.stream().collect(Collectors.toList());
|
this.context = context
|
if (showList.isEmpty()) {
|
return
|
}
|
if (width != 0) {
|
Calculation()
|
} else {
|
isNeedToLoadData = true
|
}
|
}
|
|
/**
|
* 进行计算
|
*/
|
private fun Calculation() {
|
val linearLayout = LinearLayout(context)
|
linearLayout.orientation = HORIZONTAL
|
val lp = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
// 设置margin值 单位像素
|
lp.setMargins(0, 0, 0, 25)
|
linearLayout.setLayoutParams(lp)
|
linearLayout.setPadding(padding_left, 0, padding_right, 0)
|
addView(linearLayout)
|
layout_list.add(linearLayout)
|
/**
|
* 计算出需要添加的布局 动态添加子linearLayout
|
*/
|
screenWith = getScreenWith()
|
LogUtils.e(TAG, "Calculation ---> 控件宽度为:" + screenWith)
|
screenWith -= (padding_left + padding_right)
|
LogUtils.e(TAG, "Calculation ---> 计算宽度为:" + screenWith)
|
// 思路 先以最小左右padding间距计算出一行最多可以显示几个历史 在对字的宽度 屏幕宽度 去确认实际显示的左右padding为多少 然后去显示
|
var i = 0
|
while (i < showList.size) {
|
LogUtils.e(TAG, "Calculation ---> 当前剩余宽度 = " + screenWith)
|
val name = showList[i]
|
val textWidth = getTextWidth(name, text_size.toFloat())
|
LogUtils.e(TAG, "Calculation ---> 文本:" + name + " , 宽度为:" + textWidth)
|
/**
|
* 单个item的宽度 应该为 文本宽度 + 文本左右内padding + 文本右margin
|
* 但是当item为本行最后一个view时 不需要添加右margin
|
*/
|
val itemWidth: Float
|
|
val bigItemWidth = textWidth + 2 * min_padding_width + margin_width
|
LogUtils.e(TAG, "Calculation ---> bigItemWidth = " + bigItemWidth)
|
if (screenWith >= bigItemWidth) {
|
itemWidth = bigItemWidth
|
} else {
|
val smallItemWidth = textWidth + 2 * min_padding_width
|
LogUtils.e(TAG, "Calculation ---> smallItemWidth = " + smallItemWidth)
|
itemWidth = smallItemWidth
|
}
|
LogUtils.e(TAG, "Calculation ---> itemWidth = " + itemWidth)
|
|
if (screenWith >= itemWidth) { //当前还能继续添加textview视图
|
screenWith = (screenWith - itemWidth).toInt()
|
num++
|
if (i == showList.size - 1) {
|
LogUtils.e(TAG, "Calculation ---> 满足条件 且没更多数据 显示界面")
|
LogUtils.i(TAG, "Calculation ---> 当前剩余宽度为:" + screenWith)
|
addTextView(layout_list.size - 1, num, i)
|
|
/**
|
* 对需要选中的tv做处理
|
*/
|
if (selectIndexList.isNotEmpty() && selectTextColor != 0 && selectTextBg != 0) {
|
for (j in selectIndexList.indices) {
|
tv_list[selectIndexList[j]]!!.setTextColor(selectTextColor)
|
/**
|
* 此处使用id去设置背景 而不是drawable 原因使用drawable 屏幕在从不显示到显示时 会出现显示异常
|
*/
|
// tv_list.get(selectIndesList.get(j)).setBackground(selectTextBg);
|
tv_list[selectIndexList[j]]!!
|
.setBackgroundResource(selectTextBg)
|
}
|
// selectIndesList.removeAll(selectIndesList);
|
// selectTextBg = 0;
|
// selectTextColor = 0;
|
}
|
|
num = 0
|
index = 1
|
}
|
} else if (getScreenWith() - (padding_left + padding_right) == screenWith && screenWith < itemWidth) {
|
// 当前列只有一个 并且一个就超出了长度
|
num++
|
LogUtils.e(TAG, "Calculation ---> 当前列只有一个 并且一个就超出了长度")
|
LogUtils.w(TAG, "Calculation ---> i = " + i)
|
addTextView(layout_list.size - 1, num, i)
|
val linearLayouts = LinearLayout(context)
|
linearLayouts.orientation = HORIZONTAL
|
val lps = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
lps.setMargins(0, 0, 0, 25)
|
linearLayouts.setLayoutParams(lps)
|
linearLayouts.setPadding(padding_left, 0, padding_right, 0)
|
addView(linearLayouts)
|
layout_list.add(linearLayouts)
|
index++
|
num = 0
|
screenWith = getScreenWith()
|
screenWith -= (padding_left + padding_right)
|
} else { // 当前不能再添加textview视图了
|
LogUtils.e(TAG, "Calculation ---> 不满足条件 添加到下一个视图中")
|
LogUtils.w(TAG, "Calculation ---> i = " + i)
|
i-- //当前不满足 则i不变 进行下一次循环在添加进去
|
addTextView(layout_list.size - 1, num, i)
|
val linearLayouts = LinearLayout(context)
|
linearLayouts.orientation = HORIZONTAL
|
val lps = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
lps.setMargins(0, 0, 0, 25)
|
linearLayouts.setLayoutParams(lps)
|
linearLayouts.setPadding(padding_left, 0, padding_right, 0)
|
addView(linearLayouts)
|
layout_list.add(linearLayouts)
|
index++
|
num = 0
|
screenWith = getScreenWith()
|
screenWith -= (padding_left + padding_right)
|
}
|
i++
|
}
|
}
|
|
/**
|
* 实际控件的左右padding
|
*/
|
private var Actual_padding_width = 0
|
|
/**
|
* 添加textview到布局中
|
*
|
* @param index 需要添加视图的布局下标
|
* @param num 当前布局中添加视图的个数
|
* @param i 当前添加数据在数据源中的下标
|
*/
|
private fun addTextView(index: Int, num: Int, i: Int) {
|
var i = i
|
try {
|
LogUtils.e(TAG, "addTextView ---> index = " + index)
|
LogUtils.e(TAG, "addTextView ---> num = " + num)
|
LogUtils.e(TAG, "addTextView ---> i = " + i)
|
screenWith = getScreenWith()
|
screenWith -= (padding_left + padding_right)
|
i = i + 1 - num
|
for (j in 0..<num) {
|
var name = showList[i] ?: ""
|
if (TextUtils.isEmpty(name)) name = "未命名"
|
i++
|
val textWidth = getTextWidth(name, text_size.toFloat())
|
if (textWidth > screenWith) {
|
screenWith = 0
|
} else {
|
screenWith = (screenWith - textWidth).toInt()
|
}
|
|
val tv = AppCompatTextView(context!!)
|
// 长度限制
|
val maxLength = 30
|
val displayText = if (name.length > maxLength) {
|
name.take(maxLength) + "…"
|
} else {
|
name
|
}
|
tv.text = displayText
|
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, text_size.toFloat()) //设置字体大小 像素单位
|
tv.setTextColor(textColor)
|
tv.setGravity(Gravity.CENTER)
|
tv_list.add(tv)
|
|
tv.tag = tv_list.size - 1
|
tv.setOnClickListener { v ->
|
if (onItemClick != null && canClickOn) {
|
onItemClick!!.invoke(v)
|
}
|
}
|
}
|
screenWith -= ((num - 1) * margin_width)
|
LogUtils.e(TAG, "addTextView ---> num = " + num)
|
Actual_padding_width = screenWith / (num * 2)
|
Actual_padding_width = min(Actual_padding_width, max_padding_width)
|
Actual_padding_width = max(Actual_padding_width, 20) // 设置最小padding 20
|
LogUtils.e(TAG, "addTextView ---> Actual_padding_width = " + Actual_padding_width)
|
i -= num
|
for (j in 0..<num) {
|
// view 设置padding 单位像素
|
tv_list.get(i)!!.setPadding(
|
Actual_padding_width,
|
padding_height,
|
Actual_padding_width,
|
padding_height
|
)
|
tv_list[i]!!.setBackgroundResource(backGround)
|
|
val params = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
if (j == num - 1) { // 最后一个文本框无需右margin
|
params.rightMargin = 0
|
} else {
|
params.rightMargin = margin_width
|
}
|
layout_list[index]!!.addView(tv_list[i], params)
|
i++
|
}
|
} catch (e: Exception) {
|
LogUtils.e(TAG, "addTextView ---> index : " + index)
|
LogUtils.e(TAG, "addTextView ---> i : " + i)
|
LogUtils.e(TAG, "addTextView ---> num : " + num)
|
LogUtils.e(TAG, "addTextView ---> e : " + e.message)
|
}
|
|
LogUtils.e(TAG, "===========================================================================")
|
}
|
|
/**
|
* 获取屏幕宽度
|
*/
|
private fun getScreenWith(): Int {
|
if (width != 0) {
|
return width
|
}
|
return ToolUtil.getDefaultDisplay(context!!).widthPixels
|
}
|
|
/**
|
* 获取文字的宽度
|
* 这种方法获取到的宽度是像素单位的
|
*/
|
private fun getTextWidth(text: String?, textSize: Float): Float {
|
val paint = TextPaint()
|
/**
|
* 各单位的换算比例
|
* case COMPLEX_UNIT_PX:
|
* return value;
|
* case COMPLEX_UNIT_DIP:
|
* return value * metrics.density;
|
* case COMPLEX_UNIT_SP:
|
* return value * metrics.scaledDensity;
|
* case COMPLEX_UNIT_PT:
|
* return value * metrics.xdpi * (1.0f/72);
|
* case COMPLEX_UNIT_IN:
|
* return value * metrics.xdpi;
|
* case COMPLEX_UNIT_MM:
|
* return value * metrics.xdpi * (1.0f/25.4f);
|
*
|
*/
|
paint.textSize = textSize
|
return paint.measureText(text)
|
}
|
|
var onItemClick: ((childView: View) -> Unit)? = null
|
|
fun setListener(onItemClick: (childView: View) -> Unit) {
|
this.onItemClick = onItemClick
|
}
|
|
}
|