├── samples
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ └── layout
│ │ │ │ ├── activity_listview.xml
│ │ │ │ ├── item_footer.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── item_body_second.xml
│ │ │ │ ├── item_header.xml
│ │ │ │ └── item_body_first.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── org
│ │ │ └── osori
│ │ │ └── samples
│ │ │ ├── RecyclerViewActivity.java
│ │ │ ├── ListViewActivity.java
│ │ │ ├── SampleListViewAdapter.java
│ │ │ └── SampleRecyclerViewAdapter.java
│ ├── test
│ │ └── java
│ │ │ └── org
│ │ │ └── osori
│ │ │ └── samples
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── org
│ │ └── osori
│ │ └── samples
│ │ └── ExampleInstrumentedTest.java
├── build.gradle
└── proguard-rules.pro
├── sectionadapter
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── org
│ │ │ └── osori
│ │ │ └── sectionadapter
│ │ │ ├── IndexPath.java
│ │ │ ├── SectionListViewAdapter.java
│ │ │ └── SectionAdapter.java
│ ├── test
│ │ └── java
│ │ │ └── org
│ │ │ └── osori
│ │ │ └── sectionadapter
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── org
│ │ └── osori
│ │ └── sectionadapter
│ │ └── ExampleInstrumentedTest.java
├── build.gradle
└── proguard-rules.pro
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew.bat
├── .gitignore
├── gradlew
└── README.md
/samples/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/sectionadapter/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':sectionadapter', ':samples'
--------------------------------------------------------------------------------
/samples/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | samples
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JunsuLime/Android-SectionAdapter/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/sectionadapter/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SectionAdapter
3 |
4 |
--------------------------------------------------------------------------------
/samples/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JunsuLime/Android-SectionAdapter/HEAD/samples/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/samples/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JunsuLime/Android-SectionAdapter/HEAD/samples/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/samples/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JunsuLime/Android-SectionAdapter/HEAD/samples/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/samples/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JunsuLime/Android-SectionAdapter/HEAD/samples/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/samples/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JunsuLime/Android-SectionAdapter/HEAD/samples/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/samples/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JunsuLime/Android-SectionAdapter/HEAD/samples/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/samples/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JunsuLime/Android-SectionAdapter/HEAD/samples/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/samples/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JunsuLime/Android-SectionAdapter/HEAD/samples/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/samples/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JunsuLime/Android-SectionAdapter/HEAD/samples/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/samples/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JunsuLime/Android-SectionAdapter/HEAD/samples/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/samples/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu May 18 23:57:52 KST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/sectionadapter/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/samples/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/samples/src/test/java/org/osori/samples/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package org.osori.samples;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/sectionadapter/src/test/java/org/osori/sectionadapter/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package org.osori.sectionadapter;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/samples/src/main/res/layout/activity_listview.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/samples/src/main/res/layout/item_footer.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
--------------------------------------------------------------------------------
/samples/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/samples/src/main/res/layout/item_body_second.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
14 |
--------------------------------------------------------------------------------
/samples/src/main/res/layout/item_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
--------------------------------------------------------------------------------
/sectionadapter/src/main/java/org/osori/sectionadapter/IndexPath.java:
--------------------------------------------------------------------------------
1 | package org.osori.sectionadapter;
2 |
3 | /**
4 | * IndexPath
5 | *
6 | * Concept of IndexPath is came from Swift TableView.
7 | * It have big Section in table and
8 | * Table contains Item.
9 | *
10 | * section is index of section and
11 | * item is index of item.
12 | */
13 |
14 | public class IndexPath {
15 | public int section;
16 | public int item;
17 |
18 | // Header's item value
19 | public static int HEADER = -1;
20 | // Footer's item value
21 | public static int FOOTER = -2;
22 |
23 | public IndexPath(int section, int item) {
24 | this.section = section;
25 | this.item = item;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/sectionadapter/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 |
4 | group = 'com.github.JunsuLime'
5 |
6 | android {
7 | compileSdkVersion 25
8 | buildToolsVersion "25.0.2"
9 |
10 | defaultConfig {
11 | minSdkVersion 9
12 | targetSdkVersion 25
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
17 |
18 | }
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | }
26 |
27 | dependencies {
28 | compile 'com.android.support:recyclerview-v7:25.3.0'
29 | }
30 |
--------------------------------------------------------------------------------
/samples/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/samples/src/main/res/layout/item_body_first.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
15 |
16 |
20 |
--------------------------------------------------------------------------------
/samples/src/androidTest/java/org/osori/samples/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package org.osori.samples;
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("org.osori.samples", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/sectionadapter/src/androidTest/java/org/osori/sectionadapter/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package org.osori.sectionadapter;
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("org.osori.sectionadapter.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/samples/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.2"
6 |
7 | defaultConfig {
8 | applicationId "org.osori.samples"
9 | minSdkVersion 9
10 | targetSdkVersion 25
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "android.support.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 |
25 | dependencies {
26 | compile fileTree(dir: 'libs', include: ['*.jar'])
27 | compile 'com.android.support:appcompat-v7:25.3.1'
28 | compile 'com.android.support:recyclerview-v7:25.3.1'
29 |
30 | // compile 'com.github.JunsuLime:Android-RecyclerView-SectionAdapter:v0.1.0'
31 | debugCompile project(':sectionadapter')
32 | }
33 |
--------------------------------------------------------------------------------
/samples/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 C:\Users\junsu\AppData\Local\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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/sectionadapter/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 C:\Users\junsu\AppData\Local\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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/samples/src/main/java/org/osori/samples/RecyclerViewActivity.java:
--------------------------------------------------------------------------------
1 | package org.osori.samples;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.support.v7.widget.LinearLayoutManager;
6 | import android.support.v7.widget.RecyclerView;
7 |
8 | import java.util.Arrays;
9 | import java.util.List;
10 |
11 |
12 | public class RecyclerViewActivity extends Activity {
13 |
14 | RecyclerView sampleRecyclerView;
15 | SampleRecyclerViewAdapter sampleAdapter;
16 |
17 | String[] stringArray = {
18 | "This is", "RecyclerView", "Adapter",
19 | "for", "EveryOne", ",", "Opensource", "made by", "Osori.junsuLime"
20 | };
21 |
22 | @Override
23 | protected void onCreate(Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 | setContentView(R.layout.activity_main);
26 |
27 | sampleRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
28 |
29 | List stringList = Arrays.asList(stringArray);
30 | sampleAdapter = new SampleRecyclerViewAdapter(this, stringList);
31 |
32 | sampleRecyclerView.setLayoutManager(new LinearLayoutManager(this));
33 | sampleRecyclerView.setAdapter(sampleAdapter);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/samples/src/main/java/org/osori/samples/ListViewActivity.java:
--------------------------------------------------------------------------------
1 | package org.osori.samples;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.View;
7 | import android.widget.Button;
8 | import android.widget.ListView;
9 | import android.widget.Toast;
10 |
11 | /**
12 | * Created by junsu on 2017-06-10.
13 | */
14 |
15 | public class ListViewActivity extends AppCompatActivity {
16 |
17 | Button testButton;
18 | ListView listView;
19 |
20 | SampleListViewAdapter adapter;
21 |
22 | @Override
23 | protected void onCreate(@Nullable Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 | setContentView(R.layout.activity_listview);
26 |
27 | testButton = (Button) findViewById(R.id.test_button);
28 | listView = (ListView) findViewById(R.id.list_view);
29 |
30 | testButton.setOnClickListener(new View.OnClickListener() {
31 | @Override
32 | public void onClick(View v) {
33 | Toast.makeText(ListViewActivity.this, "child count: " + listView.findViewWithTag("test"), Toast.LENGTH_SHORT).show();
34 | }
35 | });
36 |
37 | adapter = new SampleListViewAdapter(this);
38 | listView.setAdapter(adapter);
39 |
40 | listView.setSelection(30);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/samples/src/main/java/org/osori/samples/SampleListViewAdapter.java:
--------------------------------------------------------------------------------
1 | package org.osori.samples;
2 |
3 | import android.content.Context;
4 | import android.support.v4.content.ContextCompat;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import org.osori.sectionadapter.IndexPath;
12 | import org.osori.sectionadapter.SectionListViewAdapter;
13 |
14 | /**
15 | * Created by junsu on 2017-07-03.
16 | */
17 |
18 | public class SampleListViewAdapter extends SectionListViewAdapter {
19 |
20 | private Context mContext;
21 | private LayoutInflater mInflater;
22 |
23 | private final int VIEW_TYPE_FIRST_BODY = 0;
24 | private final int VIEW_TYPE_HEADER = 1;
25 |
26 | SampleListViewAdapter(Context context) {
27 | mContext = context;
28 | mInflater = LayoutInflater.from(context);
29 | }
30 |
31 | @Override
32 | public void onBindItemHolder(ViewHolder holder, IndexPath indexPath) {
33 | if (holder instanceof BodyHolder) {
34 | ((BodyHolder) holder).bind(indexPath.item);
35 | } else if (holder instanceof HeaderHolder) {
36 | //
37 | }
38 | }
39 |
40 | @Override
41 | public ViewHolder onCreateItemHolder(ViewGroup container, int viewType) {
42 | switch (viewType) {
43 | case VIEW_TYPE_FIRST_BODY:
44 | return new BodyHolder(mInflater.inflate(R.layout.item_body_first, container, false));
45 | case VIEW_TYPE_HEADER:
46 | return new HeaderHolder(mInflater.inflate(R.layout.item_header, container, false));
47 | }
48 | return null;
49 | }
50 |
51 | @Override
52 | public int getSectionCount() {
53 | return 1;
54 | }
55 |
56 | @Override
57 | public int getSectionHeaderViewType(int sectionIndex) {
58 | return VIEW_TYPE_HEADER;
59 | }
60 |
61 | @Override
62 | public int getSectionItemCount(int sectionIndex) {
63 | return 50;
64 | }
65 |
66 | @Override
67 | public int getSectionItemViewType(int sectionIndex) {
68 | return 0;
69 | }
70 |
71 |
72 | @Override
73 | public int getViewTypeCount() {
74 | return 2;
75 | }
76 |
77 | class HeaderHolder extends ViewHolder {
78 | TextView headerText;
79 | public HeaderHolder(View itemView) {
80 | super(itemView);
81 |
82 | headerText = (TextView) itemView.findViewById(R.id.header_text);
83 | headerText.setTag("test");
84 | }
85 | }
86 |
87 | class BodyHolder extends ViewHolder {
88 |
89 | ImageView bodyImage;
90 | TextView bodyText;
91 |
92 | public BodyHolder(View itemView) {
93 | super(itemView);
94 | bodyImage = (ImageView) itemView.findViewById(R.id.body_first_image);
95 | bodyText = (TextView) itemView.findViewById(R.id.body_first_text);
96 | }
97 |
98 | public void bind(int itemIndex) {
99 | bodyImage.setImageDrawable(ContextCompat.getDrawable(mContext, R.mipmap.ic_launcher_round));
100 | bodyText.setText(String.valueOf(itemIndex));
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | reated by https://www.gitignore.io/api/android,androidstudio
2 |
3 | ### Android ###
4 | # Built application files
5 | *.apk
6 | *.ap_
7 |
8 | # Files for the ART/Dalvik VM
9 | *.dex
10 |
11 | # Java class files
12 | *.class
13 |
14 | # Generated files
15 | bin/
16 | gen/
17 | out/
18 |
19 | # Gradle files
20 | .gradle/
21 | build/
22 |
23 | # Local configuration file (sdk path, etc)
24 | local.properties
25 |
26 | # Proguard folder generated by Eclipse
27 | proguard/
28 |
29 | # Log Files
30 | *.log
31 |
32 | # Android Studio Navigation editor temp files
33 | .navigation/
34 |
35 | # Android Studio captures folder
36 | captures/
37 |
38 | # Intellij
39 | .idea/
40 | *.iml
41 | .idea/workspace.xml
42 | .idea/tasks.xml
43 | .idea/gradle.xml
44 | .idea/dictionaries
45 | .idea/libraries
46 |
47 | # Keystore files
48 | *.jks
49 |
50 | # External native build folder generated in Android Studio 2.2 and later
51 | .externalNativeBuild
52 |
53 | # Google Services (e.g. APIs or Firebase)
54 | google-services.json
55 |
56 | # Freeline
57 | freeline.py
58 | freeline/
59 | freeline_project_description.json
60 |
61 | ### Android Patch ###
62 | gen-external-apklibs
63 |
64 | ### AndroidStudio ###
65 | # Covers files to be ignored for android development using Android Studio.
66 |
67 | # Built application files
68 |
69 | # Files for the ART/Dalvik VM
70 |
71 | # Java class files
72 |
73 | # Generated files
74 |
75 | # Gradle files
76 | .gradle
77 |
78 | # Signing files
79 | .signing/
80 |
81 | # Local configuration file (sdk path, etc)
82 |
83 | # Proguard folder generated by Eclipse
84 |
85 | # Log Files
86 |
87 | # Android Studio
88 | /*/build/
89 | /*/local.properties
90 | /*/out
91 | /*/*/build
92 | /*/*/production
93 | *.ipr
94 | *~
95 | *.swp
96 |
97 | # Android Patch
98 |
99 | # External native build folder generated in Android Studio 2.2 and later
100 |
101 | # NDK
102 | obj/
103 |
104 | # IntelliJ IDEA
105 | *.iws
106 | /out/
107 |
108 | # User-specific configurations
109 | .idea/libraries/
110 | .idea/.name
111 | .idea/compiler.xml
112 | .idea/copyright/profiles_settings.xml
113 | .idea/encodings.xml
114 | .idea/misc.xml
115 | .idea/modules.xml
116 | .idea/scopes/scope_settings.xml
117 | .idea/vcs.xml
118 | .idea/jsLibraryMappings.xml
119 | .idea/datasources.xml
120 | .idea/dataSources.ids
121 | .idea/sqlDataSources.xml
122 | .idea/dynamic.xml
123 | .idea/uiDesigner.xml
124 |
125 | # Keystore files
126 |
127 | # OS-specific files
128 | .DS_Store
129 | .DS_Store?
130 | ._*
131 | .Spotlight-V100
132 | .Trashes
133 | ehthumbs.db
134 | Thumbs.db
135 |
136 | # Legacy Eclipse project files
137 | .classpath
138 | .project
139 |
140 | # Mobile Tools for Java (J2ME)
141 | .mtj.tmp/
142 |
143 | # Package Files #
144 | *.jar
145 | *.war
146 | *.ear
147 |
148 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
149 | hs_err_pid*
150 |
151 | ## Plugin-specific files:
152 |
153 | # mpeltonen/sbt-idea plugin
154 | .idea_modules/
155 |
156 | # JIRA plugin
157 | atlassian-ide-plugin.xml
158 |
159 | # Mongo Explorer plugin
160 | .idea/mongoSettings.xml
161 |
162 | # Crashlytics plugin (for Android Studio and IntelliJ)
163 | com_crashlytics_export_strings.xml
164 | crashlytics.properties
165 | crashlytics-build.properties
166 | fabric.properties
167 |
168 | ### AndroidStudio Patch ###
169 | # Google Services plugin
170 |
171 | !/gradle/wrapper/gradle-wrapper.jar
172 |
173 | # End of https://www.gitignore.io/api/android,androidstudio
174 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/samples/src/main/java/org/osori/samples/SampleRecyclerViewAdapter.java:
--------------------------------------------------------------------------------
1 | package org.osori.samples;
2 |
3 | import android.content.Context;
4 | import android.support.v4.content.ContextCompat;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.ImageView;
10 | import android.widget.TextView;
11 |
12 | import org.osori.sectionadapter.SectionAdapter;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * Created by junsu
18 | */
19 |
20 | public class SampleRecyclerViewAdapter extends SectionAdapter {
21 |
22 | private Context mContext;
23 | private LayoutInflater mInflater;
24 |
25 | // first section's item list
26 | // it is define at RecyclerViewActivity.
27 | private List mItemList;
28 |
29 | // for convenience section index is defined as final variable
30 | private final int SECTION_FIRST = 0;
31 | private final int SECTION_SECOND = 1;
32 |
33 | // for convenience view type is defined as final variable
34 | private final int ITEM_HEADER = 0;
35 | private final int ITEM_FOOTER = 1;
36 | private final int ITEM_BODY_FIRST = 2;
37 | private final int ITEM_BODY_SECOND = 3;
38 |
39 | public SampleRecyclerViewAdapter(Context context, List itemList) {
40 | super(context);
41 | mContext = context;
42 |
43 | // get inflater to inflate in onCreateItemHolder.
44 | mInflater = LayoutInflater.from(context);
45 |
46 | mItemList = itemList;
47 | }
48 |
49 | @Override
50 | public RecyclerView.ViewHolder onCreateItemHolder(ViewGroup parent, int viewType) {
51 | switch (viewType) {
52 | case ITEM_BODY_FIRST:
53 | return new FirstBodyViewHolder(mInflater.inflate(R.layout.item_body_first, parent ,false));
54 | case ITEM_BODY_SECOND:
55 | return new SecondBodyViewHolder(mInflater.inflate(R.layout.item_body_second, parent, false));
56 | case ITEM_HEADER:
57 | return new HeaderViewHolder(mInflater.inflate(R.layout.item_header, parent, false));
58 | case ITEM_FOOTER:
59 | return new FooterViewHolder(mInflater.inflate(R.layout.item_footer, parent, false));
60 | }
61 | return null;
62 | }
63 |
64 | @Override
65 | public void onBindItemHolder(RecyclerView.ViewHolder holder, IndexPath indexPath) {
66 | int section = indexPath.section;
67 | int item = indexPath.item;
68 |
69 | if (holder instanceof FirstBodyViewHolder) {
70 | ((FirstBodyViewHolder) holder).bind(mItemList.get(item));
71 | }
72 | if (holder instanceof HeaderViewHolder) {
73 | ((HeaderViewHolder) holder).bind(section);
74 | }
75 | }
76 |
77 | @Override
78 | public int getSectionCount() {
79 | return 2;
80 | }
81 |
82 | @Override
83 | public int getSectionItemViewType(int sectionIndex) {
84 | switch (sectionIndex) {
85 | case SECTION_FIRST:
86 | return ITEM_BODY_FIRST;
87 | case SECTION_SECOND:
88 | return ITEM_BODY_SECOND;
89 | }
90 | return NONE_VIEW_TYPE;
91 | }
92 |
93 | @Override
94 | public int getSectionHeaderViewType(int sectionIndex) {
95 | switch (sectionIndex) {
96 | case SECTION_FIRST:
97 | return ITEM_HEADER;
98 | case SECTION_SECOND:
99 | return ITEM_HEADER;
100 | }
101 | return NONE_VIEW_TYPE;
102 | }
103 |
104 | @Override
105 | public int getSectionFooterViewType(int sectionIndex) {
106 | switch (sectionIndex) {
107 | case SECTION_FIRST:
108 | return NONE_VIEW_TYPE;
109 | case SECTION_SECOND:
110 | return ITEM_FOOTER;
111 | }
112 | return NONE_VIEW_TYPE;
113 | }
114 |
115 | @Override
116 | public int getSectionItemCount(int sectionIndex) {
117 | switch (sectionIndex) {
118 | case SECTION_FIRST:
119 | return mItemList.size();
120 | case SECTION_SECOND:
121 | return 1;
122 | }
123 | return 0;
124 | }
125 |
126 | @Override
127 | public ViewOption getItemViewOption(int viewType) {
128 | if (viewType == ITEM_BODY_FIRST) {
129 | return new ViewOption(2);
130 | }
131 | return null;
132 | }
133 |
134 | private class FirstBodyViewHolder extends RecyclerView.ViewHolder {
135 |
136 | TextView bodyText;
137 | ImageView bodyImage;
138 | public FirstBodyViewHolder(View itemView) {
139 | super(itemView);
140 | bodyText = (TextView) itemView.findViewById(R.id.body_first_text);
141 | bodyImage = (ImageView) itemView.findViewById(R.id.body_first_image);
142 | }
143 |
144 | public void bind(String stringValue) {
145 | bodyText.setText(stringValue);
146 | bodyImage.setImageDrawable(ContextCompat.getDrawable(mContext, R.mipmap.ic_launcher_round));
147 | }
148 | }
149 |
150 | private class SecondBodyViewHolder extends RecyclerView.ViewHolder {
151 |
152 | public SecondBodyViewHolder(View itemView) {
153 | super(itemView);
154 | }
155 | }
156 |
157 | private class HeaderViewHolder extends RecyclerView.ViewHolder {
158 | TextView headerText;
159 | public HeaderViewHolder(View itemView) {
160 | super(itemView);
161 | headerText = (TextView) itemView.findViewById(R.id.header_text);
162 | }
163 |
164 | public void bind(int position) {
165 | headerText.setText(String.format("%d section header", position));
166 | }
167 | }
168 |
169 | private class FooterViewHolder extends RecyclerView.ViewHolder {
170 |
171 | public FooterViewHolder(View itemView) {
172 | super(itemView);
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/sectionadapter/src/main/java/org/osori/sectionadapter/SectionListViewAdapter.java:
--------------------------------------------------------------------------------
1 | package org.osori.sectionadapter;
2 |
3 | import android.database.DataSetObservable;
4 | import android.database.DataSetObserver;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ListAdapter;
8 |
9 | /**
10 | * Created by junsu on 2017-06-11.
11 | */
12 |
13 | public abstract class SectionListViewAdapter implements ListAdapter {
14 |
15 | private static final String TAG = SectionListViewAdapter.class.getSimpleName();
16 |
17 | private final DataSetObservable mDataSetObservable = new DataSetObservable();
18 | protected final int NONE_VIEW_TYPE = -1;
19 |
20 | /**
21 | * Enable means that item is selectable and clickable.
22 | * @return True is all items are enable, false then otherwise
23 | */
24 |
25 | @Override
26 | public boolean areAllItemsEnabled() {
27 | return true;
28 | }
29 |
30 | /**
31 | * hmm ...
32 | * @param position
33 | * @return
34 | */
35 |
36 | @Override
37 | public boolean isEnabled(int position) {
38 | return true;
39 | }
40 |
41 | @Override
42 | public void registerDataSetObserver(DataSetObserver observer) {
43 | mDataSetObservable.registerObserver(observer);
44 | }
45 |
46 | @Override
47 | public void unregisterDataSetObserver(DataSetObserver observer) {
48 | mDataSetObservable.unregisterObserver(observer);
49 | }
50 |
51 | public void notifyDataSetChanged() {
52 | mDataSetObservable.notifyChanged();
53 | }
54 |
55 | public void notifyDataSetInvalidated() {
56 | mDataSetObservable.notifyInvalidated();
57 | }
58 |
59 | @Override
60 | public Object getItem(int position) {
61 | return null;
62 | }
63 |
64 | @Override
65 | public long getItemId(int position) {
66 | return 0;
67 | }
68 |
69 | /**
70 | * IndexPath is main concept of this library.
71 | * This function build IndexPath by rowPosition.
72 | *
73 | * @param position: Row position
74 | * @return: IndexPath of rowPosition
75 | */
76 | public final IndexPath buildIndexPath(int position) {
77 | int sectionCount = getSectionCount();
78 | int cur = 0; // current ref position
79 |
80 | int pathSection = -1;
81 | int pathItem = -1;
82 |
83 | for (int sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++) {
84 |
85 | // check header
86 | if (getSectionHeaderViewType(sectionIndex) != NONE_VIEW_TYPE) {
87 | if (cur == position) {
88 | pathSection = sectionIndex;
89 | pathItem = IndexPath.HEADER;
90 | break;
91 | }
92 | cur++;
93 | }
94 | // check body
95 | int sectionItemCount = getSectionItemCount(sectionIndex);
96 | if (cur <= position && position < cur + sectionItemCount) {
97 | pathSection = sectionIndex;
98 | pathItem = position - cur;
99 | break;
100 | }
101 | cur = cur + sectionItemCount;
102 |
103 | // check footer
104 | if (getSectionFooterViewType(sectionIndex) != NONE_VIEW_TYPE) {
105 | if (cur == position) {
106 | pathSection = sectionIndex;
107 | pathItem = IndexPath.FOOTER;
108 | break;
109 | }
110 | cur++;
111 | }
112 | }
113 | return new IndexPath(pathSection, pathItem);
114 | }
115 |
116 |
117 | @Override
118 | public View getView(int position, View convertView, ViewGroup parent) {
119 | IndexPath indexPath = buildIndexPath(position);
120 | int viewType;
121 |
122 | int section = indexPath.section;
123 | int item = indexPath.item;
124 |
125 | if (item == IndexPath.HEADER) {
126 | viewType = getSectionHeaderViewType(section);
127 | }
128 | else if (item == IndexPath.FOOTER) {
129 | viewType = getSectionFooterViewType(section);
130 | }
131 | else {
132 | viewType = getSectionItemViewType(section);
133 | }
134 |
135 | View view = convertView;
136 | VH holder;
137 |
138 | if (view == null) {
139 | holder = onCreateItemHolder(parent, viewType);
140 | holder.itemView.setTag(holder);
141 | view = holder.itemView;
142 | }
143 | else {
144 | holder = (VH) view.getTag();
145 | }
146 |
147 | holder.itemView.setTag(holder);
148 | view = holder.itemView;
149 |
150 | onBindItemHolder((VH) holder, indexPath);
151 |
152 | // Log.d(TAG, "getView returns view: " + view);
153 | return view;
154 | }
155 |
156 | @Override
157 | public boolean isEmpty() {
158 | return getCount() == 0;
159 | }
160 |
161 | @Override
162 | public boolean hasStableIds() {
163 | return false;
164 | }
165 |
166 | @Override
167 | public int getItemViewType(int position) {
168 | IndexPath indexPath = buildIndexPath(position);
169 | int sectionIndex = indexPath.section;
170 | int viewType;
171 |
172 | if (getSectionHeaderViewType(sectionIndex) != NONE_VIEW_TYPE
173 | && indexPath.item == IndexPath.HEADER) {
174 | viewType = getSectionHeaderViewType(sectionIndex);
175 | }
176 | else if (getSectionFooterViewType(sectionIndex) != NONE_VIEW_TYPE
177 | && indexPath.item == IndexPath.FOOTER) {
178 | viewType = getSectionFooterViewType(sectionIndex);
179 | }
180 | else {
181 | viewType = getSectionItemViewTypeInternal(sectionIndex);
182 | }
183 |
184 | return viewType;
185 | }
186 |
187 | private int getSectionItemViewTypeInternal(int sectionIndex) {
188 | int viewType = getSectionItemViewType(sectionIndex);
189 | if (viewType == NONE_VIEW_TYPE) {
190 | throw new IllegalStateException("Item's view type cannot be NONE_VIEW_TYPE, -1");
191 | }
192 | else {
193 | return viewType;
194 | }
195 | }
196 |
197 | @Override
198 | public final int getCount() {
199 | int itemCount = 0;
200 |
201 | int sectionCount = getSectionCount();
202 | for (int i = 0; i < sectionCount; i++) {
203 |
204 | // check header
205 | if (getSectionHeaderViewType(i) != NONE_VIEW_TYPE) itemCount++;
206 |
207 | itemCount += getSectionItemCount(i);
208 |
209 | // check footer
210 | if (getSectionFooterViewType(i) != NONE_VIEW_TYPE) itemCount++;
211 | }
212 | return itemCount;
213 | }
214 |
215 | public abstract void onBindItemHolder(VH holder, IndexPath indexPath);
216 |
217 | public abstract VH onCreateItemHolder(ViewGroup container, int viewType);
218 |
219 | public abstract int getSectionCount();
220 |
221 | public abstract int getSectionItemCount(int sectionIndex);
222 |
223 | public abstract int getSectionItemViewType(int sectionIndex);
224 |
225 | public int getSectionHeaderViewType(int sectionIndex) {
226 | return NONE_VIEW_TYPE;
227 | }
228 |
229 | public int getSectionFooterViewType(int sectionIndex) {
230 | return NONE_VIEW_TYPE;
231 | }
232 |
233 |
234 | public abstract class ViewHolder {
235 | public View itemView;
236 | public ViewHolder(View itemView) {
237 | this.itemView = itemView;
238 | }
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SectionAdapter
2 |
3 | ***Recycler Adapter to control section item.***
4 |
5 |
6 |
7 |
8 | ## index
9 |
10 | * Motivation
11 | * How to use it?
12 |
13 | ## Motivation
14 |
15 | When I develop application with RecyclerView, I got problem happend from NestedScrollView and RecyclerView.
16 |
17 | I deal with several list data and use NestedScrollView and some RecyclerViews. I dosen't work at some devices. So I use RecyclerView viewType to create various view.
18 |
19 | But when I deal with serveral list data on one RecyclerView Adapter, It's very hard to convert view position to list data index.
20 |
21 | My coworker, IOS develop introduce Swift TableView to me. TableView control list and it is divided with section.
22 |
23 | Swift TableView is motivation of this library.
24 |
25 | ## How to import this library
26 |
27 | Project `build.gradle`
28 |
29 | ```gradle
30 | allprojects {
31 | repositories {
32 | jcenter()
33 |
34 | // add this
35 | maven { url 'https://jitpack.io' }
36 | }
37 | }
38 | ```
39 |
40 | App module `build.gradle`
41 |
42 | ```gradle
43 | dependencies {
44 | // ......
45 |
46 | // add this
47 | compile 'com.github.JunsuLime:Android-SectionAdapter:v0.2.0'
48 | }
49 | ```
50 |
51 | ## How to use it?
52 |
53 | 1) **Make Adapter that extends SectionAdapter**
54 |
55 | For convenience, I define section index and item view type.
56 |
57 | ```java
58 | public class SampleAdapter extends SectionAdapter {
59 |
60 | private Context mContext;
61 | private LayoutInflater mInflater;
62 |
63 | // first section's item list
64 | // it is define at MainActivity.
65 | private List mItemList;
66 |
67 | // for convenience section index is defined as final variable
68 | private final int SECTION_FIRST = 0;
69 | private final int SECTION_SECOND = 1;
70 |
71 | // for convenience view type is defined as final variable
72 | private final int ITEM_HEADER = 0;
73 | private final int ITEM_FOOTER = 1;
74 | private final int ITEM_BODY_FIRST = 2;
75 | private final int ITEM_BODY_SECOND = 3;
76 |
77 | public SampleAdpater(Context context) {
78 | super(context);
79 | }
80 | }
81 | ```
82 |
83 | 2) **Override unimplemented method (must)**
84 | * `getSectionItemViewType`: return viewType of section item, return value must not be `NONE_VIEW_TYPE`, -1.
85 | ```java
86 | @Override
87 | public int getSectionItemViewType(int sectionIndex) {
88 | switch (sectionIndex) {
89 | case SECTION_FIRST:
90 | return ITEM_BODY_FIRST;
91 | case SECTION_SECOND:
92 | return ITEM_BODY_SECOND;
93 | }
94 | return NONE_VIEW_TYPE;
95 | }
96 | ```
97 |
98 | * `onCreateItemHolder`: same as `onCreateViewHolder`
99 | ```java
100 | @Override
101 | public RecyclerView.ViewHolder onCreateItemHolder(ViewGroup parent, int viewType) {
102 | switch (viewType) {
103 | case ITEM_BODY_FIRST:
104 | return new FirstBodyViewHolder(mInflater.inflate(R.layout.item_body_first, parent ,false));
105 | case ITEM_BODY_SECOND:
106 | return new SecondBodyViewHolder(mInflater.inflate(R.layout.item_body_second, parent, false));
107 | case ITEM_HEADER:
108 | return new HeaderViewHolder(mInflater.inflate(R.layout.item_header, parent, false));
109 | case ITEM_FOOTER:
110 | return new FooterViewHolder(mInflater.inflate(R.layout.item_footer, parent, false));
111 | }
112 | return null;
113 | }
114 | ```
115 |
116 | * `onBindItemHolder`: same as `onBindViewHolder`
117 | ```java
118 | @Override
119 | public void onBindItemHolder(RecyclerView.ViewHolder holder, IndexPath indexPath) {
120 | int section = indexPath.section;
121 | int item = indexPath.item;
122 |
123 | if (holder instanceof FirstBodyViewHolder) {
124 | ((FirstBodyViewHolder) holder).bind(mItemList.get(item));
125 | }
126 | if (holder instanceof HeaderViewHolder) {
127 | ((HeaderViewHolder) holder).bind(section);
128 | }
129 | }
130 | ```
131 |
132 | * `getSectionCount`: return section count you want to create
133 | ```java
134 | @Override
135 | public int getSectionCount() {
136 | return 2;
137 | }
138 | ```
139 |
140 | * `getSectionItemCount`: return section's item count you want to create
141 | ```java
142 | @Override
143 | public int getSectionItemCount(int sectionIndex) {
144 | switch (sectionIndex) {
145 | case SECTION_FIRST:
146 | return mItemList.size();
147 | case SECTION_SECOND:
148 | return 1;
149 | }
150 | return 0;
151 | }
152 | ```
153 | 3) **Override method (optional)**
154 | * `getSectionHeaderViewType`: return viewType want you want to create. If you don't want to create header in section, return `NONE_VIEW_TYPE`.
155 | ```java
156 | @Override
157 | public int getSectionHeaderViewType(int sectionIndex) {
158 | switch (sectionIndex) {
159 | case SECTION_FIRST:
160 | return ITEM_HEADER;
161 | case SECTION_SECOND:
162 | return ITEM_HEADER;
163 | }
164 | return NONE_VIEW_TYPE;
165 | }
166 | ```
167 |
168 | * `getSectionFooterViewType`: return viewType want you want to create. If you don't want to create footer in section, return `NONE_VIEW_TYPE`.
169 | ```java
170 | @Override
171 | public int getSectionFooterViewType(int sectionIndex) {
172 | switch (sectionIndex) {
173 | case SECTION_FIRST:
174 | return NONE_VIEW_TYPE;
175 | case SECTION_SECOND:
176 | return ITEM_FOOTER;
177 | }
178 | return NONE_VIEW_TYPE;
179 | }
180 | ```
181 | * `getItemViewOption`: return ViewOption object. `ViewOption` has attribute, numberOfGrid and rowLayoutParams. If you do not want any viewOption, return null.
182 | ```java
183 | @Override
184 | public ViewOption getItemViewOption(int viewType) {
185 | if (viewType == ITEM_BODY_FIRST) {
186 | return new ViewOption(2);
187 | }
188 | return null;
189 | }
190 | ```
191 |
192 | ### License
193 |
194 | ```
195 | MIT License
196 |
197 | Copyright (c) 2017 JunsuLime
198 |
199 | Permission is hereby granted, free of charge, to any person obtaining a copy
200 | of this software and associated documentation files (the "Software"), to deal
201 | in the Software without restriction, including without limitation the rights
202 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
203 | copies of the Software, and to permit persons to whom the Software is
204 | furnished to do so, subject to the following conditions:
205 |
206 | The above copyright notice and this permission notice shall be included in all
207 | copies or substantial portions of the Software.
208 |
209 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
210 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
211 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
212 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
213 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
214 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
215 | SOFTWARE.
216 | ```
217 |
--------------------------------------------------------------------------------
/sectionadapter/src/main/java/org/osori/sectionadapter/SectionAdapter.java:
--------------------------------------------------------------------------------
1 | package org.osori.sectionadapter;
2 |
3 | import android.content.Context;
4 | import android.os.Build;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.LinearLayout;
9 |
10 | import java.util.ArrayList;
11 | import java.util.Hashtable;
12 | import java.util.List;
13 |
14 | /**
15 | * Created by junsuLime
16 | *
17 | * SectionAdapter
18 | * Concept of section is came from Swift TableView.
19 | *
20 | * Concept of Row and Item
21 | *
22 | * Row: Raw position at RecyclerView.Adapter
23 | * Item: Item what you deal with
24 | * Section: Section contains item.
25 | *
26 | * Constraint1: Grid item must have distinguished viewType between other grid item or non-grid item
27 | * Constraint2: Any section of item can have only one view type
28 | * Constraint3: If you return NONE_VIEW_TYPE -1, this adapter will recognize this return value as there is no view in section
29 | * Constraint4: Current version of SectionAdapter can be adapted at orientation vertical
30 | */
31 |
32 | public abstract class SectionAdapter extends RecyclerView.Adapter {
33 |
34 | private static final String TAG = "SectionAdapter_";
35 |
36 | // Key is viewType and value is viewOption
37 | private Hashtable mViewOptionTable = new Hashtable<>();
38 |
39 | // Header or footer view type can be NONE_VIEW_TYPE, if you want not to put header or footer.
40 | public static final int NONE_VIEW_TYPE = -1;
41 | public static final int DEFAULT_GRID = 1;
42 |
43 | // Context to create inflate GridViewHolder
44 | private Context context;
45 |
46 | public SectionAdapter(Context context) {
47 | this.context = context;
48 | }
49 |
50 | @Override
51 | public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
52 | ViewOption viewOption = mViewOptionTable.get(viewType);
53 |
54 | if (viewOption == null || viewOption.numberOfGrid == DEFAULT_GRID) {
55 | return onCreateItemHolder(parent, viewType);
56 | }
57 | else {
58 | // # Constraint4
59 | LinearLayout layout = new LinearLayout(context);
60 | layout.setOrientation(LinearLayout.HORIZONTAL);
61 |
62 | // copy viewOption's layout params
63 | if (viewOption.rowItemLayoutParam != null) {
64 | RecyclerView.LayoutParams holderLp = viewOption.rowItemLayoutParam;
65 | RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(holderLp.width, holderLp.height);
66 | lp.setMargins(holderLp.leftMargin, holderLp.topMargin, holderLp.rightMargin, holderLp.bottomMargin);
67 |
68 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
69 | lp.setMarginStart(holderLp.getMarginStart());
70 | lp.setMarginEnd(holderLp.getMarginEnd());
71 | }
72 | layout.setLayoutParams(lp);
73 | }
74 | else {
75 | layout.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
76 | }
77 |
78 | int numberOfGrid = viewOption.numberOfGrid;
79 | List holders = new ArrayList<>();
80 | for (int i = 0; i < numberOfGrid; i++) {
81 | RecyclerView.ViewHolder itemHolder = onCreateItemHolder(layout, viewType);
82 | LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) itemHolder.itemView.getLayoutParams();
83 | params.width = LinearLayout.LayoutParams.MATCH_PARENT;
84 | params.weight = 1f;
85 |
86 | itemHolder.itemView.setLayoutParams(params);
87 | layout.addView(itemHolder.itemView);
88 | holders.add(itemHolder);
89 | }
90 | return new GridViewHolder(context, layout, holders);
91 | }
92 | }
93 |
94 | /**
95 | * As RecyclerView.Adapter, onCreateItemHolder's param is parent and viewType
96 | * Usage of this function is same as RecyclerView.Adapter's onCreateViewHolder
97 | *
98 | * @param parent: Container of itemView
99 | * @param viewType: Same as RecyclerView viewType. It's defined by user
100 | * @return ViewHolder what you want to return
101 | */
102 | public abstract RecyclerView.ViewHolder onCreateItemHolder(ViewGroup parent, int viewType);
103 |
104 | @Override
105 | public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
106 | IndexPath indexPath = buildIndexPath(position);
107 |
108 | // Case of GridViewHolder, onBindItemHolder is called inside of GridViewHolder
109 |
110 | if (holder instanceof GridViewHolder) {
111 | ((GridViewHolder) holder).bind(indexPath);
112 | } else {
113 | onBindItemHolder(holder, indexPath);
114 | }
115 | }
116 |
117 | /**
118 | * It's similar to RecyclerView.Adapter's onBindViewHolder.
119 | * But this function pass parameter IndexPath, not position.
120 | *
121 | * IndexPath has two attributes
122 | * 1) section
123 | * 2) item
124 | *
125 | * @param holder: Holder that you made at onCreateItemHolder
126 | * @param indexPath: holder's position
127 | */
128 | public abstract void onBindItemHolder(RecyclerView.ViewHolder holder, IndexPath indexPath);
129 |
130 | @Override
131 | public final void onViewRecycled(RecyclerView.ViewHolder holder) {
132 | if (holder instanceof GridViewHolder) {
133 | for (RecyclerView.ViewHolder itemHolder : ((GridViewHolder) holder).mHolders) {
134 | onItemRecycled(itemHolder);
135 | }
136 | }
137 | else {
138 | onItemRecycled(holder);
139 | }
140 | }
141 |
142 | /**
143 | * It's same as RecyclerView.Adapter's onViewRecycled.
144 | * @param holder: Holder that recycled.
145 | */
146 | public void onItemRecycled(RecyclerView.ViewHolder holder) {
147 | return;
148 | }
149 |
150 | /**
151 | * All row count in RecyclerView, not item count.
152 | * Its name is getItemCount, because RecyclerView.Adapter's original function name is getItemCount ..
153 | *
154 | * @return: Row count
155 | */
156 | @Override
157 | public final int getItemCount() {
158 | int itemCount = 0;
159 |
160 | int sectionCount = getSectionCount();
161 | for (int i = 0; i < sectionCount; i++) {
162 |
163 | // check header
164 | if (getSectionHeaderViewType(i) != NONE_VIEW_TYPE) itemCount++;
165 |
166 | itemCount += getRowCountInSection(i);
167 |
168 | // check footer
169 | if (getSectionFooterViewType(i) != NONE_VIEW_TYPE) itemCount++;
170 | }
171 | return itemCount;
172 | }
173 |
174 | /**
175 | * Number of section what you want to create
176 | * @return: Section count
177 | */
178 | public abstract int getSectionCount();
179 |
180 | /**
181 | * Get item count of section
182 | * @param sectionIndex: Section index
183 | * @return: Section's item count
184 | */
185 | public abstract int getSectionItemCount(int sectionIndex);
186 |
187 | @Override
188 | public final int getItemViewType(int position) {
189 | IndexPath indexPath = buildIndexPath(position);
190 | int sectionIndex = indexPath.section;
191 | int viewType;
192 |
193 | if (getSectionHeaderViewType(sectionIndex) != NONE_VIEW_TYPE
194 | && indexPath.item == IndexPath.HEADER) {
195 | viewType = getSectionHeaderViewType(sectionIndex);
196 | }
197 | else if (getSectionFooterViewType(sectionIndex) != NONE_VIEW_TYPE
198 | && indexPath.item == IndexPath.FOOTER) {
199 | viewType = getSectionFooterViewType(sectionIndex);
200 | }
201 | else {
202 | viewType = getSectionItemViewTypeInternal(sectionIndex);
203 | }
204 |
205 | ViewOption viewOption = getItemViewOption(viewType);
206 | if (viewOption != null) {
207 | // put ViewOption on view type
208 | if (mViewOptionTable.get(viewType) == null) {
209 | mViewOptionTable.put(viewType, viewOption);
210 | }
211 | }
212 | return viewType;
213 | }
214 |
215 | /**
216 | * Internal function to get section item type.
217 | * This function check item view type which view type is NONE_VIEW_TYPE.
218 | * If it is NONE_VIEW_TYPE, throw Exception.
219 | */
220 | private int getSectionItemViewTypeInternal(int sectionIndex) {
221 | int viewType = getSectionItemViewType(sectionIndex);
222 | if (viewType == NONE_VIEW_TYPE) {
223 | throw new IllegalStateException("Item's view type cannot be NONE_VIEW_TYPE, -1");
224 | }
225 | else {
226 | return viewType;
227 | }
228 | }
229 |
230 | /**
231 | * Section Item's viewType is restricted to only one viewType.
232 | * Define sectionItem's viewType in here.
233 | * This value will be transferred to onCreateItemHolder.
234 | * @param sectionIndex: Section index what you want to define viewType
235 | * @return: Item viewType
236 | */
237 | public abstract int getSectionItemViewType(int sectionIndex);
238 |
239 | /**
240 | * Section Header's viewType is restricted to only one viewType.
241 | * Define sectionItem's viewType in here.
242 | * This value will be transferred to onCreateItemHolder.
243 | * @param sectionIndex: Section index what you want to define viewType
244 | * @return: Header viewType
245 | */
246 | public int getSectionHeaderViewType(int sectionIndex) {
247 | return NONE_VIEW_TYPE;
248 | }
249 |
250 | /**
251 | * Section Item's viewType is restricted to only one viewType.
252 | * Define sectionItem's viewType in here.
253 | * This value will be transferred to onCreateItemHolder.
254 | * @param sectionIndex: Section index what you want to define viewType
255 | * @return: Footer viewType
256 | */
257 | public int getSectionFooterViewType(int sectionIndex) {
258 | return NONE_VIEW_TYPE;
259 | }
260 |
261 | /**
262 | * IndexPath is main concept of this library.
263 | * This function build IndexPath by rowPosition.
264 | *
265 | * @param position: Row position
266 | * @return: IndexPath of rowPosition
267 | */
268 | public final IndexPath buildIndexPath(int position) {
269 | int sectionCount = getSectionCount();
270 | int cur = 0; // current ref position
271 |
272 | int pathSection = -1;
273 | int pathItem = -1;
274 |
275 | for (int sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++) {
276 |
277 | // check header
278 | if (getSectionHeaderViewType(sectionIndex) != NONE_VIEW_TYPE) {
279 | if (cur == position) {
280 | pathSection = sectionIndex;
281 | pathItem = IndexPath.HEADER;
282 | break;
283 | }
284 | cur++;
285 | }
286 | // check body
287 | int sectionRowCount = getRowCountInSection(sectionIndex);
288 | if (cur <= position && position < cur + sectionRowCount) {
289 | pathSection = sectionIndex;
290 | ViewOption viewOption = getItemViewOption(getSectionItemViewTypeInternal(sectionIndex));
291 | int numberOfGrid = DEFAULT_GRID;
292 | if (viewOption != null) numberOfGrid = viewOption.numberOfGrid;
293 | // Case default grid option
294 | if (numberOfGrid == DEFAULT_GRID) {
295 | pathItem = position - cur;
296 | }
297 | // Case ItemHolder has grid option
298 | else {
299 | pathItem = (position - cur) * numberOfGrid;
300 | }
301 | break;
302 | }
303 | cur = cur + sectionRowCount;
304 |
305 | // check footer
306 | if (getSectionFooterViewType(sectionIndex) != NONE_VIEW_TYPE) {
307 | if (cur == position) {
308 | pathSection = sectionIndex;
309 | pathItem = IndexPath.FOOTER;
310 | break;
311 | }
312 | cur++;
313 | }
314 | }
315 | return new IndexPath(pathSection, pathItem);
316 | }
317 |
318 | private int getRowCountInSection(int sectionIndex) {
319 | int itemCount = getSectionItemCount(sectionIndex);
320 | if (itemCount == 0) return 0;
321 |
322 | // Get view holder info of ItemHolder
323 | ViewOption viewOption = getItemViewOption(getSectionItemViewTypeInternal(sectionIndex));
324 | if (viewOption == null || viewOption.numberOfGrid == DEFAULT_GRID) return itemCount;
325 |
326 | int gridCount = viewOption.numberOfGrid;
327 |
328 | return ((itemCount - 1) / gridCount) + 1;
329 | }
330 |
331 | /**
332 | * To provide row index for user, make this function.
333 | * When user user LayoutManager, row count is needed
334 | *
335 | * @param indexPath indexPath that you want to access
336 | * @return row index for indexPath
337 | */
338 | public final int getRowPosition(IndexPath indexPath) {
339 | int sectionCount = getSectionCount();
340 | int rowCount = 0;
341 | // wrong indexPath
342 | if (indexPath.section >= sectionCount) {
343 | return -1;
344 | }
345 |
346 | for (int i = 0; i < sectionCount; i++) {
347 |
348 | if (indexPath.section == i) {
349 | if (getSectionHeaderViewType(i) != NONE_VIEW_TYPE) {
350 | if (indexPath.item == SectionAdapter.IndexPath.HEADER) {
351 | return rowCount;
352 | }
353 | rowCount++;
354 | }
355 |
356 | int itemCount = getSectionItemCount(i);
357 | // wrong indexPath
358 | if (indexPath.item >= itemCount) {
359 | return -1;
360 | }
361 |
362 | // Get view holder info of ItemHolder
363 | ViewOption viewOption = getItemViewOption(getSectionItemViewTypeInternal(i));
364 | int numberOfGrid = DEFAULT_GRID;
365 | if (viewOption != null) numberOfGrid = viewOption.numberOfGrid;
366 |
367 | if (indexPath.item >= 0 && indexPath.item < itemCount) {
368 | rowCount += (indexPath.item / numberOfGrid);
369 | return rowCount;
370 | } else if (itemCount != 0) {
371 |
372 | // Get view holder info of ItemHolder
373 | if (numberOfGrid == DEFAULT_GRID) {
374 | rowCount += itemCount;
375 | }
376 | else {
377 | rowCount += ((itemCount - 1) / numberOfGrid) + 1;
378 | }
379 | }
380 |
381 | // check footer
382 | if (getSectionFooterViewType(i) != NONE_VIEW_TYPE) {
383 | if (indexPath.item == IndexPath.FOOTER) {
384 | return rowCount;
385 | }
386 | rowCount++;
387 | }
388 | }
389 |
390 | // check header
391 | if (getSectionHeaderViewType(i) != NONE_VIEW_TYPE) rowCount++;
392 |
393 | rowCount += getRowCountInSection(i);
394 |
395 | // check footer
396 | if (getSectionFooterViewType(i) != NONE_VIEW_TYPE) rowCount++;
397 | }
398 |
399 | // wrong indexPath
400 | return -1;
401 | }
402 |
403 | /**
404 | * This option is related with grid attribute.
405 | * You can select section that have grid option by using this function.
406 | *
407 | * ViewType has two attribute,
408 | * 1) numberOfGrid
409 | * 2) Row layout params
410 | *
411 | * @param viewType: The viewType what you want to modify
412 | * @return: ViewOption of that viewType
413 | */
414 | public ViewOption getItemViewOption(int viewType) {
415 | return null;
416 | }
417 |
418 | /**
419 | * IndexPath
420 | *
421 | * Concept of IndexPath is came from Swift TableView.
422 | * It have big Section in table and
423 | * Table contains Item.
424 | *
425 | * section is index of section and
426 | * item is index of item.
427 | */
428 | public static class IndexPath {
429 | public int section;
430 | public int item;
431 |
432 | // Header's item value
433 | public static int HEADER = -1;
434 | // Footer's item value
435 | public static int FOOTER = -2;
436 |
437 | public IndexPath(int section, int item) {
438 | this.section = section;
439 | this.item = item;
440 | }
441 | }
442 |
443 | /**
444 | * For selective grid item!
445 | * numberOfGrid != 1 (DEFAULT_GRID)
446 | */
447 | private class GridViewHolder extends RecyclerView.ViewHolder {
448 |
449 | private List mHolders;
450 |
451 | public GridViewHolder(Context context, View mergedView, List holders) {
452 | super(mergedView);
453 | mHolders = holders;
454 | }
455 |
456 | /**
457 | * Not used function, onBindItemHolder is created because this class's parent is ItemHolder
458 | * and all of this adapters view holder must extend ItemHolder
459 | *
460 | * @param startIndexPath: first item's indexPath will be passed into this function.
461 | */
462 | public void bind(IndexPath startIndexPath) {
463 | for (RecyclerView.ViewHolder viewHolder : mHolders) {
464 | if (startIndexPath.item < getSectionItemCount(startIndexPath.section)) {
465 | viewHolder.itemView.setVisibility(View.VISIBLE);
466 | SectionAdapter.this.onBindItemHolder(viewHolder, startIndexPath);
467 | startIndexPath.item += 1;
468 | } else {
469 | viewHolder.itemView.setVisibility(View.INVISIBLE);
470 | }
471 | }
472 | }
473 | }
474 |
475 |
476 | /**
477 | * To handle grid option of item
478 | *
479 | * numberOfGrid: How many items will be contained at one row
480 | * rowLayoutParam: row's layoutParams, not item...
481 | */
482 | public class ViewOption {
483 | public int numberOfGrid;
484 | public RecyclerView.LayoutParams rowItemLayoutParam;
485 |
486 | public ViewOption(int numberOfGrid) {
487 | this(numberOfGrid, null);
488 | }
489 |
490 | public ViewOption(int numberOfGrid, RecyclerView.LayoutParams rowLayoutParam) {
491 | this.numberOfGrid = numberOfGrid;
492 | this.rowItemLayoutParam = rowLayoutParam;
493 | }
494 | }
495 | }
496 |
497 |
--------------------------------------------------------------------------------