android - 使用 ConstraintLayout 的片段 onCreateView 不遵守 ConstraintSet
问题描述
我在不使用 XML 文件的情况下以编程方式构建我的所有 UI。我的主 UI 类继承自 AppCompatActivity() 并使用一个名为 ClientFragment 的片段作为 UI 的主要部分
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val layout = ConstraintLayout(this)
layout.id = R.id.constraint_layout_main_activity
layout.setBackgroundColor(Color.GREEN)
setContentView(layout)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.add(R.id.constraint_layout_main_activity, ClientFragment(), "CLIENT_FRAGMENT")
.commit()
}
}
}
我有一个名为 BaseGridFragment 的抽象基 Fragment 类,我的所有片段都将实现它。BaseGridFragment 包含显示在屏幕上的所有小部件。
abstract class BaseGridFragment : Fragment() {
protected lateinit var progressBar: ProgressBar
protected lateinit var swipeRefreshLayout: SwipeRefreshLayout
private lateinit var recyclerView: RecyclerView
protected lateinit var recyclerAdapter: RecyclerAdapter
private lateinit var constraintSet: ConstraintSet
private lateinit var gridLayoutManager: GridLayoutManager
protected lateinit var searchView: SearchView
protected val requestSender = RequestSender()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
context?.let { context ->
val layout = ConstraintLayout(context)
layout.id = R.id.constraint_layout_base_fragment
layout.setBackgroundColor(Color.BLUE)
progressBar = ProgressBar(context)
progressBar.id = R.id.progress_bar_all_loading
searchView = SearchView(context)
searchView.id = R.id.search_view_base_fragment
searchView.queryHint = "Search"
searchView.isIconifiedByDefault = false
gridLayoutManager = GridLayoutManager(context, 2)
recyclerAdapter =
RecyclerAdapter {
onItemClicked(it)
}
recyclerView = RecyclerView(context)
recyclerView.id = R.id.recycler_view_all_list_of_items
recyclerView.adapter = recyclerAdapter
recyclerView.layoutManager = gridLayoutManager
swipeRefreshLayout = SwipeRefreshLayout(context)
swipeRefreshLayout.id = R.id.swipe_refresh_layout_all_item_list_container
swipeRefreshLayout.addView(recyclerView)
swipeRefreshLayout.setOnRefreshListener { fetchData() }
layout.addView(swipeRefreshLayout)
layout.addView(progressBar)
layout.addView(searchView)
constraintSet = ConstraintSet()
constraintSet.constrainWidth(R.id.search_view_base_fragment, ConstraintSet.PARENT_ID)
constraintSet.connect(
R.id.search_view_base_fragment,
ConstraintSet.LEFT,
ConstraintSet.PARENT_ID,
ConstraintSet.LEFT
)
constraintSet.connect(
R.id.search_view_base_fragment,
ConstraintSet.TOP,
ConstraintSet.PARENT_ID,
ConstraintSet.TOP
)
constraintSet.connect(
R.id.search_view_base_fragment,
ConstraintSet.RIGHT,
ConstraintSet.PARENT_ID,
ConstraintSet.RIGHT
)
constraintSet.constrainWidth(R.id.search_view_base_fragment, ConstraintSet.PARENT_ID)
constraintSet.constrainHeight(
R.id.search_view_base_fragment,
ConstraintSet.WRAP_CONTENT
)
constraintSet.constrainWidth(R.id.progress_bar_all_loading, ConstraintSet.WRAP_CONTENT)
constraintSet.constrainHeight(R.id.progress_bar_all_loading, ConstraintSet.WRAP_CONTENT)
constraintSet.centerHorizontally(R.id.progress_bar_all_loading, ConstraintSet.PARENT_ID)
constraintSet.centerVertically(R.id.progress_bar_all_loading, ConstraintSet.PARENT_ID)
constraintSet.connect(
R.id.swipe_refresh_layout_all_item_list_container,
ConstraintSet.TOP,
R.id.search_view_base_fragment,
ConstraintSet.BOTTOM
)
constraintSet.constrainWidth(
R.id.swipe_refresh_layout_all_item_list_container,
ConstraintSet.WRAP_CONTENT
)
constraintSet.connect(
R.id.swipe_refresh_layout_all_item_list_container,
ConstraintSet.BOTTOM,
ConstraintSet.PARENT_ID,
ConstraintSet.BOTTOM
)
constraintSet.applyTo(layout)
searchView.visibility = View.GONE
swipeRefreshLayout.visibility = View.GONE
return layout
}
return null
}
abstract fun fetchData()
abstract fun onItemClicked(item: RecyclerAdapter.Item)
}
ClientFragment 实现了这个抽象类,它负责获取将填充所有小部件的数据。
class ClientFragment : BaseGridFragment() {
override fun onAttach(context: Context) {
super.onAttach(context)
(activity as AppCompatActivity).supportActionBar?.title = "Clients"
fetchData()
}
override fun fetchData() {
requestSender.sendRequest(context!!, "/api/clients", JSONArray()) { result ->
when (result) {
is RequestSender.RequestSenderResult.Success -> {
if (result.data.length() != 1) {
return@sendRequest
}
recyclerAdapter.items.clear()
val clients = result.data[0] as JSONArray
for (i in 0 until clients.length()) {
val client = clients.get(i) as JSONArray
if (client.length() != 3) {
Log.e(
"ClientActivity",
"Invalid client data length while parsing client response. Skipping client."
)
continue
}
recyclerAdapter.items.add(
RecyclerAdapter.Item(
client[0] as String,
client[1] as String,
URL(client[2] as String)
)
)
}
// Clearing data and calling notifyDataSetChanged() is relatively expensive. If
// number of items returned causes UI smoothness problem consider notifyDataSetChanged
// for specific indexes.
activity!!.runOnUiThread { recyclerAdapter.notifyDataSetChanged() }
progressBar.visibility = View.GONE
swipeRefreshLayout.visibility = View.VISIBLE
searchView.visibility = View.VISIBLE
swipeRefreshLayout.isRefreshing = false
}
is RequestSender.RequestSenderResult.Error -> {
val alert = AlertDialog.Builder(context!!)
alert.setPositiveButton("OK", null)
when (result.error) {
is RequestSender.RequestSenderError.NetworkError -> {
alert.setTitle("Network Error")
alert.setMessage(result.error.description)
}
is RequestSender.RequestSenderError.ClientError -> {
alert.setTitle("Request Error")
alert.setMessage(result.error.description)
}
is RequestSender.RequestSenderError.InternalServerError -> {
alert.setTitle("Server Error")
alert.setMessage(result.error.description)
}
is RequestSender.RequestSenderError.UnrecognizedNetworkStatusCode -> {
alert.setTitle("Network Error")
alert.setMessage(result.error.description)
}
is RequestSender.RequestSenderError.UnableToDeserializeResponse -> {
alert.setTitle("Deserialization Error")
alert.setMessage(result.error.description)
}
}
activity!!.runOnUiThread {
alert.create().show()
progressBar.visibility = View.GONE
swipeRefreshLayout.visibility = View.VISIBLE
swipeRefreshLayout.isRefreshing = false
}
}
}
}
}
override fun onItemClicked(item: RecyclerAdapter.Item) {
(activity as MainActivity).onClientClicked(item)
}
}
当我运行此代码时,BaseGridFragment 中的 ConstraintSet 无法正常工作。当应用程序第一次启动时,会有一个网络调用来获取一些数据。在获取数据时,我在屏幕中间显示了一个微调器。下载数据后,UI 更改为顶部显示搜索栏,下方显示 CardView。
MainActivity 中的根视图是一个 ConstraintLayout,BaseGridFragment 中的主视图也是如此,它包含所有其他小部件。我对两个主要视图进行了颜色编码:MainActivity 中的布局为绿色,BaseGridFragment 中的布局为蓝色。
根据我对 supportFragmentManager 工作原理的理解,以下代码应该使 BaseGridFragment 中的布局成为 MainActivity 中布局的直接子级。因此,我在 BaseGridFragment 中定义的引用 ConstraintSet.PARENT_ID 的任何约束都应该起作用:
supportFragmentManager.beginTransaction()
.add(R.id.constraint_layout_main_activity, ClientFragment(), "CLIENT_FRAGMENT")
.commit(
正如您从屏幕截图中看到的那样,蓝色区域并未像应有的那样扩展为绿色:微调器未居中,并且 CardView 根本没有显示在搜索栏下方。如果我重构我的代码并且我摆脱了所有片段并简单地将我的 BaseGridFragment 布局中的所有内容放入我的活动中,那么一切都会正常工作,所以一定有一些关于我在这里遗漏的片段的内容。
我已经寻找解决方案,但我遇到的所有示例都使用 XML 来定义它们的 UI,然后在 onCreateView() 中使用充气器。
解决方案
我没有看到您将布局参数设置为主布局
layout.layoutParams = ConstraintLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
推荐阅读
- oracle - Oracle DB Developer 导入 .dmp 时出错
- sql - 了解引用触发操作的工作原理
- unity3d - 如何从中间点开始 Vector3.Lerp?
- php - 在编辑器上更新 header.php 时,Wordpress 主题崩溃
- angularjs - 不能在 angular.js 中工作 ng-click,为什么我不明白
- php - 手动订单 - 使用前缀更新每个订单项目元标签
- python - 运行烧瓶代码时出现未找到请求的 URL 错误
- elasticsearch - “由于冲突而无法合并:[无法更新 [_source] 的启用设置,无法更新排除 [_source]] 的设置”
- c - 如何在不使用 extern 关键字的情况下访问全局值?
- node.js - 如何在承诺中使用变量?节点JS