如何将水平 RecyclerView 对齐到底部?例如,我有一个带有 LinearLayoutManager 的水平 RecyclerView,如下所示: 但是,我希望所有 RecyclerView 项目都与底线对齐,如下所示: 有没有办法做到这一点?
Ksenia's questions
不知道为什么,万一我showcaseFlatMap
用以下代码调用方法:
fun showcaseFlatMap() {
val colors: Flowable<String> = Flowable.just("orange", "red", "green")
.flatMap<String> { colorName -> simulateRemoteOperation(colorName) }
colors.subscribe { value -> println("Subscriber received: {$value}") }
}
fun simulateRemoteOperation(color: String): Flowable<String> {
return Flowable.intervalRange(1, color.length.toLong(), 0, 200, TimeUnit.MILLISECONDS)
.map { iteration -> color + iteration }
}
我得到以下结果:
Subscriber received: {orange1}
Subscriber received: {red1}
Subscriber received: {green1}
Subscriber received: {orange2}
Subscriber received: {red2}
Subscriber received: {green2}
Subscriber received: {orange3}
Subscriber received: {red3}
Subscriber received: {green3}
Subscriber received: {orange4}
Subscriber received: {green4}
Subscriber received: {orange5}
Subscriber received: {green5}
Subscriber received: {orange6}
但是,如果我更改此方法中的代码:
val colors: Flowable<String> = Flowable.just("orange", "red", "green")
.flatMap<String> { colorName -> simulateRemoteOperation(colorName) }
编码:
val colors: Flowable<String> = Flowable.just("orange", "red", "green")
colors.flatMap<String> { colorName -> simulateRemoteOperation(colorName) }
结果完全不同:
Subscriber received: {orange}
Subscriber received: {red}
Subscriber received: {green}
造成这种结果变化的原因是什么?我预计输出不会改变。
在方法的类ActorDetailsActivity
中onCreate
,我有以下代码块:
Log.d(TAG, "onCreate method was invoked")
在app/build.gradle文件中,我有以下 buildTypes:
buildTypes {
debugProguardTest {
initWith debug
useProguard true
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
matchingFallbacks = ['debug']
}
release {
useProguard true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
proguard-rules.pro文件本身如下所示:
-printusage ./removed-usaged.txt
-printseeds ./kept-seeds.txt
#-printmapping ./mapping.txt
-optimizationpasses 3
#-dontobfuscate
-dontwarn kotlinx.coroutines.flow.*
-dontwarn kotlinx.coroutines.flow.internal.*
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** w(...);
public static *** v(...);
public static *** i(...);
}
我还在gradle.properties文件中禁用了R8:
android.enableR8=false
但是,在构建项目后(使用 buildVariant debugProguardTest),方法调用
Log.d(TAG, "onCreate method was invoked")
尚未从 apk 中删除。在 Apk Analyzer 中,我仍然看到以下行:
const-string v0, "onCreate method was invoked"
为什么会这样?Proguard 如何Log.d()
从 apk 中截断方法调用?
阅读文章Using Dagger in Android apps,我遇到了以下几行:
使用活动时,请在调用 super.onCreate() 之前将 Dagger 注入活动的 onCreate() 方法,以避免片段恢复问题。在 super.onCreate() 的恢复阶段,一个活动附加了可能想要访问活动绑定的片段。
它让我感到惊讶,因为我一直使用
someComponent.inject(this)
在super.onCreate()
活动之后。并且在这方面出现了问题:之前注入匕首可以避免恢复碎片的哪些问题super.onCreate()
?短语“可能想要访问活动绑定的片段”是什么意思?如果有人可以用代码示例解释这一点,那就太好了......
我不知道为什么它没有显示ProgressBar
。在布局中,最初是这样设置的:
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/double_margin"
android:layout_marginRight="@dimen/double_margin"
android:indeterminate="true"
android:visibility="gone"
style="@style/AppTheme"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent" />
在代码中,它将 android:visibility 更改true
为按下按钮的时间和false
数据到达之后 - 虽然按下按钮和数据到达之间有很多时间 - 我设置了最多 10 秒的延迟,但它仍然根本ProgressBar
不可见。
class ActorActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_actor_details)
val moreBtn = findViewById<Button>(R.id.more_btn)
val progressBar = findViewById<ProgressBar>(R.id.progressbar)
moreBtn.setOnClickListener {
progressBar.isVisible = true
viewModel.getMoreInfo(actor.name)
}
viewModel.actor.observe(this, Observer<Actor> {
progressBar.isVisible = false
})
}
}
ProgressBar
此外,如果您最初没有在布局中设置它
android:visibility="gone"
然后他是可见的。
UPD:ProgressBar
使用-om的完整标记:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/more_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="@dimen/double_margin"
android:layout_marginTop="@dimen/double_margin"
android:text="@string/more_btn_label"
android:textColor="@android:color/white"
android:background="@color/colorPrimary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/avatar" />
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/double_margin"
android:layout_marginRight="@dimen/double_margin"
android:indeterminate="true"
android:visibility="gone"
style="@style/AppTheme"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<ImageView
android:id="@+id/avatar"
android:layout_height="0dp"
android:layout_width="match_parent"
android:contentDescription="@string/avatar"
android:scaleType="centerCrop"
android:layout_marginTop="@dimen/double_margin"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/more_btn"
tools:src="@tools:sample/avatars" />
<TextView
android:id="@+id/details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/avatar"
tools:text="@tools:sample/lorem" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
我有一个ChipGroup
小部件来显示标签:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
...
<com.google.android.material.chip.ChipGroup
android:id="@+id/tags"
android:layout_width="0dp"
android:layout_height="wrap_content"
...
android:theme="@style/Theme.MaterialComponents"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/desc"
app:singleSelection="true"
app:singleLine="false"
app:chipSpacingVertical="@dimen/tag_padding"
/>
...
</androidx.constraintlayout.widget.ConstraintLayout>
标签的数量可以是任何东西,它们的长度可以是任何东西,我想检查ChipGroup
小部件的高度何时超过两行。为了实现这个目标,我需要知道ChipGroup
. 为此,我执行以下操作:
fun displayChips(tagsView: ChipGroup, tagNames: List<String>) {
// вычисление фактической высоты дисплея
val displayWidth = resources.displayMetrics.widthPixels
Log.d(TAG, "displayWidth = $displayWidth")
var chipHeight = 0
var maxChipGroupHeight = 0
val labelCount = tagNames.size
for (i in 0 until labelCount) {
val chip = Chip(tagsView.context)
chip.text = tagNames[i]
// ...
chip.textSize = 14f
chip.maxHeight = 22
if (i == 0) {
// измерение высоты одного тэга
chip.measure(ChipGroup.LayoutParams.WRAP_CONTENT, ChipGroup.LayoutParams.WRAP_CONTENT)
chipHeight = chip.measuredHeight
Log.d(TAG, "chipHeight = $chipHeight")
// измерение высоты двух строк с тэгами (включая расстояние между строками)
maxChipGroupHeight = 2 * chipHeight + resources.getDimensionPixelSize(R.dimen.tag_padding)
Log.d(TAG, "maxChipGroupHeight = $maxChipGroupHeight")
}
tagsView.addView(chip)
// вычисление высоты всего tagsView - НЕ РАБОТАЕТ :(
tagsView.measure(displayWidth, ConstraintLayout.LayoutParams.WRAP_CONTENT)
val tagsViewHeight = tagsView.measuredHeight
Log.d(TAG, "i = $i, tagsViewHeight = $tagsViewHeight")
if (tagsViewHeight > maxChipGroupHeight) {
Log.d(TAG, "i = $i, tagsView height increased two lines")
// ..
}
}
}
作为执行此代码的结果,我得到以下日志:
显示宽度 = 1080
芯片高度 = 96
最大芯片组高度 = 204
i=0,标签视图高度=96
i=1,标签视图高度=96
i=2,标签视图高度=96
i=3,标签视图高度=96
i=4,标签视图高度=96
i=5,标签视图高度=96
尽管标签在屏幕上看起来像这样:
那些。问题是我一直得到相同的高度 tagsView.measuredHeight
(等于 96),尽管它肯定会改变(因为最后有三行带有标签)。我也试过调用方法
tagsView.requestLayout()
tagsView.invalidate()
之前tagsView.measure(...)
,但这并没有改变任何东西。
在“计算机体系结构”一书中,Tanenbaum 有以下几行:
...制造商开始相互竞争,试图发布最好的程序。这些命令没有什么价值,因为使用现有程序可以轻松解决相同的任务,但它们通常工作得更快一些。例如,许多计算机使用 INC (INCrement) 指令,该指令将数字加一。然后就已经有了通用的加法命令ADD,不需要再引入新的加一的命令了。但是,INC 命令比 ADD 命令稍快,因此它也包含在命令集中。
如果有人能解释为什么 INC 命令比 ADD 命令快,将不胜感激。
有没有办法在 Java 代码中使用Context.toast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT)
包中的方法?androidx.core.widget
比如我导入这个方法:
import static androidx.core.widget.ToastKt.toast;
然后我尝试使用它:
toast((CharSequence) errorMsg, Toast.LENGTH_LONG);
但是,在这种情况下,Android Studio 会在红色下划线,并带有“无法解析方法 toast”警告。甚至可以在 Java 代码中使用这种方法吗?
在什么“边界”情况下可以使用协程Unconfined dispatcher
执行?
Unconfined dispatcher 是一种高级机制,可以在某些极端情况下提供帮助,即不需要为以后执行而调度协程或产生不希望的副作用,因为必须立即执行协程中的某些操作。
据我了解,它转换为:“无限制调度程序是一种高级机制,在某些情况下可能有用需要马上执行。”
我是否正确理解这意味着应该在不需要使用延迟或类似机制延迟协程执行的情况下使用此调度程序?为什么进一步写出 Unconfined dispatcher 不应该在共享代码中使用?
我也不清楚挂起函数如何确定线程,在调用它之后,执行用 Dispatchers.Unconfined
. 同一篇文章的一个例子:
fun main() = runBlocking<Unit> {
launch(Dispatchers.Unconfined) { // not confined -- will work with main thread
println("Unconfined : I'm working in thread ${Thread.currentThread().name}")
delay(500)
println("Unconfined : After delay in thread ${Thread.currentThread().name}")
}
}
结论:
Unconfined : I'm working in thread main
Unconfined : After delay in thread kotlinx.coroutines.DefaultExecutor
函数是如何delay()
确定协程下一次执行的线程的kotlinx.coroutines.DefaultExecutor
?
这个 Kotlin 语言结构的名称是什么(点后面的空括号),它是什么意思?
CoroutineScope.()
我是否正确理解这代表任何扩展功能(在此示例中为 class CoroutineScope
),或者不是?
从一个片段过渡到第二个片段时,我对共享元素使用动画,同时为一个片段设置动画ImageView
。一切正常。但是当我将ImageView
第二个片段中的这个包装到另一个片段中时LinearLayout
:
<LinearLayout
android:transitionName="@string/image_transition"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@color/colorPrimaryDark">
<ImageView
android:id="@+id/iv_city_image"
android:transitionName="@string/image_transition"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="matrix"
android:adjustViewBounds="true"
android:layout_gravity="center"
android:contentDescription="@string/content_descr_city_image" />
</LinearLayout>
动画消失。为什么会这样?
这是之前发生的事情:
这就是第二个片段的整个布局之前的样子:
<?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="wrap_content"
android:orientation="vertical"
xmlns:tools="http://schemas.android.com/tools" >
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_city_image"
android:transitionName="@string/image_transition"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="matrix"
android:adjustViewBounds="true"
android:layout_gravity="center"
android:contentDescription="@string/content_descr_city_image" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@drawable/header_background"
android:orientation="vertical"
android:padding="@dimen/padding" >
<TextView
android:id="@+id/tv_city_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:textSize="22sp"
android:textStyle="bold"
android:textColor="@color/white"
tools:text="@tools:sample/cities" />
<TextView
android:id="@+id/tv_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:textSize="16sp"
android:textColor="@color/white"
tools:text="@tools:sample/cities" />
</LinearLayout>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/padding">
<TextView
android:id="@+id/tv_weather"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textStyle="bold"
android:textSize="24sp"
tools:text="@tools:sample/cities"/>
</LinearLayout>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
以及包裹ImageView
在另一个之后的样子LinearLayout
:
我正在尝试安装 MySQL(x86_64 上的 Win64 版本 8.0.12(MySQL 社区服务器 - GPL))。我在控制台中执行命令mysqld -u root -p --default-file=...
并收到以下错误:
d:\Programs\MySQL\bin>mysqld -u root -p --default-file="D:\Programs\MySQL\my.ini"
Enter password: ************
mysqld: Can not perform keyring migration : Invalid --keyring-migration-source option.
2018-10-24T19:53:39.944570Z 0 [System] [MY-010116] [Server] d:\Programs\MySQL\bin\mysqld.exe (mysqld 8.0.12) starting as process 8184
2018-10-24T19:53:39.949553Z 0 [ERROR] [MY-011084] [Server] Keyring migration failed.
2018-10-24T19:53:39.954386Z 0 [ERROR] [MY-010119] [Server] Aborting
2018-10-24T19:53:39.955692Z 0 [System] [MY-010910] [Server] d:\Programs\MySQL\bin\mysqld.exe: Shutdown complete (mysqld 8.0.12) MySQL Community Server - GPL.
为什么我在第一次启动mysql时需要这个迁移?有什么方法可以禁用此密钥迁移模式?或者如何解决这个错误?我尝试使用控制台中的选项执行命令--keyring-migration-source
,但随后出现以下错误:
d:\Programs\MySQL\bin>mysqld -u root -p --default-file="D:\Programs\MySQL\my.ini" --keyring-migration-source="D:\Programs\MySQL\lib\plugin\keyring_file.dll"
Enter password: ************
mysqld: Can not perform keyring migration : Invalid --keyring-migration-destination option.
2018-10-24T19:58:50.142426Z 0 [System] [MY-010116] [Server] d:\Programs\MySQL\bin\mysqld.exe (mysqld 8.0.12) starting as process 15164
2018-10-24T19:58:50.147956Z 0 [ERROR] [MY-011084] [Server] Keyring migration failed.
2018-10-24T19:58:50.152461Z 0 [ERROR] [MY-010119] [Server] Aborting
2018-10-24T19:58:50.153799Z 0 [System] [MY-010910] [Server] d:\Programs\MySQL\bin\mysqld.exe: Shutdown complete (mysqld 8.0.12) MySQL Community Server - GPL.
并且官方网站上的示例中给出的keyring_encrypted_file文件不在我的MySQL目录中。
PS my.ini 文件包含以下几行:
early-plugin-load=D:/Programs/MySQL/lib/plugin/keyring_file.dll
keyring_file_data=D:/Programs/MySQL/data/keyring/keyring
我还从配置文件中删除了这些行,但仍然出现相同的错误(无效的 --keyring-migration-source 选项)。
我已经覆盖equals
了数据类中的方法Point
:
data class Point(val x: Int, val y: Int) {
companion object {
val TAG = "Kotlin"
}
override fun equals(other: Any?): Boolean {
Log.d(TAG, "overrided equals")
if (other === this) return true
if (other !is Point) return false
return other.x == x && other.y == y
}
}
我正在尝试按如下方式测试此方法:
Log.d(TAG, "${Point(1, 2) == Point(1, 2)}")
Log.d(TAG, "${Point(1, 2) != Point(1, 5)}")
Log.d(TAG, "${null == Point(1, 2)}")
Log.d(TAG, "${Point(1, 2) == null}")
但在控制台中执行此代码后,我得到:
覆盖等于
真的
覆盖等于
真的
错误的
错误的
由此可见, equals
仅在前两次比较中调用了被覆盖的方法。为什么不要求第三次比较对我来说也很清楚(因为“Kotlin 中的运算符默认不支持交换性(反转操作数)”)。但我不明白为什么 equals
最后一次,第四次比较不调用覆盖的方法(Point(1, 2) == null)
......
毕竟,值null
必须包含在许多类型Any?
中。例如,代码
val n = null
Log.d(TAG, "null ${if (n is Any?) "is" else "isn't"} type of Any?")
返回
null 是 Any 的类型?
因此,这个比较必须匹配被覆盖方法的签名(override fun equals(other: Any?): Boolean
)......为什么这个比较没有调用被覆盖的方法?
kotlin.text包的Char.kt文件有以下方法equals()
:
public fun Char.equals(other: Char, ignoreCase: Boolean = false): Boolean {
if (this == other) return true
if (!ignoreCase) return false
if (this.toUpperCase() == other.toUpperCase()) return true
if (this.toLowerCase() == other.toLowerCase()) return true
return false
}
而且我不太明白为什么你需要一个带有toLowerCase()
...的字符串在比较之后的情况下
if (this.toUpperCase() == other.toUpperCase())
返回false
,比较
if (this.toLowerCase() == other.toLowerCase())
可以退货true
吗?
是否可以LiveData
在保持观察者不变的情况下更改可观察对象?特别是,我有以下情况。在 Dao (Room) 中,我返回 LiveData 作为方法的结果:
@Dao
public interface DayDao {
@Query("SELECT * FROM day WHERE date LIKE :date")
LiveData<Day> getDay(String date);
}
ViewModel 看起来像这样:
public class DayViewModel extends AndroidViewModel {
private AppDatabase appDatabase;
private LiveData<Day> day;
public DayViewModel(Application application) {
super(application);
appDatabase = App.getDatabase();
}
public LiveData<Day> getDay(String date) {
Log.e(TAG, "getDay");
day = appDatabase.dayDao().getDay(date);
if (day != null && day.getValue() != null) {
Log.e(TAG, "res = " + day.getValue().getDate());
}
return day;
}
...
}
我想从具有以下方法的片段中接收来自 LiveData 的数据:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_day, container, false);
...
model = ViewModelProviders.of(getActivity()).get(DayViewModel.class);
String date = ...;
dayLiveData = model.getDay(date);
dayLiveData.observe(this, day -> {
Log.e(TAG, "onObserve");
// Update the UI.
});
return view;
}
public void updateTitle() {
String date = ...;
dayLiveData = model.getDay(date);
}
该方法updateTitle()
是从 Activity 调用的。但是,日志显示它首先被调用 - 当onCreateView
方法被调用时 - onObserve
(并且 UI 被更新),然后getDay()
ViewModel 方法被调用,但日志显示
Log.e(TAG, "getDay");
解决了,并且
Log.e(TAG, "res = " + day.getValue().getDate());
不,虽然给定的数据date
肯定应该是(即返回的方法appDatabase.dayDao().getDay(date)
只会Day
返回结果,但返回的相同方法LiveData<Day>
不会返回)。是否仍然可以使用 LiveData 在片段方法中进行这样的 UI 更新updateTitle()
,还是不可能?
前台进程服务和可见进程服务有什么区别?
文档说:
如果满足以下任何条件,则进程被视为前台进程:
...
它包含与用户正在与之交互的操作相关联的服务。
它包含一个“在前台”运行的服务,一个名为 startForeground() 的服务。
它包含一个执行生命周期回调(onCreate()、onStart() 或 onDestroy())之一的服务。
...
和
如果满足以下任何条件,则认为进程可见:
...
它包含与可见或前台活动相关的服务。
我不清楚这些服务之间有什么区别,tk。为了使进程被认为是可见的,该服务必须与“可见活动(1)或前台活动(2)”相关联,但即使对于前台进程,它也被写入“它包含与活动,用户与之交互”(它 = 1?)或“在前台运行”(它 = 2?)。这不是一回事吗?
我正在使用一个小部件AppCompatEditText
:
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/et_body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/tooltip_body"
app:layout_constraintTop_toBottomOf="@id/et_title" />
我希望在输入这个小部件时,会弹出一个提示(提示),就像示例中的标题:
但是,如果我将其设置为 simple android:hint
,则工具提示仅在开始输入之前显示,并且在输入之后,它不会转到顶部。使用旧的支持库,这可以通过在EditText
里面放置一个标签来实现<android.support.design.widget.TextInputLayout>
,但是有没有办法只用 Jetpack ( androidx.*
) 来做到这一点?