设计每行多个文本视图的可滚动列表
Design a scrollable list of multiple textviews per line
我正在开发一个音乐 collection 应用程序,我正在努力实现我当前的目标:设计一个包含 3 个文本视图的可滚动列表 - 曲目编号、曲目标题和曲目持续时间 - 将代表歌曲一张专辑。我希望列表的布局由 3 列组成,宽度固定,按预期对齐,没有特定轨道的标题文本视图,例如,延伸到下面轨道的持续时间文本视图之上。
我考虑了以下选项:
使用一个 RecyclerView,其 ViewHolder 布局由 3 个文本视图的水平 LinearLayout 组成。这里的问题是我不知道如何以这种方式保持列对齐。我应该使用确定的 dps 为文本视图设置固定宽度吗?那么如何设置整行以填充设备的整个宽度?
每列使用 3 个 RecyclerView。我已经中途尝试了这个想法,并且列对齐得很好,但是滚动一个 RV 显然不会滚动其他 RV,并且要解决这个问题 - 正如我在另一个问题中看到的那样 - 我需要搞砸滚动机制,它似乎仍然很容易出错,RV 仍然可能设法不同步或应用程序崩溃。
我知道有 GridLayout(我不熟悉,不知道它是否能解决我的问题),但由于它现在被认为是遗留的,我宁愿避免使用它。
最重要的是,我想知道 "best practice" 假设有一种方法可以解决这样的问题。
谢谢!
对此的最佳解决方案是使用 RecyclerView。
1 - 您可以将 TextView 的宽度设置为 match_parent 如果您希望它们填满屏幕的宽度。
2 - 您只需要一个显示视图列表的回收器视图。
如果你能给我一张你想要达到的目标的图片
我试图为一行创建一个包含 3 个 TextView 的可滚动列表。
正如您提到的,我们可以使用 RecyclerView 作为可滚动列表。
activity_main.xml
<ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ConstraintLayout>
对于一个连续有 3 个 TextView 的列表项,我使用了如下的 GridLayout。
list_item.xml
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="3"
android:rowCount="1" >
<TextView
android:id="@+id/trackNumber"
android:layout_columnWeight="1"
android:layout_width="0dp"
android:background="@android:color/holo_red_light" />
<TextView
android:id="@+id/trackTitle"
android:layout_columnWeight="6"
android:layout_width="0dp"
android:ellipsize="end"
android:background="@android:color/holo_green_light" />
<TextView
android:id="@+id/trackDuration"
android:layout_columnWeight="1"
android:layout_width="0dp"
android:background="@android:color/holo_blue_light" />
</GridLayout>
为每个 TextView 设置一个 layout_columnWeight
值以指定应为其分配的水平 space 的相对比例。
每个 TextView 的 layout_width
值都设置为 0dp
以占据分配的 space.
的完整宽度
但是我们可以使用 ConstraintLayout 而不是 GridLayout 并获得相同的结果。
正如上面问题中提到的,GridLayout 被标记为旧版。
最好使用 ConstraintLayout。
list_item.xml
<ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/trackNumber"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="1"
android:background="@android:color/holo_red_light"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/trackTitle"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/trackTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="6"
android:ellipsize="end"
android:background="@android:color/holo_green_light"
app:layout_constraintStart_toEndOf="@id/trackNumber"
app:layout_constraintEnd_toStartOf="@id/trackDuration"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/trackDuration"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="1"
android:background="@android:color/holo_blue_light"
app:layout_constraintStart_toEndOf="@id/trackTitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</ConstraintLayout>
为每个 TextView 设置一个 layout_constraintHorizontal_weight
值以指定应为其分配的水平 space 的相对比例。
每个 TextView 的 layout_width
值都设置为 0dp
以占据分配的 space.
的完整宽度
这只是一个 Kotlin class 来存储曲目信息。
class TrackInfo(
var trackNumber: Int,
var trackTitle: String?,
var durationMinutes: Short,
var durationSeconds: Short
)
这是 RecyclerView 的适配器 class 和 ViewHolder class。
class TrackInfoAdapter(private val items: List<TrackInfo>, private val mContext: Context) :
RecyclerView.Adapter<ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false)
)
}
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val trackInfo = items[position]
holder.trackNumber.text = trackInfo.trackNumber.toString()
holder.trackTitle.text = trackInfo.trackTitle
// Use String.format() to add leading zero for single digit durationSeconds value
val formattedDurationSeconds = "%02d".format(trackInfo.durationSeconds)
holder.trackDuration.text = "${trackInfo.durationMinutes}:$formattedDurationSeconds"
}
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val trackNumber = view.trackNumber
val trackTitle = view.trackTitle
val trackDuration = view.trackDuration
}
这是 MainActivity。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Prepare list of track information
val trackInfoList = mutableListOf<TrackInfo>().apply {
// track 1
add(
TrackInfo(1, "Lady GaGa - Poker Face",
4, 4)
)
// track 2
add(
TrackInfo(2, "T.I. featuring Rihanna - Live Your Life",
4, 1)
)
// track 3
add(
TrackInfo(3, "Kanye West - Stronger",
5, 11)
)
// track 4
add(
TrackInfo(4, "Leon Haywood - I Wanna Do Something Freaky To You",
6, 0)
)
// track 5
add(
TrackInfo(5, "Hilary Duff - Reach Out",
4, 16)
)
}
recyclerView.layoutManager = LinearLayoutManager(baseContext)
// Set track information list to Adapter of RecyclerView
recyclerView.adapter = TrackInfoAdapter(trackInfoList, baseContext)
}
}
结果:
我希望这能回答您问题的第一部分。
您提到过“当有一个没有标题的特定曲目时,在持续时间文本视图之上拉伸”。
为此,可以使用 android:layout_columnSpan
属性将 GridLayout 的 children 配置为跨越多个单元格。
但是要处理 trackTitle null 情况并相应地设置 TextView 的 layout_columnSpan 等属性,我认为最好为列表项编写一个自定义视图 class。
这是受 Farshad Tahmasbi's answer I found 启发的替代解决方案。
在这个screenshot中你可以看到结果。 (持续时间列现在为空)。
mTracksRecyclerView.setAdapter(new TrackListAdapter(getContext(), musicItem.getTracks()));
GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), 12);
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int type = position % 3 ;
if (type == 0) {
// Position
return 2;
} else if (type == 1) {
// Title
return 8;
} else {
// Duration
return 2;
}
}
});
mTracksRecyclerView.setLayoutManager(gridLayoutManager);
我正在开发一个音乐 collection 应用程序,我正在努力实现我当前的目标:设计一个包含 3 个文本视图的可滚动列表 - 曲目编号、曲目标题和曲目持续时间 - 将代表歌曲一张专辑。我希望列表的布局由 3 列组成,宽度固定,按预期对齐,没有特定轨道的标题文本视图,例如,延伸到下面轨道的持续时间文本视图之上。
我考虑了以下选项:
使用一个 RecyclerView,其 ViewHolder 布局由 3 个文本视图的水平 LinearLayout 组成。这里的问题是我不知道如何以这种方式保持列对齐。我应该使用确定的 dps 为文本视图设置固定宽度吗?那么如何设置整行以填充设备的整个宽度?
每列使用 3 个 RecyclerView。我已经中途尝试了这个想法,并且列对齐得很好,但是滚动一个 RV 显然不会滚动其他 RV,并且要解决这个问题 - 正如我在另一个问题中看到的那样 - 我需要搞砸滚动机制,它似乎仍然很容易出错,RV 仍然可能设法不同步或应用程序崩溃。
我知道有 GridLayout(我不熟悉,不知道它是否能解决我的问题),但由于它现在被认为是遗留的,我宁愿避免使用它。
最重要的是,我想知道 "best practice" 假设有一种方法可以解决这样的问题。
谢谢!
对此的最佳解决方案是使用 RecyclerView。
1 - 您可以将 TextView 的宽度设置为 match_parent 如果您希望它们填满屏幕的宽度。
2 - 您只需要一个显示视图列表的回收器视图。
如果你能给我一张你想要达到的目标的图片
我试图为一行创建一个包含 3 个 TextView 的可滚动列表。
正如您提到的,我们可以使用 RecyclerView 作为可滚动列表。
activity_main.xml
<ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ConstraintLayout>
对于一个连续有 3 个 TextView 的列表项,我使用了如下的 GridLayout。
list_item.xml
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="3"
android:rowCount="1" >
<TextView
android:id="@+id/trackNumber"
android:layout_columnWeight="1"
android:layout_width="0dp"
android:background="@android:color/holo_red_light" />
<TextView
android:id="@+id/trackTitle"
android:layout_columnWeight="6"
android:layout_width="0dp"
android:ellipsize="end"
android:background="@android:color/holo_green_light" />
<TextView
android:id="@+id/trackDuration"
android:layout_columnWeight="1"
android:layout_width="0dp"
android:background="@android:color/holo_blue_light" />
</GridLayout>
为每个 TextView 设置一个 layout_columnWeight
值以指定应为其分配的水平 space 的相对比例。
每个 TextView 的 layout_width
值都设置为 0dp
以占据分配的 space.
但是我们可以使用 ConstraintLayout 而不是 GridLayout 并获得相同的结果。 正如上面问题中提到的,GridLayout 被标记为旧版。 最好使用 ConstraintLayout。
list_item.xml
<ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/trackNumber"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="1"
android:background="@android:color/holo_red_light"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/trackTitle"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/trackTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="6"
android:ellipsize="end"
android:background="@android:color/holo_green_light"
app:layout_constraintStart_toEndOf="@id/trackNumber"
app:layout_constraintEnd_toStartOf="@id/trackDuration"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/trackDuration"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="1"
android:background="@android:color/holo_blue_light"
app:layout_constraintStart_toEndOf="@id/trackTitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</ConstraintLayout>
为每个 TextView 设置一个 layout_constraintHorizontal_weight
值以指定应为其分配的水平 space 的相对比例。
每个 TextView 的 layout_width
值都设置为 0dp
以占据分配的 space.
这只是一个 Kotlin class 来存储曲目信息。
class TrackInfo(
var trackNumber: Int,
var trackTitle: String?,
var durationMinutes: Short,
var durationSeconds: Short
)
这是 RecyclerView 的适配器 class 和 ViewHolder class。
class TrackInfoAdapter(private val items: List<TrackInfo>, private val mContext: Context) :
RecyclerView.Adapter<ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater.from(mContext).inflate(R.layout.list_item, parent, false)
)
}
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val trackInfo = items[position]
holder.trackNumber.text = trackInfo.trackNumber.toString()
holder.trackTitle.text = trackInfo.trackTitle
// Use String.format() to add leading zero for single digit durationSeconds value
val formattedDurationSeconds = "%02d".format(trackInfo.durationSeconds)
holder.trackDuration.text = "${trackInfo.durationMinutes}:$formattedDurationSeconds"
}
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val trackNumber = view.trackNumber
val trackTitle = view.trackTitle
val trackDuration = view.trackDuration
}
这是 MainActivity。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Prepare list of track information
val trackInfoList = mutableListOf<TrackInfo>().apply {
// track 1
add(
TrackInfo(1, "Lady GaGa - Poker Face",
4, 4)
)
// track 2
add(
TrackInfo(2, "T.I. featuring Rihanna - Live Your Life",
4, 1)
)
// track 3
add(
TrackInfo(3, "Kanye West - Stronger",
5, 11)
)
// track 4
add(
TrackInfo(4, "Leon Haywood - I Wanna Do Something Freaky To You",
6, 0)
)
// track 5
add(
TrackInfo(5, "Hilary Duff - Reach Out",
4, 16)
)
}
recyclerView.layoutManager = LinearLayoutManager(baseContext)
// Set track information list to Adapter of RecyclerView
recyclerView.adapter = TrackInfoAdapter(trackInfoList, baseContext)
}
}
结果:
我希望这能回答您问题的第一部分。
您提到过“当有一个没有标题的特定曲目时,在持续时间文本视图之上拉伸”。
为此,可以使用 android:layout_columnSpan
属性将 GridLayout 的 children 配置为跨越多个单元格。
但是要处理 trackTitle null 情况并相应地设置 TextView 的 layout_columnSpan 等属性,我认为最好为列表项编写一个自定义视图 class。
这是受 Farshad Tahmasbi's answer I found
在这个screenshot中你可以看到结果。 (持续时间列现在为空)。
mTracksRecyclerView.setAdapter(new TrackListAdapter(getContext(), musicItem.getTracks()));
GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), 12);
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int type = position % 3 ;
if (type == 0) {
// Position
return 2;
} else if (type == 1) {
// Title
return 8;
} else {
// Duration
return 2;
}
}
});
mTracksRecyclerView.setLayoutManager(gridLayoutManager);