尝鲜使用Kotlin写了一段时间Android。说大幅度的减少了Java代码一点不夸张。用Java的时候动不动就new一个OnClickListener()匿名类,动不动就类型转换的地方都可以省下很多。更不用说特殊的地方使用data class更是少些不知道多少代码。

Jetbrains给Android带来的不仅是Kotlin,还有Anko。从Anko的官方说明来看这是一个雄心勃勃的要代替XML写Layout的新的开发方式。Anko最重要的一点是引入了DSL(Domain Specific Language)的方式开发Android界面布局。当然,本质是代码实现布局。不过使用Anko完全不用经历Java纯代码写Android的痛苦。因为本身是来自Kotlin的,所以自然的使用这种方式开发就具有了:

  • 类型安全,不再需要那么多的findById()之后的类型转换。
  • null安全,Kotlin里,如果一个变量用?表示为可空,并且使用?之后再调用的时候,即使变量为空也不会引发异常。
  • 无需设备解析XML,因为Anko本质是代码实现的界面和布局,所以省去了这些麻烦。
  • 代码复用,可以通过继承AnkoComponent的方式实现代码复用。XML布局是每一个Activity,每一个View各自专属一个,

    代码复用比较少。

来一个列子看一下。为了不太墨迹,一些不必要的xml声明此处略去。

<RelativeLayout>

    <TextView
android:id="@+id/sample_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:text="Sample text view"
android:textSize="25sp" /> <Button
android:id="@+id/sample_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/sample_text_view"
android:text="Sample button" /> </RelativeLayout>
    relativeLayout {
val textView = textView("Sample text view") {
textSize = 25f
}.lparams {
width = matchParent
alignParentTop()
} button("Sample button").lparams {
width = matchParent
below(textView)
}
}

准备工作

首先,安装一个Kotlin的插件是必须的。有了这个插件才可以使用Kotlin,然后才可以使用Anko。安装这个插件和Android Studio里安装别的插件市一样的。只需要使用Kotlin查找就可以找到,之后安装即可。

build.gradle里添加下面的代码:

dependencies {
compile 'org.jetbrains.anko:anko-sdk15:0.8.3' // sdk19, sdk21, sdk23 are also available
compile 'org.jetbrains.anko:anko-support-v4:0.8.3' // In case you need support-v4 bindings
compile 'org.jetbrains.anko:anko-appcompat-v7:0.8.3' // For appcompat-v7 bindings
}

然后sync一把。配置的问题解决。

写一个ListView热身

首先创建一个ListView的item点击之后跳转的activity。这里叫做TabDemo1

现在就创建这个listview,并在listview的item点击之后调转到相应的activity去。

这个listview非常简单,只在一个竖直的布局中放置,并且宽度和高度都是填满竖直

布局。

    // 1
verticalLayout {
padding = dip(16)
// 2
val list = listView() {
// 3
adapter = ArrayAdapter<String>(this@MainActivity, android.R.layout.simple_list_item_1, items)
// 4
onItemClickListener = object : AdapterView.OnItemClickListener {
override fun onItemClick(parent: AdapterView<*>?, v: View?, position: Int, id: Long) {
when (position) {
0 -> {
// 5
startActivity<TabDemo1>()
}
}
}
}
}.lparams(width = matchParent) { // 6
height = matchParent
}
}

分别解释:

  1. 竖直布局。本质是LinearLayout,并且orientation的值为vertical。但是

    水平方向的就没有vetialLayout这种可以直接使用的了,需要自己写明orientation。
  2. 创建一个listview。
  3. 给这个listview添加adapter。这里简单实用ArrayAdapter<String>
  4. 添加OnItemClickListenerobject : AdapterView.OnItemClickListener用来

    创建实现某个接口的匿名类。
  5. startActivity<TabDemo1>(),是Anko的语法糖。startActivity(SourceActivity.this, DestActivity.class)

    可以直接简化为startActivity<DestActivity>()。简单了不少。
  6. lparams中设置layout params相关的内容。默认的都是wrap content。这个设置为

    宽、高都为match parent。

用Fragment写一个Tab布局

热身结束。我们来开始真正的开发阶段。

下面要开发的是一个日记App。一共有三个tab,第一个是日记列表,第二个tab是写日记,第三个tab可以设置一些字体大小等(这里只用来占位,不做实现)。

每一个tab都用一个Fragment来展示内容。这三个tab分别HomeListFragment, DetailFragment,DiarySettingsFragment。这个三个fragment都在一个叫做TabDemo1的托管Activity里。

