apply plugin: 'com.android.application' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "imangazaliev.notelin" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets { main.java.srcDirs += 'src/main/kotlin' androidTest.java.srcDirs += 'src/androidTest/kotlin' } } dependencies { ... } kapt { generateStubs = true } buildscript { ext.kotlin_version = '1.0.2-eap-15' repositories { mavenCentral() maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } repositories { mavenCentral() }
allprojects { repositories { jcenter() mavenCentral() maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } maven { url "https://mvn.arello-mobile.com/" } maven { url "https://jitpack.io" } maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } } }
dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" compile "com.android.support:appcompat-v7:23.1.1" compile 'com.android.support:recyclerview-v7:23.1.1' compile 'com.android.support:cardview-v7:23.1.1' // Android Kotlin compile 'com.pawegio.kandroid:kandroid:0.5.0@aar' //ActiveAndroid DB compile 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT' //FAB compile 'com.melnykov:floatingactionbutton:1.3.0' //MaterialDialog compile 'com.github.afollestad.material-dialogs:core:0.8.5.6@aar' //MVP compile 'com.arello-mobile:moxy:0.4.2' compile 'com.arello-mobile:moxy-android:0.4.2' kapt 'com.arello-mobile:moxy-compiler:0.4.2' //RX compile 'io.reactivex:rxjava:1.1.0' compile 'io.reactivex:rxandroid:1.1.0' //Depency Injection kapt 'com.google.dagger:dagger-compiler:2.0.2' compile 'com.google.dagger:dagger:2.0.2' provided 'org.glassfish:javax.annotation:10.0-b28' //EventBus compile 'org.greenrobot:eventbus:3.0.0' }
class Note : Model { @Column(name = "title") public var title: String? = null @Column(name = "text") public var text: String? = null @Column(name = "create_date") public var createDate: Date? = null @Column(name = "change_date") public var changeDate: Date? = null constructor(title: String, createDate: Date, changeDate: Date) { this.title = title this.createDate = createDate this.changeDate = changeDate } constructor() fun getInfo(): String = ":\n$title\n" + " :\n${formatDate(createDate)}\n" + " :\n${formatDate(changeDate)}"; }
var note = Note(" ", Date()) note.save() ... note.delete()
<meta-data android:name="AA_DB_NAME" android:value="Notelin.db" /> <meta-data android:name="AA_DB_VERSION" android:value="1" />
class NotelinApplication : Application() { ... }
class NoteDao { /** * */ fun createNote(): Note { var note = Note(" ", Date()) note.save() return note } /** * */ fun saveNote(note: Note) = note.save() /** * View */ fun loadAllNotes() = Select().from(Note::class.java).execute<Note>() /** * id */ fun getNoteById(noteId: Long) = Select().from(Note::class.java).where("id = ?", noteId).executeSingle<Note>() /** * */ fun deleteAllNotes() { Delete().from(Note::class.java).execute<Note>(); } /** * id */ fun deleteNote(note: Note) { note.delete() } }
@StateStrategyType(value = AddToEndSingleStrategy::class) interface MainView : MvpView { fun onNotesLoaded(notes: List<Note>) fun updateView() fun onSearchResult(notes: List<Note>) fun onAllNotesDeleted() fun onNoteDeleted() fun showNoteInfoDialog(noteInfo: String) fun hideNoteInfoDialog() fun showNoteDeleteDialog(notePosition: Int) fun hideNoteDeleteDialog() fun showNoteContextDialog(notePosition: Int) fun hideNoteContextDialog() }
class MainActivity : MvpAppCompatActivity(), MainView {
class MainActivity : MainView, MvpAppCompatActivity() {
@InjectViewState class MainPresenter: MvpPresenter<MainView>() { }
@Module class NoteDaoModule { @Provides @Singleton fun provideNoteDao() : NoteDao= NoteDao() }
@Singleton @Component(modules = arrayOf(NoteDaoModule::class)) interface AppComponent { fun inject(mainPresenter : MainPresenter) }
class NotelinApplication : Application() { companion object { lateinit var graph: AppComponent } override fun onCreate() { super.onCreate() graph = DaggerAppComponent.builder().noteDaoModule(NoteDaoModule()).build() } }
@InjectViewState class MainPresenter : MvpPresenter<MainView>() { @Inject lateinit var mNoteDao: NoteDao init { NotelinApplication.graph.inject(this) } }
@InjectPresenter lateinit var mPresenter: MainPresenter
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.activities.MainActivity"> <TextView android:id="@+id/tvNotesIsEmpty" android:layout_width="match_parent" android:layout_height="match_parent" android:text="@string/notes_is_empty" android:gravity="center" /> <android.support.v7.widget.RecyclerView android:id="@+id/rvNotesList" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical" app:layoutManager="android.support.v7.widget.LinearLayoutManager" /> <com.melnykov.fab.FloatingActionButton android:id="@+id/fabButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|right" android:layout_margin="16dp" android:src="@mipmap/ic_add" app:fab_colorNormal="@color/colorPrimary" app:fab_colorPressed="@color/colorPrimaryDark" /> </FrameLayout>
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) }
fabButon.attachToRecyclerView(rvNotesList)
import kotlinx.android.synthetic.main.activity_main.*
override fun onFirstViewAttach() { super.onFirstViewAttach() loadAllNotes() } /** * View */ fun loadAllNotes() { mNotesList = mNoteDao.loadAllNotes() viewState.onNotesLoaded(mNotesList) }
class NotesAdapter : RecyclerView.Adapter<NotesAdapter.ViewHolder> { private var mNotesList: List<Note> = ArrayList() constructor(notesList: List<Note>) { mNotesList = notesList } /** * View ViewHolder , . */ override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder { var v = LayoutInflater.from(viewGroup.context).inflate(R.layout.note_item_layout, viewGroup, false); return ViewHolder(v); } /** * View i */ override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) { var note = mNotesList[i]; viewHolder.mNoteTitle.text = note.title; viewHolder.mNoteDate.text = formatDate(note.changeDate); } override fun getItemCount(): Int { return mNotesList.size } /** * ViewHolder, . */ class ViewHolder : RecyclerView.ViewHolder { var mNoteTitle: TextView var mNoteDate: TextView constructor(itemView: View) : super(itemView) { mNoteTitle = itemView.findViewById(R.id.tvItemNoteTitle) as TextView mNoteDate = itemView.findViewById(R.id.tvItemNoteDate) as TextView } } }
@file:JvmName("DateUtils") package imangazaliev.notelin.utils import java.text.SimpleDateFormat import java.util.* fun formatDate(date: Date?): String { var dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm"); return dateFormat.format(date) }
String formattedDate = DateUtils.formatDate(date);
override fun onNotesLoaded(notes: List<Note>) { rvNotesList.adapter = NotesAdapter(notes) updateView() } override fun updateView() { rvNotesList.adapter.notifyDataSetChanged() if (rvNotesList.adapter.itemCount == 0) { rvNotesList.visibility = View.GONE tvNotesIsEmpty.visibility = View.VISIBLE } else { rvNotesList.visibility = View.VISIBLE tvNotesIsEmpty.visibility = View.GONE } }
class ItemClickSupport private constructor(private val recyclerView: RecyclerView) { companion object { fun addTo(view: RecyclerView) = view.getTag(R.id.item_click_support) as? ItemClickSupport ?: ItemClickSupport(view) fun removeFrom(view: RecyclerView): ItemClickSupport? { val support = view.getTag(R.id.item_click_support) as? ItemClickSupport support?.detach(view) return support } } interface ItemClickListener { fun onClick(recyclerView: RecyclerView, position: Int, view: View) } interface ItemLongClickListener { fun onLongClick(recyclerView: RecyclerView, position: Int, view: View): Boolean } private var itemClickListener: ItemClickListener? = null private var itemLongClickListener: ItemLongClickListener? = null private val onClickListener = View.OnClickListener { val holder = recyclerView.getChildViewHolder(it) itemClickListener?.onClick(recyclerView, holder.adapterPosition, it) } private val onLongClickListener = View.OnLongClickListener listener@{ val position = recyclerView.getChildViewHolder(it).adapterPosition return@listener itemLongClickListener?.onLongClick(recyclerView, position, it) ?: false } private val attachListener = object : RecyclerView.OnChildAttachStateChangeListener { override fun onChildViewAttachedToWindow(view: View) { if (itemClickListener != null) { view.setOnClickListener(onClickListener) } if (itemLongClickListener != null) { view.setOnLongClickListener(onLongClickListener) } } override fun onChildViewDetachedFromWindow(view: View) = Unit } init { recyclerView.setTag(R.id.item_click_support, this) recyclerView.addOnChildAttachStateChangeListener(attachListener) } fun setOnItemClickListener(listener: ItemClickListener): ItemClickSupport { itemClickListener = listener return this } fun setOnItemClickListener(listener: (RecyclerView, Int, View) -> Unit) = setOnItemClickListener(object : ItemClickListener { override fun onClick(recyclerView: RecyclerView, position: Int, view: View) { listener(recyclerView, position, view) } }) fun setOnItemLongClickListener(listener: ItemLongClickListener): ItemClickSupport { itemLongClickListener = listener return this } fun setOnItemLongClickListener(block: (RecyclerView, Int, View) -> Boolean): ItemClickSupport = setOnItemLongClickListener(object : ItemLongClickListener { override fun onLongClick(recyclerView: RecyclerView, position: Int, view: View): Boolean { return block (recyclerView, position, view) } }) private fun detach(view: RecyclerView) { view.removeOnChildAttachStateChangeListener(attachListener) view.setTag(R.id.item_click_support, null) } }
with(ItemClickSupport.addTo(rvNotesList)) { setOnItemClickListener { recyclerView, position, v -> mPresenter.openNote(this@MainActivity, position) } setOnItemLongClickListener { recyclerView, position, v -> mPresenter.showNoteContextDialog(position); true } }
/** * */ fun openNote(activity: Activity, position: Int) { val intent = Intent(activity, NoteActivity::class.java) intent.putExtra("note_id", mNotesList[position].id) activity.startActivity(intent) }
/** * */ fun showNoteContextDialog(position: Int) { viewState.showNoteContextDialog(position) } /** * */ fun hideNoteContextDialog() { viewState.hideNoteContextDialog() }
fabButton.setOnClickListener { mPresenter.openNewNote(this) }
fun openNewNote(activity: Activity) { val newNote = mNoteDao.createNote() mNotesList.add(newNote) sortNotesBy(getCurrentSortMethod()) openNote(activity, mNotesList.indexOf(newNote)) }
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_search" android:icon="@android:drawable/ic_menu_search" android:title="@string/search" app:actionViewClass="android.support.v7.widget.SearchView" app:showAsAction="always" /> </menu>
override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.main, menu) initSearchView(menu) return true } private fun initSearchView(menu: Menu) { var searchViewMenuItem = menu.findItem(R.id.action_search); var searchView = searchViewMenuItem.actionView as SearchView; searchView.onQueryChange { query -> mPresenter.search(query) } searchView.setOnCloseListener { mPresenter.search(""); false } }
/** * */ fun search(query: String) { if (query.equals("")) { viewState.onSearchResult(mNotesList) } else { val searchResults = mNotesList.filter { it.title!!.startsWith(query, ignoreCase = true) } viewState.onSearchResult(searchResults) } }
override fun onSearchResult(notes: List<Note>) { rvNotesList.adapter = NotesAdapter(notes) }
<item android:title="@string/sort_by"> <menu> <item android:id="@+id/menuSortByName" android:title="@string/sort_by_title" /> <item android:id="@+id/menuSortByDate" android:title="@string/sort_by_date" /> </menu> </item>
override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.menuSortByName -> mPresenter.sortNotesBy(MainPresenter.SortNotesBy.NAME) R.id.menuSortByDate -> mPresenter.sortNotesBy(MainPresenter.SortNotesBy.DATE) } return super.onOptionsItemSelected(item) }
/** * */ fun sortNotesBy(sortMethod: SortNotesBy) { mNotesList.sortWith(sortMethod) viewState.updateView() }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:id="@+id/etTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:background="#EEEEEE" android:textColor="#212121" android:paddingLeft="10dp" android:paddingTop="10dp" android:paddingBottom="5dp" android:hint="" /> <TextView android:id="@+id/tvNoteDate" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:background="#EEEEEE" android:textColor="#212121" android:paddingLeft="10dp" android:paddingBottom="10dp" /> <EditText android:id="@+id/etText" android:layout_width="match_parent" android:layout_height="match_parent" android:textColor="#000000" android:gravity="start" android:hint="" android:background="@null" android:paddingLeft="10dp" /> </LinearLayout>
class MyActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) MyActivityUI().setContentView(this) } } class MyActivityUI : AnkoComponent { companion object { val ET_TITLE_ID = View.generateViewId() val TV_NOTE_DATE_ID = View.generateViewId() val ET_TEXT_ID = View.generateViewId() } override fun createView(ui: AnkoContext<MainActivit>) = with(ui) { verticalLayout { editText { id = ET_TITLE_ID singleLine = true backgroundColor = 0xEE.gray.opaque textColor = 0x21.gray.opaque leftPadding = dip(10) topPadding = dip(10) bottomPadding = dip(5) hint = "" }.lparams(matchParent, wrapContent) textView { id = TV_NOTE_DATE_ID singleLine = true backgroundColor = 0xEE.gray.opaque textColor = 0x21.gray.opaque leftPadding = dip(10) bottomPadding = dip(10) }.lparams(matchParent, wrapContent) editText { id = ET_TEXT_ID textColor = Color.BLACK gravity = Gravity.START hint = "" background = null leftPadding = dip(10) } } } }
interface NoteView : MvpView { fun showNote(note: Note) fun onNoteSaved() fun onNoteDeleted() fun showNoteInfoDialog(noteInfo: String) fun hideNoteInfoDialog() fun showNoteDeleteDialog() fun hideNoteDeleteDialog() }
class NoteActivity : MvpAppCompatActivity(), NoteView { @InjectPresenter lateinit var mPresenter: NotePresenter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_note) val noteId = intent.extras.getLong("note_id", -1) mPresenter.showNote(noteId) } }
@InjectViewState class NotePresenter : MvpPresenter<NoteView>() { @Inject lateinit var mNoteDao: NoteDao lateinit var mNote: Note init { NotelinApplication.graph.inject(this) } fun showNote(noteId: Long) { mNote = mNoteDao.getNoteById(noteId) viewState.showNote(mNote) } }
fun inject(notePresenter: NotePresenter)
override fun showNote(note: Note) { tvNoteDate.text = DateUtils.formatDate(note.changeDate) etTitle.setText(note.title) etText.setText(note.text) }
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <item android:id="@+id/menuSaveNote" android:title="@string/save" app:showAsAction="never" /> </menu>
override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.note, menu) return true } Activity: override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.menuSaveNote -> mPresenter.saveNote(etTitle.text.toString(), etText.text.toString()) } return super.onOptionsItemSelected(item) }
Source: https://habr.com/ru/post/275255/
All Articles