Scala&Android下的开发trait经验分享

前一段时间写了一个在Android中加入AdMob的博客,详见http://my.oschina.net/noahxiao/blog/61987

还有一个用Scala开发Android应用-使用trait与implicit优化Activity,详见http://my.oschina.net/noahxiao/blog/61720

首先说明一下,我在android下是采用scala语言开发的。并不想讨论太多语言的好坏。只是把我开发时的经验与大家分享一下。

class ScalaAndroidActivity extends Activity with AdMobAdvertising with TestDataSource1 with TestDataSource2

大家先不要晕scala语言的继承关系是可以这样写的。with...

1、trait AdMobAdvertising

package org.noahx.common

import FindView._
import android.app.Activity
import android.widget.LinearLayout
import com.google.ads.AdView
import com.google.ads.AdSize
import com.google.ads.AdRequest
import android.os.Bundle

trait AdMobAdvertising extends Activity with FindView {

  def adLinearLayout: LinearLayout
  
  def adUnitId:String="a14xxxxxxxxxx"

  lazy val adView = new AdView(this, AdSize.BANNER, adUnitId)

  override def onCreate(savedInstanceState: Bundle) = {
    super.onCreate(savedInstanceState)

    adLinearLayout.addView(adView)
    adView.loadAd(new AdRequest())
  }

  override def onDestroy() = {
    if (adView != null) {
      adView.destroy()
    }
    super.onDestroy()
  }

}

以上就是我写的trait,这样我们可以不用太多考虑AdMob本身,只要实现adLinearLayout这个方法就可以了。来看看Activity

package org.noahx.scalaandroid

import android.app.Activity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.view.View
import org.noahx.common.FindView._
import android.widget.LinearLayout
import com.google.ads.AdView
import com.google.ads.AdSize
import com.google.ads.AdRequest
import org.noahx.common.AdMobAdvertising

class ScalaAndroidActivity extends Activity with AdMobAdvertising {

  lazy val text = findView[TextView](R.id.text1)
  lazy val button = findView[Button](R.id.button1)

  override def adLinearLayout = findView[LinearLayout](R.id.adLinearLayout) //注意这里决定AdMob放在哪个LinearLayout中

  override def onCreate(savedInstanceState: Bundle) = {
    setContentView(R.layout.main)  //这个要放在super之前

    super.onCreate(savedInstanceState)

    button.onClick { view: View =>
      text.setText("hello scala1!!!")
    }

  }

}

用这样一个AdMobAdvertising的trait只要在想加入AdMob的Activity中with一下,指定一个LinearLayout就可以了。

是不是看上去很干净

当然如果你的adUnitId号是变动的也可以override掉,在Activity中加入如下代码

override def adUnitId="xxxxxxxxxxxxxxxx"

  

2、trait BaseDataSource

在做完上面的模式后我在想,是不是访问sqlite数据时也可以这样来写呢

我们先看看BaseDataSource

package org.noahx.common

import android.app.Activity
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.os.Bundle

trait BaseDataSource extends Activity {

  def getSQLiteOpenHelper(): SQLiteOpenHelper

  val sqliteOpenHelper = getSQLiteOpenHelper()

  var database: SQLiteDatabase = null
  
  def getDatabase()=database

  private def open() = {
    database = sqliteOpenHelper.getWritableDatabase()
  }

  private def close() = {
    database.close()
  }

  override def onCreate(savedInstanceState: Bundle) = {
    super.onCreate(savedInstanceState)
    open()
  }

  override def onResume() = {
    open()
    super.onResume()
  }

  override def onPause() {
    close()
    super.onPause()
  }

  override def onDestroy() = {
    close()
    super.onDestroy()
  }

}

我们可以看到这个也是继承Activity,里面已经定义好了何时创建连接,何时关闭连接。缺少的只是getSQLiteOpenHelper没有实现

下面我们来看看SQLiteOpenHelper,这个目前本身没什么特点。

package org.noahx.scalaandroid
import android.database.sqlite.SQLiteOpenHelper
import android.database.sqlite.SQLiteDatabase.CursorFactory
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.util.Log

class TestSQLiteOpenHelper(context: Context, dbName: String, cFactory: CursorFactory, ver: Int) extends SQLiteOpenHelper(context: Context, dbName: String, cFactory: CursorFactory, ver: Int) {

