掌握 Anko,看這一篇就夠了!
平時開發android時,我們的UI佈局程式碼一般都是寫在xml中,當然也有少數寫在Java程式碼中,這就導致了這樣的局面:xml佈局清晰可見,但不能動態改變,Java程式碼佈局比較靈活,但比較難用而且冗餘難維護,所以一般都是用xml先編排出佈局,然後再用程式碼進行進一步修改搭配使用。那麼有沒有更清晰且高效的方式呢?有!Anko就很好的解決了這個問題。
Anko是JetBrains開發的一個強大的庫,它主要的目的是用來替代以前XML的方式來使用程式碼生成UI佈局的,它包含了很多的非常有幫助的函式和屬性來避免讓你寫很多的模版程式碼。
這是一個很有趣的特性,我推薦你可以嘗試下,但是在該系列文章裡我們暫時不太多使用它(只在一些地方使用並會做好相關的解釋),因為對於現階段的我們來說使用XML更容易一些,而且Anko其實並不難,看這一篇你就能學會如何使用常用的Anko方法來優化程式碼了,所以我們會把更多的重點放到Kotlin的學習上來。
如果你想研究Anko原始碼一探究竟,點選我這裡給出的Github傳送。
首先讓我們來使用Anko簡化一些程式碼:
val recyclerView: RecyclerView = find(R.id.recyclerView)
以上程式碼寫在MainActivity:onCreate中,這是一段用來簡化獲取RecyclerView的程式碼。就像你將要看到的,任何時候你使用了Anko庫中的某些東西,它們都會以屬性名、方法等方式被匯入。
你可能會問:“find()
這個方法是哪裡來的?”這是因為Anko使用了擴充套件函式在Android框架中增加了一些新的功能。
而至於什麼是擴充套件函式
verticalLayout( )、relativeLayout( )
等方法”就可以了,因為這些都是Anko給Activity加的擴充套件方法,所以在activity內部可以直接呼叫到這些方法(比如這裡的find()
)。
關於什麼是擴充套件函式,可以看這篇文章:。。。
我們將會在之後的文章中更加詳細介紹給大家一些其它的擴充套件函式並教會大家怎麼去編寫自己的擴充套件函式,所以還請點選綠色的加號保持對我的關注!
(1)我們再來看一個載入佈局的例子:
verticalLayout {
val name = editText()
button("Say Hello" ) {
onClick { toast("Hello, ${name.text}!")
}
}
上面是一個DSL(Domain Specific Language),使用的是 Kotlin語言,作用是建立了一個Button,放在 LinearLayout 內,併為其設定了一個點選監聽器onClick。
在這個載入佈局的例子中,button 方法接了一個字串引數,這樣的 Helper 方法同樣適用於 TextView、EditText、ImageView。
如果我們不需要 View 其它的屬性,我們可以省略 “{}” 直接寫 “button(“Ok”)” 或只有 “button()”,如下:
verticalLayout {
button("Ok")
button("Cancel")
}
- 關於控制元件顯示文字問題:
雖然我們的正式編碼中不會存在例程中這樣的HardCode,因為我們一般會使用Java的字串來進行相應的替換,但是大多數時候字串都是放在 res/values/ 目錄下的,並且是執行時呼叫的,例如:getString(R.string.login)。
幸運的是,Anko中可以使用這樣的兩個 helper 方法:“button(R.string.login)” 和 “button{textResource = R.string.login}”。
注意,這些屬性不是 “text,hint,image”, 而是“textResource,hintResource,imageResource”。
番外:什麼是DSL?
Domain Specific Language,即“領域相關語言”,說白了它就是某個行業中的行話。
舉個例子:
在構建證券交易系統的過程中,在證券交易活動中存在許多專業的金融術語和過程。現在要為該交易過程建立一個軟體解決方案,那麼開發者/構建者就必須瞭解證券交易活動,其中涉及到哪些物件、它們之間的規則以及約束條件是怎麼樣的。那麼就讓領域專家(這裡就是證券交易專家)來描述證券交易活動中涉及的活動。但是領域專家習慣使用他們熟練使用的行業術語來表達,解決方案的構建者無法理解。如果解決方案的模型構建者要理解交易活動,就必須讓領域專家用雙方都能理解的自然語言來解釋。這種解釋的過程中,解決方案的模型構建者就理解了領域知識。這個過程中雙方使用的語言就被稱為“共同語言”。
在上面的描述,可以看到在需求收集的過程中,如果要成功構建模型,則需要一種領域專家和構建者(也就是通常的領域分析師/業務分析師)都能理解的“共同語言”。但是這種共同語言的建立過程沒有保證,不能夠保證在收集過程中得到的資訊完整的描述了領域活動中所有的業務規則和活動。
如果能夠讓領域專家通過簡單的程式設計方式描述領域中的所有活動和規則,那麼就能在一定程度上保證描述的完整性。DSL 就是為了解決這個問題而提出的。DSL 的特點:
- 用於專門領域,不能用於其他領域
- 表現力有限
- 不描述解答域,僅描述問題域DSL 與通用程式語言的區別:
- DSL 有更高階的抽象,不涉及類似資料結構的細節
- DSL 表現力有限,其只能描述該領域的模型,而通用程式語言能夠描述任意的模型
好了,書歸正傳,我們再舉一個設定佈局引數的例子。
(2)在“onCreate()”中載入線性佈局:
注意:我們不需要繼承其它的類,只要標準的Activity、Fragment、FragmentActivity 就好。
override fun onCreate(savedInstanceState: Bundle?) {
super<Activity>.onCreate(savedInstanceState)
verticalLayout {
padding = dip(30)
editText {
hint = "Name"
textSize = 24f
}
editText {
hint = "Password"
textSize = 24f
}
button("Login") {
textSize = 26f
}
}
}
我們會發現,我們不需要顯示的呼叫 setContentView(R.layout.something)
, Anko 自動為Activity且只會對Activity進行 “set content view”,這一點我們會在Anko的原始碼中看到,下文會有解釋。
擴充套件屬性
這裡的padding、 hint、textSize 是 擴充套件屬性。大多數 View 都具有這些屬性,並且允許使用text = "Some text"
來代替setText("Some text")
。擴充套件函式
上述程式碼中的verticalLayout是一個豎直方向的 LinearLayout,其中的editText和 button 都是 擴充套件函式。這些函式存在於 Android 框架中的大部 View 中:Activities、Fragments (android.support包中的) 甚至 Context 也同樣適用。所以如果有一個 Context 例項的話,我們可以寫出下面的DSL結構:
val name = with(myContext) {
editText {
hint = "Name"
}
}
這裡的變數 name 就成為了 EditText 型別。
- Layouts 和 LayoutParams
在父佈局中,佈局控制元件可能有著這樣的xml佈局程式碼:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android_layout_marginLeft="5dip"
android_layout_marginTop="10dip"
android:text="Login"
android:textSize="26sp"/>
在Anko中,我們可以在 Button 的後面使用 lparams 來實現類似與 xml 中同樣的效果。
linearLayout {
button("Login") {
textSize = 26f
}.lparams(width = wrapContent) {
horizontalMargin = dip(5)
topMargin = dip(10)
}
}
如果指定了 lparams 但是沒有指定 width 或者 height,那麼預設是 “wrapContent”,但是我們可以自己通過使用 named arguments 來指定。下面對該程式碼中涉及到的屬性做出一些解釋:
- horizontalMargin: 同時設定 left 和 right margins
- verticalMargin: 同時設定 top 和 bottom
- margin: 同時設定4個方向的 margins
不過這裡有一點需要做出說明:lparams的使用在不同的佈局中是不同的,例如在RelativeLayout中:
val ID_OK = 1
relativeLayout {
button("Ok") {
id = ID_OK
}.lparams { alignParentTop() }
button("Cancel").lparams { below(ID_OK) }
}
- Include tag
使用 include tag 可以很容易向 DSL 插入 一個 XML layout :
include<View>(R.layout.something) {
backgroundColor = Color.RED
}.lparams(width = matchParent) { margin = dip(12) }
通常可以使用 lparams,如果型別不是 View,仍然可以用 {}:
include<TextView>(R.layout.textfield) {
text = "Hello, world!"
}
- Styles
Anko 支援 styling:style 是一個簡單的函式,接受一個View,效果作用於這個 View,並且當這個 View 是一個ViewGroup 時,可以遞迴的作用於這個View的child View:
verticalLayout {
editText {
hint = "Name"
}
editText {
hint = "Password"
}
}.style { view -> when(view) {
is EditText -> view.textSize = 20f
}
}
- Listeners
- Kotlin中的效果(沒有使用Anko):
button.setOnClickListener(object : OnClickListener {
override fun onClick(v: View) {
login(name, password)
}
})
- 使用Anko的程式碼:
button("Login") {
onClick {
login(name, password)
}
}
當一個Listener有多個方法時,Anko就顯得更方便了。
- Kotlin中的效果(沒有使用Anko):
seekBar.setOnSeekBarChangeListener(object: OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
// Do Something
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
// Just an empty method
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
// Another empty method
}
})
- 使用Anko的程式碼:
seekBar {
onSeekBarChangeListener {
onProgressChanged { seekBar, progress, fromUser ->
// Do Something
}
}
}
可以看到Anko的使用讓程式碼大大減少且清晰了很多。如果你同時設定了 onProgressChanged 和 onStartTrackingTouch,對於這種多個相同的方法被合併的情況,最後的一個有效。另外,“onClick{}、onCheckedChange{}、onDateChange{}、onDrawerOpen{}、onItemClick{}、onScrollChange{}”等都有類似用法。
上面的程式碼簡單粗暴,那麼這樣光鮮亮麗的程式碼是怎麼實現的呢?我們簡單說一說
前面都是一些關於Anko用法的一些相關知識,也都是表象的一些東西。接下來寫點深入的,我們一起去看看Anko的原始碼,對這些特性的實現一探究竟(對此沒有興趣的同學可以直接跳到第四部分看更多用法的例子)。
(1)首先給出目的碼:
verticalLayout {
val name = editText()
button("Say Hello") {
onClick { toast("Hello, ${name.text}!")
}
}
(2)其次給出程式碼工作流程
**1、建立控制元件:**Anko都是定義單例的
2、呼叫init():這個函式引數是我們傳入的
3、addView():它根據傳入的上下文物件(如果是acvtivity就setContentView(),如果ViewManager就addView())
(3)最後分析原始碼
verticalLayout點進去,看到如下程式碼:
inline fun Activity.verticalLayout(): LinearLayout = verticalLayout({})
inline fun Activity.verticalLayout(init: _LinearLayout.() -> Unit): LinearLayout {
return ankoView(`$$Anko$Factories$CustomViews`.VERTICAL_LAYOUT_FACTORY, init)
}
verticalLayout() 其實是個函式,引數也是個函式。這裡就涉及到了“閉包”,簡單來說就是:“verticalLayout”這個方法的引數(init)也是個函式,這個引數可以理解為在_LinearLayout類中擴充套件的匿名方法或者程式碼塊。其中_LinearLayout是LinearLayout的子類,這個咱們後面講的lparam時候再說。這個方法返回是一個LineaerLayout,咱們先來看看他的程式碼是怎麼生成LinearLayout。
- ankoView($$Anko$Factories$CustomViews
.VERTICAL_LAYOUT_FACTORY, init)
object `$$Anko$Factories$CustomViews` {
val VERTICAL_LAYOUT_FACTORY = {ctx: Context ->
val view = _LinearLayout(ctx)
view.orientation = LinearLayout.VERTICAL
view
}}
建立一個單例工廠類,裡面有個函式屬性:
val VERTICAL_LAYOUT_FACTORY:(Context)-> _LinearLayout
裡面的程式碼很簡單,就是把佈局的方向設定成“VERTICAL”,這裡就不說了。
最關鍵的是return後面的ankoView是什麼,咱們點進去看一下:
inline fun <T : View> Activity.ankoView(factory: (ctx: Context) -> T, init: T.() -> Unit): T {
val view = factory(this)
view.init()
AnkoInternals.addView(this, view)
return view
}
得出結論是:
- ankoView是Activity擴充套件的一個方法並且需要兩個引數
- val view = factory(this) :通過工廠類構建這個控制元件
- view.init() :控制元件初始化做一些動作 然後返回
那麼AnkoInternals.addView(this, view) 是什麼作用呢?咱們接著點進去一探究竟:
fun <T : View> addView(activity: Activity, view: T) {
createAnkoContext(activity, { AnkoInternals.addView(this, view) }, true)
}
fun <T : View> addView(manager: ViewManager, view: T) {
return when (manager) {
is ViewGroup -> manager.addView(view)
is AnkoContext<*> -> manager.addView(view, null)
else -> throw AnkoException("$manager is the wrong parent")
}
}
inline fun <T> T.createAnkoContext(
ctx: Context,
init: AnkoContext<T>.() -> Unit,
setContentView: Boolean = false): AnkoContext<T> {
val dsl = AnkoContextImpl(ctx, this, setContentView)
dsl.init()
return dsl
}
可以看到,呼叫的第一個函式就是呼叫createAnkoContext(…)方法,而這個方法需要三個引數,主要是第二個引數“init: AnkoContext.() -> Unit”,當然這也是個函式,他是怎麼傳這個引數的呢?
是這樣的:{ AnkoInternals.addView(this, view) } 呼叫了上述程式碼的第二個方法,這裡面的 this 就是AnkoContext,其實就是 AnkoContextImpl <_LinearLayout>,而這個方法實際上就是呼叫它們的addView方法。
AnkoContextImpl是什麼呢?我們繼續看程式碼:
open class AnkoContextImpl<T>(
override val ctx: Context,
override val owner: T,
private val setContentView: Boolean) : AnkoContext<T> {
private var myView: View? = null
override val view: View
get() = myView ?: throw IllegalStateException("View was not set previously")
override fun addView(view: View?, params: ViewGroup.LayoutParams?) {
if (view == null)return
if (myView != null) {
alreadyHasView()
}
this.myView = view
if (setContentView) {
doAddView(ctx, view)
}
}
private fun doAddView(context: Context, view: View) {
when (context) {
is Activity -> context.setContentView(view)
is ContextWrapper -> doAddView(context.baseContext, view)
else -> throw IllegalStateException("Context is not an Activity, can't set content view")
}
}
open protected fun alreadyHasView(): Unit = throw IllegalStateException("View is already set: $myView")
}
這麼多程式碼其實就幹了一件事情,就是: is Activity -> context.setContentView(view),那麼到此為止我們就知道了目的碼到底是怎樣一步步載入佈局並“自動setContentView”的了。對於其它控制元件,程式碼邏輯和 verticalLayout 都是差不多的。
最後我們來分析下 lparams 是怎麼實現的,先給出目的碼:
textView {
}.lparams(WRAP_CONTENT, WRAP_CONTENT) {
centerHorizontally()
below(circleImgView)
topMargin = kIntHeight(0.02f)
}
然後電擊進入到 lparams 程式碼:
fun <T : View> T.lparams(
width: Int = android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
height: Int = android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
init: RelativeLayout.LayoutParams.() -> Unit = {}): T {
val layoutParams = RelativeLayout.LayoutParams(width, height)
layoutParams.init()
[email protected].layoutParams = layoutParams
return this
}
看到這裡我想大家應該都明白了,Anko的寫法大致就是這樣實現的。
(1)Intent Helpers
傳統的 Kotlin 啟動新的 Activity 的方式是建立一個 Intent,同時可能傳遞一些引數,最後將建立的 Intent 通過 Context 的 startActivity() 方法傳遞,就像這樣:
val intent = Intent(this, javaClass<SomeActivity>())
intent.putExtra("id", 5)
intent.putExtra("name", "John")
startActivity(intent)
而通過 Anoko 我們只需要一行程式碼來實現:
startActivity<SomeActivity>("id" to 5, "name" to "ActivityName")
startActivity 方法接收一個鍵值對,並且把這些鍵值作為 Intent 的 parameters 傳遞。而另一個我們熟知的方法 startActivityForResult() 也支援相同的語法。
(2)Popular Intent Shorthands
幾乎所有的應用程式都會有呼叫預設瀏覽器或者開啟系統郵件的程式碼,Anko 也提供了輔助方法:
browse("http://somewebsite.org (http://somewebsite.org/)")
email("[email protected] (mailto:[email protected])", "Here I am!", "Message text")
(3)AlertDialogs
Anko 提供了一個建立含有文字、列表、進度條甚至你自己的 DLS 佈局的宣告方式建立一個簡單的文字對話方塊。
- 有兩個底部按鈕的對話方塊
alert("Order", "Do you want to order this item?") {
positiveButton("Yes") { processAnOrder() }
negativeButton("No") { }
}.show()
- 單選列表的對話方塊
val flowers = listOf("Chrysanthemum", "Rose", "Hyacinth")selector("What is your favorite flower?", flowers) {
i -> toast("So your favorite flower is ${flowers[i]}, right?")
}
- 不顯示進度的 Loading Dialg
gressDialog("Please wait a minute.", "Downloading…")
indeterminateProgressDialog("Fetching the data…")
- 通過 Anko 的 DSL 來建立一個自定義佈局
alert {
customView {
verticalLayout {
val familyName = editText {
hint = "Family name"
}
val firstName = editText {
hint = "First name"
}
positiveButton("Register") { register(familyName.text, firstName.text) }
}
}
}.show()
(3)Services
Android 系統的服務,比如 WifiManager , LocationManager 或者 Vibrator,Anko 都可以通過給 Context 新增擴充套件屬性來實現:
if (!wifiManager.isWifiEnabled()) {
vibrator.vibrate(200)
toast("Wifi is disabled. Please turn on!")
}
(4)Asynchronous Tasks
在 Android 中非同步任務使用最多的可能就是 AsyncTask 了,儘管它很流行,但是使用起來有諸多不便,比如:在使用 AsyncTask 時,當執行到 postExecute 的時候,如果 Activity 被銷燬,會出現一些異常。Anko 提供了幾種方式來實現相同的效果。
async(someExecutor) { // Omit the parameter to use the default executor
// This code will be executed in background
}
async() {…} 方法通過 ThreadExecutor 來執行 {} 中的程式碼,我們可以選擇使用預設的或者自定義的,而如果你想在 async() 中回到 “UI執行緒” 來操作檢視,可以使用 uiThread() 方法:
async {
// Do some work ...
uiThread {
toast("The work is done!")
}
}
uiThread() 在 async() 中有特殊的語義:
async() 沒有持有一個 Context 的例項,只有一個 WeakReference(弱引用) 來持有 Context 的例項,所以即使 lambda 表示式一直都不結束, Context 的例項也是不會洩露的。(UIThread 依賴於呼叫者,如果它被 Activity 呼叫,如果 activity.isFinishing() 返回 true ,那麼 uiThread 不會執行,這就避免了剛才提及的那個問題。)
async {
uiThread {
/* Safe version. This code won't be executed
if the underlying Context is gone. */
}
ctx.uiThread {
/* Here we are calling the `uiThread`
extension function for Context directly,
so we are holding a reference to it. */
}
}
(5)Logging
Android SDK 提供 android.util.Log 類來提供一些 logging 方法,並且我們在開頭也教過大家如何定製自己的LogUtil,這些方法都很實用,但是我們每次必須傳遞一個 Tag 引數,同時這個 Tag 資訊必須是 String 型別的,這就略顯麻煩。不過現在我們可以通過 AnkoLogger 類擺脫這些惱人的問題:
class SomeActivity : Activity(), AnkoLogger {
fun someMethod() {
info("Info message")
debug(42) // .toString() method will be called automatically
}
}
預設的 Tag 名是當前的類名( 本例中的是SomeActivity),但是通過重寫 AnkoLogger 的 loggerTag 屬性我們是可以來更改的,而且每個方法有兩個版本,這意味著我們還可以這樣寫:
info("String " + "concatenation")
info { "String " + "concatenation" }
結語:
到此為止,我們學會了使用 Anko 庫中很多東西,發現 Anko 確實能幫助我們很大程度上地簡化程式碼,但是我們現在還沒有使用庫中更多的東西,比如:例項化Intent、Fragment的建立、資料庫的訪問等等。
我們將會在之後學習中瞭解到更多有趣的例子,有志共同進步的同學請頭像右側點選“(+)”保持對我的關注,後續好文第一時間推送給你。
聯絡方式:
相關推薦
掌握 Anko,看這一篇就夠了!
平時開發android時,我們的UI佈局程式碼一般都是寫在xml中,當然也有少數寫在Java程式碼中,這就導致了這樣的局面:xml佈局清晰可見,但不能動態改變,Java程式碼佈局比較靈活,但比較難用而且冗餘難維護,所以一般都是用xml先編排出佈局,然後
想做好PPT折線圖,看這一篇就夠了!
12月 image 菊花 -c 強調 spa any border 線圖 配圖主題無關今天鄭少跟大家聊聊折線圖的使用方法,或者你有疑問,折線圖很簡單,插入修改數據不就好了嗎?如果你要是這樣想的,恭喜你,有可能你會做出下面這樣的效果。如果你要是稍微懂一點折線圖的使用方法,你就
百萬併發下的Nginx優化,看這一篇就夠了!
本文作者主要分享在 Nginx 效能方面的實踐經驗,希望能給大家帶來一些系統化思考,幫助大家更有效地去做 Nginx。 優化方法論 我重點分享如下兩個問題: 保持併發連線數,怎麼樣做到記憶體有效使用。 在高併發的同時保持高吞吐量的重要要點。 實現層面主要是三方面優化,主要聚焦
Android 必須知道2018年流行的框架庫及開發語言,看這一篇就夠了!
導語2017 已經悄悄的走了,2018 也已經匆匆的來了,我們在總結過去的同時,也要展望一下未來,來規劃一下今年要學哪些新技術。這幾年優秀Android的開源庫不斷推出,新技術層出不窮,需要我們不斷去了解和掌握,在提高自身開發水平的同時,我們需要付出更多學習精力和時間。俗話說
Android 必須知道2018年流行的框架庫及開發語言,看這一篇就夠了!
本文更新時間:2018年07月12日15:50:40導語 2017 已經悄悄的走了,2018 也已經匆匆的來了,我們在總結過去的同時,也要展望一下未來,來規劃一下今年要學哪些新技術。這幾年優秀Android的開源庫不斷推出,新技術層出不窮,需要我們不斷去了解和掌握,在提
【大資料】華為內部狂轉好文,大資料,看這一篇就夠了!
來源:華為IT產品解決方案導讀科技的進步在很多的時候總會超出我們的想象,試想如果未來我們一個人擁
如何低成本實現Flutter富文字,看這一篇就夠了!
作者:閒魚技術-玄川 背景 閒魚是國內最早使用Flutter 的團隊,作為一個電商App商品詳情頁是非常重要場景,其中最主要
【K8S】Service服務詳解,看這一篇就夠了!!
k8s用名稱空間namespace把資源進行隔離,預設情況下,相同的名稱空間裡的服務可以相互通訊,反之進行隔離。 1.1 Service Kubernetes中一個應用服務會有一個或多個例項(Pod,Pod可以通過rs進行多複本的建立),每個例項(Pod)的IP地址由網路外掛動態隨機分配(Pod重啟後IP地址
【FastDFS】FastDFS 分散式檔案系統的安裝與使用,看這一篇就夠了!!
## 寫在前面 > 有不少小夥伴在實際工作中,對於如何儲存檔案(圖片、視訊、音訊等)沒有一個很好的解決思路。都明白不能將檔案儲存在單臺伺服器的磁碟上,也知道需要將檔案進行副本備份。如果自己手動寫檔案的副本機制,那就太麻煩了,這會涉及冗餘副本機制、伺服器的排程、副本檢測、伺服器節點檢測、檔案副本存放策略
Azure IOT 設備固件更新技巧,看這一篇就夠了
trigger 物聯網平臺 搭建 href ice 有效 面板 調用 創建 嫌長不看版 今天為大家準備的硬菜是:在 Azure IoT 中心創建 Node.js 控制臺應用,進行端到端模擬固件更新,為基於 Intel Edison 的設備安裝新版固件的流程。通過創建模擬設備
Linux 問題故障定位,看這一篇就夠了
1. 背景 有時候會遇到一些疑難雜症,並且監控外掛並不能一眼立馬發現問題的根源。這時候就需要登入伺服器進一步深入分析問題的根源。那麼分析問題需要有一定的技術經驗積累,並且有些問題涉及到的領域非常廣,才能定位到問題。所以,分析問題和踩坑是非常鍛鍊一個人的成長和提升自我能力。如果我們有一套好的分析工具,那將是事
C語言從入門到精通,看這一篇就夠了
影響 內容 當前 位置 replace 雙精度 下標 寄存器變量 一個 No.1 計算機與程序設計語言的關系 計算機系統由硬件系統和軟件系統構成,硬件相當於人類的肉體,而軟件相當於人類的靈魂,如果脫離了靈魂,人類就是一具行屍走肉 No.2 C語言的特點 代碼簡潔,靈活性高
【MYSQL學習筆記02】MySQL的高階應用之Explain(完美詳細版,看這一篇就夠了)
版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/wx1528159409 最近學習MySQL的高階應用Explain,寫一篇學習心得與總結,目錄腦圖如下: 一、Explain基本概念 1. Explain定義 · 我們知道M
抖音內容運營全解剖,看這一篇就夠了 !
抖音的火爆已經不用多說,作為短視訊的頭部APP,抖音已經從微信手中奪走不少使用者時間,成為新的“時間黑洞”。 比如:“中毒了,我每天晚上要刷2個小時”,“下一站,逃離微信,上抖音”… 一個企業運營抖音的目的是什麼? 答案顯而易見,無非就是做品牌營銷、擴大品牌影響力。 在短視訊領域積累
理解Sharding jdbc原理,看這一篇就夠了
相比於Spring基於AbstractRoutingDataSource實現的分庫分表功能,Sharding jdbc在單庫單表擴充套件到多庫多表時,相容性方面表現的更好一點。例如,spring實現的分庫分表sql寫法如下: select id, name, price,
產品設計教程:如何理解 px,dp,dpi, pt,看這一篇就夠了
先聊聊熟悉的幾個單位 圍繞著各種螢幕做設計和開發的人會碰到下面幾個單位:in, pt, px, dpi,dip/dp, sp 下面先簡單回顧下前四個單位: "in" inches的縮寫,英寸。就是螢幕的物理長度單位。一英寸等於2.54cm。比如Android手機
中後臺產品的表格設計,看這一篇就夠了(原型規範下載)
中後臺產品的表格設計,看這一篇就夠了(原型規範下載) 2018年4月16日luodonggan 中後臺產品的表格設計,看這一篇就夠了(原型規範下載) 經過了將近一年的後臺產品經歷,踩了很多坑,試了很多錯,也學習到了很多東西,目前也形成了自己的一套規範。本文將其中的部分收穫彙總成文,
Linux 常用指令 ,看這一篇就夠了—— 摘自《Linux Probe》
touch:用於建立空白檔案或設定檔案的時間,ps:黑客可以用touch指令來修改檔案的最後修改時間,以隱藏自己的修改行為。 mkdir:用於建立空白的目錄,如mkdir path,可以結合引數-p來遞迴建立檔案目錄,如mkdir -p a/b/c/d/e cp:用於複製檔案或目錄,如cp 1.txt p
樹狀陣列(Binary Indexed Tree),看這一篇就夠了
定義 根據維基百科的定義: A Fenwick tree or binary indexed tree is a data structure that can efficiently update elements and calculate pr
Cookie介紹及在Android中的使用總結超詳細,看這一篇就夠了
Cookie介紹 cookie的起源 早期Web開發面臨的最大問題之一是如何管理狀態。簡言之,伺服器端沒有辦法知道兩個請求是否來自於同一個瀏覽器。那時的辦法是在請求的頁面中插入一個token,並且在下一次請求中將這個token返回(至伺服器)。這就需要在form中插入一個包含toke