子路宇晟的博客

love and peace


  • Home

  • Archives

ImageView ScaleType and Transition

Posted on 2019-09-01 | Edited on 2020-04-06 | Comments: | Views:

ImageView 是很常用的控件,记录下ScaleType和Transition animation

ImageView ScaleType

image-20190827232228863

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:id="@+id/faceLayout"
android:layout_height="match_parent">

<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
// 这三个属性尽量搭配一起使用,除了center和matrix,其他效果都一致,显示正常不拉伸的图片。
android:layout_width="0dp"
android:layout_height="wrap_content"
android:adjustViewBounds="true"

ImageView Transition

https://medium.com/@andkulikov/animate-all-the-things-transitions-in-android-914af5477d50

1,改变图片属性

1
2
3
4
5
6
7
8
9
10
val animator = ValueAnimator.ofFloat(0f, 1f)
animator.duration = Constants.ThrottleDuration.NORMAL
animator.addUpdateListener { animation ->
val animatorValue = animation.animatedValue as Float
videoView.translationX = 50 * animatorValue
videoView.translationY = 100 * animatorValue
videoView.scaleX = 1f - 0.8f * animatorValue
videoView.scaleY = 1f - 0.5f * animatorValue
}
animator.start()

2,利用transition改变layoutParams

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
val marginHorizontal = UtilResource.getDimensionPixelSize(R.dimen.margin_horizontal)
val container = mRoot.transitionLayout
val transitionSet = TransitionSet()
.addTransition(ChangeBounds())
.addTransition(ChangeImageTransform()).addListener(object : TransitionListenerAdapter(){
override fun onTransitionStart(transition: Transition) {
super.onTransitionStart(transition)

UtilLog.d(sTAG, "------full start ")
}

override fun onTransitionEnd(transition: Transition) {
super.onTransitionEnd(transition)
UtilLog.d(sTAG, "------full end")
}
})

TransitionManager.beginDelayedTransition(container, transitionSet)
val params = videoView.layoutParams as ViewGroup.MarginLayoutParams
UtilLog.d(sTAG, "------full $isScale $params")
params.height = if (isScale) ViewGroup.LayoutParams.MATCH_PARENT else ViewGroup.LayoutParams.WRAP_CONTENT
if (isScale) {
params.setMargins(0, 0, 0, 0)
} else {
params.setMargins(marginHorizontal, marginHorizontal, marginHorizontal, 0)
}
videoView.layoutParams = params
(videoView as ImageView).scaleType =
if (isScale) ImageView.ScaleType.CENTER_CROP else ImageView.ScaleType.FIT_START
Read more »

Hexo+Github Pages CDN & HTTPS

Posted on 2019-08-18 | Edited on 2020-04-06 | Comments: | Views:

在Hexo和Github Pages的加持下,现在我们可以只专注在写Blog上,而不用额外的精力管网站发布和运营。一起看起来很美好,然而,一段时间过去了,会发现文章没写几篇,网站倒是卡成姥爷,浏览器还一直显示不安全。需要一些手段来解决下。

名词概念

CDN

全称是Content Delivery Network,最小CDN网络可以由一个DNS服务器和几台缓存服务器组成,不同地区的访问请求,CDN会智能安排最近的服务器响应,而且还有Cache来加速。所以CDN的效果取决于DNS服务器调度算法和缓存服务器的数量以及质量,CDN的作用域是域名而不是服务器。

DNS

Domain Name System是互联网的一项服务,它作为域名和IP地址互相映射的一个分布式系统,让用户更方便访问互联网。互联网通信是只认IP地址的,但一串数字人是记不住的,人容易记住的是apple.com这样的单词,这个叫域名,让电脑知道apple.com就是要访问122.224.45.229,就通过DNS域名解析系统。DNS系统中,常见的资源记录类型有:

  • 主机记录(A记录):RFC 1035定义,A记录是用于名称解析的重要记录,它将特定的主机名映射到对应主机的IP地址上。
  • 别名记录(CNAME记录): RFC 1035定义,CNAME记录用于将某个别名指向到某个A记录上,这样就不需要再为某个新名字另外创建一条新的A记录。
  • IPv6主机记录(AAAA记录): RFC 3596定义,与A记录对应,用于将特定的主机名映射到一个主机的IPv6地址。
  • 服务位置记录(SRV记录): RFC 2782定义,用于定义提供特定服务的服务器的位置,如主机(hostname),端口(port number)等

