写了个APP「原色」—— 基于中国传统色
简介
这是一个工具类APP
- 颜色筛选以及关模糊查询
- 颜色详情信息查看以及复制
- 色卡分享
- 自定义主题色(长按色卡)
- 小组件支持
已上架应用宝/App Store,搜索原色即可找到
最初是做了个1.0版本(MVP),功能比较简单,后面感觉没什么可加的就放置一边了
最近比较空闲又拿起来,bug修一点加一点,界面改了又改哈哈哈,然后现在迭代到2.0版本(预览图为 iOS)
除了界面大换新,也增加了一些功能,比如颜色搜索、筛选、小组件等。Android与iOS基本一致,除了搜索筛选界面不一样:
下面介绍一下一些功能的实现以及碰到的问题
色卡与文字处理
在1.0版本对色卡的背景颜色和文字颜色关系处理比较粗暴简单,当系统出去浅色模式下。文字就在原来颜色的基础上降低亮度;在深色模式下文字就降低亮度,但是这种方式在部分过亮或者过暗背景上还是很难看清。
2.0版本对色卡和文字颜色都做了动态处理:
色卡:渐变处理,从上往下,比例为0——0.3——1.0。
在浅色模式下颜色为color(alpha=0.7)——color——color;
在深色模式下颜色为color(brightness + 0.2)——color——color
色卡文字:根据颜色是否为亮色进行处理,判断规则为:
颜色为亮色,则降低0.3亮度,否则 降低0.1亮度
在iOS上有用于修改view
亮度的方法:brightness(Double)
,可惜安卓没有直接修改视图或者颜色亮度的方法,于是我就通过修改颜色 HSL
来达到类似的效果。为了和ios的brightness
一致,changeBrightness
的范围我设置为[-1F, 1F]
,但outHsl[2]
的范围是[0F, 1F]
,所以计算做了一些调整:
// 修改颜色亮度
@ColorInt
fun @receiver:ColorInt Int.brightness(changeBrightness: Float): Int {
val outHsl = FloatArray(3)
ColorUtils.colorToHSL(this, outHsl)
if (changeBrightness <= 0) {
outHsl[2] = outHsl[2] * (1 + changeBrightness)
} else {
outHsl[2] = outHsl[2] + (1 - outHsl[2]) / 10 * changeBrightness * 10
}
return ColorUtils.HSLToColor(outHsl)
}
// 判断颜色为两色或者暗色
fun @receiver:ColorInt Int.isLight(): Boolean {
val red = Color.valueOf(this).red()
val green = Color.valueOf(this).green()
val blue = Color.valueOf(this).blue()
val brightness = (red * 299 + green * 587 + blue * 114) / 1000
return brightness > 0.5
}
颜色信息展示(BottomSheet)
设置BottomSheet默认完全展开,设置方法如下:
override fun onStart() {
super.onStart()
val behavior = BottomSheetBehavior.from(requireView().parent as View)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
至于圆角处理,只需要在主题文件里写好就行了:
<!--Rounded Bottom Sheet-->
<style name="ThemeOverlay.App.BottomSheetDialog" parent="ThemeOverlay.Material3.BottomSheetDialog">
<item name="bottomSheetStyle">@style/ModalBottomSheetDialog</item>
</style>
<style name="ModalBottomSheetDialog" parent="Widget.Material3.BottomSheet.Modal">
<item name="shapeAppearance">@style/ShapeAppearance.App.LargeComponent</item>
<item name="shouldRemoveExpandedCorners">false</item>
</style>
<style name="ShapeAppearance.App.LargeComponent" parent="ShapeAppearance.Material3.LargeComponent">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">24dp</item>
</style>
如果不修改sheet背景色(默认为白色/黑色),只需要设置以上主题就可以了,但是如果修改了背景色,就需要在代码里对背景进行圆角处理,不能直接设置背景色,不然在圆角下面还会有颜色:
// 会存在背景色
// binding.bottomSheetLayout.setBackgroundColor(sheetBackground)
// 设置圆角背景
binding.bottomSheetLayout.setCornerBackground(24, 24, 0, 0, sheetBackground)
private fun View.setCornerBackground(leftRadius: Int, topRadius: Int, rightRadius: Int, bottomRadius: Int, @ColorInt color: Int) {
val shape = ShapeDrawable(RoundRectShape(
floatArrayOf(
leftRadius.dp(requireContext()).toFloat(),
leftRadius.dp(requireContext()).toFloat(),
topRadius.dp(requireContext()).toFloat(),
topRadius.dp(requireContext()).toFloat(),
rightRadius.dp(requireContext()).toFloat(),
rightRadius.dp(requireContext()).toFloat(),
bottomRadius.dp(requireContext()).toFloat(),
bottomRadius.dp(requireContext()).toFloat(),
), null, null)
)
shape.paint.color = color
this.background = shape
}
fun Int.dp(context: Context): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this.toFloat(),
context.resources.displayMetrics
).toInt()
}
小技巧(应该算啊吧):当我们有icon
需要适配深色模式的时候,可以把android:tint
的值设置为?android:attr/textColorPrimary
,就不用自己做额外处理了
<vector android:autoMirrored="true" android:height="24dp"
android:tint="?android:attr/textColorPrimary" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>
SearchView背景色修改
可以看之前发的文章
效果参考上面的搜索筛选界面
滚动到指定位置(带偏移)
点击左上角的骰子图标,可以随机颜色(滚动到某一位置),通常我们使用recyclerView.scrollToPosition(int position)
就可以实现。但是这个方法,会滚动到item的最边缘(红线位置),但是我希望他能够保留一定边距(绿色框框),看起来界面会和谐一点
解决办法如下:
private fun RecyclerView.scrollToPositionWithOffset(position: Int, offset: Int) {
(layoutManager as GridLayoutManager)
.scrollToPositionWithOffset(position, offset)
}
// 调用
binding.recyclerView.scrollToPositionWithOffset(
Random.nextInt(0, adapter.itemCount - 1),
16.dp(this)
)
用了kotlin扩展方法方便调用,这里的layoutManager
根据实际情况来,我这里用列表到的是GridLayoutManager
,
小组件(App Widget)
提供了两种布局,小尺寸只显示颜色名称,大尺寸显示拼音和名称,效果如下:
可能在部分系小尺寸统显示有问题,懒得搞了,这个组件大小搞的我脑壳疼,也没看到过什么好的解决方案,以下是我的配置:
// 31以下
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="57dp"
android:minHeight="51dp"
android:updatePeriodMillis="0"
android:previewImage="@drawable/appwidget_preview"
android:initialLayout="@layout/layout_wide_widget"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen">
</appwidget-provider>
// 31及以上
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:targetCellWidth="4"
android:targetCellHeight="2"
android:minResizeWidth="57dp"
android:minResizeHeight="51dp"
android:maxResizeWidth="530dp"
android:maxResizeHeight="450dp"
android:updatePeriodMillis="0"
android:previewImage="@drawable/appwidget_preview"
android:initialLayout="@layout/layout_wide_widget"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:widgetFeatures="reconfigurable">
</appwidget-provider>
因为布局比较简单,所以尺寸兼容效果相对好一点
主动刷新小组件
当我们app没有运行的时候,添加小组件是没有数据的,当我们打开app的时候,通知小组件更新
// 刷新 Widget
sendBroadcast(Intent(this, ColorWidgetProvider::class.java).apply {
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
val ids = AppWidgetManager.getInstance(application)
.getAppWidgetIds(ComponentName(application, ColorWidgetProvider::class.java))
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
})
周期更新小组件
可通过配置updatePeriodMillis
来设置时间,但是容易失效,所以使用WorkManager
来通知更新,虽然WorkManager
保证了周期执行,但如果app不在后台的话还是无法更新的,因为发送了广播app收不到,可能再加个服务就可以了,不加不加了
遗留的小问题
MIUI无法添加小组件
这段代码在MIUI上不生效,无法弹出添加小组件的弹窗
AppWidgetManager.getInstance(this).requestPinAppWidget(xxx)
如果添加该权限并授权,可以成功添加,但是无任何弹窗提示
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
当然最奇怪的还是我居然在MIUI的安卓小部件里找不到我自己的组件,我在原生都能看得到我的小组件的,也不知道是不是还需要配置什么,再一次头大
总结
这个app断断续续也写了好几个月,也也没啥功能还写了这么久。之前还看了下swiftUI,写了个iOS版本的,给我的感觉就是上手简单,写起来效率快多了,
其实这篇文章早就可以发了,就为了等app上架,可真煎熬。
个人开发者上架应用真的是难于上青天,对于安卓平台,国内一些主流应用市场(华米OV)都不对个人开发者开放了,要求低点的比如酷安、应用宝个人是可以上传的,但是需要软著,这又是一个头疼的事,申请基本一个月起步,除非花几百块找别人,三五天下证;
PS:现在App需要备案了,除非你不联网,应用宝就可以上架,酷安也要强制备案
ios也让我很难受,可能是我自己的问题,我注册流程走到付款了,当时想着先写完app再注册好了,就没付款,后来再去注册就提示账户存在问题,邮件联系后告诉我:
您的账号由于一个或多个原因,您无法完成 Apple Developer Program 的注册。
我想问清楚具体是什么原因,客服告知由系统判定,他们无法知道也无法干预,然后我寻思罢了,我再注册一个,还是失败,这次提示:
您使用另一 Apple ID 通过 Apple Developer App 验证了身份。要继续,请使用之前用于验证您身份的 Apple ID。
然后我又去把原来的账号注销掉,依旧无法注册成功...,最后无奈使用别人的信息注册了一个乛 з乛
所以,想注册苹果开发者的,注意最好是在同一个设备上一次性完成注册。
来源:juejin.cn/post/7294441582983626788