📜 ⬆️ ⬇️

Snake layout and "quantum" particles in Android applications (Part 1)

image

An article about libraries that allowed me to do without the MVP pattern and XML markup in android applications.

Motivation


Only starting to develop serious applications for Android (Java) at work, I inherited the code of ardent php-shnik. The first 2 weeks of analyzing the functional style of the project drove me to the hell of a programmer, and I began to doubt my abilities. “If every project is written this way, I’m unlikely to become a coder,” I thought. But after a while my future mentor got a job, who introduced me to the MVP pattern. It was a balm for my soul, I loved programming the most. But on one MVP I did not last long.

I learned the technology behind the technology: Kotlin, Retrofit, RxJava, Dagger, etc. Each of them satisfied my technological hunger, which was not enough for long. Constant jumps between layout files (xml), models, presenters, views were very tiring. The permanent rendering of the image of a miserable one page (module), described in 4-6 files, made me fear. I prograstiroval, work entered into stagnation, this prospect did not suit me. Until I became acquainted with the anko library. I did not have enough words to describe the admiration for this technology. Anko allows you to lay out a page using DSL, without resorting to xml. This allowed me to reduce the description of the module (page) to 2-3 files. But the article is not about her, but about its continuation. The Internet is filled with articles and examples of working with anko.
')
Some time passed, and in anko I did not find peace. I was no longer satisfied that the description of one textView contains more than 3 lines of code, that for the unfortunate sheet (RecyclerView) I will have to create an adapter every time. I decided to write my own library called Koatl. The main idea in the library: if possible, use no more than 1-2 lines per one layout particle (textView, button, etc). I will show it in the application-dictionary for an example.

Practice


Link to github for the first part .

In Android Studio, create a new project with Kotlin support. There is no need for xml files, so you can uncheck the “Generate Layout File” option in the last page of the project creation.

here


build.gradle (project level)
buildscript { ext.kotlin_version = '1.2.40' repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.1.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } allprojects { repositories { google() jcenter() maven { url 'https://jitpack.io' } } } task clean(type: Delete) { delete rootProject.buildDir } 


