在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称:scaloid开源软件地址:https://gitee.com/scalalibs/scaloid开源软件介绍:Simpler AndroidScaloid is a library that simplifies your Android code. It makes your code easy to understand and maintain by leveraging Scala language. For example, the code block shown below: val button = new Button(context)button.setText("Greet")button.setOnClickListener(new OnClickListener() { def onClick(v: View) { Toast.makeText(context, "Hello!", Toast.LENGTH_SHORT).show() }})layout.addView(button) is reduced to: SButton("Greet", toast("Hello!")) Benefits
DemosFork one of this to start a new project:
Learn how Scaloid can be used in action:
Contents
Other linksCore design principle"Being practically simple" is number one principle of Scaloid. Most frequently used things should be written shorter, like Huffman coding. To do this, I first observed Android programs I wrote, and thought that which part of the code is more fundamental than others. For example, what is the most essential part of buttons? Buttons should have some visible things on it, such as title or image, so the buttons are created like this: UI Layout without XMLAndroid SDK leverages XML to build UI layouts. However, XML is considered still a bit verbose, and lacks programmability. Scaloid composes UI layout in Scala DSL style, therefore achieve both clarity and programmability. For example, suppose a legacy XML layout as shown below: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="20dip"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Sign in" android:layout_marginBottom="25dip" android:textSize="24.5sp"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="ID"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/userId"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Password"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/password" android:inputType="textPassword"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/signin" android:text="Sign in"/> <LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"> <Button android:text="Help" android:id="@+id/help" android:layout_width="match_parent" android:layout_height="wrap_content"/> <Button android:text="Sign up" android:id="@+id/signup" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout></LinearLayout> is reduced to: new SVerticalLayout { STextView("Sign in").textSize(24.5.sp).<<.marginBottom(25.dip).>> STextView("ID") SEditText() STextView("Password") SEditText() inputType TEXT_PASSWORD SButton("Sign in") new SLinearLayout { SButton("Help") SButton("Sign up") }.wrap.here}.padding(20.dip) The layout description shown above is highly programmable. You can easily wire your logic into the layout: new SVerticalLayout { STextView("Sign in").textSize(24.5.sp).<<.marginBottom(25.dip).>> STextView("ID") val userId = SEditText() STextView("Password") val pass = SEditText() inputType TEXT_PASSWORD SButton("Sign in", signin(userId.text, pass.text)) new SLinearLayout { SButton("Help", openUri("http://help.url")) SButton("Sign up", openUri("http://signup.uri")) }.wrap.here}.padding(20.dip) Because a Scaloid layout description is plain Scala code, it is type-safe. Automatic layout converterThis converter turns an Android XML layout into a Scaloid layout: Migration tipScaloid is fully compatible with legacy xml layout files.You can access a widget described in xml layout as: onCreate { setContentView(R.layout.main) val name = find[EditText](R.id.name) // do something with `name`} Responsive layoutBasically, a layout written in Scaloid is just an ordinary Scala code, so you can just freely composite the layout according to the device configuration: import org.scaloid.util.Configuration._if(long) SButton("This button is shown only for a long screen " + "dimension ("+ width + ", " + height + ")")if(landscape) new SLinearLayout { SButton("Buttons for") SButton("landscape layout") if(dpi <= HDPI) SButton("You have a high resolution display!")}.here Please refer to this blog post for more detail: Further readings about Scaloid layoutLifecycle managementWith Android API, Registering and unregistering BroadcastReceiver can be done as: var connectivityListener: BroadcastReceiver = nulldef onResume() { super.onResume() // ... connectivityListener = new BroadcastReceiver { def onReceive(context: Context, intent: Intent) { doSomething() } } registerReceiver(connectivityListener, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))}def onPause() { unregisterReceiver(connectivityListener) // ... super.onPause()} In Scaloid, the directly equivalent code is: broadcastReceiver(ConnectivityManager.CONNECTIVITY_ACTION) { doSomething()} Scaloid has highly flexible resource register/unregister management architecture.If this code is written in services, registering and unregistering is done in onCreate and onDestroy respectively.If the same code is in activities, registering and unregistering is done in onResume and onPause respectively.This is just a default behavior.You can override a preference that determine when the register/unregister preforms.Overriding it is simple as well: broadcastReceiver(ConnectivityManager.CONNECTIVITY_ACTION) { doSomething()}(this, onStartStop) Then, the receiver is registered onStart, and unregistered onStop. onDestroy can be called in many times!You can declare def openFileStream(file: File): InputStream = { val stream = new FileInputStream(file) onDestroy(stream.close()) // automatically closed when the Activity is destroyed!! stream}
Further reading: Refer to this blog post for more details. Asynchronous task processingAndroid API provides Instead of: activity.runOnUiThread { new Runnable() { def run() { debug("Running only in Activity class") } }} In Scaloid, use it like this: runOnUiThread(debug("Running in any context")) Running a job asynchronously and notifying the UI thread is a very frequently used pattern. Although Android API provides a helper class new AsyncTask[String, Void, String] { def doInBackground(params: Array[String]) = { doAJobTakeSomeTime(params) } override def onPostExecute(result: String) { alert("Done!", result) }}.execute("param") Using Future { val result = doAJobTakeSomeTime(params) runOnUiThread(alert("Done!", result))} When you don't want to build sophisticate UI interactions, but just want to display something by calling a single Scaloid method (e.g. Future { alert("Done!", doAJobTakeSomeTime(params))} It is a great win as it exposes your idea clearly. Just like we thrown away Using Further reading: Refer to this blog post for an important consideration when using Implicit conversionsScaloid employs several implicit conversions. Some of the available implicit conversions are shown below: Uri conversionString => Uri The functions such as play ringtones play("content://media/internal/audio/media/50") , open a URI: openUri("http://scaloid.org") , or wherever you want. Alternatively, you can specify the conversion as: val uri:Uri = "http://scaloid.org".toUri Unit conversionUnits val inPixel:Int = 32.dipval inPixel2:Int = 22.sp Reversely, pixel unit can also be converted into val inDip:Double = 35.px2dipval inSp:Double = 27.px2sp Resource IDsScaloid provides several implicit conversions that convert from def toast(msg:CharSequence) = ...toast(R.string.my_message) // implicit conversion works! Although Scaloid provides these conversions implicitly, explicit conversion may be required in some context. In this case, methods warn("Will display the content of the resource: " + R.string.my_message.r2String) Currently, Further reading: Context as an implicit parameterMany methods in the Android API require an instance of a class implicit val ctx = ... or just extend trait Intentnew Intent(context, classOf[MyActivity]) is reduced to: SIntent[MyActivity] When a method takes an startService(new Intent(context, classOf[MyService]))stopService(new Intent(context, classOf[MyService])) is reduced to: startService[MyService]stopService[MyService] or val intent = // initialize the intent and put some attributes on itintent.start[MyActivity] An intent that has a long list of extra attributes: new Intent().putExtra("valueA", valueA).putExtra("valueB", valueB).putExtra("valueC", valueC) is reduced to: new Intent().put(valueA, valueB, valueC) Toasttoast("hi, there!") If you want a longer toast: longToast("long toast") DialogProgressDialog.show(context, "Dialog", "working...", true) is reduced to: spinnerDialog("Dialog", "working...") When you call activity.runOnUiThread(new Runnable() { public void run() { Toast.makeText(activity, "hi, there!", Toast.LENGTH_SHORT).show(); }}); Pending intentPendingIntent.getActivity(context, 0, new Intent(context, classOf[MyActivity]), 0)PendingIntent.getService(context, 0, new Intent(context, classOf[MyService]), 0) is reduced to: pendingActivity[MyActivity]pendingService[MyService] Open URIsThis opens a web browser (or another view assigned to the http protocol). openUri("http://scaloid.org") System servicesGetting system service objects become much simpler. The following legacy code: val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE).asInstanceOf[Vibrator]vibrator.vibrate(500) is reduced to: vibrator.vibrate(500) Under the hood, Scaloid defines a function def vibrator(implicit ctx: Context) = ctx.getSystemService(Context.VIBRATOR_SERVICE).asInstanceOf[Vibrator] All the system service accessors available in Android API level 8 are defined (e.g. Enriched Implicit classesSuppose an Android class ListenersAndroid API defines many listener interfaces for callback notifications. For example, find[Button](R.id.search).setOnClickListener(new View.OnClickListener { def onClick(v:View) { openUri("http://scaloid.org") }}) Scaloid provides a shortcut that dramatically reduces the length of the code: find[Button](R.id.search).onClick(openUri("http://scaloid.org")) All other listener-appending methods such as Some conventions we employed for method naming are:
button.onClick(info("touched"))button.onClick((v:View) => info("touched a button "+v))
Multiple method listenersMethods inputField.beforeTextChanged(saveTextStatus()) is equivalent to: inputField.addTextChangedListener(new TextWatcher { def beforeTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { saveTextStatus() } def onTextChanged(p1: CharSequence, p2: Int, p3: Int, p4: Int) {} def afterTextChanged(p1: Editable) {}}) Also, we override inputField.beforeTextChanged((s:CharSequence, _:Int, _:Int) => saveText(s)) Other listeners in Android API can also be accessed in this way. Layout contextIn Android API, layout information is stored into a LinearLayout layout = new LinearLayout(context);Button button = new Button(context);button.setText("Click");LinearLayout.LayoutParams params = new LinearLayout.LayoutParams();params.weight = 1.0f; // sets some valuebutton.setLayoutParams(params
|
请发表评论