现在就从这个托管activity:TabDemo1开始。这里我们不使用默认的ActionBar,而是用完全自定义的方式来写一个我们自己的action bar。所以需要把界面设定为全屏模式。设置全屏的模式的方法有很多,我们用设置style的方式来实现。

    <style name="AppTheme.NoActionBar" parent="Theme.AppCompat.Light.NoActionBar">
</style>

之后把这个style应用在activity在AndroidManifest.xml配置中。

这个时候这个托管activity的界面布局就是一个完全的白板了。这个白板现在要分为上中下三部分。上部为我们自定义的action bar,最下面的是tab bar,剩下的部分就是每个tab的内容的fragment。

我们来看一下这个布局应该怎么写:

    // 1
relativeLayout {
id = ID_RELATIVELAYOUT backgroundColor = Color.LTGRAY // 2
linearLayout {
id = ID_TOP_BAR
backgroundColor = ContextCompat.getColor(ctx, R.color.colorPrimary)
orientation = LinearLayout.HORIZONTAL titleTextView = textView {
text = "Some Title"
textSize = 16f
textColor = Color.WHITE
gravity = Gravity.CENTER_HORIZONTAL or Gravity.CENTER_VERTICAL
}.lparams {
width = dip(0)
height = matchParent
weight = 1f
}
}.lparams {
width = matchParent
height = dip(50)
alignParentTop()
} // 3
linearLayout {
id = ID_BOTTOM_TAB_BAR
orientation = LinearLayout.HORIZONTAL
backgroundColor = Color.WHITE // 4
homeListTab = weightTextView {
text = "List"
normalDrawable = resources.getDrawable(R.mipmap.tab_my_normal)
selectedDrawable = resources.getDrawable(R.mipmap.tab_my_pressed)
onClick { tabClick(0) }
} detailTab = weightTextView {
text = "Detail"
normalDrawable = resources.getDrawable(R.mipmap.tab_channel_normal)
selectedDrawable = resources.getDrawable(R.mipmap.tab_channel_pressed)
onClick { tabClick(1) }
} settingsTab = weightTextView {
text = "Settings"
normalDrawable = resources.getDrawable(R.mipmap.tab_better_normal)
selectedDrawable = resources.getDrawable(R.mipmap.tab_better_pressed)
onClick { tabClick(2) }
} }.style { // 5
view ->
when (view) {
is TextView -> {
view.padding = dip(5)
view.compoundDrawablePadding = dip(3)
view.textSize = 10f
view.gravity = Gravity.CENTER
}
else -> {
}
}
}.lparams {
height = dip(50)
width = matchParent
alignParentBottom()
} // 6
fragmentContainer = frameLayout {
id = ID_FRAMELAYOUT
backgroundColor = Color.GREEN
}.lparams {
below(ID_TOP_BAR)
above(ID_BOTTOM_TAB_BAR)
width = matchParent
height = matchParent
}
}
  1. 前文的例子用了一个verticalLayout, 这里用的是relativeLayout的布局。

  2. 这里是自定义action bar。使用换一个linearLayout。如前所述,要横向布局linear layout

    就需要单独的指定orientation:orientation =LinearLayout.HORIZONTAL。这里比较简单,只有一个显示title的text view。

    这里需要注意gravity = Gravity.CENTER_HORIZONTAL or Gravity.CENTER_VERTICAL

    可以直接写成gravity = Gravity.CENTER。这里是为了突出or的用法。Kotlin里的or

    就是java的|操作符的作用。

  3. 这部分的布局是tab bar。

  4. 这里用的是weightTextView而不是textView。后面会详细的讲解这一部分。

  5. 给tab bar添加style。此style不是彼style。这个style,会遍历tab bar的linear layout内部的全部的view,然后根据when表达式匹配对应的规则,之后给对应于规则的view设置相应的属性。比如,这里会用when语句查看view是否为textView,如果是的话就给这个view设置padding、drawable padding、text size以及gravity属性。tab bar的linear layout有三个text view,所以他们都会被设置这些属性。

  6. 每一个tab的内容展示用fragment就是这里了。准确的说是fragment的container。

    这个container是一个framelayout。在action bar之下,在tab bar之上。在布局的时候有below(ID_TOP_BAR), above(ID_BOTTOM_TAB_BAR)ID_TOP_BARID_BOTTOM_TAB_BAR就分别是action bar和tab bar的id值。这些id值自由设定。

