diff --git a/build.gradle b/build.gradle
index 1b7886d..a60bf2e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,5 +15,6 @@ buildscript {
allprojects {
repositories {
jcenter()
+ maven { url "https://jitpack.io" }
}
}
diff --git a/example/app/build.gradle b/example/app/build.gradle
index 254885a..7fbbc01 100644
--- a/example/app/build.gradle
+++ b/example/app/build.gradle
@@ -1,4 +1,6 @@
apply plugin: 'com.android.application'
+// This does not break the build when Android Studio is missing the JRebel for Android plugin.
+apply plugin: 'com.zeroturnaround.jrebel.android'
android {
compileSdkVersion 22
diff --git a/example/app/src/main/AndroidManifest.xml b/example/app/src/main/AndroidManifest.xml
index e626243..7af6e55 100644
--- a/example/app/src/main/AndroidManifest.xml
+++ b/example/app/src/main/AndroidManifest.xml
@@ -20,6 +20,10 @@
android:name=".MainActivity"
android:label="@string/app_name" >
+
+
diff --git a/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/LayoutSelectorActivity.java b/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/LayoutSelectorActivity.java
index 2c71e82..7e670fe 100644
--- a/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/LayoutSelectorActivity.java
+++ b/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/LayoutSelectorActivity.java
@@ -28,6 +28,20 @@ public void onClick(View v) {
}
);
+ final Button linearWithLoadMoreButton =
+ (Button) findViewById(R.id.recycler_linear_with_load_more_button);
+ linearWithLoadMoreButton.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(LayoutSelectorActivity.this, MainActivity.class);
+ intent.putExtra("Type", "LinearLoadMore");
+ startActivity(intent);
+ }
+ }
+ );
+
+
final Button gridButton = (Button) findViewById(R.id.recycler_grid_button);
gridButton.setOnClickListener(
new View.OnClickListener() {
@@ -39,5 +53,17 @@ public void onClick(View v) {
}
}
);
+
+ final Button sectionButton = (Button) findViewById(R.id.recycler_section_header_button);
+ sectionButton.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(LayoutSelectorActivity.this, MainActivity2.class);
+ intent.putExtra("Type", "Header (SLM) ");
+ startActivity(intent);
+ }
+ }
+ );
}
}
diff --git a/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/MainActivity.java b/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/MainActivity.java
index 8cc61a7..42c513f 100644
--- a/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/MainActivity.java
+++ b/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/MainActivity.java
@@ -5,15 +5,13 @@
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.GridLayoutManager;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
+import android.widget.Toast;
import java.util.Arrays;
import java.util.List;
@@ -24,6 +22,7 @@
import io.realm.RealmBasedRecyclerViewAdapter;
import io.realm.RealmConfiguration;
import io.realm.RealmResults;
+import io.realm.RealmViewHolder;
public class MainActivity extends AppCompatActivity {
@@ -54,27 +53,33 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
type = getIntent().getStringExtra("Type");
+ if (type.equals("Grid")) {
+ setContentView(R.layout.activity_main_grid_layout);
+ } else {
+ setContentView(R.layout.activity_main_linear_layout);
+ }
+ realmRecyclerView = (RealmRecyclerView) findViewById(R.id.realm_recycler_view);
+
setTitle(getResources().getString(R.string.activity_layout_name, type));
resetRealm();
realm = Realm.getInstance(this);
- realmRecyclerView = (RealmRecyclerView) findViewById(R.id.realm_recycler_view);
-
- if (type.equals("Grid")) {
- GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
- realmRecyclerView.setLayoutManager(gridLayoutManager);
- } else {
- LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
- linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
- realmRecyclerView.setLayoutManager(linearLayoutManager);
+ boolean isLoadMore = type.equals("LinearLoadMore");
+ if (isLoadMore) {
+ realm.beginTransaction();
+ for (int i = 0; i < 60; i++) {
+ QuoteModel quoteModel = realm.createObject(QuoteModel.class);
+ quoteModel.setId(i + 1);
+ quoteModel.setQuote(quotes.get((int) (quoteModel.getId() % quotes.size())));
+ }
+ realm.commitTransaction();
}
-
+
RealmResults quoteModels =
- realm.where(QuoteModel.class).findAllSorted("id", false);
+ realm.where(QuoteModel.class).findAllSorted("id", isLoadMore ? true : false);
quoteAdapter = new QuoteRecyclerViewAdapter(getBaseContext(), quoteModels, true, true);
realmRecyclerView.setAdapter(quoteAdapter);
@@ -86,6 +91,24 @@ public void onRefresh() {
}
}
);
+
+ if (isLoadMore) {
+ realmRecyclerView.setOnLoadMoreListener(
+ new RealmRecyclerView.OnLoadMoreListener() {
+ @Override
+ public void onLoadMore(Object lastItem) {
+ if (lastItem instanceof QuoteModel) {
+ Toast.makeText(
+ MainActivity.this,
+ ((QuoteModel) lastItem).getId() + " ",
+ Toast.LENGTH_SHORT).show();
+ }
+ asyncLoadMoreQuotes();
+ }
+ }
+ );
+ realmRecyclerView.enableShowLoadMore();
+ }
}
@Override
@@ -122,7 +145,7 @@ public QuoteRecyclerViewAdapter(
super(context, realmResults, automaticUpdate, animateIdType);
}
- public class ViewHolder extends RecyclerView.ViewHolder {
+ public class ViewHolder extends RealmViewHolder {
public FrameLayout container;
public TextView quoteTextView;
public ViewHolder(FrameLayout container) {
@@ -133,15 +156,15 @@ public ViewHolder(FrameLayout container) {
}
@Override
- public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
+ public ViewHolder onCreateRealmViewHolder(ViewGroup viewGroup, int viewType) {
View v = inflater.inflate(R.layout.item_view, viewGroup, false);
ViewHolder vh = new ViewHolder((FrameLayout) v);
return vh;
}
@Override
- public void onBindViewHolder(ViewHolder viewHolder, int i) {
- final QuoteModel quoteModel = realmResults.get(i);
+ public void onBindRealmViewHolder(ViewHolder viewHolder, int position) {
+ final QuoteModel quoteModel = realmResults.get(position);
viewHolder.quoteTextView.setOnClickListener(
new View.OnClickListener() {
@Override
@@ -156,6 +179,11 @@ public void onClick(View v) {
}
viewHolder.quoteTextView.setText(quoteModel.getQuote());
}
+
+ @Override
+ public ViewHolder convertViewHolder(RealmViewHolder viewHolder) {
+ return ViewHolder.class.cast(viewHolder);
+ }
}
private void asyncAddQuote() {
@@ -223,6 +251,36 @@ protected void onPostExecute(Void aVoid) {
remoteItem.execute();
}
+ private void asyncLoadMoreQuotes() {
+ AsyncTask remoteItem = new AsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ // Add some delay to the refresh/remove action.
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ Realm instance = Realm.getInstance(MainActivity.this);
+ instance.beginTransaction();
+ for (int i = 0; i < 60; i++) {
+ QuoteModel quoteModel = instance.createObject(QuoteModel.class);
+ quoteModel.setId(i + 100); // That is to offset for primary key
+ quoteModel.setQuote(quotes.get((int) (quoteModel.getId() % quotes.size())));
+ }
+ instance.commitTransaction();
+ instance.close();
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void aVoid) {
+ super.onPostExecute(aVoid);
+ realmRecyclerView.disableShowLoadMore();
+ }
+ };
+ remoteItem.execute();
+ }
+
private void resetRealm() {
RealmConfiguration realmConfig = new RealmConfiguration
.Builder(this)
diff --git a/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/MainActivity2.java b/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/MainActivity2.java
new file mode 100644
index 0000000..8991929
--- /dev/null
+++ b/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/MainActivity2.java
@@ -0,0 +1,174 @@
+package co.moonmonkeylabs.realmrecyclerview.example;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import co.moonmonkeylabs.realmrecyclerview.RealmRecyclerView;
+import co.moonmonkeylabs.realmrecyclerview.example.models.CountryModel;
+import io.realm.Realm;
+import io.realm.RealmBasedRecyclerViewAdapter;
+import io.realm.RealmConfiguration;
+import io.realm.RealmResults;
+import io.realm.RealmViewHolder;
+
+public class MainActivity2 extends AppCompatActivity {
+
+ private RealmRecyclerView realmRecyclerView;
+ private CountryRecyclerViewAdapter countryAdapter;
+ private Realm realm;
+ private String type;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main_linear_layout_headers);
+
+ type = getIntent().getStringExtra("Type");
+ setTitle(getResources().getString(R.string.activity_layout_name, type));
+
+ resetRealm();
+ realm = Realm.getInstance(this);
+
+ realmRecyclerView = (RealmRecyclerView) findViewById(R.id.realm_recycler_view);
+
+ // Init Realm with n country names
+ final String[] countryNames = getResources().getStringArray(R.array.country_names);
+ realm.beginTransaction();
+ for (int i = 0; i < countryNames.length; i++) {
+ CountryModel countryModel = new CountryModel(i, countryNames[i]);
+ realm.copyToRealm(countryModel);
+ }
+ realm.commitTransaction();
+
+ RealmResults countryModels =
+ realm.where(CountryModel.class).findAllSorted("name", true);
+ countryAdapter = new CountryRecyclerViewAdapter(getBaseContext(), countryModels);
+ realmRecyclerView.setAdapter(countryAdapter);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ realm.close();
+ realm = null;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_header_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+ if (id == R.id.add_b_tiem) {
+ asyncAddCountry("Belgium");
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ public class CountryRecyclerViewAdapter extends RealmBasedRecyclerViewAdapter<
+ CountryModel, CountryRecyclerViewAdapter.ViewHolder> {
+
+ public CountryRecyclerViewAdapter(
+ Context context,
+ RealmResults realmResults) {
+ super(context, realmResults, true, true, true, "name");
+ }
+
+ public class ViewHolder extends RealmViewHolder {
+
+ public FrameLayout container;
+
+ public TextView countryTextView;
+
+ public ViewHolder(FrameLayout container) {
+ super(container);
+ this.container = container;
+ this.countryTextView = (TextView) container.findViewById(R.id.quote_text_view);
+ }
+ }
+
+ @Override
+ public ViewHolder onCreateRealmViewHolder(ViewGroup viewGroup, int viewType) {
+ View v = inflater.inflate(R.layout.item_view, viewGroup, false);
+ return new ViewHolder((FrameLayout) v);
+ }
+
+ @Override
+ public void onBindRealmViewHolder(ViewHolder viewHolder, int position) {
+ final CountryModel quoteModel = realmResults.get(rowWrappers.get(position).realmIndex);
+ viewHolder.countryTextView.setText(quoteModel.getName());
+ viewHolder.countryTextView.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ asyncRemoveCountry(quoteModel.getId());
+ }
+ }
+ );
+ }
+
+ @Override
+ public ViewHolder convertViewHolder(RealmViewHolder viewHolder) {
+ return ViewHolder.class.cast(viewHolder);
+ }
+ }
+
+ private void resetRealm() {
+ RealmConfiguration realmConfig = new RealmConfiguration
+ .Builder(this)
+ .deleteRealmIfMigrationNeeded()
+ .build();
+ Realm.deleteRealm(realmConfig);
+ }
+
+ private void asyncRemoveCountry(final long id) {
+ AsyncTask remoteItem = new AsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ Realm instance = Realm.getInstance(MainActivity2.this);
+ CountryModel countryModel =
+ instance.where(CountryModel.class).equalTo("id", id).findFirst();
+ if (countryModel != null) {
+ instance.beginTransaction();
+ countryModel.removeFromRealm();
+ instance.commitTransaction();
+ }
+ instance.close();
+ return null;
+ }
+ };
+ remoteItem.execute();
+ }
+
+ private void asyncAddCountry(final String name) {
+ AsyncTask remoteItem = new AsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ Realm instance = Realm.getInstance(MainActivity2.this);
+ instance.beginTransaction();
+ CountryModel countryModel =
+ instance.where(CountryModel.class).equalTo("name", name).findFirst();
+ if (countryModel == null) {
+ CountryModel newCountryModel = new CountryModel(1000, name);
+ instance.copyToRealm(newCountryModel);
+ instance.commitTransaction();
+ }
+ instance.close();
+ return null;
+ }
+ };
+ remoteItem.execute();
+ }
+}
diff --git a/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/models/CountryModel.java b/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/models/CountryModel.java
new file mode 100644
index 0000000..a350677
--- /dev/null
+++ b/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/models/CountryModel.java
@@ -0,0 +1,38 @@
+package co.moonmonkeylabs.realmrecyclerview.example.models;
+
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+
+/**
+ * The name model contains a name and a unique id.
+ */
+public class CountryModel extends RealmObject {
+
+ @PrimaryKey
+ private long id;
+ private String name;
+
+ public CountryModel() {
+ }
+
+ public CountryModel(long id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/models/QuoteModel.java b/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/models/QuoteModel.java
index fa1bda1..ef611b9 100644
--- a/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/models/QuoteModel.java
+++ b/example/app/src/main/java/co/moonmonkeylabs/realmrecyclerview/example/models/QuoteModel.java
@@ -1,18 +1,3 @@
-/*
- * Copyright 2014 Realm Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
package co.moonmonkeylabs.realmrecyclerview.example.models;
import io.realm.RealmObject;
diff --git a/example/app/src/main/res/layout/activity_layout_selector.xml b/example/app/src/main/res/layout/activity_layout_selector.xml
index 93adfcf..e14ba36 100644
--- a/example/app/src/main/res/layout/activity_layout_selector.xml
+++ b/example/app/src/main/res/layout/activity_layout_selector.xml
@@ -11,10 +11,24 @@
android:layout_margin="20dp"
android:text="@string/linear_demo"/>
+
+
+
+
diff --git a/example/app/src/main/res/layout/activity_main.xml b/example/app/src/main/res/layout/activity_main.xml
index 5db879b..3ef358d 100644
--- a/example/app/src/main/res/layout/activity_main.xml
+++ b/example/app/src/main/res/layout/activity_main.xml
@@ -9,5 +9,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rrvIsRefreshable="true"
- app:rrvEmptyLayoutId="@layout/empty_view"/>
+ app:rrvEmptyLayoutId="@layout/empty_view"
+
+ />
diff --git a/example/app/src/main/res/layout/activity_main_grid_layout.xml b/example/app/src/main/res/layout/activity_main_grid_layout.xml
new file mode 100644
index 0000000..9f575a9
--- /dev/null
+++ b/example/app/src/main/res/layout/activity_main_grid_layout.xml
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/example/app/src/main/res/layout/activity_main_linear_layout.xml b/example/app/src/main/res/layout/activity_main_linear_layout.xml
new file mode 100644
index 0000000..56a1a6b
--- /dev/null
+++ b/example/app/src/main/res/layout/activity_main_linear_layout.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/example/app/src/main/res/layout/activity_main_linear_layout_headers.xml b/example/app/src/main/res/layout/activity_main_linear_layout_headers.xml
new file mode 100644
index 0000000..e54ca45
--- /dev/null
+++ b/example/app/src/main/res/layout/activity_main_linear_layout_headers.xml
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/example/app/src/main/res/menu/menu_header_main.xml b/example/app/src/main/res/menu/menu_header_main.xml
new file mode 100644
index 0000000..c439cbd
--- /dev/null
+++ b/example/app/src/main/res/menu/menu_header_main.xml
@@ -0,0 +1,11 @@
+
diff --git a/example/app/src/main/res/values/arrays.xml b/example/app/src/main/res/values/arrays.xml
new file mode 100644
index 0000000..98256b9
--- /dev/null
+++ b/example/app/src/main/res/values/arrays.xml
@@ -0,0 +1,209 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Australia
+ - Austria
+ - Azerbaijan
+ - Bahamas
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Burma
+ - Burundi
+ - Cambodia
+ - Cameroon
+ - Canada
+ - Cape Verde
+ - Central African Republic
+ - Chad
+ - Chile
+ - China
+ - Colombia
+ - Comoros
+ - Congo, Democratic Republic of the
+ - Congo, Republic of the
+ - Costa Rica
+ - Cote d\'voire
+ - Croatia
+ - Cuba
+ - Cyprus
+ - Czech Republic
+ - Denmark
+ - Djibouti
+ - Dominica
+ - Dominican Republic
+ - Ecuador
+ - Egypt
+ - El Salvador
+ - Equatorial Guinea
+ - Eritrea
+ - Estonia
+ - Ethiopia
+ - Fiji
+ - Finland
+ - France
+ - Gabon
+ - Gambia, The
+ - Georgia
+ - Ghana
+ - Greece
+ - Grenada
+ - Guatemala
+ - Guinea
+ - Guinea-Bissau
+ - Guyana
+ - Haiti
+ - Holy See
+ - Honduras
+ - Hong Kong
+ - Hungary
+ - Iceland
+ - India
+ - Indonesia
+ - Iran
+ - Iraq
+ - Ireland
+ - Israel
+ - Italy
+ - Jamaica
+ - Japan
+ - Jordan
+ - Kazakhstan
+ - Kenya
+ - Kiribati
+ - Korea, North
+ - Korea, South
+ - Kosovo
+ - Kuwait
+ - Kyrgyzstan
+ - Laos
+ - Latvia
+ - Lebanon
+ - Lesotho
+ - Liberia
+ - Libya
+ - Liechtenstein
+ - Lithuania
+ - Luxembourg
+ - Macau
+ - Macedonia
+ - Madagascar
+ - Malawi
+ - Malaysia
+ - Maldives
+ - Mali
+ - Malta
+ - Marshall Islands
+ - Mauritania
+ - Mauritius
+ - Mexico
+ - Micronesia
+ - Moldova
+ - Monaco
+ - Mongolia
+ - Montenegro
+ - Morocco
+ - Mozambique
+ - Namibia
+ - Nauru
+ - Nepal
+ - Netherlands
+ - Netherlands Antilles
+ - New Zealand
+ - Nicaragua
+ - Niger
+ - Nigeria
+ - North Korea
+ - Norway
+ - Oman
+ - Pakistan
+ - Palau
+ - Palestinian Territories
+ - Panama
+ - Papua New Guinea
+ - Paraguay
+ - Peru
+ - Philippines
+ - Poland
+ - Portugal
+ - Qatar
+ - Romania
+ - Russia
+ - Rwanda
+ - Saint Kitts and Nevis
+ - Saint Lucia
+ - Saint Vincent and the Grenadines
+ - Samoa
+ - San Marino
+ - Sao Tome and Principe
+ - Saudi Arabia
+ - Senegal
+ - Serbia
+ - Seychelles
+ - Sierra Leone
+ - Singapore
+ - Slovakia
+ - Slovenia
+ - Solomon Islands
+ - Somalia
+ - South Africa
+ - South Korea
+ - South Sudan
+ - Spain
+ - Sri Lanka
+ - Sudan
+ - Suriname
+ - Swaziland
+ - Sweden
+ - Switzerland
+ - Syria
+ - Taiwan
+ - Tajikistan
+ - Tanzania
+ - Thailand
+ - Timor-Leste
+ - Togo
+ - Tonga
+ - Trinidad and Tobago
+ - Tunisia
+ - Turkey
+ - Turkmenistan
+ - Tuvalu
+ - Uganda
+ - Ukraine
+ - United Arab Emirates
+ - United Kingdom
+ - Uruguay
+ - Uzbekistan
+ - Vanuatu
+ - Venezuela
+ - Vietnam
+ - Yemen
+ - Zambia
+ - Zimbabwe
+
+
+
+
\ No newline at end of file
diff --git a/example/app/src/main/res/values/dimens.xml b/example/app/src/main/res/values/dimens.xml
index 47c8224..a31af36 100644
--- a/example/app/src/main/res/values/dimens.xml
+++ b/example/app/src/main/res/values/dimens.xml
@@ -2,4 +2,6 @@
16dp
16dp
+
+ 96dp
diff --git a/example/app/src/main/res/values/strings.xml b/example/app/src/main/res/values/strings.xml
index 3219c37..1ae0c5b 100644
--- a/example/app/src/main/res/values/strings.xml
+++ b/example/app/src/main/res/values/strings.xml
@@ -4,5 +4,7 @@
%s Example
Linear Layout Example
+ Linear Layout With Load More Example
Grid Layout Example
+ Section Header Layout Example
diff --git a/example/build.gradle b/example/build.gradle
index be515a8..52742fb 100644
--- a/example/build.gradle
+++ b/example/build.gradle
@@ -3,9 +3,14 @@
buildscript {
repositories {
jcenter()
+ maven {
+ url 'https://repos.zeroturnaround.com/nexus/content/repositories/zt-public-releases'
+ }
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
+ // This does not break the build when Android Studio is missing the JRebel for Android plugin.
+ classpath 'com.zeroturnaround.jrebel.android:jr-android-gradle:0.9.+'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -15,6 +20,7 @@ buildscript {
allprojects {
repositories {
jcenter()
+ maven { url "https://jitpack.io" }
}
}
diff --git a/library/build.gradle b/library/build.gradle
index e1628ac..394a30c 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -23,4 +23,6 @@ dependencies {
compile 'com.android.support:recyclerview-v7:22.0.+'
compile 'io.realm:realm-android:0.82.+'
+
+ compile 'com.github.TonicArtos:SuperSLiM:ed0ba4b4d2'
}
diff --git a/library/src/main/java/co/moonmonkeylabs/realmrecyclerview/LoadMoreListItemView.java b/library/src/main/java/co/moonmonkeylabs/realmrecyclerview/LoadMoreListItemView.java
new file mode 100755
index 0000000..eb5e4e7
--- /dev/null
+++ b/library/src/main/java/co/moonmonkeylabs/realmrecyclerview/LoadMoreListItemView.java
@@ -0,0 +1,51 @@
+package co.moonmonkeylabs.realmrecyclerview;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.FrameLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+public class LoadMoreListItemView extends FrameLayout {
+
+ TextView loadingText;
+
+ ProgressBar spinner;
+
+ public LoadMoreListItemView(Context context) {
+ super(context);
+ inflate(context, R.layout.load_more_item_view, this);
+
+ loadingText = (TextView) findViewById(R.id.load_more_view_text);
+ spinner = (ProgressBar) findViewById(R.id.load_more_spinner);
+
+ setLayoutParams(new RealmRecyclerView.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ getResources().getDimensionPixelSize(R.dimen.load_more_item_height)));
+ }
+
+ public void hideLoadingText() {
+ loadingText.setVisibility(View.GONE);
+ }
+
+ public void setLoadingText(int textResourceId) {
+ loadingText.setText(textResourceId);
+ loadingText.setVisibility(View.VISIBLE);
+ }
+
+ public void setLoadingText(String text) {
+ loadingText.setText(text);
+ loadingText.setVisibility(View.VISIBLE);
+ }
+
+ public void hideSpinner() {
+ spinner.setVisibility(View.GONE);
+ }
+
+ public void showSpinner() {
+ spinner.setVisibility(View.VISIBLE);
+ }
+}
diff --git a/library/src/main/java/co/moonmonkeylabs/realmrecyclerview/RealmRecyclerView.java b/library/src/main/java/co/moonmonkeylabs/realmrecyclerview/RealmRecyclerView.java
index 689808a..ff732ce 100644
--- a/library/src/main/java/co/moonmonkeylabs/realmrecyclerview/RealmRecyclerView.java
+++ b/library/src/main/java/co/moonmonkeylabs/realmrecyclerview/RealmRecyclerView.java
@@ -3,16 +3,24 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewStub;
import android.widget.FrameLayout;
+import android.widget.Toast;
+
+import com.tonicartos.superslim.LayoutManager;
+
+import io.realm.RealmBasedRecyclerViewAdapter;
/**
* A recyclerView that has a few extra features.
* - Automatic empty state
* - Pull-to-refresh
+ * - LoadMore
*/
public class RealmRecyclerView extends FrameLayout {
@@ -20,20 +28,35 @@ public interface OnRefreshListener {
void onRefresh();
}
+ public interface OnLoadMoreListener {
+ void onLoadMore(Object lastItem);
+ }
+
+ private enum Type {
+ LinearLayout,
+ Grid,
+ LinearLayoutWithHeaders
+ }
+
private SwipeRefreshLayout swipeRefreshLayout;
private RecyclerView recyclerView;
-
private ViewStub emptyContentContainer;
+ private RealmBasedRecyclerViewAdapter adapter;
+ private boolean hasLoadMoreFired;
+ private boolean showShowLoadMore;
// Attributes
private boolean isRefreshable;
private int emptyViewId;
+ private Type type;
+ private int gridSpanCount;
// State
private boolean isRefreshing;
// Listener
private OnRefreshListener onRefreshListener;
+ private OnLoadMoreListener onLoadMoreListener;
public RealmRecyclerView(Context context) {
super(context);
@@ -58,8 +81,8 @@ private void init(Context context, AttributeSet attrs) {
recyclerView = (RecyclerView) findViewById(R.id.rrv_recycler_view);
emptyContentContainer = (ViewStub) findViewById(R.id.rrv_empty_content_container);
+ swipeRefreshLayout.setEnabled(isRefreshable);
if (isRefreshable) {
- swipeRefreshLayout.setEnabled(isRefreshable);
swipeRefreshLayout.setOnRefreshListener(recyclerViewRefreshListener);
}
@@ -68,22 +91,125 @@ private void init(Context context, AttributeSet attrs) {
emptyContentContainer.inflate();
}
+ if (type == null) {
+ throw new IllegalStateException("A type has to be specified via XML attribute");
+ }
+ switch (type) {
+ case LinearLayout:
+ recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+ break;
+
+ case Grid:
+ if (gridSpanCount == -1) {
+ throw new IllegalStateException("For GridLayout, a span count has to be set");
+ }
+ recyclerView.setLayoutManager(new GridLayoutManager(getContext(), gridSpanCount));
+ break;
+
+ case LinearLayoutWithHeaders:
+ recyclerView.setLayoutManager(new LayoutManager(getContext()));
+ break;
+ }
recyclerView.setHasFixedSize(true);
+
+ recyclerView.addOnScrollListener(
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ super.onScrolled(recyclerView, dx, dy);
+ }
+ }
+ );
+
+ recyclerView.addOnScrollListener(
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ }
+
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ maybeFireLoadMore();
+ }
+ }
+ );
+ }
+
+ public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
+ this.onLoadMoreListener = onLoadMoreListener;
+ }
+
+ public void enableShowLoadMore() {
+ showShowLoadMore = true;
+ ((RealmBasedRecyclerViewAdapter) recyclerView.getAdapter()).addLoadMore();
+ }
+
+ public void disableShowLoadMore() {
+ showShowLoadMore = false;
+ ((RealmBasedRecyclerViewAdapter) recyclerView.getAdapter()).removeLoadMore();
+ }
+
+ private void maybeFireLoadMore() {
+ if (hasLoadMoreFired) {
+ return;
+ }
+ if (!showShowLoadMore) {
+ return;
+ }
+
+ final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
+ int visibleItemCount = layoutManager.getChildCount();
+ int totalItemCount = layoutManager.getItemCount();
+ int firstVisibleItemPosition = findFirstVisibleItemPosition();
+
+ if (totalItemCount == 0) {
+ return;
+ }
+
+ if (firstVisibleItemPosition + visibleItemCount + 3 > totalItemCount) {
+ if (onLoadMoreListener != null) {
+ hasLoadMoreFired = true;
+ onLoadMoreListener.onLoadMore(adapter.getLastItem());
+ }
+ }
+ }
+
+ public int findFirstVisibleItemPosition() {
+ switch (type) {
+ case LinearLayout:
+ return ((LinearLayoutManager) recyclerView.getLayoutManager())
+ .findFirstVisibleItemPosition();
+ case Grid:
+ return ((GridLayoutManager) recyclerView.getLayoutManager())
+ .findFirstVisibleItemPosition();
+ case LinearLayoutWithHeaders:
+ return ((LayoutManager) recyclerView.getLayoutManager())
+ .findFirstVisibleItemPosition();
+ default:
+ throw new IllegalStateException("Type of layoutManager unknown." +
+ "In this case this method needs to be overridden");
+ }
}
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray typedArray =
context.obtainStyledAttributes(attrs, R.styleable.RealmRecyclerView);
- if (typedArray != null) {
- isRefreshable =
- typedArray.getBoolean(R.styleable.RealmRecyclerView_rrvIsRefreshable, false);
- emptyViewId =
- typedArray.getResourceId(R.styleable.RealmRecyclerView_rrvEmptyLayoutId, 0);
+ isRefreshable =
+ typedArray.getBoolean(R.styleable.RealmRecyclerView_rrvIsRefreshable, false);
+ emptyViewId =
+ typedArray.getResourceId(R.styleable.RealmRecyclerView_rrvEmptyLayoutId, 0);
+ int typeValue = typedArray.getInt(R.styleable.RealmRecyclerView_rrvLayoutType, -1);
+ if (typeValue != -1) {
+ type = Type.values()[typeValue];
}
+ gridSpanCount = typedArray.getInt(R.styleable.RealmRecyclerView_rrvGridLayoutSpanCount, -1);
+ typedArray.recycle();
}
- public void setAdapter(final RecyclerView.Adapter adapter) {
+ public void setAdapter(final RealmBasedRecyclerViewAdapter adapter) {
+ this.adapter = adapter;
recyclerView.setAdapter(adapter);
if (adapter != null) {
@@ -136,15 +262,17 @@ private void updateEmptyContentContainerVisibility(RecyclerView.Adapter adapter)
adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
}
- public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {
- recyclerView.setLayoutManager(layoutManager);
- }
-
-
//
// Pull-to-refresh
//
+ /**
+ * Only if custom is set for the manager, this method should be used to set the manager.
+ */
+ public void setLayoutManager(RecyclerView.LayoutManager layoutManger) {
+ recyclerView.setLayoutManager(layoutManger);
+ }
+
public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
this.onRefreshListener = onRefreshListener;
}
diff --git a/library/src/main/java/io/realm/RealmBasedRecyclerViewAdapter.java b/library/src/main/java/io/realm/RealmBasedRecyclerViewAdapter.java
index d13b9a2..d0e56ed 100644
--- a/library/src/main/java/io/realm/RealmBasedRecyclerViewAdapter.java
+++ b/library/src/main/java/io/realm/RealmBasedRecyclerViewAdapter.java
@@ -19,44 +19,111 @@
import android.content.Context;
import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.tonicartos.superslim.GridSLM;
+import com.tonicartos.superslim.LinearSLM;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
+import co.moonmonkeylabs.realmrecyclerview.LoadMoreListItemView;
+import co.moonmonkeylabs.realmrecyclerview.R;
import difflib.Delta;
import difflib.DiffUtils;
import difflib.Patch;
import io.realm.internal.ColumnType;
import io.realm.internal.TableOrView;
-public abstract class RealmBasedRecyclerViewAdapter extends RecyclerView.Adapter {
+public abstract class RealmBasedRecyclerViewAdapter
+
+ extends RecyclerView.Adapter {
+
+ public void addLoadMore() {
+ loadMoreItem = new Object();
+ notifyDataSetChanged();
+ }
+
+ public void removeLoadMore() {
+ loadMoreItem = null;
+ notifyDataSetChanged();
+ }
+
+ public class RowWrapper {
+
+ public final boolean isRealm;
+ public final int realmIndex;
+ public final int sectionHeaderIndex;
+ public final String header;
+
+ public RowWrapper(int realmIndex, int sectionHeaderIndex) {
+ this(true, realmIndex, sectionHeaderIndex, null);
+ }
+
+ public RowWrapper(int sectionHeaderIndex, String header) {
+ this(false, -1, sectionHeaderIndex, header);
+ }
+
+ public RowWrapper(boolean isRealm, int realmIndex, int sectionHeaderIndex, String header) {
+ this.isRealm = isRealm;
+ this.realmIndex = realmIndex;
+ this.sectionHeaderIndex = sectionHeaderIndex;
+ this.header = header;
+ }
+ }
private static final List EMPTY_LIST = new ArrayList<>(0);
+ private Object loadMoreItem;
+
+ protected final int HEADER_VIEW_TYPE = 100;
+ private final int LOAD_MORE_VIEW_TYPE = 101;
+
protected LayoutInflater inflater;
protected RealmResults realmResults;
protected List ids;
+ protected List rowWrappers;
+
private RealmChangeListener listener;
private boolean animateResults;
private long animateColumnIndex;
private ColumnType animateIdType;
+ private boolean addSectionHeaders;
+ private String headerColumnName;
+
+ public RealmBasedRecyclerViewAdapter(
+ Context context,
+ RealmResults realmResults,
+ boolean automaticUpdate,
+ boolean animateResults) {
+ this(context, realmResults, automaticUpdate, animateResults, false, null);
+ }
public RealmBasedRecyclerViewAdapter(
Context context,
RealmResults realmResults,
boolean automaticUpdate,
- boolean animateResults) {
+ boolean animateResults,
+ boolean addSectionHeaders,
+ String headerColumnName) {
if (context == null) {
throw new IllegalArgumentException("Context cannot be null");
}
this.animateResults = animateResults;
+ this.addSectionHeaders = addSectionHeaders;
+ this.headerColumnName = headerColumnName;
this.inflater = LayoutInflater.from(context);
this.listener = (!automaticUpdate) ? null : getRealmChangeListener();
+ rowWrappers = new ArrayList<>();
+
// If automatic updates aren't enabled, then animateResults should be false as well.
this.animateResults = (automaticUpdate && animateResults);
if (animateResults) {
@@ -73,33 +140,97 @@ public RealmBasedRecyclerViewAdapter(
animateIdType = columnType;
}
+ if (addSectionHeaders && headerColumnName == null) {
+ throw new IllegalStateException(
+ "A headerColumnName is required for section headers");
+ }
+
updateRealmResults(realmResults);
}
- private List getIdsOfRealmResults() {
- if (!animateResults || realmResults.size() == 0) {
- return EMPTY_LIST;
+ public abstract VH onCreateRealmViewHolder(ViewGroup viewGroup, int viewType);
+
+ public abstract void onBindRealmViewHolder(VH holder, int position);
+
+ public abstract VH convertViewHolder(RealmViewHolder viewHolder);
+
+ public RealmViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+ if (viewType == HEADER_VIEW_TYPE) {
+ View view = inflater.inflate(R.layout.header_item, viewGroup, false);
+ return new RealmViewHolder((TextView) view);
+ } else if (viewType == LOAD_MORE_VIEW_TYPE) {
+ return new RealmViewHolder(new LoadMoreListItemView(viewGroup.getContext()));
}
+ return onCreateRealmViewHolder(viewGroup, viewType);
+ }
- List ids = new ArrayList(realmResults.size());
- for (int i = 0; i < realmResults.size(); i++) {
- if (animateIdType == ColumnType.INTEGER) {
- ids.add(realmResults.get(i).row.getLong(animateColumnIndex));
- } else if (animateIdType == ColumnType.STRING) {
- ids.add(realmResults.get(i).row.getString(animateColumnIndex));
+ @Override
+ public void onBindViewHolder(RealmViewHolder holder, int position) {
+ if (getItemViewType(position) == LOAD_MORE_VIEW_TYPE) {
+ holder.loadMoreView.showSpinner();
+ } else {
+ if (addSectionHeaders) {
+ final String header = (String) rowWrappers.get(position).header;
+ final GridSLM.LayoutParams layoutParams =
+ GridSLM.LayoutParams.from(holder.itemView.getLayoutParams());
+ // Setup the header
+ if (header != null) {
+ holder.headerTextView.setText(header);
+ if (layoutParams.isHeaderInline()) {
+ layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ } else {
+ layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+ layoutParams.isHeader = true;
+ } else {
+ onBindRealmViewHolder(convertViewHolder(holder), position);
+ }
+ layoutParams.setSlm(LinearSLM.ID);
+ if (header != null) {
+ layoutParams.setFirstPosition(position);
+ } else {
+ layoutParams.setFirstPosition(rowWrappers.get(position).sectionHeaderIndex);
+ }
+ holder.itemView.setLayoutParams(layoutParams);
} else {
- throw new IllegalStateException("Unknown animatedIdType");
+ onBindRealmViewHolder(convertViewHolder(holder), position);
}
}
- return ids;
+ }
+
+ public Object getLastItem() {
+ if (addSectionHeaders) {
+ return realmResults.get(rowWrappers.get(rowWrappers.size() - 1).realmIndex);
+ } else {
+ return realmResults.get(realmResults.size() - 1);
+ }
}
@Override
public int getItemCount() {
+ int loadMoreCount = loadMoreItem == null ? 0 : 1;
+ if (addSectionHeaders) {
+ return rowWrappers.size() + loadMoreCount;
+ }
+
if (realmResults == null) {
return 0;
}
- return realmResults.size();
+ return realmResults.size() + loadMoreCount;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (loadMoreItem != null && position == getItemCount() - 1) {
+ return LOAD_MORE_VIEW_TYPE;
+ } else if (!rowWrappers.isEmpty() && !rowWrappers.get(position).isRealm) {
+ return HEADER_VIEW_TYPE;
+ }
+ return getItemRealmViewType(position);
+ }
+
+ public int getItemRealmViewType(int position) {
+ return super.getItemViewType(position);
}
/**
@@ -119,15 +250,86 @@ public void updateRealmResults(RealmResults queryResults) {
}
this.realmResults = queryResults;
+
+ updateRowWrappers();
ids = getIdsOfRealmResults();
+
notifyDataSetChanged();
}
+ /**
+ * Method that create the header string that should be used. Override this method to have
+ * a custom header.
+ */
+ public String createHeaderFromColumnValue(String columnValue) {
+ return columnValue.substring(0, 1);
+ }
+
+ private List getIdsOfRealmResults() {
+ if (!animateResults || realmResults.size() == 0) {
+ return EMPTY_LIST;
+ }
+
+ if (addSectionHeaders) {
+ List ids = new ArrayList(rowWrappers.size());
+ for (int i = 0; i < rowWrappers.size(); i++) {
+ final RowWrapper rowWrapper = rowWrappers.get(i);
+ if (rowWrapper.isRealm) {
+ ids.add(getRealmRowId(rowWrappers.get(i).realmIndex));
+ } else {
+ ids.add(rowWrappers.get(i).header);
+ }
+ }
+ return ids;
+ } else {
+ List ids = new ArrayList(realmResults.size());
+ for (int i = 0; i < realmResults.size(); i++) {
+ ids.add(getRealmRowId(i));
+ }
+ return ids;
+ }
+ }
+
+ private Object getRealmRowId(int realmIndex) {
+ if (animateIdType == ColumnType.INTEGER) {
+ return realmResults.get(realmIndex).row.getLong(animateColumnIndex);
+ } else if (animateIdType == ColumnType.STRING) {
+ return realmResults.get(realmIndex).row.getString(animateColumnIndex);
+ } else {
+ throw new IllegalStateException("Unknown animatedIdType");
+ }
+ }
+
+ private void updateRowWrappers() {
+ if (addSectionHeaders) {
+ String lastHeader = "";
+ int headerCount = 0;
+ int sectionFirstPosition = 0;
+ rowWrappers.clear();
+
+ final long headerIndex = realmResults.getTable().getColumnIndex(headerColumnName);
+ int i = 0;
+ for (RealmObject result : realmResults) {
+ String header = createHeaderFromColumnValue(result.row.getString(headerIndex));
+ if (!TextUtils.equals(lastHeader, header)) {
+ // Insert new header view and update section data.
+ sectionFirstPosition = i + headerCount;
+ lastHeader = header;
+ headerCount += 1;
+
+ rowWrappers.add(new RowWrapper(sectionFirstPosition, header));
+ }
+ rowWrappers.add(new RowWrapper(i++, sectionFirstPosition));
+ }
+ }
+ }
+
private RealmChangeListener getRealmChangeListener() {
return new RealmChangeListener() {
@Override
public void onChange() {
if (animateResults && ids != null && !ids.isEmpty()) {
+ updateRowWrappers();
List newIds = getIdsOfRealmResults();
// If the list is now empty, just notify the recyclerView of the change.
if (newIds.isEmpty()) {
@@ -136,22 +338,50 @@ public void onChange() {
return;
}
Patch patch = DiffUtils.diff(ids, newIds);
- List deltas = patch.getDeltas();
+ List deltas = patch.getDeltas();
ids = newIds;
if (deltas.isEmpty()) {
// Nothing has changed - most likely because the notification was for
// a different object/table
- } else if (deltas.size() > 5) {
+ } else if (deltas.size() > 1) {
notifyDataSetChanged();
} else {
- for (Delta delta: deltas) {
- if (delta.getType() == Delta.TYPE.INSERT) {
+ Delta delta = deltas.get(0);
+ if (delta.getType() == Delta.TYPE.INSERT) {
+ if (delta.getRevised().size() == 1) {
notifyItemInserted(delta.getRevised().getPosition());
- } else if (delta.getType() == Delta.TYPE.DELETE) {
+ } else {
+ notifyDataSetChanged();
+ }
+ } else if (delta.getType() == Delta.TYPE.DELETE) {
+ if (delta.getOriginal().size() == 1) {
notifyItemRemoved(delta.getOriginal().getPosition());
} else {
- notifyItemChanged(delta.getRevised().getPosition());
+ // Note: The position zero check is to hack around a indexOutOfBound
+ // exception that happens when the zero position is animated out.
+ if (delta.getOriginal().getPosition() == 0) {
+ notifyDataSetChanged();
+ return;
+ } else {
+ notifyItemRangeRemoved(
+ delta.getOriginal().getPosition(),
+ delta.getOriginal().size());
+ }
+ }
+
+ if (delta.getOriginal().getPosition() - 1 > 0) {
+ notifyItemRangeChanged(
+ 0,
+ delta.getOriginal().getPosition() - 1);
+ }
+ if (delta.getOriginal().getPosition() > 0 &&
+ newIds.size() > 0) {
+ notifyItemRangeChanged(
+ delta.getOriginal().getPosition(),
+ newIds.size() - 1);
}
+ } else {
+ notifyDataSetChanged();
}
}
} else {
diff --git a/library/src/main/java/io/realm/RealmViewHolder.java b/library/src/main/java/io/realm/RealmViewHolder.java
new file mode 100644
index 0000000..0b4b4e8
--- /dev/null
+++ b/library/src/main/java/io/realm/RealmViewHolder.java
@@ -0,0 +1,31 @@
+package io.realm;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.TextView;
+
+import co.moonmonkeylabs.realmrecyclerview.LoadMoreListItemView;
+
+/**
+ * ViewHolder used with {@link RealmBasedRecyclerViewAdapter}
+ */
+public class RealmViewHolder extends RecyclerView.ViewHolder {
+
+ public TextView headerTextView;
+
+ public LoadMoreListItemView loadMoreView;
+
+ public RealmViewHolder(View itemView) {
+ super(itemView);
+ }
+
+ public RealmViewHolder(TextView headerTextView) {
+ super(headerTextView);
+ this.headerTextView = headerTextView;
+ }
+
+ public RealmViewHolder(LoadMoreListItemView loadMoreView) {
+ super(loadMoreView);
+ this.loadMoreView = loadMoreView;
+ }
+}
\ No newline at end of file
diff --git a/library/src/main/res/layout/header_item.xml b/library/src/main/res/layout/header_item.xml
new file mode 100644
index 0000000..525b2d7
--- /dev/null
+++ b/library/src/main/res/layout/header_item.xml
@@ -0,0 +1,18 @@
+
+
diff --git a/library/src/main/res/layout/load_more_item_view.xml b/library/src/main/res/layout/load_more_item_view.xml
new file mode 100755
index 0000000..ffc5815
--- /dev/null
+++ b/library/src/main/res/layout/load_more_item_view.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml
index c644217..fa19ff5 100644
--- a/library/src/main/res/values/attrs.xml
+++ b/library/src/main/res/values/attrs.xml
@@ -3,5 +3,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/library/src/main/res/values/dimen.xml b/library/src/main/res/values/dimen.xml
new file mode 100644
index 0000000..b4f4bec
--- /dev/null
+++ b/library/src/main/res/values/dimen.xml
@@ -0,0 +1,4 @@
+
+
+ 50dp
+
\ No newline at end of file