CNAME

想想为啥要用CNAME。用户输入的是xxx.com,打开的是xxx.github.io网页,这中间的魔法就是通过CNAME实现的,我们在DNS域名解析服务里设定了解析规则,*和www开头的记录都重定向到hxx.github.io域名。这里用CNAME的好处是github换IP操作对我们透明,从而不需要手动改配置。

实施

Read more »

Android Google Play 上车指南

Posted on 2019-08-11 | Edited on 2020-04-06 | Comments: | Views:

如果你的Android 应用不只在中国运营,还想国际化,比如服务一下美帝人民,那上Google Play是必须的。这里记录下

必要的准备

  • 一个Gmail帐号,Google Play Console 登录只接受Google帐号
  • 一张支持境外支付的信用卡(visa/mastercard),注册需要支付$25
  • 文档理解-Android APP bundle
  • 文档理解-APP Signing
  • 软件著作权(不需要)

帐号注册

这个流程需要自带梯子,注册地址,按提示走一般没啥幺蛾子

image-20190811144655052

App Bundle

Android生态过了10年,系统碎片化严重,应用越来越大。Google终于决定要有所改变了,在2018年中推出了Android App Bundle这个新的文件格式。期望来达到三个目的:1,动态分发,服务器根据用户机型动态下发对应资源的APK,减少应用包体积(落后iOS N年);2,动态功能模块,比如支付,播放这种功能可以在使用时候动态加载;3,应用安全,集成App signing,保证APP安装和升级的合法性。使用也比较简单

  • 需要Android Studio 3.2+,命令行可以使用bundletool
  • 添加Dynamic Delivery支持
  • 用AS打一个Android APP bundle文件,在AS工具栏选build,选build bundles,文件格式是.aab
  • 测试aab bundle文件
  • 上传到Google play签名

一个20M的测试apk,使用app bundle经过google play下发到用户手机只有6m左右,美滋滋。

Read more »

WorkManager IN Jetpack

Posted on 2019-08-03 | Edited on 2020-04-19 | Comments: | Views:

WorkManager是Android Jetpack的架构组件(Architecture component)之一,官方对它的定义是用来管理Android后台作业。

简单上手

1,添加依赖

1
2
3
4
5
6
7
8
9
// 在app的build.gradle里添加
//A: Kotlin + coroutines
implementation 'androidx.work:work-runtime-ktx:2.1.0'

//B: Java only
implementation 'androidx.work:work-runtime:2.1.0'

//C: optional - RxJava2 support
implementation 'androidx.work:work-rxjava2:2.1.0'

2,实现一个Worker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 相当于一个Task
class CountNumWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
private val count = inputData.getInt(COUNT, 0)
private val name = inputData.getString(NAME) ?: "bob"
override fun doWork(): Result {
var sum = 0
for (i in 0..count) {
sum += i
sleep()
Log.d(TAG, "-----$name count $count || sum $sum")
}
return Result.success()
}

private fun sleep() {
val time = Math.random() * 400 + 100
Log.d(CountNumWorker.TAG, "----$time")
try {
Thread.sleep(time.toLong(), 0)
} catch (e: InterruptedException) {
Log.e(CountNumWorker.TAG, "----$e")
}
}

companion object {
const val TAG = "CountNumWorker"
const val COUNT = "count"
const val NAME = "name"
}
}

3,创建一个WorkRequest

1
2
// 定义怎么运行Worker,下面这个worker将只运行一次,对应的还有周期运行的PeriodicWorkRequestBuilder
val workRequest = OneTimeWorkRequestBuilder<CountNumWorker2>().build()

4,执行Worker

1
2
// 调用WorkManager单例开始worker
WorkManager.getInstance().enqueue(workRequest)
Read more »

自定义验证码输入View

Posted on 2019-07-29 | Edited on 2020-04-06 | Comments: | Views:

我们现在要实现一个自定义输入框,显示用户接受到的验证码,大概是长这样

思路分析

Q:需要有哪些功能?

A:框代表的是验证码长度,这个需要可配置;只接收数字并可以编辑删除;输入和未输入有状态区别

Q:可以怎么实现?

A:直觉上是直接继承EditView,重写onDraw方法

Q:为什么要继承EditView而不是TextView甚至是View?