另外,在java写的时候常用的findViewById()方法在Kotlin和Anko中可以改为的find<FrameLayout>(ID_FRAMELAYOUT)。不见得简单,但是增加了类型安全。不用再强制类型转换。也不用担心相关的错误再发生。

上文第4点用到了weightTextView。这是一个自定义的view。在Anko布局中,可以根据自己的需要自定义各种各样的view。但是,需要经过一个小小的处理之后才可以使用到Anko的布局中。这个小小的处理就叫做扩展。下面看看如何给Anko添加weightTextView扩展的。

首先自定义一个view:WeightTextView

class WeightTextView(context: Context) : TextView(context) {
var normalDrawable: Drawable? = null
var selectedDrawable: Drawable? = null init {
var layoutParams = LinearLayout.LayoutParams(dip(50),
LinearLayout.LayoutParams.MATCH_PARENT, 1f)
layoutParams.weight = 1f
this.layoutParams = layoutParams
} override fun setSelected(selected: Boolean) {
super.setSelected(selected) if (selected) {
this.backgroundColor = ContextCompat.getColor(context, R.color.textGray)
this.textColor = ContextCompat.getColor(context, R.color.textYellow) if (selectedDrawable != null) {
this.setCompoundDrawablesWithIntrinsicBounds(null, selectedDrawable, null, null)
}
} else {
this.backgroundColor = ContextCompat.getColor(context, android.R.color.transparent)
this.textColor = ContextCompat.getColor(context, R.color.textGray)
if (normalDrawable != null) {
this.setCompoundDrawablesWithIntrinsicBounds(null, normalDrawable, null, null)
}
}
}
}

附加解释:

方法setSelected()是被迫添加的。在使用Anko,相当于使用代码开发Android布局的时候selector不起作用。只好把点击后的高亮效果写在自定义的text view里。

下面看看如何扩展Anko,来使用我们上面的自定义view。

    public inline fun ViewManager.weightTextView() = weightTextView {}
public inline fun ViewManager.weightTextView(init: WeightTextView.() -> Unit) = ankoView({ WeightTextView(it) }, init)

这部分涉及到的语法内容可以参考官网

这里简单介绍一下。拿官网的例子说一下:

class HTML {
fun body() { ... }
}

现在有这么一个HTML类,那么调用的时候可以这样:

html {
body()
}

在这么一个lambda表达式里就可以直接这样调用HTML类的方法了,中间的过程是怎么样的呢

fun html(init: HTML.() -> Unit): HTML {
val html = HTML() // create the receiver object
html.init()
return html
}

其实灰常的简单呢。在方法html()里,参数是一个HTML类的扩展方法,并且此方法无参,返回Unit(java的void)。

在方法执行的过程中,首先初始化了HTML。之后调用了这个作为参数传入的扩展方法。在具体调用html()方法的时候,可以只简单写一个lambda表达式作为传入的HTML扩展方法。既然是一个类的扩展方法,那当然可以调用这个类内部的方法了。

为了帮助理解,这里给出一个参数是方法的方法:

fun main(args: Array<String>) {
calling("yo") { p ->
println("method called $p")
} calling("yoyo", ::called)
} fun calling(param: String, func: (String) -> Unit) {
func(param)
} fun called(p: String) {
println("output string $p")
}

第一个是用lambda表达式作为传入方法,第二个是已经定义好的一个方法作为传入方法。

Fragment的处理

本文中的重点在于使用Anko做布局,具体的逻辑处理java写和Kotlin写没有什么区别。这里只简单介绍一下。

为了保证兼容,这里使用Support v4来处理Fragment的显示等操作。在activity的一开始就把需要的fragemnt都加载进来。

    fun prepareTabFragments() {
val fm = supportFragmentManager
homeListFragment = HomeListFragment.newInstance()
fm.beginTransaction()
.add(ID_FRAMELAYOUT, homeListFragment)
.commit()
detailFragment = DetailFragment.newInstance(null)
detailFragment?.modelChangeListener = homeListFragment
fm.beginTransaction()
.add(ID_FRAMELAYOUT, detailFragment)
.commit()
settingsFragment = DiarySettingsFragment.newInstance()
fm.beginTransaction()
.add(ID_FRAMELAYOUT, settingsFragment)
.commit()
}

