豪言壮志
今天天气不错,挺风和日丽的,出去逛逛街,溜溜弯,拍上一张美图,配上高逼格的格言警句,发到朋友圈,一片鸡汤文就此诞生。。。
最近会有事没事会在朋友圈发动态:写一句格言警句,配上一张意境美图。因为懒,我总是在想,图片能够自动随机网上选择该多好,能不能直接将文字写在图片上,能不能一键分享到朋友圈,再配上二维码,那感觉,妙。。。
花半天时间找了一下,目标就它了:微信的「图片编辑器」,然后自己配上图片的素材,不就能满足上述条件嘛。
可是图片素材只能是手机相册,万一以后用完了怎么办,而且没有二维码。微信估计不干了:你行你上啊。对啊,那就自己干。
确定效果图
先不说马赛克、涂鸦、裁剪等功能,我现在需要的是文字编辑的功能,所以就先搞一搞这个。
微信的效果我就不贴出来了,大家估计都体验过吧,哈哈。
什么,你没有用过?呵呵,(尴尬),那就只是你一个而已。。。对,你应该与时俱进一下。
纳尼,你们都没有用过?!好吧,你们怎么看到我的文章的,难道不是搜索微信图片编辑器过来的吗~
给大家贴上目标效果图:
哦哦,你要微信的效果是吧,OK,拿出你的手机,给我发张图片就可以啦。
开始实现布局界面
好,开始撸代码了。
创建工程,引用最新的 Kotlin 库:
// dependency.gradle
def anko_version = "0.10.5"
ext {
ankoSdk = "org.jetbrains.anko:anko-sdk25:$anko_version" // sdk15, sdk19, sdk21, sdk23 are also available
ankoAppCompat = "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
ankoListeners = "org.jetbrains.anko:anko-sdk25-listeners:$anko_version"
}
// basemodule.gradle
dependencies {
// 最新的 api 引用方式
api ankoSdk
api ankoAppCompat
api ankoListeners
}
在 `MainActivity` 中创建布局:
relativeLayout {
editToolBar = linearLayout {
button("编辑")
}
// todo: 之后采用 constraintLayout
editMask = verticalLayout {
relativeLayout {
textView("取消").lparams(wrapContent, wrapContent) {
alignParentLeft()
}
textView("完成").lparams {
alignParentRight()
}
}
editView = editText {
padding = 15
gravity = Gravity.TOP
backgroundColor = Color.GREEN
// 刚进入界面时不获取焦点
isFocusable = false
}.lparams(matchParent, 0, 1f)
// scrollView 是为 view 漂浮在软键盘上做准备
scrollView {
linearLayout {
textView("tool bar")
}
}
}.lparams(matchParent, matchParent) {
alignParentBottom()
}
}
额,界面效果丑是丑了点,但咱看中的是实力,对吧。况且,以后咱可以美颜呀~~
向上弹出界面
网上通用的几个解决方案:Dialog,PopupWindow,Activity 等都可以实现相同的效果。
但是我觉得比较重,再加上要写比较多的代码(这是主要原因),所以我采用动画的方式实现这个效果。
给「编辑」按钮添加点击事件,开启动画:
onClick {
ValueAnimator
.ofInt(0, rootView.height) // 设置遮罩的高度变化是从 0 到 整个界面的高度
.setDuration(100) // 动画时长:100ms
.apply {
// 添加监听,动画中数值的每一次变化,都是遮罩界面高度的变化
addUpdateListener {
editMask.layoutParams = editMask.layoutParams.apply {
// 遮罩高度动态变化,值为每次动画改变后的值
height = it.animatedValue as Int
}
}
}
.start() // 开始动画
}
好啦,基本上完事大吉啦,聪明如你,肯定知道这个动画什么意思吧。如果你第一次接触,就仔细看代码,有注释哦。
细节优化
动画启动时,需要隐藏「编辑」按钮;
点击「取消」或「完成」时,隐藏「编辑界面」,显示「编辑」按钮;
动画不需要每次都创建等等。
最终界面布局:
relativeLayout {
editToolBar = linearLayout {
button("编辑").onClick {
openEditMask()
}
}.lparams(matchParent, wrapContent) {
alignParentBottom()
}
// todo: 之后采用 constraintLayout
editMask = verticalLayout {
relativeLayout {
textView("取消").lparams(wrapContent, wrapContent) {
alignParentLeft()
}.onClick { closeEditMask() }
textView("完成").lparams {
alignParentRight()
}.onClick { closeEditMask() }
}
editView = editText {
padding = 15
gravity = Gravity.TOP
backgroundColor = Color.GREEN
// 刚进入界面时不获取焦点
isFocusable = false
}.lparams(matchParent, 0, 1f)
// scrollView 是为 view 漂浮在软键盘上做准备
scrollView {
linearLayout {
textView("tool bar")
}
}
}.lparams(matchParent, 0) {
alignParentBottom()
}
}
提取出来的方法:
// 关闭遮罩
private fun openEditMask() {
openAnimator.start()
editToolBar.visibility = View.GONE
}
// 关闭遮罩
private fun closeEditMask() {
closeAnimator.apply {
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
editToolBar.visibility = View.VISIBLE
}
})
}.start()
}
// 呈现动画
private val openAnimator by lazy {
ValueAnimator
.ofInt(0, window.decorView.height)
.setDuration(100)
.apply {
addUpdateListener {
val h = it.animatedValue as Int
editMask.layoutParams = editMask.layoutParams.apply {
height = h
}
}
}
}
// 关闭动画
private val closeAnimator by lazy {
ValueAnimator
.ofInt(window.decorView.height, 0)
.setDuration(100)
.apply {
addUpdateListener {
val h = it.animatedValue as Int
editMask.layoutParams = editMask.layoutParams.apply {
height = h
}
}
}
}
结束
这个小功能算是搞完了,感觉好简单啊,真不知道为什么我弄了一天才写出来,哎,看来我不是天才,你才是啊。
如果文章对你有帮助的话,欢迎关注公众号:goodKotlin,你的关注,就是最大的支持。
文章源码:GitHub: Ethos(https://github.com/xwdoor/Ethos),欢迎大家给颗星星,哈哈。
推荐阅读:
1. 图片编辑器–向上弹出文字编辑框(遮罩)界面
2. 图片编辑器–视图布局 View 悬浮在软键盘上