build.gradle (application level)
 apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 27 defaultConfig { applicationId "com.brotandos.dictionary" minSdkVersion 19 targetSdkVersion 27 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.android.support:appcompat-v7:27.1.1' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" implementation 'com.github.Brotandos:koatl:v0.1.1' } 


MainActivity.kt
 import android.annotation.SuppressLint import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.support.v4.app.Fragment import android.support.v4.app.FragmentManager import org.jetbrains.anko.frameLayout class MainActivity : AppCompatActivity() { private val fragManager = supportFragmentManager private val container = 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) frameLayout { id = container } // If there are any instances saved, return if (savedInstanceState != null) return // else run default fragment changeFragment(DictionaryFragment()) } @SuppressLint("PrivateResource") private fun changeFragment(f: Fragment, needToCleanStack: Boolean = false) { if (needToCleanStack) clearBackStack() fragManager.beginTransaction() .setCustomAnimations( R.anim.abc_fade_in, R.anim.abc_fade_out, R.anim.abc_popup_enter, R.anim.abc_popup_exit) .replace(container, f) .addToBackStack(f::class.simpleName) .commit() } private fun clearBackStack() { if (fragManager.backStackEntryCount == 0) return val first = fragManager.getBackStackEntryAt(0) fragManager.popBackStack(first.id, FragmentManager.POP_BACK_STACK_INCLUSIVE) } override fun onBackPressed() { if (fragManager.backStackEntryCount > 1) fragManager.popBackStack() else finish() } } 


Add 2 icons to the drawable folder (Image Asset)




res \ values ​​\ styles.xml
 <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> </resources> 


Paste the following files in the same folder as MainActivity
Models.kt
 data class Dictionary(var title: String, val items: MutableList<DictionaryItem>) data class DictionaryItem(val key: String, val value: String) 


G.kt
 object G { object Color { const val CARD_SHADOW_1: Int = 0xFFEEEEEE.toInt() const val CARD_SHADOW_2: Int = 0xFFDDDDDD.toInt() const val CARD: Int = 0xFFFEFEFE.toInt() const val PRIMARY: Int = 0xFF3F51B5.toInt() } } 


Styles.kt
 import android.content.Context import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.view.View import com.brotandos.koatlib.times import org.jetbrains.anko.dip fun Context.cardBg(color: Int, radius: Int = dip(2)) = GradientDrawable() * { shape = GradientDrawable.RECTANGLE cornerRadius = radius.toFloat() setColor(color) } fun View.layerCard(color: Int = G.Color.CARD) = LayerDrawable(arrayOf( context.cardBg(G.Color.CARD_SHADOW_1, dip(4)), context.cardBg(G.Color.CARD_SHADOW_2, dip(3)), context.cardBg(color) )) * { setLayerInset(0, 0, 0, 0, 0) setLayerInset(1, 0, 0, 0, dip(1)) setLayerInset(2, dip(1), dip(1), dip(1), dip(2)) } val bgLayerCard: View.() -> Unit = { background = layerCard() } 


Here is the demonstrative code of the dictionary fragment itself:

 import android.graphics.Color import android.support.v7.widget.RecyclerView import android.widget.TextView import com.brotandos.koatlib.* import org.jetbrains.anko.imageResource import org.jetbrains.anko.matchParent import org.jetbrains.anko.sdk25.coroutines.onClick class DictionaryFragment: KoatlFragment() { private val dictionary: Dictionary private val icCollapsed = R.drawable.ic_collapsed private val icExpanded = R.drawable.ic_expanded private lateinit var vList: RecyclerView init { val list = mutableListOf<DictionaryItem>() for (i in 0 until 20) list += DictionaryItem("key-$i", "value-$i") dictionary = Dictionary("First dictionary", list) } //     override fun markup() = KUI { //  view     'v'.  LinearLayout    vVertical { // FrameLayout, bg(colorRes: Int) - -,   background  vFrame(bg(R.color.colorPrimary)) { // TextView,  - Float.sp    //  lp -   layoutParams // submissive  width = wrapContent  height = wrapContent // g5 - gravity = Gravity.CENTER. //          // 1 - -, 2 - -, 5 - , 456 -    .. vLabel(dictionary.title, 10f.sp, text(Color.WHITE)).lp(submissive, g5) }.lparams(matchParent, 50.dp) //   Int.dp   //   RecyclerView.      //      vList = vList(linear).forEachOf(dictionary.items) { item, i -> // item -  , i -  // bgLayerCard -   Styles.kt. //     CSS   view- // mw -   width = matchParent  height = wrapContent vVertical(bgLayerCard, mw) { lateinit var vValue: TextView // content456  ,   gravity = Gravity.CENTER_VERTICAL //     , //        view- vLinear(content456) { vImage(icCollapsed) { onClick { if (this@vImage.resourceId == icCollapsed) { imageResource = icExpanded vValue.visible() } else { imageResource = icCollapsed vValue.hidden() } } }.lp(row, 5f()) // row -  ,   width = matchParent  height = wrapContent //  Float.invoke()    LinearLayout'  weight // vLabel(item.key).lp(row, 1f()) }.lp(dominant) // dominant - width = matchParent, height = matchParent // hidden - visibility = View.GONE vValue = vLabel(item.value, text(G.Color.PRIMARY), hidden).lp(dominant, 1f(), m(2.dp)) }.llp(row, m(2.dp)) //  m(number: Int)  ,   margin }.lp(row, m(5.dp)) } } } 

Result:


For now, stay here. I think there is enough to discuss here. Here is the library itself . In the next article I will write about the library with "quantum" particles.

Since the library was written under itself, there is no documentation either, some (and maybe many) features remained undescribed. My friends liked the libraries, so I decided to publish. I decided to do the documentation as soon as the libraries come out in people.

Source: https://habr.com/ru/post/354576/


All Articles