每一个tab项被点击的时候的处理:

    fun tabClick(index: Int) {
info("index is $index")
val ft = supportFragmentManager.beginTransaction()
ft.hide(homeListFragment)
ft.hide(detailFragment)
ft.hide(settingsFragment) // unselect all textviews
homeListTab?.isSelected = false
detailTab?.isSelected = false
settingsTab?.isSelected = false when (index) {
0 -> {
homeListTab?.isSelected = true
ft.show(homeListFragment)
}
1 -> {
detailTab?.isSelected = true
ft.show(detailFragment)
}
2 -> {
settingsTab?.isSelected = true
ft.show(settingsFragment)
}
else -> { }
} ft.commit()
}

分别开始每一个Fragment

在开始之前需要考虑一个很严重的事情:数据存在什么地方。本来应该是SQLite或者存在云上的。存在云裳就可以实现同一个账号登录在任何地方都可以同步到同样的内容。这里只简单模拟,存放在app的内存里。存放在Application派生类AnkoApplication

静态属性diaryDataSource里。diaryDataSource是一个ArrayList一样的列表。

class AnkoApplication : Application() {

    override fun onCreate() {
super.onCreate()
} companion object {
var diaryDataSource = mutableListOf<DiaryModel>()
}
}

第一个tab,HomeListFragment

HomeListFragment类作为第一个tab内容展示fragment,用来显示全部的日记列表的布局就非常简单了,和我们前面的例子没有什么太大的差别。就是在一个verticalLayout里放一个list view。这个list view的data source只需要一个列表。

    // 1
var view = with(ctx) {
verticalLayout {
backgroundColor = Color.WHITE listView = listView {
adapter = ArrayAdapter<DiaryModel>(ctx,
android.R.layout.simple_list_item_1,
AnkoApplication.diaryDataSource) onItemClick { adapterView, view, i, l ->
toast("clicked index: $i, content: ${AnkoApplication.diaryDataSource[i].toString()}")
}
} // 2
emptyTextView = textView {
text = resources.getString(R.string.list_view_empty)
textSize = 30f
gravity = Gravity.CENTER
}.lparams {
width = matchParent
height = matchParent
}
}
}
// 3
listView?.emptyView = emptyTextView return view
  1. 在activity里的布局可以直接写vertical{},但是在fragment里不可以这样。直接写vertical{}就已经把这个layout添加到父view上了,这fragment里是不行的。在fragment里需要创建一个单独的view,并返回。用with语句来创建这样一个单独的view。
  2. 在vertial layout里添加了一个textview。
  3. 上面一步创建的textview作为list view没有数据的时候显示的empty view来使用。

第二个tab,DetailFragment

日记的内容包括,日记title,日记本身的内容还有日记的日期。

所以布局上就包括日记的title、内容输入用的EditText以及为了说明用的text view,还有edit text里的hint。最后还有一个选择

日期的控件。

    return with(ctx) {
verticalLayout {
padding = dip(10)
backgroundColor = Color.WHITE
textView("TITLE") { }.lparams(width = matchParent) titleEditText = editText {
hint = currentDateString()
lines = 1
}.lparams(width = matchParent) {
topMargin = dip(5)
} textView("CONTENT") { }.lparams(width = matchParent) {
topMargin = dip(15)
} contentEditText = editText {
hint = "what's going on..."
setHorizontallyScrolling(false)
}.lparams(width = matchParent) {
// height = matchParent
topMargin = dip(5)
} button(R.string.button_select_time) {
gravity = Gravity.CENTER
onClick {
val fm = activity.supportFragmentManager
var datePicker = DatePickerFragment.newInstance(diaryModel?.date)
datePicker.setTargetFragment(this@DetailFragment, DetailFragment.REQUEST_DATE)
datePicker.show(fm, "date")
}
}
// *
button(R.string.button_detail_ok) {
onClick {
v ->
println("ok button clicked")
try {
var model = diaryModel!!
model.title = titleEditText?.text.toString()
model.content = contentEditText?.text.toString()
AnkoApplication.diaryDataSource.add(model) modelChangeListener?.modelChanged() toast(R.string.model_saved_ok)
} catch(e: Exception) {
Log.d("##DetailFragment", "error: ${e.toString()}")
toast(R.string.model_save_error)
}
}
}.lparams {
topMargin = dip(10)
width = matchParent
}
}.style {
view ->
when (view) {
is Button -> {
view.gravity = Gravity.CENTER
}
is TextView -> {
view.gravity = Gravity.LEFT
view.textSize = 20f
view.textColor = Color.DKGRAY
}
}
}
}

