├── .gitignore ├── README.md ├── app_java ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── tech │ │ └── thdev │ │ └── android_mvp_sample │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── tech │ │ │ └── thdev │ │ │ └── android_mvp_sample │ │ │ ├── adapter │ │ │ ├── ImageAdapter.java │ │ │ ├── contract │ │ │ │ └── ImageAdapterContract.java │ │ │ └── holder │ │ │ │ └── ImageViewHolder.java │ │ │ ├── data │ │ │ ├── ImageItem.java │ │ │ └── source │ │ │ │ └── image │ │ │ │ ├── SampleImageLocalDataSource.java │ │ │ │ ├── SampleImageRepository.java │ │ │ │ └── SampleImageSource.java │ │ │ ├── listener │ │ │ └── OnItemClickListener.java │ │ │ ├── util │ │ │ └── ImageAsync.java │ │ │ └── view │ │ │ └── main │ │ │ ├── MainActivity.java │ │ │ └── presenter │ │ │ ├── MainContract.java │ │ │ └── MainPresenter.java │ └── res │ │ ├── drawable │ │ ├── reload.png │ │ ├── sample_00.png │ │ ├── sample_01.png │ │ ├── sample_02.png │ │ ├── sample_03.png │ │ ├── sample_04.png │ │ ├── sample_05.png │ │ ├── sample_06.png │ │ ├── sample_07.png │ │ ├── sample_08.png │ │ ├── sample_09.png │ │ ├── sample_10.png │ │ ├── sample_11.png │ │ ├── sample_12.png │ │ ├── sample_13.png │ │ └── sample_14.png │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── content_main.xml │ │ └── item_image.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── tech │ └── thdev │ └── android_mvp_sample │ └── ExampleUnitTest.java ├── app_kotlin ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── tech │ │ └── thdev │ │ └── app_kotlin │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── tech │ │ │ └── thdev │ │ │ └── app_kotlin │ │ │ ├── adapter │ │ │ ├── ImageAdapter.kt │ │ │ ├── ImageViewHolder.kt │ │ │ └── contract │ │ │ │ └── ImageAdapterContract.kt │ │ │ ├── data │ │ │ ├── ImageItem.kt │ │ │ └── source │ │ │ │ └── image │ │ │ │ ├── SampleImageLocalDataSource.kt │ │ │ │ ├── SampleImageRepository.kt │ │ │ │ └── SampleImageSource.kt │ │ │ ├── util │ │ │ └── ImageAsync.kt │ │ │ └── view │ │ │ └── main │ │ │ ├── MainActivity.kt │ │ │ └── presenter │ │ │ ├── MainContract.kt │ │ │ └── MainPresenter.kt │ └── res │ │ ├── drawable │ │ ├── reload.png │ │ ├── sample_00.png │ │ ├── sample_01.png │ │ ├── sample_02.png │ │ ├── sample_03.png │ │ ├── sample_04.png │ │ ├── sample_05.png │ │ ├── sample_06.png │ │ ├── sample_07.png │ │ ├── sample_08.png │ │ ├── sample_09.png │ │ ├── sample_10.png │ │ ├── sample_11.png │ │ ├── sample_12.png │ │ ├── sample_13.png │ │ └── sample_14.png │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── content_main.xml │ │ └── item_image.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── tech │ └── thdev │ └── app_kotlin │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images └── device-2016-10-23-174436.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /captures 3 | .externalNativeBuild 4 | ### Android template 5 | # Built application files 6 | *.apk 7 | *.ap_ 8 | 9 | # Files for the ART/Dalvik VM 10 | *.dex 11 | 12 | # Java class files 13 | *.class 14 | 15 | # Generated files 16 | bin/ 17 | gen/ 18 | out/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | .idea/ 24 | 25 | # Local configuration file (sdk path, etc) 26 | local.properties 27 | 28 | # Proguard folder generated by Eclipse 29 | proguard/ 30 | 31 | # Log Files 32 | *.log 33 | 34 | # Android Studio Navigation editor temp files 35 | .navigation/ 36 | 37 | # Android Studio captures folder 38 | captures/ 39 | 40 | # Intellij 41 | *.iml 42 | .idea/workspace.xml 43 | 44 | # Keystore files 45 | *.jks 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android MVP Sample 2 | 3 | [![License](https://img.shields.io/hexpm/l/plug.svg)]() 4 | 5 | Android MVP Sample. 6 | 7 | ## Sample list 8 | 9 | - [Java](https://github.com/taehwandev/AndroidMVPSample/app_java) 10 | - [Kotlin](https://github.com/taehwandev/AndroidMVPSample/app_kotlin) 11 | 12 | ## Blog post 13 | 14 | 한국어 자료 15 | - [Android MVP 무작정 따라하기 - Intro](http://thdev.tech/androiddev/2016/10/12/Android-MVP-Intro.html) 16 | - [Android MVP 무작정 따라하기 - MVC 구조 이해하기](http://thdev.tech/androiddev/2016/10/23/Android-MVC-Architecture.html) 17 | - [Android MVP 무작정 따라하기 - Presenter/View 생성하기](http://thdev.tech/androiddev/2016/11/28/Android-MVP-One.html) 18 | - [Android MVP 무작정 따라하기 - Presenter/View 생성하기 Other](http://thdev.tech/androiddev/2016/11/30/Android-MVP-Two.html) 19 | - [Android MVP 무작정 따라하기 - Presenter 분리하기](http://thdev.tech/androiddev/2016/12/23/Android-MVP-Three.html) 20 | - [Android MVP 무작정 따라하기 - Adapter Contract 정의하기](http://thdev.tech/androiddev/2016/12/26/Android-MVP-Four.html) 21 | - [Android MVP 무작정 따라하기 - Adapter Contract 정의하기 2번째(동영상)](http://thdev.tech/androiddev/2016/12/27/Android-MVP-Four-Two.html) 22 | - [Android MVP 무작정 따라하기 - Adapter OnClick 정의하기(동영상)](http://thdev.tech/androiddev/2016/12/29/Android-MVP-Four-Three.html) 23 | - [Android MVP 무작정 따라하기 - Model 정의하기](http://thdev.tech/androiddev/2016/12/29/Android-MVP-Model-One.html) 24 | - [Android MVP 무작정 따라하기 - Google Architecture의 Model](http://thdev.tech/androiddev/2017/01/09/Android-MVP-Model-Two.html) 25 | - [Android MVP 무작정 따라하기 - Google Architecture Model(영상 포함)](http://thdev.tech/androiddev/2017/01/29/Android-MVP-Google-Architecture-Model.html) 26 | 27 | ## License 28 | 29 | ``` 30 | Copyright 2017 Tae-hwan 31 | 32 | Licensed under the Apache License, Version 2.0 (the "License"); 33 | you may not use this file except in compliance with the License. 34 | You may obtain a copy of the License at 35 | 36 | http://www.apache.org/licenses/LICENSE-2.0 37 | 38 | Unless required by applicable law or agreed to in writing, software 39 | distributed under the License is distributed on an "AS IS" BASIS, 40 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 41 | See the License for the specific language governing permissions and 42 | limitations under the License. 43 | ``` 44 | -------------------------------------------------------------------------------- /app_java/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app_java/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion compileSdkVersionInfo 5 | buildToolsVersion buildToolsVersionInfo 6 | 7 | defaultConfig { 8 | applicationId "tech.thdev.android_mvp_sample" 9 | minSdkVersion minSdkVerisonInfo 10 | targetSdkVersion targetSdkVersionInfo 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | } 29 | 30 | dependencies { 31 | implementation fileTree(dir: 'libs', include: ['*.jar']) 32 | 33 | implementation "androidx.appcompat:appcompat:$appcompatVersion" 34 | implementation "com.google.android.material:material:$materialVersion" 35 | 36 | // ButterKnife 37 | implementation "com.jakewharton:butterknife:$butterKnifeVersion" 38 | annotationProcessor "com.jakewharton:butterknife-compiler:$butterKnifeVersion" 39 | 40 | // Image library 41 | implementation "com.github.bumptech.glide:glide:$glideVersion" 42 | annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion" 43 | } -------------------------------------------------------------------------------- /app_java/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/tae-hwan/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app_java/src/androidTest/java/tech/thdev/android_mvp_sample/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("tech.thdev.android_mvp_sample", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app_java/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app_java/src/main/java/tech/thdev/android_mvp_sample/adapter/ImageAdapter.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample.adapter; 2 | 3 | import android.view.ViewGroup; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.recyclerview.widget.RecyclerView; 7 | 8 | import java.util.ArrayList; 9 | 10 | import tech.thdev.android_mvp_sample.adapter.contract.ImageAdapterContract; 11 | import tech.thdev.android_mvp_sample.adapter.holder.ImageViewHolder; 12 | import tech.thdev.android_mvp_sample.data.ImageItem; 13 | import tech.thdev.android_mvp_sample.listener.OnItemClickListener; 14 | 15 | /** 16 | * Created by tae-hwan on 10/23/16. 17 | */ 18 | public class ImageAdapter extends RecyclerView.Adapter implements ImageAdapterContract.Model, ImageAdapterContract.View { 19 | 20 | private OnItemClickListener onItemClickListener; 21 | 22 | private ArrayList imageItems; 23 | 24 | @Override 25 | public void addItems(ArrayList imageItems) { 26 | this.imageItems = imageItems; 27 | } 28 | 29 | @Override 30 | public void clearItem() { 31 | if (imageItems != null) { 32 | imageItems.clear(); 33 | } 34 | } 35 | 36 | @Override 37 | public void notifyAdapter() { 38 | notifyDataSetChanged(); 39 | } 40 | 41 | @Override 42 | public int getItemCount() { 43 | return imageItems != null ? imageItems.size() : 0; 44 | } 45 | 46 | @NonNull 47 | @Override 48 | public ImageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 49 | return new ImageViewHolder(parent, onItemClickListener); 50 | } 51 | 52 | @Override 53 | public void onBindViewHolder(@NonNull ImageViewHolder holder, int position) { 54 | holder.onBind(getItem(position), position); 55 | } 56 | 57 | @Override 58 | public void setOnClickListener(OnItemClickListener clickListener) { 59 | this.onItemClickListener = clickListener; 60 | } 61 | 62 | @Override 63 | public ImageItem getItem(int position) { 64 | return imageItems.get(position); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app_java/src/main/java/tech/thdev/android_mvp_sample/adapter/contract/ImageAdapterContract.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample.adapter.contract; 2 | 3 | import java.util.ArrayList; 4 | 5 | import tech.thdev.android_mvp_sample.data.ImageItem; 6 | import tech.thdev.android_mvp_sample.listener.OnItemClickListener; 7 | 8 | /** 9 | * Created by tae-hwan on 12/27/16. 10 | */ 11 | public interface ImageAdapterContract { 12 | 13 | interface View { 14 | 15 | void setOnClickListener(OnItemClickListener clickListener); 16 | 17 | void notifyAdapter(); 18 | } 19 | 20 | interface Model { 21 | 22 | void addItems(ArrayList imageItems); 23 | 24 | void clearItem(); 25 | 26 | ImageItem getItem(int position); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app_java/src/main/java/tech/thdev/android_mvp_sample/adapter/holder/ImageViewHolder.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample.adapter.holder; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.ImageView; 7 | 8 | import androidx.recyclerview.widget.RecyclerView; 9 | 10 | import butterknife.BindView; 11 | import butterknife.ButterKnife; 12 | import tech.thdev.android_mvp_sample.R; 13 | import tech.thdev.android_mvp_sample.data.ImageItem; 14 | import tech.thdev.android_mvp_sample.listener.OnItemClickListener; 15 | import tech.thdev.android_mvp_sample.util.ImageAsync; 16 | 17 | /** 18 | * Created by tae-hwan on 12/26/16. 19 | */ 20 | public class ImageViewHolder extends RecyclerView.ViewHolder { 21 | 22 | private OnItemClickListener onItemClickListener; 23 | 24 | @BindView(R.id.img_view) 25 | ImageView imageView; 26 | 27 | public ImageViewHolder(ViewGroup parent, OnItemClickListener onItemClickListener) { 28 | super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false)); 29 | 30 | this.onItemClickListener = onItemClickListener; 31 | 32 | ButterKnife.bind(this, itemView); 33 | } 34 | 35 | public void onBind(ImageItem item, final int position) { 36 | itemView.setOnClickListener(new View.OnClickListener() { 37 | 38 | @Override 39 | public void onClick(View v) { 40 | if (onItemClickListener != null) { 41 | onItemClickListener.onItemClick(position); 42 | } 43 | } 44 | }); 45 | 46 | new ImageAsync(imageView.getContext(), imageView).execute(item.getImageRes()); 47 | } 48 | } -------------------------------------------------------------------------------- /app_java/src/main/java/tech/thdev/android_mvp_sample/data/ImageItem.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample.data; 2 | 3 | /** 4 | * Created by tae-hwan on 10/23/16. 5 | */ 6 | public class ImageItem { 7 | 8 | private int imageRes; 9 | private String title; 10 | 11 | public ImageItem(int imageRes, String title) { 12 | this.imageRes = imageRes; 13 | this.title = title; 14 | } 15 | 16 | public int getImageRes() { 17 | return imageRes; 18 | } 19 | 20 | public String getTitle() { 21 | return title; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app_java/src/main/java/tech/thdev/android_mvp_sample/data/source/image/SampleImageLocalDataSource.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample.data.source.image; 2 | 3 | import android.content.Context; 4 | 5 | import java.util.ArrayList; 6 | 7 | import tech.thdev.android_mvp_sample.data.ImageItem; 8 | 9 | /** 10 | * Created by tae-hwan on 1/30/17. 11 | */ 12 | public class SampleImageLocalDataSource implements SampleImageSource { 13 | 14 | @Override 15 | public void getImages(Context context, int size, LoadImageCallback loadImageCallback) { 16 | ArrayList items = new ArrayList<>(); 17 | 18 | for (int i = 0; i < size; i++) { 19 | final int random = (int) (Math.random() * 15); 20 | final String name = String.format("sample_%02d", random); 21 | final int resource = context.getResources().getIdentifier(name, "drawable", context.getApplicationContext().getPackageName()); 22 | items.add(new ImageItem(resource, name)); 23 | } 24 | 25 | if (loadImageCallback != null) { 26 | loadImageCallback.onImageLoaded(items); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app_java/src/main/java/tech/thdev/android_mvp_sample/data/source/image/SampleImageRepository.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample.data.source.image; 2 | 3 | import android.content.Context; 4 | 5 | import java.util.ArrayList; 6 | 7 | import tech.thdev.android_mvp_sample.data.ImageItem; 8 | 9 | /** 10 | * Created by tae-hwan on 1/30/17. 11 | * 12 | * Memory cache / Local/Remote DataSource 13 | */ 14 | public class SampleImageRepository implements SampleImageSource { 15 | 16 | private static SampleImageRepository sampleImageRepository; 17 | 18 | public static SampleImageRepository getInstance() { 19 | if (sampleImageRepository == null) { 20 | sampleImageRepository = new SampleImageRepository(); 21 | } 22 | return sampleImageRepository; 23 | } 24 | 25 | private SampleImageLocalDataSource sampleImageLocalDataSource; 26 | 27 | private SampleImageRepository() { 28 | sampleImageLocalDataSource = new SampleImageLocalDataSource(); 29 | } 30 | 31 | @Override 32 | public void getImages(Context context, int size, final LoadImageCallback loadImageCallback) { 33 | sampleImageLocalDataSource.getImages(context, size, new LoadImageCallback() { 34 | @Override 35 | public void onImageLoaded(ArrayList list) { 36 | if (loadImageCallback != null) { 37 | loadImageCallback.onImageLoaded(list); 38 | } 39 | } 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app_java/src/main/java/tech/thdev/android_mvp_sample/data/source/image/SampleImageSource.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample.data.source.image; 2 | 3 | import android.content.Context; 4 | 5 | import java.util.ArrayList; 6 | 7 | import tech.thdev.android_mvp_sample.data.ImageItem; 8 | 9 | /** 10 | * Created by tae-hwan on 1/30/17. 11 | */ 12 | public interface SampleImageSource { 13 | 14 | interface LoadImageCallback { 15 | 16 | void onImageLoaded(ArrayList list); 17 | } 18 | 19 | void getImages(Context context, int size, LoadImageCallback loadImageCallback); 20 | } 21 | -------------------------------------------------------------------------------- /app_java/src/main/java/tech/thdev/android_mvp_sample/listener/OnItemClickListener.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample.listener; 2 | 3 | /** 4 | * Created by tae-hwan on 12/26/16. 5 | */ 6 | public interface OnItemClickListener { 7 | 8 | void onItemClick(int position); 9 | } 10 | -------------------------------------------------------------------------------- /app_java/src/main/java/tech/thdev/android_mvp_sample/util/ImageAsync.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample.util; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.os.AsyncTask; 7 | import android.widget.ImageView; 8 | 9 | import java.lang.ref.WeakReference; 10 | 11 | /** 12 | * Created by tae-hwan on 12/26/16. 13 | */ 14 | public class ImageAsync extends AsyncTask { 15 | 16 | private Context context; 17 | private final WeakReference imageViewReference; 18 | 19 | public ImageAsync(Context context, ImageView imageView) { 20 | this.context = context; 21 | imageViewReference = new WeakReference<>(imageView); 22 | } 23 | 24 | @Override 25 | protected Bitmap doInBackground(Integer... params) { 26 | BitmapFactory.Options options = new BitmapFactory.Options(); 27 | options.inSampleSize = 2; 28 | return BitmapFactory.decodeResource(context.getResources(), params[0], options); 29 | } 30 | 31 | @Override 32 | protected void onPreExecute() { 33 | super.onPreExecute(); 34 | imageViewReference.get().setImageResource(0); 35 | } 36 | 37 | @Override 38 | protected void onPostExecute(Bitmap bitmap) { 39 | super.onPostExecute(bitmap); 40 | imageViewReference.get().setImageBitmap(bitmap); 41 | } 42 | } -------------------------------------------------------------------------------- /app_java/src/main/java/tech/thdev/android_mvp_sample/view/main/MainActivity.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample.view.main; 2 | 3 | import android.os.Bundle; 4 | import android.view.Menu; 5 | import android.view.MenuItem; 6 | import android.view.View; 7 | import android.widget.Toast; 8 | 9 | import androidx.appcompat.app.AppCompatActivity; 10 | import androidx.appcompat.widget.Toolbar; 11 | import androidx.recyclerview.widget.LinearLayoutManager; 12 | import androidx.recyclerview.widget.RecyclerView; 13 | 14 | import com.google.android.material.floatingactionbutton.FloatingActionButton; 15 | import com.google.android.material.snackbar.Snackbar; 16 | 17 | import butterknife.BindView; 18 | import butterknife.ButterKnife; 19 | import tech.thdev.android_mvp_sample.R; 20 | import tech.thdev.android_mvp_sample.adapter.ImageAdapter; 21 | import tech.thdev.android_mvp_sample.data.source.image.SampleImageRepository; 22 | import tech.thdev.android_mvp_sample.view.main.presenter.MainContract; 23 | import tech.thdev.android_mvp_sample.view.main.presenter.MainPresenter; 24 | 25 | public class MainActivity extends AppCompatActivity implements MainContract.View { 26 | 27 | @BindView(R.id.recycler_view) 28 | RecyclerView recyclerView; 29 | 30 | private ImageAdapter imageAdapter; 31 | 32 | private MainPresenter mainPresenter; 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | setContentView(R.layout.activity_main); 38 | 39 | ButterKnife.bind(this); 40 | 41 | imageAdapter = new ImageAdapter(); 42 | recyclerView.setAdapter(imageAdapter); 43 | 44 | mainPresenter = new MainPresenter(); 45 | mainPresenter.attachView(this); 46 | mainPresenter.setImageAdapterModel(imageAdapter); 47 | mainPresenter.setImageAdapterView(imageAdapter); 48 | mainPresenter.setSampleImageData(SampleImageRepository.getInstance()); 49 | 50 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 51 | setSupportActionBar(toolbar); 52 | 53 | recyclerView.setLayoutManager(new LinearLayoutManager(this)); 54 | 55 | mainPresenter.loadItems(this, false); 56 | 57 | FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); 58 | fab.setOnClickListener(new View.OnClickListener() { 59 | @Override 60 | public void onClick(View view) { 61 | Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) 62 | .setAction("Action", null).show(); 63 | } 64 | }); 65 | } 66 | 67 | @Override 68 | public void showToast(String title) { 69 | Toast.makeText(this, title, Toast.LENGTH_SHORT).show(); 70 | } 71 | 72 | @Override 73 | protected void onDestroy() { 74 | super.onDestroy(); 75 | 76 | mainPresenter.detachView(); 77 | } 78 | 79 | @Override 80 | public boolean onCreateOptionsMenu(Menu menu) { 81 | // Inflate the menu; this adds items to the action bar if it is present. 82 | getMenuInflater().inflate(R.menu.menu_main, menu); 83 | return true; 84 | } 85 | 86 | @Override 87 | public boolean onOptionsItemSelected(MenuItem item) { 88 | // Handle action bar item clicks here. The action bar will 89 | // automatically handle clicks on the Home/Up button, so long 90 | // as you specify a parent activity in AndroidManifest.xml. 91 | int id = item.getItemId(); 92 | 93 | //noinspection SimplifiableIfStatement 94 | if (id == R.id.action_reload) { 95 | mainPresenter.loadItems(this, true); 96 | return true; 97 | } 98 | 99 | return super.onOptionsItemSelected(item); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app_java/src/main/java/tech/thdev/android_mvp_sample/view/main/presenter/MainContract.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample.view.main.presenter; 2 | 3 | import android.content.Context; 4 | 5 | import tech.thdev.android_mvp_sample.adapter.contract.ImageAdapterContract; 6 | import tech.thdev.android_mvp_sample.data.source.image.SampleImageRepository; 7 | 8 | /** 9 | * Created by tae-hwan on 12/22/16. 10 | */ 11 | public interface MainContract { 12 | 13 | interface View { 14 | 15 | void showToast(String title); 16 | } 17 | 18 | interface Presenter { 19 | 20 | void attachView(View view); 21 | 22 | void setImageAdapterModel(ImageAdapterContract.Model adapterModel); 23 | 24 | void setImageAdapterView(ImageAdapterContract.View adapterView); 25 | 26 | void detachView(); 27 | 28 | void setSampleImageData(SampleImageRepository sampleImageData); 29 | 30 | void loadItems(Context context, boolean isClear); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app_java/src/main/java/tech/thdev/android_mvp_sample/view/main/presenter/MainPresenter.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.android_mvp_sample.view.main.presenter; 2 | 3 | import android.content.Context; 4 | 5 | import java.util.ArrayList; 6 | 7 | import tech.thdev.android_mvp_sample.adapter.contract.ImageAdapterContract; 8 | import tech.thdev.android_mvp_sample.data.ImageItem; 9 | import tech.thdev.android_mvp_sample.data.source.image.SampleImageRepository; 10 | import tech.thdev.android_mvp_sample.data.source.image.SampleImageSource; 11 | import tech.thdev.android_mvp_sample.listener.OnItemClickListener; 12 | 13 | /** 14 | * Created by tae-hwan on 12/22/16. 15 | */ 16 | public class MainPresenter implements MainContract.Presenter, OnItemClickListener { 17 | 18 | private MainContract.View view; 19 | 20 | private ImageAdapterContract.Model adapterModel; 21 | private ImageAdapterContract.View adapterView; 22 | 23 | private SampleImageRepository sampleImageData; 24 | 25 | @Override 26 | public void attachView(MainContract.View view) { 27 | this.view = view; 28 | } 29 | 30 | @Override 31 | public void detachView() { 32 | view = null; 33 | } 34 | 35 | @Override 36 | public void setSampleImageData(SampleImageRepository sampleImageData) { 37 | this.sampleImageData = sampleImageData; 38 | } 39 | 40 | @Override 41 | public void loadItems(Context context, final boolean isClear) { 42 | sampleImageData.getImages(context, 10, new SampleImageSource.LoadImageCallback() { 43 | @Override 44 | public void onImageLoaded(ArrayList list) { 45 | if (list != null) { 46 | if (isClear) { 47 | adapterModel.clearItem(); 48 | } 49 | adapterModel.addItems(list); 50 | adapterView.notifyAdapter(); 51 | } 52 | } 53 | }); 54 | 55 | 56 | } 57 | 58 | @Override 59 | public void setImageAdapterModel(ImageAdapterContract.Model adapterModel) { 60 | this.adapterModel = adapterModel; 61 | } 62 | 63 | @Override 64 | public void setImageAdapterView(ImageAdapterContract.View adapterView) { 65 | this.adapterView = adapterView; 66 | 67 | this.adapterView.setOnClickListener(this); 68 | } 69 | 70 | @Override 71 | public void onItemClick(int position) { 72 | ImageItem imageItem = adapterModel.getItem(position); 73 | view.showToast(imageItem.getTitle()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/reload.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_00.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_01.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_02.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_03.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_04.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_05.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_06.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_07.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_08.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_09.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_10.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_11.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_12.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_13.png -------------------------------------------------------------------------------- /app_java/src/main/res/drawable/sample_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/drawable/sample_14.png -------------------------------------------------------------------------------- /app_java/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app_java/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /app_java/src/main/res/layout/item_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app_java/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app_java/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app_java/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app_java/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app_java/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app_java/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/AndroidMVPSample/e6ce0eba9989c200e04f3d7bb75180d2d2f32383/app_java/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app_java/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #BB86FC 4 | #6200EE 5 | #3700B3 6 | #03DAC5 7 | -------------------------------------------------------------------------------- /app_java/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /app_java/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidMVPSample-Java 3 | Settings 4 | Reload 5 | 6 | -------------------------------------------------------------------------------- /app_java/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 | 10 | 11 | 15 | 16 |