activity 主布局页面:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_white"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="48dp"
android:background="@color/colorBase" >
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="@string/app_title"
android:layout_marginLeft="20dp"
android:layout_gravity="center_vertical"
android:layout_centerInParent="true"
android:layout_alignParentLeft="true"
android:textSize="12sp"/>
<TextView
android:id="@+id/submit"
android:textSize="20sp"
android:textColor="@color/colorWhite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:gravity="center_vertical"
android:text="保存"/>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我的应用"
android:textColor="#333333"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="(按住可拖动调整顺序)"
android:textColor="#808080"
android:textSize="13sp" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewExist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingRight="10dp" />
<View
android:layout_width="match_parent"
android:layout_height="4dp"
android:background="@color/line_color">
</View>
<HorizontalScrollView
android:id="@+id/horizonLScrollView"
android:layout_width="match_parent"
android:layout_height="35dp"
android:scrollbarThumbHorizontal="@color/transparent"
android:scrollbars="none">
<RadioGroup
android:id="@+id/rg_tab"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal" />
</HorizontalScrollView>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/line_color">
</View>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewAll"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:paddingRight="10dp" />
</LinearLayout>
activity 代码:
package com.daobo.wand
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*
import android.view.View.OnClickListener
import androidx.recyclerview.widget.GridLayoutManager
import com.daobo.wand.ui.*
import android.widget.RadioButton
import android.util.TypedValue
import android.view.Gravity
import androidx.recyclerview.widget.RecyclerView
import android.app.Activity
import android.os.Build
import android.util.DisplayMetrics
import android.widget.Toast
import android.widget.CompoundButton
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import com.daobo.wand.ui.adapter.FunctionAdapter
import com.daobo.wand.ui.adapter.FunctionBlockAdapter
import com.daobo.wand.ui.listener.DefaultItemCallback
import com.daobo.wand.ui.listener.DefaultItemTouchHelper
import com.daobo.wand.ui.util.SFUtils
/**
* 主菜单页面
* @author xiaolong.li
* @time 2020-02-14
*/
class MainActivity : AppCompatActivity(), OnClickListener {
private var blockAdapter: FunctionBlockAdapter? = null
private var functionAdapter: FunctionAdapter? = null
private var gridManager: GridLayoutManager? = null
private var scrollTab = ArrayList<String>()
private var itemWidth = 0
private var lastRow = 0
private var isMove = false//滑动状态
private var scrollPosition = 0
private var currentTab: String? = null//当前的标签
private var tabWidth = 0//标签宽度
private var allData: ArrayList<FunctionItem>? = null
private var selData: ArrayList<FunctionItem>? = null
private var sfUtils: SFUtils? = null
private val maxCount = 14
private var isDrag = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
addListener()
submit.setOnClickListener(this)
}
private fun init () {
sfUtils = SFUtils(this)
allData = sfUtils!!.getAllFunctionItems() //sfUtils!!.getAllFunctionWithState()
selData = sfUtils!!.getSelectFunctionItem()
blockAdapter = FunctionBlockAdapter(this, selData!!)
recyclerViewExist.layoutManager = GridLayoutManager(this, 4)
recyclerViewExist.adapter = blockAdapter
recyclerViewExist.addItemDecoration(SpaceItemDecoration(4, dip2px(this, 10.0F)))
val callback = DefaultItemCallback(blockAdapter!!)
val helper = DefaultItemTouchHelper(callback)
helper.attachToRecyclerView(recyclerViewExist)
gridManager = GridLayoutManager(this, 4)
gridManager!!.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
val fi = allData!![position]
return if (fi.isTitle) 4 else 1
}
}
functionAdapter = FunctionAdapter(this, allData!!)
recyclerViewAll.layoutManager = gridManager
recyclerViewAll.adapter = functionAdapter
val spaceDecoration = SpaceItemDecoration(4, dip2px(this, 10.0F))
recyclerViewAll.addItemDecoration(spaceDecoration)
itemWidth = getAtyWidth(this) / 4 + dip2px(this, 2.0F)
resetEditHeight(selData!!.size)
initTab()
}
private fun initTab () {
try {
val tabs = sfUtils!!.getTabFunctionItems()
if (tabs != null && tabs.isNotEmpty()) {
currentTab = tabs[0].mName
val padding = dip2px(this, 10.0F)
val size = tabs.size
for (i in 0 until size) {
val item = tabs[i]
if (item.isTitle) {
scrollTab.add(item.mName.toString())
val rb = RadioButton(this)
rb.setPadding(padding, 0, padding, 0)
rb.buttonDrawable = null
rb.gravity = Gravity.CENTER
rb.text = item.mName
rb.tag = item.subItemCount
rb.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14F)
try {
rb.setTextColor(ContextCompat.getColorStateList(this, R.color.colorGray))
} catch (e: Exception) {
e.printStackTrace()
}
rb.setCompoundDrawablesWithIntrinsicBounds(null, null, null,
ContextCompat.getDrawable(this, R.drawable.bg_block_tab))
rb.setOnCheckedChangeListener(onCheckedChangeListener)
rg_tab.addView(rb)
}
}
(rg_tab.getChildAt(0) as RadioButton).isChecked = true
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun resetEditHeight(size: Int) {
var size = size
try {
if (size == 0) {
size = 1
}
var row = size / 4 + if (size % 4 > 0) 1 else 0
if (row <= 0)
row = 1
if (lastRow != row) {
lastRow = row
val params = recyclerViewExist.layoutParams
params.height = itemWidth * row
recyclerViewExist.layoutParams = params
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private val onCheckedChangeListener =
CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
try {
val position = buttonView.tag as Int
val text = buttonView.text.toString()
if (currentTab != text && isChecked) {
currentTab = text
moveToPosition(position)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun moveToPosition(position: Int) {
val first = gridManager!!.findFirstVisibleItemPosition()
val end = gridManager!!.findLastVisibleItemPosition()
if (first == -1 || end == -1)
return
if (position <= first) { //移动到前面
gridManager!!.scrollToPosition(position)
} else if (position >= end) { //移动到后面
isMove = true
scrollPosition = position
gridManager!!.smoothScrollToPosition(recyclerViewAll, null, position)
} else {//中间部分
val n = position - gridManager!!.findFirstVisibleItemPosition()
if (n > 0 && n < allData!!.size) {
val top = gridManager!!.findViewByPosition(position)!!.top
recyclerViewAll.scrollBy(0, top)
}
}
}
private val onScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
try {
if (isMove && newState == RecyclerView.SCROLL_STATE_IDLE) {
isMove = false
val view = gridManager!!.findViewByPosition(scrollPosition)
if (view != null) {
val top = view.top
recyclerView.scrollBy(0, top)
}
}
isDrag = newState == RecyclerView.SCROLL_STATE_DRAGGING
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (isDrag) { //拖动过程中
val position = gridManager!!.findFirstVisibleItemPosition()
if (position > 0) {
for (i in 0 until position + 1) {
if (allData!![i].isTitle) {
currentTab = allData!![i].mName
}
}
scrollTab(currentTab!!)
}
}
}
}
private fun scrollTab(newTab: String) {
try {
val position = scrollTab.indexOf(currentTab)
val targetPosition = scrollTab.indexOf(newTab)
currentTab = newTab
if (targetPosition != -1) {
val x = (targetPosition - position) * getTabWidth()
val radioButton = rg_tab.getChildAt(targetPosition) as RadioButton
radioButton.setOnCheckedChangeListener(null)
radioButton.isChecked = true
radioButton.setOnCheckedChangeListener(onCheckedChangeListener)
horizonLScrollView.scrollBy(x, 0)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun getTabWidth(): Int {
if (tabWidth == 0) {
if (rg_tab != null && rg_tab.childCount !== 0) {
tabWidth = rg_tab.width / rg_tab.childCount
}
}
return tabWidth
}
private fun dip2px(context: Context, dpValue: Float): Int {
val scale = context.resources.displayMetrics.density
return (dpValue * scale + 0.5f).toInt()
}
private fun getAtyWidth(context: Context): Int {
return try {
val mDm = DisplayMetrics()
(context as Activity).windowManager.defaultDisplay
.getMetrics(mDm)
mDm.widthPixels
} catch (e: Exception) {
0
}
}
private fun addListener() {
functionAdapter!!.setOnItemAddListener(object : FunctionAdapter.OnItemAddListener {
override fun add(item: FunctionItem): Boolean {
if (selData != null && selData!!.size < maxCount) { // 更新选择列表,所有列表已在内部进行更新
try {
selData!!.add(item)
resetEditHeight(selData!!.size)
blockAdapter!!.notifyDataSetChanged()
item.mIsSelect = true
return true
} catch (e: Exception) {
e.printStackTrace()
}
return false
} else {
Toast.makeText(this@MainActivity, "选中的模块不能超过" + maxCount + "个",
Toast.LENGTH_SHORT).show()
return false
}
}
})
blockAdapter!!.setOnItemRemoveListener(object : FunctionBlockAdapter.OnItemRemoveListener {
override fun remove(item: FunctionItem) {
// 更新所有列表,选择列表已在内部进行更新
try {
if (item?.mName != null) {
for (i in allData!!.indices) {
val data = allData!![i]
if (data?.mName != null) {
if (item.mName.equals(data.mName)) {
data.mIsSelect = false
break
}
}
}
functionAdapter!!.notifyDataSetChanged()
}
resetEditHeight(selData!!.size)
} catch (e: Exception) {
e.printStackTrace()
}
}
})
recyclerViewAll.addOnScrollListener(onScrollListener)
}
override fun onClick(v: View?) {
//TODO("not implemented")
// To change body of created functions use File | Settings | File Templates.
var intent = Intent()
when(v?.id) {
R.id.submit ->{
//点击了
sfUtils!!.saveSelectFunctionItem(selData!!)
sfUtils!!.saveAllFunctionWithState(allData!!)
Toast.makeText(this@MainActivity, "保存成功!",
Toast.LENGTH_SHORT).show()
}
}
}
}
自定义控件代码:SquareRelativeLayout
package com.daobo.wand.ui
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.RelativeLayout
class SquareRelativeLayout : RelativeLayout {
constructor (context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)
constructor (context: Context, attrs: AttributeSet) : super(context, attrs)
constructor (context: Context) : super(context)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var widthMeasureSpec = widthMeasureSpec
var heightMeasureSpec = heightMeasureSpec
setMeasuredDimension(
View.getDefaultSize(0, widthMeasureSpec),
View.getDefaultSize(0, heightMeasureSpec)
)
val childWidthSize = measuredWidth
widthMeasureSpec =
View.MeasureSpec.makeMeasureSpec(childWidthSize, View.MeasureSpec.EXACTLY)
heightMeasureSpec = widthMeasureSpec
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
}
SpaceItemDecoration类:
package com.daobo.wand.ui
import androidx.recyclerview.widget.RecyclerView
import android.graphics.Canvas
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
class SpaceItemDecoration (row: Int, space: Int) : ItemDecoration() {
private var mRow = row
private val mSpace = space
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
outRect.left = mSpace
outRect.bottom = mSpace
//if (parent.getChildAdapterPosition(view) % row == 0)
//outRect.left = 0
}
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDrawOver(c, parent, state)
}
}
bean 类:
package com.daobo.wand.ui
class TabItem(tabName : String, functionItems : ArrayList<FunctionItem>) {
private var mTtabName = tabName
private var mFunctionItems: ArrayList<FunctionItem>? = functionItems
fun getTabName(): String {
return mTtabName
}
fun setTabName(tabName: String) {
this.mTtabName = tabName
}
fun getFunctionItems(): ArrayList<FunctionItem>? {
return mFunctionItems
}
fun setFunctionItems(functionItems: ArrayList<FunctionItem>) {
this.mFunctionItems = functionItems
}
}
package com.daobo.wand.ui
class FunctionItem {
var mName: String? = null
var mIsSelect = false
var mImageUrl = ""
var mBackground = ""
var isTitle = false
var subItemCount = 0
constructor(name: String, isSelect: Boolean, imageUrl: String, background: String) {
this.mName = name
this.mIsSelect = isSelect
this.mImageUrl = imageUrl
this.mBackground = background
}
constructor(name: String, isTitle: Boolean, subItemCount: Int) {
this.mName = name
this.isTitle = isTitle
this.subItemCount = subItemCount
}
constructor(name: String, isTitle: Boolean) {
this.mName = name
this.isTitle = isTitle
}
}
package com.daobo.wand.ui.viewbean
class FuncItemViewBean {
var Name : String ? = ""
var BtnImgSrc : Int = 0
var IvImgSrc : Int = 0
}
item 布局xml: layout_function_text
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="name" type="String"/>
</data>
<TextView
android:id="@+id/func_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_white"
android:drawableLeft="@drawable/item_block_left"
android:gravity="center_vertical"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textColor="@color/black"
android:textSize="18sp"
android:text="@{name}"/>
</layout>
layout_function_item
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="funcItem" type="com.daobo.wand.ui.viewbean.FuncItemViewBean"/>
</data>
<com.daobo.wand.ui.SquareRelativeLayout
android:id="@+id/item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_grid_press">
<LinearLayout
android:id="@+id/layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:contentDescription="@string/app_name"
android:scaleType="centerInside"
android:src="@{funcItem.ivImgSrc}"/>
<TextView
android:id="@+id/text"
android:layout_marginTop="2dp"
android:lines="2"
android:textSize="12sp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:gravity="center_horizontal"
android:textColor="@color/black"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{funcItem.Name}"/>
</LinearLayout>
<ImageView
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginRight="1dp"
android:layout_marginTop="1dp"
android:background="@null"
android:contentDescription="@string/app_name"
android:padding="4dp"
android:scaleType="centerInside"
android:src="@{funcItem.btnImgSrc}" />
</com.daobo.wand.ui.SquareRelativeLayout>
</layout>
自定义 drawable xml
bg_block_tab:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/item_block_tab_checked" android:state_checked="true" />
<item android:drawable="@drawable/item_block_tab_unchecked" android:state_checked="false" />
</selector>
bg_blue:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="@color/titlecolor" />
</shape>
bg_grid_press:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<solid android:color="#f5f5f5" />
</shape>
</item>
<item android:state_pressed="false">
<shape>
<solid android:color="#f4f4f4" />
</shape>
</item>
</selector>
bg_white:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/colorWhite"/>
</shape>
适配器代码:
package com.daobo.wand.ui.adapter
import androidx.recyclerview.widget.RecyclerView
import com.daobo.wand.ui.FunctionItem
import android.content.Context
import android.view.ViewGroup
import android.view.LayoutInflater
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView.Adapter
import com.daobo.wand.BR
import com.daobo.wand.R
import com.daobo.wand.databinding.LayoutGridItemBinding
import com.daobo.wand.ui.viewbean.FuncItemViewBean
class FunctionAdapter : Adapter<RecyclerView.ViewHolder> {
private var data = ArrayList<FunctionItem>()
private val context: Context
constructor(context: Context, data: ArrayList<FunctionItem>) {
this.context = context
if (data != null) {
this.data = data
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (0 == viewType) {
TitleViewHolder(DataBindingUtil.inflate(LayoutInflater.from(context),
R.layout.layout_function_text, parent, false))
} else {
FunctionViewHolder(DataBindingUtil.inflate(LayoutInflater.from(context),
R.layout.layout_grid_item, parent, false))
}
}
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
if (0 == getItemViewType(position)) {
var binding : ViewDataBinding = DataBindingUtil.getBinding(viewHolder.itemView)!!
binding.setVariable(BR.name, data[position].mName)
binding.executePendingBindings()
//val holder = viewHolder as TitleViewHolder
//holder.text.setText(data[position].mName)
} else {
var binding : ViewDataBinding = DataBindingUtil.getBinding(viewHolder.itemView)!!
val fi = data[position]
var funcItemViewBean = FuncItemViewBean()
funcItemViewBean.BtnImgSrc = if (fi.mIsSelect) R.drawable.item_block_selected else R.drawable.item_block_add
funcItemViewBean.IvImgSrc = context.resources.getIdentifier(fi.mImageUrl, "drawable",
context.packageName)
funcItemViewBean.Name = fi.mName
binding.setVariable(BR.funcItem, funcItemViewBean)
binding.executePendingBindings()
else R.drawable.ic_block_add)
var layoutGridItemBinding = binding as LayoutGridItemBinding
layoutGridItemBinding.btn.setOnClickListener {
val f = data[position]
if (!f.mIsSelect) {
if (listener != null) {
if (listener!!.add(f)) {
f.mIsSelect = true
notifyDataSetChanged()
}
}
}
}
}
}
override fun getItemViewType(position: Int): Int {
return if (data[position].isTitle) 0 else 1
}
override fun getItemCount(): Int {
return data.size
}
inner class TitleViewHolder(dataBinding: ViewDataBinding) : RecyclerView.ViewHolder(dataBinding.root)
inner class FunctionViewHolder(dataBinding: ViewDataBinding) : RecyclerView.ViewHolder(dataBinding.root)
interface OnItemAddListener {
fun add(item: FunctionItem): Boolean
}
private var listener: OnItemAddListener? = null
fun setOnItemAddListener(listener: OnItemAddListener) {
this.listener = listener
}
}
package com.daobo.wand.ui.adapter
import androidx.recyclerview.widget.RecyclerView
import com.daobo.wand.ui.listener.ItemTouchHelperAdapter
import com.daobo.wand.ui.FunctionItem
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import com.daobo.wand.BR
import com.daobo.wand.R
import com.daobo.wand.databinding.LayoutGridItemBinding
import com.daobo.wand.ui.viewbean.FuncItemViewBean
import java.util.*
import kotlin.collections.ArrayList
class FunctionBlockAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>, ItemTouchHelperAdapter {
private var data = ArrayList<FunctionItem>()
private var context: Context? = null
constructor(context: Context, data: ArrayList<FunctionItem>) {
this.context = context
if (data != null) {
this.data = data
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return FunctionViewHolder(DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.layout_grid_item, parent, false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val fi = data[position]
var funcItemViewBean = FuncItemViewBean()
funcItemViewBean.BtnImgSrc = R.drawable.item_block_delete
funcItemViewBean.IvImgSrc = context!!.resources.getIdentifier(fi.mImageUrl, "drawable",
context!!.packageName)
funcItemViewBean.Name = fi.mName
var binding : ViewDataBinding = DataBindingUtil.getBinding(holder.itemView)!!
binding.setVariable(BR.funcItem, funcItemViewBean)
binding.executePendingBindings()
var layoutGridItemBinding = binding as LayoutGridItemBinding
layoutGridItemBinding.btn.setOnClickListener {
val fi = data.removeAt(position)
if (listener != null) {
listener!!.remove(fi)
}
notifyDataSetChanged()
}
}
override fun getItemCount(): Int {
return data.size
}
override fun onItemMove(holder: RecyclerView.ViewHolder, fromPosition: Int, targetPosition: Int) {
if (fromPosition < data.size && targetPosition < data.size) {
Collections.swap(data, fromPosition, targetPosition)
notifyItemMoved(fromPosition, targetPosition)
}
}
override fun onItemSelect(holder: RecyclerView.ViewHolder) {
holder.itemView.scaleX = 0.8f
holder.itemView.scaleY = 0.8f
}
override fun onItemClear(holder: RecyclerView.ViewHolder) {
holder.itemView.scaleX = 1.0f
holder.itemView.scaleY = 1.0f
}
override fun onItemDismiss(holder: RecyclerView.ViewHolder) {
}
inner class FunctionViewHolder(dataBinding: ViewDataBinding) : RecyclerView.ViewHolder(dataBinding.root)
interface OnItemRemoveListener {
fun remove(item: FunctionItem)
}
private var listener: OnItemRemoveListener? = null
fun setOnItemRemoveListener(listener: OnItemRemoveListener) {
this.listener = listener
}
}
listener包下面定义的监听:
package com.daobo.wand.ui.listener
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
class DefaultItemCallback : ItemTouchHelper.Callback {
private var touchHelperAdapter: ItemTouchHelperAdapter
constructor(touchHelperAdapter: ItemTouchHelperAdapter) {
this.touchHelperAdapter = touchHelperAdapter
}
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val dragFlags =
ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT //允许上下左右的拖动
return ItemTouchHelper.Callback.makeMovementFlags(dragFlags, 0)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
touchHelperAdapter.onItemMove(
viewHolder,
viewHolder.adapterPosition,
target.adapterPosition
)
return true
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE)
touchHelperAdapter.onItemSelect(viewHolder!!)
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
if (!recyclerView.isComputingLayout)
touchHelperAdapter.onItemClear(viewHolder)
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
override fun isLongPressDragEnabled(): Boolean {
return true
}
override fun isItemViewSwipeEnabled(): Boolean {
return false
}
}
package com.daobo.wand.ui.listener
import androidx.recyclerview.widget.ItemTouchHelper
class DefaultItemTouchHelper : ItemTouchHelper {
constructor(callback: ItemTouchHelper.Callback): super(callback)
}
package com.daobo.wand.ui.listener
import androidx.recyclerview.widget.RecyclerView
interface ItemTouchHelperAdapter {
fun onItemMove(holder: RecyclerView.ViewHolder, fromPosition: Int, targetPosition: Int)
fun onItemSelect(holder: RecyclerView.ViewHolder)
fun onItemClear(holder: RecyclerView.ViewHolder)
fun onItemDismiss(holder: RecyclerView.ViewHolder)
}
注意:在layout_function_item binding imageview 的src 资源,我们需要定义下:
package com.daobo.wand.ui.util
import android.os.Build
import android.view.View
import android.widget.ImageView
import androidx.annotation.RequiresApi
import androidx.core.content.res.ResourcesCompat
import androidx.databinding.BindingAdapter
class ImageBindingUtil {
companion object {
@BindingAdapter("android:src")
@JvmStatic
fun setSrc(view: ImageView, resId: Int) {
view.setImageResource(resId)
}
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
@JvmStatic
@BindingAdapter("background")
fun setBackground(view: View, id: Int) {
//view.resources.getDrawable(id)
view.background = ResourcesCompat.getDrawable(view.resources, id, null)
}
}
}
binding adapter方法只需要全局static 声明下,但是要使用注解@JvmStatic
工具类 用户来初始化数据,且将数据保存到preferences中:
package com.daobo.wand.ui.util
import android.content.Context
import android.content.SharedPreferences
import com.daobo.wand.ui.FunctionItem
import com.daobo.wand.ui.TabItem
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
class SFUtils {
private var context: Context? = null
private var sp: SharedPreferences? = null
var tabItems : ArrayList<TabItem> = ArrayList()
constructor(context: Context) {
this.context = context
sp = context.getSharedPreferences("menudata", Context.MODE_PRIVATE)
initData()
}
/**
* 把已有的菜单数据 配置到
* assets menuData 配置文件中
*/
fun getAllFunctionWithState(): ArrayList<FunctionItem> {
var allData = sp!!.getString("allData", "")
var functionItems = ArrayList<FunctionItem>()
var tabItems: List<TabItem>? = null
if ("" == allData) {
try {
val ins = context!!.assets.open("menuData.txt")
val isr = InputStreamReader(ins)
val br = BufferedReader(isr)
var str: String? = br.readLine()
val sbuf = StringBuffer()
while (str != null) {
sbuf.append(str)
}
br.close()
isr.close()
ins.close()
allData = sbuf.toString()
tabItems = Gson().fromJson(allData, object : TypeToken<List<TabItem>>(){}.type)
if (tabItems != null) {
for (i in tabItems!!.indices) {
val functionItem = FunctionItem(tabItems[i].getTabName(), true)
functionItems.add(functionItem)
functionItems.addAll(tabItems[i].getFunctionItems()!!)
}
}
} catch (e: IOException) {
e.printStackTrace()
}
} else {
functionItems = Gson().fromJson(allData, object : TypeToken<List<FunctionItem>>(){}.type)
}
return functionItems
}
/**
* 把用户的选择 保存到Preferences 文件
* 即使 app 推出运行,内存数据清理掉
* */
fun getSelectFunctionItem(): ArrayList<FunctionItem> {
val selData = sp!!.getString("selData", "")
var functionItems: ArrayList<FunctionItem>? = null
functionItems = if ("" == selData) {
ArrayList()
} else {
Gson().fromJson(selData, object : TypeToken<List<FunctionItem>>(){}.type)
}
return functionItems!!
}
fun getAllFunctionItems () : ArrayList<FunctionItem>{
var functionItems = ArrayList<FunctionItem>()
for (i in tabItems!!.indices) {
val functionItem = FunctionItem(tabItems[i].getTabName(), true)
functionItems.add(functionItem)
functionItems.addAll(tabItems[i].getFunctionItems()!!)
}
return functionItems
}
fun getTabFunctionItems(): ArrayList<FunctionItem> {
var tabItemsCount = 0 //用于标记之后滑动的位置
var functionItems = ArrayList<FunctionItem>()
for (i in tabItems!!.indices) {
val functionItem =
FunctionItem(tabItems[i].getTabName(), true, tabItemsCount)
tabItemsCount += tabItems[i].getFunctionItems()!!.size + 1
functionItems.add(functionItem)
}
return functionItems
}
fun getTabNames(): ArrayList<FunctionItem> {
var allData = sp!!.getString("allData", "")
var functionItems = ArrayList<FunctionItem>()
var tabItems: List<TabItem>? = null
if ("" == allData) {
try {
val ins = context!!.assets.open("menuData.txt")
val isr = InputStreamReader(ins)
val br = BufferedReader(isr)
var str: String? = br.readLine()
val sbuf = StringBuffer()
while (str != null) {
sbuf.append(str)
}
br.close()
isr.close()
ins.close()
allData = sbuf.toString()
tabItems = Gson().fromJson(allData, object : TypeToken<List<TabItem>>(){}.type)
if (tabItems != null) {
var tabItemsCount = 0 //用于标记之后滑动的位置
for (i in tabItems!!.indices) {
val functionItem =
FunctionItem(tabItems[i].getTabName(), true, tabItemsCount)
tabItemsCount += tabItems[i].getFunctionItems()!!.size + 1
functionItems.add(functionItem)
}
}
} catch (e: IOException) { e.printStackTrace() }
}
//else{
// functionItems = Gson().fromJson(allData, object : TypeToken<List<FunctionItem>>(){}.type)
// }
return functionItems
}
fun saveSelectFunctionItem(selData: List<FunctionItem>) {
sp!!.edit().putString("selData", Gson().toJson(selData)).apply()
}
fun saveAllFunctionWithState(allData: List<FunctionItem>) {
sp!!.edit().putString("allData", Gson().toJson(allData)).apply()
}
private fun initData () {
var arrayList : ArrayList<FunctionItem> = ArrayList()
arrayList.add(FunctionItem("充值中心",false,"icon_home_selected","#86c751"))
arrayList.add(FunctionItem("信用卡还款",false,"icon_home_selected","#86c751"))
arrayList.add(FunctionItem("生活缴费",false,"icon_home_selected","#86c751"))
arrayList.add(FunctionItem("城市服务",false,"icon_home_selected","#86c751"))
arrayList.add(FunctionItem("生活号",false,"icon_home_selected","#86c751"))
arrayList.add(FunctionItem("我的客服",false,"icon_home_selected","#86c751"))
arrayList.add(FunctionItem("我的快递",false,"icon_home_selected","#86c751"))
arrayList.add(FunctionItem("医疗健康",false,"icon_home_selected","#86c751"))
arrayList.add( FunctionItem("记账本",false,"icon_home_selected","#86c751"))
arrayList.add(FunctionItem("城市一卡通",false,"icon_home_selected","#86c751"))
arrayList.add(FunctionItem("发票管家",false,"icon_home_selected","#86c751"))
arrayList.add(FunctionItem("蚂蚁宝卡",false,"icon_home_selected","#86c751"))
arrayList.add(FunctionItem("车主服务",false,"icon_home_selected","#86c751"))
arrayList.add( FunctionItem("天天有料",false,"icon_home_selected","#86c751"))
var tabItem : TabItem = TabItem("便民生活", arrayList)
tabItems.add(tabItem)
var arrayList1 : ArrayList<FunctionItem> = ArrayList()
arrayList1.add(FunctionItem("余额宝",false,"icon_home_selected","#86c751"))
arrayList1.add(FunctionItem("花呗",false,"icon_home_selected","#86c751"))
arrayList1.add(FunctionItem("芝麻信用",false,"icon_home_selected","#86c751"))
arrayList1.add(FunctionItem("蚂蚁借呗",false,"icon_home_selected","#86c751"))
arrayList1.add(FunctionItem("股票",false,"icon_home_selected","#86c751"))
arrayList1.add(FunctionItem("保险服务",false,"icon_home_selected","#86c751"))
arrayList1.add(FunctionItem("汇率换算",false,"icon_home_selected","#86c751"))
arrayList1.add(FunctionItem("理财小工具",false,"icon_home_selected","#86c751"))
var tabItem1 : TabItem = TabItem("财务管理",arrayList1)
tabItems.add(tabItem1)
var arrayList2 : ArrayList<FunctionItem> = ArrayList()
arrayList2.add(FunctionItem("转账",false,"icon_home_selected","#86c751"))
arrayList2.add(FunctionItem("红包",false,"icon_home_selected","#86c751"))
arrayList2.add(FunctionItem("AA收款",false,"icon_home_selected","#86c751"))
arrayList2.add(FunctionItem("亲密付",false,"icon_home_selected","#86c751"))
arrayList2.add(FunctionItem("上银汇款",false,"icon_home_selected","#86c751"))
arrayList2.add(FunctionItem("话费卡转让",false,"icon_home_selected","#86c751"))
var tabItem2 : TabItem = TabItem("资金往来",arrayList2)
tabItems.add(tabItem2)
var arrayList3 : ArrayList<FunctionItem> = ArrayList()
arrayList3.add(FunctionItem("游戏中心",false,"icon_home_selected","#86c751"))
arrayList3.add(FunctionItem("出境",false,"icon_home_selected","#86c751"))
arrayList3.add(FunctionItem("彩票",false,"icon_home_selected","#86c751"))
arrayList3.add(FunctionItem("人脸识别",false,"icon_home_selected","#86c751"))
arrayList3.add(FunctionItem("奖励金",false,"icon_home_selected","#86c751"))
arrayList3.add(FunctionItem("世界杯",false,"icon_home_selected","#86c751"))
var tabItem3 : TabItem = TabItem("购物娱乐",arrayList3)
tabItems.add(tabItem3)
var arrayList4 : ArrayList<FunctionItem> = ArrayList()
arrayList4.add(FunctionItem("大学生活",false,"icon_home_selected","#86c751"))
arrayList4.add(FunctionItem("爱心捐赠",false,"icon_home_selected","#86c751"))
arrayList4.add(FunctionItem("蚂蚁森林",false,"icon_home_selected","#86c751"))
arrayList4.add(FunctionItem("蚂蚁庄园",false,"icon_home_selected","#86c751"))
arrayList4.add(FunctionItem("中小学",false,"icon_home_selected","#86c751"))
arrayList4.add(FunctionItem("运动",false,"icon_home_selected","#86c751"))
var tabItem4 : TabItem = TabItem("教育公益",arrayList4)
tabItems.add(tabItem4)
var arrayList5 : ArrayList<FunctionItem> = ArrayList()
arrayList5.add(FunctionItem("淘票票",false,"icon_home_selected","#86c751"))
arrayList5.add(FunctionItem("滴滴出行",false,"icon_home_selected","#86c751"))
arrayList5.add(FunctionItem("饿了么外卖",false,"icon_home_selected","#86c751"))
arrayList5.add(FunctionItem("天猫",false,"icon_home_selected","#86c751"))
arrayList5.add(FunctionItem("淘宝",false,"icon_home_selected","#86c751"))
arrayList5.add(FunctionItem("火车票机票",false,"icon_home_selected","#86c751"))
var tabItem5 : TabItem = TabItem("第三方服务",arrayList5)
tabItems.add(tabItem5)
}
}
要使用我的图片资源,下载链接 。
主菜单页面布局与交互设计
本文介绍了一个Android应用的主菜单页面布局实现方案及交互设计思路,包括XML布局文件、自定义控件、适配器及监听器等组件的设计与实现细节。
874

被折叠的 条评论
为什么被折叠?