需要注意打星号的地方。按钮在点击之后会弹出一个dialog fragment来显示日期view。用户可以在这个日期view里选择相应的日期。但是,如何从日期dialog fragment传递选择的日期给DetailFragment呢?这里就涉及到两个fragment之间传递数据的问题。

选择日期的dialog fragment是DatePickerFragment

    var pickerView = DatePicker(activity)
pickerView.calendarViewShown = false
pickerView.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
pickerView.init(year, month, day) {
view, year, month, day ->
mDate = GregorianCalendar(year, month, day).time arguments.putSerializable(EXTRA_DATE, mDate)
} return AlertDialog.Builder(activity)
.setView(pickerView)
.setTitle(R.string.date_picker_title)
.setPositiveButton(R.string.picker_button_ok) { dialog, which ->
toast("hello world!")
sendResult(Activity.RESULT_OK)
}.create()

首先DatePickerFragment要继承DialogFragment之后override方法onCreateDialog(savedInstanceState: Bundle)。在这个方法里使用上面代码创建一个包含日期选择器的dialog。

在选择日期的时候,会触发DatePickerOnDateChangedListener接口的onDateChanged方法。我们在这个方法里记录选择好的日期数据,在dialog的positive按钮点击之后把这个数据发送给DetailFragment

那么怎么发送呢?使用target fargment方法。在detail fragment弹出dialog fragment的时候,把detail fragment设置为target fragment。

button(R.string.button_select_time) {
gravity = Gravity.CENTER
onClick {
val fm = activity.supportFragmentManager
var datePicker = DatePickerFragment.newInstance(diaryModel?.date)
// *
datePicker.setTargetFragment(this@DetailFragment, DetailFragment.REQUEST_DATE)
datePicker.show(fm, "date")
}
}