  val databaseCreateTest1 = "create table test1 (id integer primary key autoincrement, name text not null)"

  val databaseCreateTest2 = "create table test2 (id integer primary key autoincrement, name text not null)"

  def this(context: Context) = {
    this(context, "test.db", null, 1)
  }

  override def onCreate(database: SQLiteDatabase) = {
    database.execSQL(databaseCreateTest1)
    database.execSQL(databaseCreateTest2)
  }

  override def onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) = {
    Log.w(classOf[TestSQLiteOpenHelper].getName(), "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data")
    db.execSQL("DROP TABLE IF EXISTS " + "test1")
    db.execSQL("DROP TABLE IF EXISTS " + "test2")
    onCreate(db)

  }
}

下面我们要基于BaseDataSource写自己的DataSource了,这才是重点

(scala语言的定义与文件名无关,可以一个.scala文件中写多个class,trait,object,甚至全部程序写一个.scala文件中)

package org.noahx.scalaandroid

import org.noahx.common.BaseDataSource
import scala.collection.mutable.ListBuffer
import android.content.ContentValues

trait TestBaseDataSource extends BaseDataSource {

  def getSQLiteOpenHelper() = new TestSQLiteOpenHelper(this)

}

trait TestDataSource1 extends TestBaseDataSource {
  private val tableName = "test1"

  def createTest1(name: String) = {
    val values = new ContentValues()
    values.put("name", name)
    val insertId = database.insert(tableName, null, values)
  }

  def getTest1s(): List[String] = {
    var words = new ListBuffer[String]

    val cursor = database.query(tableName, Array("name"), null, null, null, null, null)

    cursor.moveToFirst()
    while (!cursor.isAfterLast()) {
      words += cursor.getString(0)
      cursor.moveToNext()
    }
    cursor.close()

    words.toList
  }
}

trait TestDataSource2 extends TestBaseDataSource {
  private val tableName = "test2"

  def createTest2(name: String) = {
    val values = new ContentValues()
    values.put("name", name)
    val insertId = database.insert(tableName, null, values)
  }

  def getTest2s(): List[String] = {
    var words = new ListBuffer[String]

    val cursor = database.query(tableName, Array("name"), null, null, null, null, null)

    cursor.moveToFirst()
    while (!cursor.isAfterLast()) {
      words += cursor.getString(0)
      cursor.moveToNext()
    }
    cursor.close()

    words.toList
  }
}

首先定义了TestBaseDataSource来统一设置helper,然后TestDataSource1与TestDataSource2就可以直接使用database这个对象了

来看看最终的Activity吧

package org.noahx.scalaandroid

import android.app.Activity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.view.View
import org.noahx.common.FindView._
import android.widget.LinearLayout
import com.google.ads.AdView
import com.google.ads.AdSize
import com.google.ads.AdRequest
import org.noahx.common.AdMobAdvertising
import android.util.Log

class ScalaAndroidActivity extends Activity with AdMobAdvertising with TestDataSource1 with TestDataSource2 {

  lazy val text = findView[TextView](R.id.text1)
  lazy val button = findView[Button](R.id.button1)
  
  override def adLinearLayout = findView[LinearLayout](R.id.adLinearLayout)

  override def onCreate(savedInstanceState: Bundle) = {
    setContentView(R.layout.main)

    super.onCreate(savedInstanceState)
    
    for(i <-0 to 10){
      createTest1("test1."+i)
      createTest2("test2."+i)
    }

    button.onClick { view: View =>
      text.setText("hello scala!!!")
      
      Log.i(classOf[ScalaAndroidActivity].getName(),getTest1s().toString())
      Log.i(classOf[ScalaAndroidActivity].getName(),getTest2s().toString())
    }

  }

}

这样的效果就是如果哪个Activity想用TestDataSource1,就直接加到父行为中,并在这个Activity中可以直接调用如:createTest1这样的方法直接操作数据库

由以下代码来写数据库

for(i <-0 to 10){
      createTest1("test1."+i)
      createTest2("test2."+i)
    }

onClick时由Log输出结果

      Log.i(classOf[ScalaAndroidActivity].getName(),getTest1s().toString())
      Log.i(classOf[ScalaAndroidActivity].getName(),getTest2s().toString())

后台打印结果如下

你可能感兴趣的:(scala,android,sqlite,admob,trait)