A:如果继承View,那控件的逻辑能力也需要自己实现,比如输入法的弹出和隐藏,输入法内容的接收需要重写onCreateInputConnection等方法。EditView比TextView多的能力是设置Selection光标位置,文字默认可编辑可定义输入法类型。

综上:该自定义需求,系统控件的EditView功能上可以满足,只是需要自定义魔改下UI。

Read more »

Android cool animation

Posted on 2019-07-20 | Edited on 2020-04-19 | Comments: | Views:

动画用来描述一个非静态的场景。在android软件中有两种形式,1,算出初态和终态之间的插值,并在一段时间渲染播放这些状态,如animation,animator,layout transition。2,在一段时间内算法不断的绘制状态,这些状态的差异和连贯性在视觉里形成动画。一般有自定义view animation,这就是本文要记录的点。

常见方案

自定义视图动画,往往是因为常规的属性动画已经无法满足设计师要求了。常见的也有三种:

效果上限 实现复杂度 工程依赖
Canvas 中等 中等 无
SVG 中等 需要专业软件制作SVG 无
Lottie 高(anything) 需要AE软件输出动画Json 需要依赖Airbnb lib
SVGA 中高 需要AE软件 需要集成播放lib

可以看出想要更好的效果,投入必然需要更多:0。脑补了下要实现的操作完成动画用Canvas应该就够了。当然没有AE设计师和自己也不会sketch也是事实。

怎么搞

1,得有个自定义view,复写onDraw 就有Canvas了

1
2
3
4
5
6
7
8
9
class AnimationView : View {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
}
}

2,有画布,还得需要笔

1
2
3
4
5
6
7
8
9
private lateinit var mCirclePaint: Paint
mCirclePaint = Paint(Paint.ANTI_ALIAS_FLAG) // 抗锯齿,线条流畅
mCirclePaint.apply {
isDither = true // 动态采样
style = Paint.Style.STROKE // 线条样式
color = Color.parseColor("#6C9CFF") // 画笔颜色
strokeCap = Paint.Cap.ROUND // 线头形状
strokeWidth = 30f // 线条宽度
}
Read more »

Android NDK 上手指南

Posted on 2019-07-12 | Edited on 2020-04-06 | Comments: | Views:

对于大部分android应用开发者来说,NDK开发是陌生的,很多时候只是会引用到一些so库需要写个aidl来JNI调用。最近做的一个app有媒体播放和音效处理,所以就需要上手一下NDK开发。

NDK的作用

NDK是一套开发工具,通过它,就可以在android应用里调用并调试C和C++代码,特定场景下就可以做的更多更好:

  • 需要密集计算的,如游戏或物理仿真模拟,如android早期实现实时blur效果
  • 复用我们已经封装好的C和C++库,如io调度,引擎渲染库

环境配置

为了能调试Native代码,以下组件是必须的:

  • AndroidStudio(3.4.1)和SDK
  • Android NDK:native开发全家桶
  • CMake:外部编译工具,可以搭配Gradle来编译原生库。ndk-build的备胎,已转正
  • LLDB:用调试原生代码的工具

认识小伙伴

ndk-build

看过android源码编译的会有印象,编译模块都是通过make命令来执行android.mk和Application.mk文件来构建的。

Read more »

String to JsonObject

Posted on 2019-06-28 | Edited on 2020-04-06 | Comments: | Views:

JsonObject

今天搞了一天JsonObject,心态爆炸,记录一下,细节还是挺好玩的

String to JsonObject

1
2
3
4
5
6
7
8
9
10
// JsonParser
val json = "{"name":"bob","age":"18","score":{"eng":"g","fre":"h"}}"
val jsonObject = JsonParser().parse(json).asJsonObject
val name = jsonObject.get("name").asString // bob
val scoreObject = jsonObject.getAsJsonObject("score")

// fromJson
val json = "{"name":"bob","age":"18","score":{"eng":"g","fre":"h"}}"
val jsonObject = Gson().fromJson<JsonObject>(json, JsonObject::class.java)
val name = jsonObject.get("age").asString // 18

isJsonPrimitive

区别于private私有,primitive是元数据的意思,Json数据会被Gson解析成一棵LinkedTreeMap,tree上的node都是JsonElement,它有4个子类,JsonObject,JsonArray,JsonPrimitive,JsonNull。叶子节点一定是元数据JsonPrimitive或JsonNull。如果前端把一个json JSON.stringify塞到一个key里,那它就是一个叶子节点JsonPrimitive。如果需要把这个叶子节点还原成json,则需要额外parse。