在标星下面的一行代码中。datePicker.setTargetFragment(this@DetailFragment,DetailFragment.REQUEST_DATE)DetailFragment设定为target fragment,并且指定REQUEST_DATE这code,为以后取出数据使用。

    companion object Factory {
val REQUEST_DATE = 0`
}

在positive按钮点击之后执行方法sendResult回传数据

    private fun sendResult(resultCode: Int) {
if (targetFragment == null)
return var i = Intent()
i.putExtra(EXTRA_DATE, mDate)
// *
targetFragment.onActivityResult(targetRequestCode, resultCode, i)
}

调用targetFragmentonActivityResult()方法来回传日期数据。

DetailFragment中通过override方法onActivityResult()来接收数据。

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode != Activity.RESULT_OK) {
return
} if (requestCode != REQUEST_DATE) {
return
} var date = data?.getSerializableExtra(DatePickerFragment.EXTRA_DATE) as Date
diaryModel?.date = date
}

日期数据传输这部分到这里结束。

全文也可以在这里画上一个句点了。以上还有很多关于Anko没有使用的地方。Anko也是可以实现代码界面分离的。继承AnkoComponent可以写出独立的布局文件,并且可以用anko preview插件来预览界面效果。就拿setting这个tab的fragment来举例:

首先定义一个独立的布局文件:

class SettingsUI<T> : AnkoComponent<T> {
override fun createView(ui: AnkoContext<T>) = with(ui) {
verticalLayout {
backgroundColor = ContextCompat.getColor(ctx, R.color.SnowWhite)
textView { text = resources.getString(R.string.settings_title) } button("activity with the same `AnkoComponent`") {
id = ID_BUTTON
}
}
} companion object Factory {
public val ID_BUTTON = 101
}
}

把这个布局文件用在DiarySettingsFragment上:

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
val view = SettingsUI<DiarySettingsFragment>().createView(AnkoContext.create(ctx, DiarySettingsFragment())) return view
}

然后这个布局还可以用在我们刚刚创建的TempActivity上:

    override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
SettingsUI<TempActivity>().setContentView(this) val button = find<Button>(SettingsUI.ID_BUTTON)
button.text = "you are in `TempActivity`, CLICK!" button.onClick {
toast("${TempActivity::class.java.simpleName}")
}
}

Activity上使用就简单很多了,只需要这么一句SettingsUI<TempActivity>().setContentView(this)

代码在这里。除了布局Anko还有其他的一些语法糖糖也很是不错,不过这里就不多说了。有更多想了解的,请移步官网

使用Kotlin&Anko, 扔掉XML开发Android应用的更多相关文章

  1. [Android]使用Kotlin+Anko开发Android(一)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4800656.html  Kotlin是由JetBrains开发 ...

  2. 使用Kotlin优雅的开发Android应用

    来源:https://juejin.im/post/5915c0a744d904006c4e3bcd demo下载地址:https://github.com/xiehui999/KotlinForAn ...

  3. 使用Kotlin开发Android应用(IV):自定义视图和Android扩展

    在读完扩展函数和默认值这篇文章之后,那么接下来要介绍什么呢?在本系列第一篇文章中我们说过,Kotlin使得Android开发更加简单,本文我们将进一步作介绍. 自定义视图 你应该还记得,在说到Kotl ...

  4. 用Kotlin开发Android应用(II):创建新项目

    这是关于Kotlin的第二篇.各位高手发现问题,请继续“拍砖”. 原文标题:Kotlin for Android(II): Create a new project 原文链接:http://anton ...

  5. 用Kotlin开发Android应用(I):介绍

    关于Kotlin,网上已有一些介绍的文章,包括Antonio Leiva的这组blog翻译稿.不过,我还是想跟进它们.翻译它们,以锻炼自己的英文翻译.各位高手发现问题,请及时“拍砖”. 原文题目:Ko ...

  6. [Android]使用Kotlin开发Android(二)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4829007.html [TOC] 使用Kotlin+OkHtt ...

  7. 使用Kotlin开发Android应用(I):简单介绍

    使用Kotlin开发Android应用(I):简单介绍 @author ASCE1885的 Github 简书 微博 CSDN 原文链接 Kotlin是一门基于JVM的编程语言.它正成长为Androi ...

  8. 让你的代码量减少3倍!使用kotlin开发Android(一)

    让你的代码量减少3倍!使用kotlin开发Android(一) 创建Kotlin工程 本文同步自博主的私人博客:wing的地方酒馆 写在前面 使用kotlin开发android已经两周多了.得到的好处 ...

  9. 即刻开始使用Kotlin开发Android的12个原因(KAD 30)

    作者:Antonio Leiva 时间:Jul, 11, 2017 原文链接:https://antonioleiva.com/reasons-kotlin-android/ 这组文章已到最后了,它们 ...

随机推荐

  1. make ;makefile; cmake; qmake的区分

    1. make 是用来执行Makefile的.2. Makefile是类unix环境下(比如Linux)的类似于批处理的"脚本"文件.其基本语法是: 目标+依赖+命令,只有在目标文 ...

  2. node.js中对同步,异步,阻塞与非阻塞的理解

    我们都知道javascript是单线程的,node.js是一个基于Chrome V8 引擎的 javascript 运行时环境,注意 node.js 不是一门语言,别搞错了. javascript为什 ...

  3. HTML标签归纳

    首先,按下h1,再按下Tab键就可以变成<h1></h1>,Ctrl+d可以复制当前行,Ctrl+Alt+Insert可以快速新建文件,Home可以跳到当前行最前面,End同理 ...

  4. c# 多个事件公用一个相应方法判断事件来源

    假设下边的相应方法有多个事件共同使用.根据事件的sender 判断来源,做相应的处理 假设事件来源DataManSystem;private void OnSystemConnected(object ...

  5. redis 数据类型为string命令整理以及示例

    #设置值 SET key value [EX seconds] [PX milliseconds] [NX|XX] SET命令加上选项已经可以完全取代SETNX, SETEX, PSETEX的功能,所 ...

  6. 分析params_s方法

    /** * 解析启动模式参数 * @param $opt */ static public function params_s($opt) { //判断传入了s参数但是值,则提示错误 if ((iss ...

  7. hdu 1598 (并查集加贪心) 速度与激情

    题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1598 一道带有贪心思想的并查集 所以说像二分,贪心这类基础的要掌握的很扎实才行. 用结构体数组储存公 ...

  8. Luogu 2577[ZJOI2005]午餐 - 动态规划

    Solution 啊... 我太菜了唔 不看题解是不可能的, 这辈子都不可能的. 首先一个队伍中排队轮到某个人的时间是递增的, 又要加上吃饭时间, 所以只能使吃饭时间递减, 才能满足最优,于是以吃饭时 ...

  9. VS2010错误

    1.用VS2010生成C++程序时,链接器工具错误 LNK1123: fatal error LNK1123: failure during conversion to COFF: file inva ...

  10. Delphi中静态方法重载还是覆盖的讨论

    Delphi中静态方法重载还是覆盖的讨论 新人学习Delphi的时候,容易搞不懂的一个问题,当子类方法和基类方法同名,并且参数也一样的时候,叫做什么呢?是覆盖,还是重载呢? 答案是隐藏父类方法. 一般 ...