如何用Java/Kotlin创建含放射状按钮布局的Android RecyclerView/ListView
实现Android放射状按钮布局(含中央控制台)
一、整体布局结构
用ConstraintLayout作为父容器,将中央控制台(如搜索按钮)固定在布局中心,再叠加RecyclerView,通过内边距让RecyclerView的子View避开中央区域,实现放射状按钮环绕中心控制台的效果。
示例布局代码(XML):
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 中央控制台区域 --> <LinearLayout android:id="@+id/central_control" android:layout_width="120dp" android:layout_height="120dp" android:background="#EEEEEE" android:gravity="center" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <Button android:id="@+id/search_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="搜索" /> </LinearLayout> <!-- 放射状按钮的RecyclerView --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/radiant_recycler" android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" android:padding="60dp" /> <!-- 内边距匹配中央控制台的半宽,避免按钮覆盖 --> </androidx.constraintlayout.widget.ConstraintLayout>
二、自定义放射状LayoutManager
RecyclerView默认的LayoutManager不支持放射状排列,需要自定义实现核心布局逻辑,以下是Kotlin版本:
class RadiantLayoutManager : RecyclerView.LayoutManager() { private val centerX get() = width / 2f private val centerY get() = height / 2f // 中心到按钮的距离(可根据需求调整) private val radius = 200f override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams { return RecyclerView.LayoutParams( RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT ) } override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) { if (itemCount == 0) { detachAndScrapAttachedViews(recycler) return } detachAndScrapAttachedViews(recycler) // 计算每个按钮的角度间隔 val angleStep = 360f / itemCount for (i in 0 until itemCount) { val view = recycler.getViewForPosition(i) addView(view) measureChildWithMargins(view, 0, 0) // 转换角度为弧度,计算按钮中心坐标 val angle = Math.toRadians((i * angleStep).toDouble()).toFloat() val viewCenterX = centerX + radius * cos(angle) val viewCenterY = centerY + radius * sin(angle) // 计算按钮的左上右下坐标 val left = (viewCenterX - view.measuredWidth / 2f).toInt() val top = (viewCenterY - view.measuredHeight / 2f).toInt() val right = left + view.measuredWidth val bottom = top + view.measuredHeight layoutDecorated(view, left, top, right, bottom) } } // 放射状布局无需滚动,禁止RecyclerView滚动 override fun canScrollVertically(): Boolean = false override fun canScrollHorizontally(): Boolean = false }
三、RecyclerView适配器
常规适配器实现按钮数据绑定,Kotlin示例:
class RadiantButtonAdapter(private val buttonTexts: List<String>) : RecyclerView.Adapter<RadiantButtonAdapter.ButtonViewHolder>() { inner class ButtonViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val btn: Button = itemView.findViewById(R.id.sr_btn) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ButtonViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_radiant_button, parent, false) return ButtonViewHolder(view) } override fun onBindViewHolder(holder: ButtonViewHolder, position: Int) { holder.btn.text = buttonTexts[position] holder.btn.setOnClickListener { // 自定义按钮点击逻辑 } } override fun getItemCount() = buttonTexts.size }
对应按钮item布局item_radiant_button.xml:
<?xml version="1.0" encoding="utf-8"?> <Button xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/sr_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="SR" android:backgroundTint="#2196F3" android:textColor="#FFFFFF" />
四、在Activity中初始化
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val recyclerView = findViewById<RecyclerView>(R.id.radiant_recycler) recyclerView.layoutManager = RadiantLayoutManager() // 示例按钮数据,可根据业务需求动态生成 val buttonTexts = listOf("SR1", "SR2", "SR3", "SR4", "SR5", "SR6") recyclerView.adapter = RadiantButtonAdapter(buttonTexts) // 中央搜索按钮点击事件 findViewById<Button>(R.id.search_btn).setOnClickListener { // 自定义搜索逻辑 } } }
五、Java版本核心代码(可选)
自定义LayoutManager的Java实现:
public class RadiantLayoutManager extends RecyclerView.LayoutManager { private float getCenterX() { return getWidth() / 2f; } private float getCenterY() { return getHeight() / 2f; } private final float radius = 200f; @Override public RecyclerView.LayoutParams generateDefaultLayoutParams() { return new RecyclerView.LayoutParams( RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT ); } @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { if (getItemCount() == 0) { detachAndScrapAttachedViews(recycler); return; } detachAndScrapAttachedViews(recycler); float angleStep = 360f / getItemCount(); for (int i = 0; i < getItemCount(); i++) { View view = recycler.getViewForPosition(i); addView(view); measureChildWithMargins(view, 0, 0); float angle = (float) Math.toRadians(i * angleStep); float viewCenterX = getCenterX() + radius * (float) Math.cos(angle); float viewCenterY = getCenterY() + radius * (float) Math.sin(angle); int left = (int) (viewCenterX - view.getMeasuredWidth() / 2f); int top = (int) (viewCenterY - view.getMeasuredHeight() / 2f); int right = left + view.getMeasuredWidth(); int bottom = top + view.getMeasuredHeight(); layoutDecorated(view, left, top, right, bottom); } } @Override public boolean canScrollVertically() { return false; } @Override public boolean canScrollHorizontally() { return false; } }
适配器与Activity的Java实现逻辑与Kotlin一致,仅需语法转换即可。
内容的提问来源于stack exchange,提问作者Jafar Sadiq SH
相关产品推荐
相关产品推荐