1
2
3
4
5
6
7
8
val json = "{"name":"bob","age":"18","score":{"eng":"g","fre":"h"}}"
val jsonObject = JsonParser().parse(json).asJsonObject
val scoreObject = jsonObject.getAsJsonObject("score")
jsonObject.addProperty("scorePri", scoreObject.toString())
println(jsonObject) // {"name":"name","age":"age","score":{"eng":"g","fre":"h"},"scorePri":"{\"eng\":\"g\",\"fre\":\"h\"}"}

val scorePriObject = jsonObject.getAsJsonPrimitive("scorePri").asString
val eng = JsonParser().parse(scorePriObject).asJsonObject.get("eng").asString // g

safe opration

硬解json不确定key是不是null,kotlin可以用?操作符确保安全。取出来的数据也可以用isXXX来预先检查数据类型。

1
2
3
4
5
6
7
8
9
10
// null
val json = "{"name":"bob","age":"18","score":{"eng":"g","fre":"h"}}"
val jsonObject = JsonParser().parse(json).asJsonObject
val name = jsonObject.get("hobby")?.asString // null

// check type isXXX
JsonElement.isJsonArray
JsonElement.isJsonObject
JsonElement.isJsonPrimitive
JsonElement.isJsonNull
Read more »

2018

Posted on 2019-04-18 | Edited on 2020-04-06 | Comments: | Views:

今年经历了很多,很多的第一次,感谢生活让我丰满

工作

作为移动端Leader,写的代码少了,需要思考的东西变多。主要参与并推进了这些事:

  • 外卖敏捷小组EO,并上线
  • 外卖小组和移动端OKR推进未落实
  • 再惠合伙人App RN版上线
  • Flutter技术栈引入,并重构再惠合伙人App
  • 工程师文化建设,消除iOS,android工程师意识隔阂;组织大家技术分享和Blog输出

OKR从结果上来看并没啥用,是值得深思的事儿。敏捷小组流程引入好处是产品开发节奏更好控制,坏处是工程师积极性有负面影响。以前产品一有问题,大家不论前后端都会扑上去看,分了以后就没见过这个画面了。

生活

两人在一起久了,就会把自己最真实的一面毫无保留的曝露出来,这时难免需要磨合并重新了解一下彼此。日常点滴,都是以后的回忆。

3月和女票,合租室友一块去陈行公园玩,春风和煦,好不放松,虽然是个小公园,但由于各种原因,竟然是上半年唯一去的公园。

4月清明回家扫墓,自从毕业后就没回去扫墓了,对于普通来说人会分两次消失,一次是肉体死亡,在物理上被这个世界除名,一次精神上死亡,当所有人记忆中都没有这个人时,他的存在也就被彻底抹去了。清明就是这样一种形式,给自己的亲人拔拔草,松松土,记忆有份传承。

5月女票爸比生日,我们合伙买了个大金链子贺寿,未来女婿好感值+1。顺便去看了一下装修公司,对比下来选了一家全包的。

Read more »

Fish Redux 使用指南

Posted on 2019-03-16 | Edited on 2020-04-06 | Comments: | Views:

FishRedux 使用

为啥要使用Fish redux

  • 年前就被咸鱼大佬安利,种草已久
  • 想要对比Flutter Redux,学习体会它带来的优越性
  • 支持一下国产,现在Flutter生态还在早期

准备工作

1,Redux.js 文档

2,Fish Redux 文档

3,Pub使用文档

萌新(没接触过Redux):建议按顺序看一遍,扎实的理论基础是很有用处的

熟练玩家(Demo用过Redux):建议把Fish Redux文档的简介和Component看下,尤其是Component,这是它独特的地方

老司机(项目中用过Redux.js或Flutter Redux):可以重点看下Component中的Effect,Dependencies,Page,还有Adapter

Read more »
<i class="fa fa-angle-left" aria-label="Previous page"></i>1234<i class="fa fa-angle-right" aria-label="Next page"></i>
子路宇晟

子路宇晟

二次元爱好者,非专业视觉体验师

32 posts
9 tags
GitHub E-Mail
© 2020 子路宇晟
Powered by Hexo v4.2.0
|
Theme – NexT.Gemini v6.5.0